Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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);
      }
    }
  }
}