static nxweb_result curtime_on_request(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp) { nxweb_set_response_content_type(resp, "text/html"); resp->last_modified=nxe_get_current_http_time(conn->tdata->loop); const char* max_age_str=nxweb_get_request_parameter(req, "max_age"); int max_age=0; if (max_age_str) { max_age=atoi(max_age_str); resp->max_age=max_age; } nxweb_response_append_str(resp, nxe_get_current_http_time_str(conn->tdata->loop)); nxweb_response_append_str(resp, " [max-age="); nxweb_response_append_uint(resp, max_age); nxweb_response_append_str(resp, "]"); return NXWEB_OK; }
int nxweb_select_handler(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp, nxweb_handler* handler, nxe_data handler_param) { conn->handler=handler; conn->handler_param=handler_param; // since nxweb_select_handler() could be called several times // make sure all changed fields returned to initial state time_t if_modified_since_original=req->if_modified_since; // save original value const char* uri_original=req->uri; const int num_filters=handler->num_filters; nxweb_filter** filters=handler->filters; if (num_filters) { // init filters int i; nxweb_filter* filter; nxweb_filter_data* fdata; for (i=0; i<num_filters; i++) { filter=filters[i]; assert(filter->init); req->filter_data[i]=filter->init(filter, conn, req, resp); } // let filters decode uri const char* uri=req->uri; for (i=num_filters-1; i>=0; i--) { fdata=req->filter_data[i]; if (!fdata || fdata->bypass) continue; filter=filters[i]; if (!filter->decode_uri) continue; uri=filter->decode_uri(filter, conn, req, resp, req->filter_data[i], uri); // should return the same uri if unchanged } if (req->uri!=uri) { // uri changed if (handler->prefix_len && !nxweb_url_prefix_match(uri, strlen(uri), handler->prefix, handler->prefix_len)) { // ensure it still matches nxweb_log_error("uri %s doesn't match prefix %s after decode", uri, handler->prefix); } req->uri=uri; } } req->path_info=req->uri+handler->prefix_len; // check cache if (req->get_method && !req->content_length && handler->on_generate_cache_key) { // POST requests are not cacheable /* * Cache_key is unique ID for each request with all its parameters, headers and options * that could affect the response. * Cache_key must conform to file path syntax, as many filters are going to use it as a file name. * Handlers will typically use full request uri (host, port, ssl, path, query string) * as initial key (file-path-encoded, see note above). * Each filter shall add its own differentiators to the key in translate_cache_key() method * regardless of whether it implements itself caching or not. * E.g. gzip filter must differentiate requests coming with accept_gzip_encoding flag set * from requests without that flag. So it is going to append '$gzip' suffix to all requests * that it could possibly compress (it might not compress some content at the end * for various reasons, e.g. flags in response, but those is not going to affect the cache_key). * Each filter can have its own cache_key. Cache_key of a filter is not affected by filters * coming after it in filter chain. */ if (handler->on_generate_cache_key(conn, req, resp)==NXWEB_NEXT) return NXWEB_NEXT; if (resp->cache_key && *resp->cache_key) { const char* cache_key=resp->cache_key; if (num_filters) { int i; nxweb_filter* filter; nxweb_filter_data* fdata; for (i=0; i<num_filters; i++) { fdata=req->filter_data[i]; if (!fdata || fdata->bypass) continue; filter=filters[i]; if (!filter->translate_cache_key) continue; if (filter->translate_cache_key(filter, conn, req, resp, fdata, cache_key)==NXWEB_NEXT) continue; cache_key=fdata->cache_key; assert(cache_key && *cache_key); } resp->cache_key=cache_key; } if (handler->memcache) { nxweb_result res=nxweb_cache_try(conn, resp, resp->cache_key, req->if_modified_since, 0); if (res==NXWEB_OK) { conn->hsp.cls->start_sending_response(&conn->hsp, resp); return NXWEB_OK; } else if (res==NXWEB_REVALIDATE) { // fall through } else if (res!=NXWEB_MISS) return res; } if (resp->last_modified) { // in case one of filters has already determined resp->last_modified if (req->if_modified_since && resp->last_modified<=req->if_modified_since) { resp->status_code=304; resp->status="Not Modified"; conn->hsp.cls->start_sending_response(&conn->hsp, resp); return NXWEB_OK; } } if (num_filters) { time_t check_time=resp->last_modified? resp->last_modified : nxe_get_current_http_time(conn->tdata->loop); int i; nxweb_filter* filter; nxweb_filter_data* fdata; for (i=num_filters-1; i>=0; i--) { filter=filters[i]; fdata=req->filter_data[i]; if (filter->serve_from_cache && fdata && !fdata->bypass) { nxweb_result r=filter->serve_from_cache(filter, conn, req, resp, fdata, check_time); if (r==NXWEB_OK) { // filter has served content (which has not expired by check_time) // process it through filters & send to client for (i++; i<num_filters; i++) { filter=filters[i]; nxweb_filter_data* fdata1=req->filter_data[i]; if (fdata1 && !fdata1->bypass && filter->do_filter) { if (filter->do_filter(filter, conn, req, resp, fdata1)==NXWEB_DELAY) { resp->run_filter_idx=i+1; // resume from next filter return NXWEB_OK; } } } if (handler->memcache) { nxweb_cache_store_response(conn, resp); } conn->hsp.cls->start_sending_response(&conn->hsp, resp); return NXWEB_OK; } /* else if (r==NXWEB_REVALIDATE) { // filter has content but it has expired // the filter has already set if_modified_since field in request (revalidation mode) // it must be ready to process 304 Not Modified response // on the way back in its do_filter() } else { // no cached content OR cached content's last_modified is older than req->if_modified_since } */ } } } } } nxweb_result r=NXWEB_OK; if (handler->on_select) r=handler->on_select(conn, req, resp); if (r!=NXWEB_OK) { if (num_filters) { // filters have been initialized => finalize them int i; nxweb_filter* filter; nxweb_filter_data* fdata; for (i=0; i<num_filters; i++) { filter=filters[i]; fdata=req->filter_data[i]; if (fdata && filter->finalize) filter->finalize(filter, conn, req, resp, fdata); req->filter_data[i]=0; // call no more } } // restore saved fields req->uri=uri_original; req->if_modified_since=if_modified_since_original; // reset changed fields conn->handler=0; conn->handler_param=(nxe_data)0; resp->cache_key=0; resp->last_modified=0; resp->mtype=0; resp->content_type=0; resp->content_charset=0; resp->sendfile_path=0; if (resp->sendfile_fd>0) { close(resp->sendfile_fd); } resp->sendfile_fd=0; if (resp->sendfile_info.st_ino) memset(&resp->sendfile_info, 0, sizeof(resp->sendfile_info)); } return r; }
static void nxweb_http_server_proxy_events_sub_on_message(nxe_subscriber* sub, nxe_publisher* pub, nxe_data data) { nxweb_log_debug("nxweb_http_server_proxy_events_sub_on_message"); nxweb_http_proxy_request_data* rdata=(nxweb_http_proxy_request_data*)((char*)sub-offsetof(nxweb_http_proxy_request_data, proxy_events_sub)); nxweb_http_server_connection* conn=rdata->conn; nxe_loop* loop=sub->super.loop; if (data.i==NXD_HCP_RESPONSE_RECEIVED) { nxe_unset_timer(loop, NXWEB_TIMER_BACKEND, &rdata->timer_backend); //nxweb_http_request* req=&conn->hsp.req; nxd_http_proxy* hpx=rdata->hpx; nxweb_http_response* presp=&hpx->hcp.resp; nxweb_http_response* resp=&conn->hsp._resp; resp->status=presp->status; resp->status_code=presp->status_code; resp->content_type=presp->content_type; resp->content_length=presp->content_length; if (resp->content_length<0) resp->chunked_autoencode=1; // re-encode chunked or until-close content resp->ssi_on=presp->ssi_on; resp->templates_on=presp->templates_on; resp->headers=presp->headers; time_t backend_time_delta=presp->date? presp->date-nxe_get_current_http_time(loop) : 0; nxd_http_proxy_pool_report_backend_time_delta(hpx->pool, backend_time_delta); backend_time_delta=nxd_http_proxy_pool_get_backend_time_delta(hpx->pool); resp->date=presp->date? presp->date-backend_time_delta : 0; resp->last_modified=presp->last_modified? presp->last_modified-backend_time_delta : 0; resp->expires=presp->expires? presp->expires-backend_time_delta : 0; resp->cache_control=presp->cache_control; resp->max_age=presp->max_age; resp->no_cache=presp->no_cache; resp->cache_private=presp->cache_private; resp->content_out=&rdata->rb_resp.data_out; nxweb_start_sending_response(conn, resp); rdata->response_sending_started=1; nxweb_server_config.access_log_on_proxy_response(&conn->hsp.req, hpx, presp); //nxweb_log_error("proxy request [%d] start sending response", conn->hpx->hcp.request_count); } else if (data.i==NXD_HCP_REQUEST_COMPLETE) { rdata->proxy_request_complete=1; //nxweb_log_error("proxy request [%d] complete", conn->hpx->hcp.request_count); } else if (data.i<0) { rdata->proxy_request_error=1; if (/*rdata->hpx->hcp.request_complete ||*/ rdata->proxy_request_complete) { nxweb_log_warning("proxy request conn=%p rc=%d retry=%d error=%d; error after request_complete; ignored", conn, rdata->hpx->hcp.request_count, rdata->retry_count, data.i); // ignore errors after request_complete; keep connection running } else if (rdata->response_sending_started) { nxweb_log_error("proxy request conn=%p rc=%d retry=%d error=%d; error while response_sending_started but backend request not complete; request failed", conn, rdata->hpx->hcp.request_count, rdata->retry_count, data.i); // ignore errors after response_sending_started; keep connection running fail_proxy_request(rdata); } else { nxe_unset_timer(loop, NXWEB_TIMER_BACKEND, &rdata->timer_backend); if (rdata->retry_count>=NXWEB_PROXY_RETRY_COUNT || rdata->hpx->hcp.req_body_sending_started /*|| rdata->response_sending_started*/) { nxweb_log_error("proxy request conn=%p rc=%d retry=%d error=%d; failed", conn, rdata->hpx->hcp.request_count, rdata->retry_count, data.i); fail_proxy_request(rdata); } else { if (rdata->retry_count || !(data.i==NXE_ERROR || data.i==NXE_HUP || data.i==NXE_RDHUP || data.i==NXE_RDCLOSED)) { nxweb_log_warning("proxy request conn=%p rc=%d retry=%d error=%d; retrying", conn, rdata->hpx->hcp.request_count, rdata->retry_count, data.i); } retry_proxy_request(rdata); } } } }