// Check authentication type. static int is_auth_httprequest_required(request_rec* rec) { const apr_array_header_t* requires = ap_requires(rec); struct require_line* rl = (struct require_line*)requires->elts; int it; AP_LOG_DEBUG(rec, " Core::AuthType=%s", ap_auth_type(rec)); AP_LOG_DEBUG(rec, " Core::AuthName=%s", ap_auth_name(rec)); if(strcasecmp(ap_auth_type(rec), HR_AUTH)!=0) return FALSE; // Type is not match. for(it=0; it<requires->nelts; it++) { AP_LOG_DEBUG(rec, " Core::Requires[%d]=%s", it, rl[it].requirement); if(strcasecmp(rl[it].requirement, "valid-request")==0) return TRUE; // Found. } return FALSE; }
// Multicast packet. static apr_status_t multicast_packet(request_rec* rec, multicast_conf* conf, const char* _msg) { int r, s = socket(AF_INET, SOCK_DGRAM, 0); char* msg = unescape_specialchars(rec->pool, _msg); AP_LOG_DEBUG(rec, "unescaped string: %s", msg); r = setsockopt(s, SOL_IP, IP_MULTICAST_IF, conf->multicast_interface.addr, conf->multicast_interface.len); if(r!=0) { r = errno; AP_LOG_ERR(rec, "setsockopt() failed - %s(%d)", strerror(r), r); goto FINALLY; } r = connect(s, conf->multicast_address.addr, conf->multicast_address.len); if(r!=0) { r = errno; AP_LOG_ERR(rec, "connect() failed - %s(%d)", strerror(r), r); goto FINALLY; } r = send(s, msg, strlen(msg), 0); r = (r>=0)? APR_SUCCESS: errno; FINALLY: if(s>=0) { shutdown(s, SHUT_RDWR); close(s); } return r; }
// apr_table_do() callback proc: Copy headers from apache-request to curl-request. static int each_headers_proc(void* rec, const char* key, const char* value) { if(!is_bypass_header(key)) { context* c = (context*)rec; char* h = apr_psprintf(c->rec->pool, "%s: %s", key, value); AP_LOG_DEBUG(c->rec, "++ %s", h); c->headers = curl_slist_append(c->headers, h); } return TRUE; }
// CURLOPT_HEADERFUNCTION callback proc: Copy headers from curl-response to apache-response. static size_t curl_header_proc(const void* _ptr, size_t size, size_t nmemb, void* _info) { context* c = (context*)_info; const char* ptr = (const char*)_ptr; if(strncmp(ptr, "HTTP/1.", sizeof("HTTP/1.") - 1)==0) { int mv, s; if(sscanf(ptr, "HTTP/1.%d %d ", &mv, &s)==2) c->status = s; return nmemb; } if(is_break_status(c->status)) { char* v = strchr(ptr, ':'); if(v && v[1] && (!is_bypass_header(ptr))) { char* h = apr_pstrdup(c->rec->pool, ptr); char* k, *v, *next; k = apr_strtok(h, ":", &next); v = apr_strtok(next, "\r\n", &next); AP_LOG_DEBUG(c->rec, "%s => %s", k, v); apr_table_set(c->rec->err_headers_out, k, v); } } return nmemb; }
// Break request. static int break_request_handler(request_rec *rec) { AP_LOG_DEBUG(rec, "Incomming %s", __FUNCTION__); if(!is_auth_httprequest_required(rec)) return DECLINED; // Not required. return is_break_status(rec->status)? rec->status: OK; }
// // Main functions. // static int check_auth_handler(request_rec *rec) { auth_conf* conf = (auth_conf*)ap_get_module_config(rec->per_dir_config, &auth_httprequest_module); context ctx; CURLcode ret; const char* secret, *url; int threaded_mpm; int code=0; AP_LOG_DEBUG(rec, "Incomming %s URI=%s", __FUNCTION__, conf->url); // Check requires. if(!is_auth_httprequest_required(rec)) return DECLINED; // Not required. if(!conf->url[0]) return OK; // URL is empty. AP_LOG_DEBUG(rec, " %s %s", rec->method, rec->uri); // Enable to dump authorize result. if(conf->dump==ENABLED) { ap_add_output_filter(DUMP_AUTH_RESULT, apr_pmemdup(rec->pool, &ctx, sizeof(ctx)), rec, rec->connection); } // Skip nested request. secret = apr_table_get(rec->headers_in, SECRET); if(secret) { AP_LOG_DEBUG(rec, " %s: %s", SECRET, secret); if(strstr(secret, conf->secret)) { AP_LOG_DEBUG(rec, "Check config. Nested request."); rec->user = apr_pstrdup(rec->pool, conf->url); return OK; } } // Initialize callback-context. ctx.curl = curl_easy_init(); ctx.rec = rec; ctx.headers = NULL; ctx.status = 0; // Initialize libcurl for MPM. ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm); curl_easy_setopt(ctx.curl, CURLOPT_NOSIGNAL, threaded_mpm); // Bypass request headers, set 'X-Auth-HttpRequest-URI'. apr_table_do(each_headers_proc, &ctx, rec->headers_in, NULL); each_headers_proc(&ctx, X_AUTH_HTTPREQUEST_URL, rec->uri); each_headers_proc(&ctx, X_AUTH_HTTPREQUEST_METHOD, rec->method); if(conf->secret[0]) each_headers_proc(&ctx, SECRET, conf->secret); // Setup URL to bypass response headers and body. curl_easy_setopt(ctx.curl, CURLOPT_URL, url = apr_psprintf(rec->pool, conf->url, rec->uri)); curl_easy_setopt(ctx.curl, CURLOPT_CUSTOMREQUEST, "HEAD"); curl_easy_setopt(ctx.curl, CURLOPT_HTTPHEADER, ctx.headers); curl_easy_setopt(ctx.curl, CURLOPT_USERAGENT, apr_psprintf(rec->pool, "%s %s", VERSION, curl_version())); curl_easy_setopt(ctx.curl, CURLOPT_WRITEHEADER, &ctx); curl_easy_setopt(ctx.curl, CURLOPT_HEADERFUNCTION, curl_header_proc); // Request. ret = curl_easy_perform(ctx.curl); curl_easy_getinfo(ctx.curl, CURLINFO_RESPONSE_CODE, &code); AP_LOG_DEBUG(rec, "curl result(%d) %d", ret, code); // Cleanup. curl_slist_free_all(ctx.headers); curl_easy_cleanup(ctx.curl); // Result. if(ret==0) { if(is_break_status(code)) { // Break request, set custom response. if(conf->errdoc) { char* msg = apr_psprintf(rec->pool, conf->errdoc, code); AP_LOG_DEBUG(rec, "Custom response: %s", msg); ap_custom_response(rec, code, msg); } return code; } if(is_authorized_status(code)) { // Set 'REMOTE_USER' and 'AUTH_TYPE'. rec->user = apr_pstrdup(rec->pool, conf->url); // => ENV['REMOTE_USER'] AP_LOG_DEBUG(rec, "== AUTHORIZED(%s, %s)", rec->ap_auth_type, rec->user); } else { AP_LOG_DEBUG(rec, "== PATH THRU"); } } else { static const char* const ce[8] = { "", "E_PROTOCOL", "E_INIT", "E_MALFORMAT", "E_MALFORMAT_USER", "E_PROXY", "E_HOST", "E_CONNECT", }; AP_LOG_WARN(rec, "Check config. http resuest failed [%s] %s(CURLcode = %d)", url, ((ret<8)? ce[ret]: ce[0]), ret); } return OK; }
// // Output filter. // static apr_status_t resize_output_filter(ap_filter_t* f, apr_bucket_brigade* in_bb) { request_rec* rec =f->r; resize_conf* conf = (resize_conf*)ap_get_module_config(rec->per_dir_config, &resizeimage_module); const char* content_type, *target_type = "JPEG"; const char* image_url, *resize_param, *image_hash=NULL; Magick::Blob blob; char* vlob = NULL; size_t vlob_length = 0; int cache_hit = FALSE; AP_LOG_VERBOSE(rec, "Incoming %s.", __FUNCTION__); // Pass thru by request types. if(rec->status!=HTTP_OK || rec->main!=NULL || rec->header_only || (rec->handler!= NULL && strcmp(rec->handler, "default-handler") == 0)) goto PASS_THRU; AP_LOG_VERBOSE(rec, "-- Checking responce headers."); // Obtain and erase x-resize-image header or pass through. image_url = get_and_unset_header(rec->headers_out, X_RESIZE); if(image_url== NULL || image_url[0]=='\0') { image_url = get_and_unset_header(rec->err_headers_out, X_RESIZE); } if(image_url==NULL || image_url[0]=='\0') goto PASS_THRU; // Check content-type content_type = rec->content_type; if(content_type) { if(strcasecmp(content_type, "image/jpeg")==0) { target_type = "JPEG"; } else if(strcasecmp(content_type, "image/png")==0) { target_type = "PNG"; } else if(strcasecmp(content_type, "image/gif")==0) { target_type = "GIF"; } else goto PASS_THRU; } // Resize parameter resize_param = get_and_unset_header(rec->headers_out, X_RESIZE_PARAM); if(resize_param==NULL || resize_param[0]=='\0') { resize_param = get_and_unset_header(rec->err_headers_out, X_RESIZE_PARAM); } if(resize_param[0]=='\0') resize_param = NULL; // Image hash image_hash = get_and_unset_header(rec->headers_out, X_RESIZE_HASH); if(image_hash==NULL || image_hash[0]=='\0') { image_hash = get_and_unset_header(rec->err_headers_out, X_RESIZE_HASH); } // Open image and resize. AP_LOG_INFO(rec, "URL: %s, %s => %s (%s)", image_url, content_type, resize_param, image_hash); if(image_hash) { // Try memcached... image_hash = apr_psprintf(rec->pool, "%s:%s:%s", image_hash, target_type, resize_param); memcached_return r; uint32_t flags; vlob = memcached_get(conf->memc, image_hash, strlen(image_hash), &vlob_length, &flags, &r); if(r==MEMCACHED_SUCCESS) { AP_LOG_DEBUG(rec, "Restored from memcached: %s, len=%d", image_hash, vlob_length); cache_hit = TRUE; goto WRITE_DATA; } else { AP_LOG_DEBUG(rec, "Can't restore from memcached: %s - %s(%d)", image_hash, memcached_strerror(conf->memc, r), r); } } // Reszize try { Magick::Image image; image.read(image_url); if(resize_param) image.zoom(resize_param); image.magick(target_type); image.quality(conf->jpeg_quality); image.write(&blob); vlob = (char*)blob.data(); vlob_length = blob.length(); } catch(Magick::Exception& err) { AP_LOG_ERR(rec, __FILE__ ": Magick failed: %s", err.what()); goto PASS_THRU; } if(image_hash) { // Store to memcached... memcached_return r = memcached_set(conf->memc, image_hash, strlen(image_hash), vlob, vlob_length, conf->expire, 0); if(r==MEMCACHED_SUCCESS) { AP_LOG_DEBUG(rec, "Stored to memcached: %s(len=%d)", image_hash, vlob_length); } else { AP_LOG_DEBUG(rec, "Can't store from memcached: %s(len=%d) - %s(%d)", image_hash, vlob_length,memcached_strerror(conf->memc, r), r); } } WRITE_DATA: AP_LOG_VERBOSE(rec, "-- Creating resize buckets."); // Drop all content and headers related. while(!APR_BRIGADE_EMPTY(in_bb)) { apr_bucket* b = APR_BRIGADE_FIRST(in_bb); apr_bucket_delete(b); } rec->eos_sent = 0; rec->clength = 0; unset_header(rec, "Content-Length"); unset_header(rec, "Content-Encoding"); unset_header(rec, "Last-Modified"); unset_header(rec, "ETag"); // Start resize bucket. { apr_off_t remain = vlob_length, offset = 0; while(remain>0) { apr_off_t bs = (remain<AP_MAX_SENDFILE)? remain: AP_MAX_SENDFILE; char* heap = (char*)malloc(bs); memcpy(heap, vlob+offset, bs); apr_bucket* b = apr_bucket_heap_create(heap, bs, free, in_bb-> bucket_alloc); APR_BRIGADE_INSERT_TAIL(in_bb, b); remain -= bs; offset += bs; } APR_BRIGADE_INSERT_TAIL(in_bb, apr_bucket_eos_create(in_bb->bucket_alloc)); ap_set_content_length(rec, vlob_length); if(cache_hit) free(vlob); } AP_LOG_VERBOSE(rec, "-- Create done."); PASS_THRU: AP_LOG_VERBOSE(rec, "-- Filter done."); ap_remove_output_filter(f); return ap_pass_brigade(f->next, in_bb); }