Exemple #1
0
static nxweb_result download_on_request(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp) 
{
    nxweb_parse_request_parameters( req, 0 );
    const char *name_space = nx_simple_map_get_nocase( req->parameters, "namespace" );

    char * url = malloc(strlen(req->path_info)+1);
    if (!url) {return NXWEB_ERROR;}
    strcpy(url, req->path_info);
    url[strlen(req->path_info)] = '\0';

    nxweb_url_decode( url, 0 );
    char *fname = url, *temp;
    while (temp = strstr(fname, "/")) {fname = temp + 1;}

    char *strip_args = strstr(fname, "?");

    int fname_len = strip_args ? (strip_args - fname) : strlen(fname);
    char fname_with_space[1024] = {0};
    if ( strlen(url) > sizeof(fname_with_space)-1 ) {
	char err_msg[1024] = {0};
	snprintf( err_msg, sizeof(err_msg)-1, 
		"<html><body>File not found<br/>namespace:%s<br/>filename:%.*s</body></html>", 
		name_space, fname_len, fname );
	nxweb_send_http_error(resp, 404, err_msg);
	resp->keep_alive=0;
	free(url);
	return NXWEB_MISS;
    }

    if( name_space ) {sprintf( fname_with_space, "%s:/", name_space);}
    else {strcpy( fname_with_space, ":/" );}

    strncat( fname_with_space, fname, fname_len );

    size_t file_size = 0;
    char *pfile_data = kcdbget( g_kcdb, fname_with_space, strlen(fname_with_space), &file_size );
    if ( pfile_data == NULL ) {
	char err_msg[1024] = {0};
	snprintf( err_msg, sizeof(err_msg)-1, 
		"<html><body>File not found<br/>namespace:%s<br/>filename:%.*s</body></html>", 
		name_space, fname_len, fname );
	nxweb_send_http_error(resp, 404, err_msg);
	resp->keep_alive=0;
	free(url);
	return NXWEB_MISS;
    }

    KC_DATA *ptmp = nxb_alloc_obj(req->nxb, sizeof(KC_DATA));
    nxweb_set_request_data(req, UPLOAD_HANDLER_KEY, (nxe_data)(void *)ptmp, upload_request_data_finalize);
    ptmp->data_ptr = pfile_data;

    nxweb_send_data( resp, pfile_data + RECORD_HEADER_LEN, file_size - RECORD_HEADER_LEN, "application/octet-stream" );
    free(url);
    return NXWEB_OK;
}
Exemple #2
0
static inline nxweb_result invoke_request_handler(nxweb_http_server_connection* conn, nxweb_http_request* req,
        nxweb_http_response* resp, nxweb_handler* h, nxweb_handler_flags flags) {
  if (conn->connection_closing) return; // do not process if already closing
  if (flags&NXWEB_PARSE_PARAMETERS) nxweb_parse_request_parameters(req, 1); // !!(flags&NXWEB_PRESERVE_URI)
  if (flags&NXWEB_PARSE_COOKIES) nxweb_parse_request_cookies(req);
  nxb_start_stream(req->nxb);
  nxweb_result res=NXWEB_OK;
  if (h->on_request) {
    if (flags&NXWEB_INWORKER) {
      nxw_worker* w=nxw_get_worker(&conn->tdata->workers_factory);
      if (!w) {
        nxweb_send_http_error(resp, 503, "Service Unavailable");
        nxweb_start_sending_response(conn, resp);
        return NXWEB_ERROR;
      }
      nxe_subscribe(conn->tdata->loop, &w->complete_efs.data_notify, &conn->worker_complete);
      nxw_start_worker(w, invoke_request_handler_in_worker, conn, &conn->worker_job_done);
      conn->in_worker=1;
    }
    else {
      res=h->on_request(conn, req, resp);
      nxd_http_server_proto_finish_response(resp);
      if (res!=NXWEB_ASYNC) nxweb_start_sending_response(conn, resp);
    }
  }
  return res;
}
Exemple #3
0
static nxweb_result python_on_post_data(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp) {
  if (req->content_length>0 && req->content_length<NXWEB_MAX_REQUEST_BODY_SIZE) {
    // fallback to default in-memory buffering
    return NXWEB_NEXT;
  }
  if (!conn->handler->dir) {
    nxweb_log_warning("python handler temp upload file dir not set => skipping file buffering for %s", req->uri);
    return NXWEB_NEXT;
  }
  nxe_ssize_t upload_size_limit=conn->handler->size? conn->handler->size : NXWEB_MAX_PYTHON_UPLOAD_SIZE;
  if (req->content_length > upload_size_limit) {
    nxweb_send_http_error(resp, 413, "Request Entity Too Large");
    resp->keep_alive=0; // close connection
    nxweb_start_sending_response(conn, resp);
    return NXWEB_OK;
  }
  char* fname_template=nxb_alloc_obj(req->nxb, strlen(conn->handler->dir)+sizeof("/py_upload_tmp_XXXXXX")+1);
  strcat(strcpy(fname_template, conn->handler->dir), "/py_upload_tmp_XXXXXX");
  if (nxweb_mkpath(fname_template, 0755)==-1) {
    nxweb_log_error("can't create path to temp upload file %s; check permissions", fname_template);
    nxweb_send_http_error(resp, 500, "Internal Server Error");
    resp->keep_alive=0; // close connection
    nxweb_start_sending_response(conn, resp);
    return NXWEB_OK;
  }
  int fd=mkstemp(fname_template);
  if (fd==-1) {
    nxweb_log_error("can't open (mkstemp()) temp upload file for %s", req->uri);
    nxweb_send_http_error(resp, 500, "Internal Server Error");
    resp->keep_alive=0; // close connection
    nxweb_start_sending_response(conn, resp);
    return NXWEB_OK;
  }
  unlink(fname_template); // auto-delete on close()
  nxd_fwbuffer* fwb=nxb_alloc_obj(req->nxb, sizeof(nxd_fwbuffer));
  nxweb_set_request_data(req, PYTHON_HANDLER_KEY, (nxe_data)(void*)fwb, python_request_data_finalize);
  nxd_fwbuffer_init(fwb, fd, upload_size_limit);
  conn->hsp.cls->connect_request_body_out(&conn->hsp, &fwb->data_in);
  conn->hsp.cls->start_receiving_request_body(&conn->hsp);
  return NXWEB_OK;
}
Exemple #4
0
static nxweb_result delete_on_request(
	nxweb_http_server_connection* conn, 
	nxweb_http_request* req, 
	nxweb_http_response* resp) 
{
    nxweb_set_response_content_type(resp, "text/plain");
    nxweb_parse_request_parameters(req, 0);
    const char *name_space = nx_simple_map_get_nocase(req->parameters, "namespace");

    char * url = malloc(strlen(req->path_info)+1);
    if (!url) {
	nxweb_send_http_error(resp, 500, "Please retry");
	resp->keep_alive=0;
	return NXWEB_ERROR;
    }
    strcpy(url, req->path_info);
    url[strlen(req->path_info)] = '\0';

    nxweb_url_decode(url, 0);
    char *fname = url, *temp;
    while (temp = strstr(fname, "/")) {fname = temp + 1;}
    char *strip_args = strstr(fname, "?");
    int fname_len = strip_args?strip_args-fname:strlen(fname);
    char fname_with_space[1024]= {0};
    if (strlen(url) > sizeof(fname_with_space)-1) {
	nxweb_send_http_error(resp, 414, "Request URI Too Long");
	resp->keep_alive=0;
	free(url);
	return NXWEB_MISS;
    }

    if (name_space) {sprintf( fname_with_space, "%s:/", name_space);}
    else {strcpy(fname_with_space, ":/");}
    strncat(fname_with_space, fname, fname_len);

    kcdbremove(g_kcdb, fname_with_space, strlen( fname_with_space));
    nxweb_response_printf(resp, "OK\n");
    free(url);
    return NXWEB_OK;
}
Exemple #5
0
/////////////// clean //////////////////////
static nxweb_result clean_on_request(
	nxweb_http_server_connection* conn, 
	nxweb_http_request* req, 
	nxweb_http_response* resp) 
{
    if (strlen(req->path_info) <= 1 || strlen(req->path_info) >= 4 ) {
	nxweb_send_http_error(resp, 403, "NUMBER error!\nExample:\nhttp://host:port/clean/24");
	resp->keep_alive=0;
	return NXWEB_ERROR;
    }

    char tmp[8] = {0};
    strcpy(tmp, (req->path_info) +1);
    int interval_hour = atoi(tmp);
    if (interval_hour < 0 || interval_hour > 99) {
	nxweb_send_http_error(resp, 403, "NUMBER error!\nExample:\nhttp://host:port/clean/24");
	resp->keep_alive=0;
	return NXWEB_ERROR;
    }

    WipeData(interval_hour);
    nxweb_response_printf(resp, "OK");
    return NXWEB_OK;
}
Exemple #6
0
static void fail_proxy_request(nxweb_http_proxy_request_data* rdata) {

  nxweb_log_debug("fail_proxy_request");

  nxweb_http_server_connection* conn=rdata->conn;
  if (rdata->response_sending_started) {
    nxweb_http_server_connection_finalize(conn, 0);
  }
  else {
    nxweb_http_response* resp=&conn->hsp._resp;
    nxweb_send_http_error(resp, 504, "Gateway Timeout");
    nxweb_start_sending_response(conn, resp);
    rdata->response_sending_started=1;
    rdata->proxy_request_complete=1; // ignore further backend errors
  }
}
Exemple #7
0
static nxweb_result default_on_headers(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp) {
  nxweb_send_http_error(resp, 404, "Not Found");
  return NXWEB_ERROR;
}
Exemple #8
0
static void nxweb_http_server_connection_events_sub_on_message(nxe_subscriber* sub, nxe_publisher* pub, nxe_data data) {
  nxweb_http_server_connection* conn=(nxweb_http_server_connection*)((char*)sub-offsetof(nxweb_http_server_connection, events_sub));
  //nxe_loop* loop=sub->super.loop;
  nxweb_http_request* req=&conn->hsp.req;
  nxweb_http_response* resp=&conn->hsp._resp;
  if (data.i==NXD_HSP_REQUEST_RECEIVED) {
    assert(nxweb_server_config.request_dispatcher);
    assert(nxweb_server_config.access_log_on_request_received);

    nxweb_log_debug("nxweb_http_server_connection_events_sub_on_message NXD_HSP_REQUEST_RECEIVED");

    req->received_time=nxweb_get_loop_time(conn);
    nxweb_server_config.access_log_on_request_received(conn, req);
    nxweb_server_config.request_dispatcher(conn, req, resp);
    if (!conn->handler) conn->handler=&nxweb_default_handler;

    if (conn->hsp.state==HSP_SENDING_HEADERS || resp->run_filter_idx) return; // one of callbacks has already started sending response

    nxweb_handler* h=conn->handler;
    nxweb_handler_flags flags=h->flags;

    if (flags&_NXWEB_HANDLE_MASK) {
      if (((!(flags&NXWEB_HANDLE_GET) || !req->get_method)
        && (!(flags&NXWEB_HANDLE_POST) || !req->post_method)
        && (!(flags&NXWEB_HANDLE_OTHER) || !req->other_method))
        || (req->content_length && !(flags&(NXWEB_HANDLE_POST|NXWEB_ACCEPT_CONTENT)))) {
          nxweb_send_http_error(resp, 405, "Method Not Allowed");
          if (req->content_length) resp->keep_alive=0; // close connection if there is body pending
          nxweb_start_sending_response(conn, resp);
          return;
      }
    }

    if (h->on_headers) {
      if (NXWEB_OK!=h->on_headers(conn, req, resp)) {
        // request processing terminated by http error response
        if (req->content_length) resp->keep_alive=0; // close connection if there is body pending
        nxweb_start_sending_response(conn, resp);
        return;
      }
    }

    if (conn->hsp.state==HSP_SENDING_HEADERS) return; // one of callbacks has already started sending headers

    if (req->content_length) {
      if (h->on_post_data) h->on_post_data(conn, req, resp);
      if (conn->hsp.state!=HSP_SENDING_HEADERS && !conn->hsp.cls->get_request_body_out_pair(&conn->hsp)) { // stream still not connected
        if (req->content_length>NXWEB_MAX_REQUEST_BODY_SIZE) {
          nxweb_send_http_error(resp, 413, "Request Entity Too Large");
          resp->keep_alive=0; // close connection
          nxweb_start_sending_response(conn, resp);
          return;
        }
        nxe_loop* loop=conn->tdata->loop;
        nxd_ibuffer_init(&conn->ib, conn->hsp.nxb, req->content_length>0? req->content_length+1 : NXWEB_MAX_REQUEST_BODY_SIZE);
        conn->hsp.cls->connect_request_body_out(&conn->hsp, &conn->ib.data_in);
        conn->hsp.cls->start_receiving_request_body(&conn->hsp);
        req->buffering_to_memory=1;
      }
    }
    else {
      invoke_request_handler(conn, req, resp, h, flags);
    }
  }
  else if (data.i==NXD_HSP_REQUEST_BODY_RECEIVED) {
    assert(conn->handler);

    nxweb_log_debug("nxweb_http_server_connection_events_sub_on_message NXD_HSP_REQUEST_BODY_RECEIVED");

    nxweb_handler* h=conn->handler;
    nxweb_handler_flags flags=h->flags;
    if (h->on_post_data_complete) h->on_post_data_complete(conn, req, resp);
    if (req->buffering_to_memory && conn->hsp.cls->get_request_body_out_pair(&conn->hsp)==&conn->ib.data_in) {
      int size;
      req->content=nxd_ibuffer_get_result(&conn->ib, &size);
      assert(req->content_received==size);
    }
    invoke_request_handler(conn, req, resp, h, flags);
  }
  else if (data.i==NXD_HSP_REQUEST_COMPLETE) {

    nxweb_log_debug("nxweb_http_server_connection_events_sub_on_message NXD_HSP_REQUEST_COMPLETE");

    conn->hsp.cls->request_cleanup(sub->super.loop, &conn->hsp);
    assert(!conn->handler);
  }
  else if (data.i==NXD_HSP_RESPONSE_READY) {

    nxweb_log_debug("nxweb_http_server_connection_events_sub_on_message NXD_HSP_RESPONSE_READY");

    // this must be subrequest
    assert(!conn->response_ready); // make sure this happens only once
    conn->response_ready=1;
    if (conn->on_response_ready) conn->on_response_ready(conn, conn->on_response_ready_data);
  }
  else if (data.i<0) {

    nxweb_log_debug("nxweb_http_server_connection_events_sub_on_message data.i=%d", data.i);

    if (conn->handler && conn->handler->on_error) conn->handler->on_error(conn, req, resp);
    if (conn->hsp.headers_bytes_received) {
      nxweb_log_warning("conn %p error: i=%d errno=%d state=%d rc=%d br=%d", conn, data.i, errno, conn->hsp.state, conn->hsp.request_count, conn->hsp.headers_bytes_received);
    }
    int good=(!conn->hsp.headers_bytes_received && (data.i==NXE_RDHUP || data.i==NXE_HUP || data.i==NXE_RDCLOSED)); // normal close
    nxweb_http_server_connection_finalize(conn, good); // bad connections get RST'd
  }
}
Exemple #9
0
nxweb_result nxweb_cache_store_response(nxweb_http_server_connection* conn, nxweb_http_response* resp) {
  nxe_time_t loop_time=nxweb_get_loop_time(conn);
  if (!resp->status_code) resp->status_code=200;

  if (resp->status_code==200 && resp->sendfile_path // only cache content from files
      && resp->content_length>=0 && resp->content_length<=NXWEB_MAX_CACHED_ITEM_SIZE // must be small
      && resp->sendfile_offset==0 && resp->sendfile_end==resp->content_length // whole file only
      && resp->sendfile_end>=resp->sendfile_info.st_size // st_size could be zero if not initialized
      && alignhash_size(_nxweb_cache)<NXWEB_MAX_CACHED_ITEMS+16) {

    const char* fpath=resp->sendfile_path;
    const char* key=resp->cache_key;
    if (nxweb_cache_try(conn, resp, key, 0, resp->last_modified)!=NXWEB_MISS) return NXWEB_OK;

    nxweb_cache_rec* rec=nx_calloc(sizeof(nxweb_cache_rec)+resp->content_length+1+strlen(key)+1);

    rec->expires_time=loop_time+NXWEB_DEFAULT_CACHED_TIME;
    rec->last_modified=resp->last_modified;
    rec->content_type=resp->content_type;       // assume content_type and content_charset come
    rec->content_charset=resp->content_charset; // from statically allocated memory, which won't go away
    rec->content_length=resp->content_length;
    rec->gzip_encoded=resp->gzip_encoded;
    char* ptr=((char*)rec)+offsetof(nxweb_cache_rec, content);
    int fd;
    if ((fd=open(fpath, O_RDONLY))<0 || read(fd, ptr, resp->content_length)!=resp->content_length) {
      if (fd>0) close(fd);
      nx_free(rec);
      nxweb_log_error("nxweb_cache_file_store_and_send(): [%s] stat() was OK, but open/read() failed", fpath);
      nxweb_send_http_error(resp, 500, "Internal Server Error");
      return NXWEB_ERROR;
    }
    close(fd);
    resp->content=ptr;
    ptr+=resp->content_length;
    *ptr++='\0';
    strcpy(ptr, key);
    key=ptr;

    int ret=0;
    ah_iter_t ci;
    pthread_mutex_lock(&_nxweb_cache_mutex);
    ci=alignhash_set(nxweb_cache, _nxweb_cache, key, &ret);
    if (ci!=alignhash_end(_nxweb_cache)) {
      if (ret!=AH_INS_ERR) {
        alignhash_value(_nxweb_cache, ci)=rec;
        cache_rec_link(rec);
        rec->ref_count++;
        cache_check_size();
        pthread_mutex_unlock(&_nxweb_cache_mutex);
        nxweb_log_info("memcached %s", key);
        conn->hsp.req_data=rec;
        assert(!conn->hsp.req_finalize);
        conn->hsp.req_finalize=cache_rec_unref;
        //nxweb_start_sending_response(conn, resp);
        return NXWEB_OK;
      }
      else { // AH_INS_ERR => key already exists (added by other thread)
        nx_free(rec);
        rec=alignhash_value(_nxweb_cache, ci);
        resp->content_length=rec->content_length;
        resp->content=rec->content;
        resp->content_type=rec->content_type;
        resp->content_charset=rec->content_charset;
        resp->last_modified=rec->last_modified;
        resp->gzip_encoded=rec->gzip_encoded;
        rec->ref_count++;
        pthread_mutex_unlock(&_nxweb_cache_mutex);
        conn->hsp.req_data=rec;
        assert(!conn->hsp.req_finalize);
        conn->hsp.req_finalize=cache_rec_unref;
        //nxweb_start_sending_response(conn, resp);
        return NXWEB_OK;
      }
    }
    pthread_mutex_unlock(&_nxweb_cache_mutex);
    nx_free(rec);
  }
  return NXWEB_OK;
}
Exemple #10
0
static nxweb_result python_on_request(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp) {
  nxb_buffer* nxb=req->nxb;
  nxweb_handler* handler=conn->handler;
  const char* request_uri=req->uri;
  char* query_string=strchr(request_uri, '?');
  int ulen=query_string? (query_string-request_uri) : strlen(request_uri);
  if (query_string) query_string++;
  int pfxlen=req->path_info? (req->path_info - req->uri) : 0;
  int plen=ulen-pfxlen;
  const char* path_info=request_uri+pfxlen;
  if (handler->uri && *handler->uri) {
    pfxlen=strlen(handler->uri);
    ulen=pfxlen+plen;
    char* u=nxb_alloc_obj(nxb, ulen+1);
    memcpy(u, handler->uri, pfxlen);
    memcpy(u+pfxlen, path_info, plen);
    u[ulen]='\0';
    request_uri=u;
    path_info=request_uri+pfxlen;
  }
  const char* host_port=req->host? strchr(req->host, ':') : 0;

  int content_fd=0;

  if (req->content_length) {
    nxd_fwbuffer* fwb=nxweb_get_request_data(req, PYTHON_HANDLER_KEY).ptr;

    if (fwb) {
      if (fwb->error || fwb->size > fwb->max_size) {
        nxweb_send_http_error(resp, 413, "Request Entity Too Large"); // most likely cause
        return NXWEB_ERROR;
      }
      else if (req->content_received!=fwb->size) {
        nxweb_log_error("content_received does not match upload stored size for %s", req->uri);
        nxweb_send_http_error(resp, 500, "Internal Server Error");
        return NXWEB_ERROR;
      }
      else {
        content_fd=fwb->fd;
        if (lseek(content_fd, 0, SEEK_SET)==-1) {
          nxweb_log_error("can't lseek() temp upload file for %s", req->uri);
          nxweb_send_http_error(resp, 500, "Internal Server Error");
          return NXWEB_ERROR;
        }
      }
    }
  }


  nxweb_log_debug("invoke python");

  PyGILState_STATE gstate=PyGILState_Ensure();

  PyObject* py_func_args=PyTuple_New(1);
  PyObject* py_environ=PyDict_New();
  assert(PyDict_Check(py_environ));

  dict_set(py_environ, "SERVER_NAME", PyString_FromStringAndSize(req->host, host_port? (host_port-req->host) : strlen(req->host)));
  dict_set(py_environ, "SERVER_PORT", PyString_FromString(host_port? host_port+1 : ""));
  dict_set(py_environ, "SERVER_PROTOCOL", PyString_FromString(req->http11? "HTTP/1.1" : "HTTP/1.0"));
  dict_set(py_environ, "SERVER_SOFTWARE", PyString_FromString(PACKAGE_STRING));
  dict_set(py_environ, "GATEWAY_INTERFACE", PyString_FromString("CGI/1.1"));
  dict_set(py_environ, "REQUEST_METHOD", PyString_FromString(req->method));
  dict_set(py_environ, "REQUEST_URI", PyString_FromStringAndSize(request_uri, ulen));
  dict_set(py_environ, "SCRIPT_NAME", PyString_FromStringAndSize(request_uri, pfxlen));
  dict_set(py_environ, "PATH_INFO", PyString_FromStringAndSize(path_info, plen));
  dict_set(py_environ, "QUERY_STRING", PyString_FromString(query_string? query_string : ""));
  dict_set(py_environ, "REMOTE_ADDR", PyString_FromString(conn->remote_addr));
  dict_set(py_environ, "CONTENT_TYPE", PyString_FromString(req->content_type? req->content_type : ""));
  dict_set(py_environ, "CONTENT_LENGTH", PyInt_FromLong(req->content_received));
  if (req->cookie) dict_set(py_environ, "HTTP_COOKIE", PyString_FromString(req->cookie));
  if (req->host) dict_set(py_environ, "HTTP_HOST", PyString_FromString(req->host));
  if (req->user_agent) dict_set(py_environ, "HTTP_USER_AGENT", PyString_FromString(req->user_agent));
  if (req->if_modified_since) {
    struct tm tm;
    gmtime_r(&req->if_modified_since, &tm);
    char ims[32];
    nxweb_format_http_time(ims, &tm);
    dict_set(py_environ, "HTTP_IF_MODIFIED_SINCE", PyString_FromString(ims));
  }

  if (req->headers) {
    // write added headers
    // encode http headers into CGI variables; see 4.1.18 in https://tools.ietf.org/html/rfc3875
    char hname[256];
    memcpy(hname, "HTTP_", 5);
    char* h=hname+5;
    nx_simple_map_entry* itr;
    for (itr=nx_simple_map_itr_begin(req->headers); itr; itr=nx_simple_map_itr_next(itr)) {
      nx_strtoupper(h, itr->name);
      char* p;
      for (p=h; *p; p++) {
        if (*p=='-') *p='_';
      }
      dict_set(py_environ, hname, PyString_FromString(itr->value));
    }
  }

  dict_set(py_environ, "wsgi.url_scheme", PyString_FromString(conn->secure? "https" : "http"));

  if (req->content_length) {
    if (content_fd) {
      dict_set(py_environ, "nxweb.req.content_fd", PyInt_FromLong(content_fd));
    }
    else {
      dict_set(py_environ, "nxweb.req.content", PyByteArray_FromStringAndSize(req->content? req->content : "", req->content_received));
    }
  }
  if (req->if_modified_since) dict_set(py_environ, "nxweb.req.if_modified_since", PyLong_FromLong(req->if_modified_since));
  dict_set(py_environ, "nxweb.req.uid", PyLong_FromLongLong(req->uid));
  if (req->parent_req) {
    nxweb_http_request* preq=req->parent_req;
    while (preq->parent_req) preq=preq->parent_req; // find root request
    if (preq->uid) {
      dict_set(py_environ, "nxweb.req.root_uid", PyLong_FromLongLong(preq->uid));
    }
  }

  // call python
  PyTuple_SetItem(py_func_args, 0, py_environ);
  PyObject* py_result=PyObject_CallObject(py_nxweb_on_request_func, py_func_args);
  Py_DECREF(py_func_args);
  if (py_result && PyTuple_Check(py_result) && PyTuple_Size(py_result)==3) {
    PyObject* py_status=PyTuple_GET_ITEM(py_result, 0);
    PyObject* py_headers=PyTuple_GET_ITEM(py_result, 1);
    PyObject* py_body=PyTuple_GET_ITEM(py_result, 2);

    if (py_status && PyString_Check(py_status)) {
      const char* status_string=PyString_AS_STRING(py_status);
      int status_code=0;
      const char* p=status_string;
      while (*p && *p>='0' && *p<='9') {
        status_code=status_code*10+(*p-'0');
        p++;
      }
      while (*p && *p==' ') p++;
      if (status_code>=200 && status_code<600 && *p) {
        resp->status_code=status_code;
        resp->status=nxb_copy_str(nxb, p);
      }
    }

    if (py_headers && PyList_Check(py_headers)) {
      const int size=PyList_Size(py_headers);
      int i;
      for (i=0; i<size; i++) {
        PyObject* py_header_tuple=PyList_GET_ITEM(py_headers, i);
        if (py_header_tuple && PyTuple_Check(py_header_tuple) && PyTuple_Size(py_header_tuple)==2) {
          PyObject* py_name=PyTuple_GET_ITEM(py_header_tuple, 0);
          PyObject* py_value=PyTuple_GET_ITEM(py_header_tuple, 1);
          if (py_name && PyString_Check(py_name) && py_value && PyString_Check(py_value)) {
            nxweb_add_response_header_safe(resp, PyString_AS_STRING(py_name), PyString_AS_STRING(py_value));
          }
        }
      }
    }

    if ((!resp->status_code || resp->status_code==200) && !resp->content_type) resp->content_type="text/html";

    char* rcontent=0;
    nxe_ssize_t rsize=0;
    if (PyByteArray_Check(py_body)) {
      rcontent=PyByteArray_AS_STRING(py_body);
      rsize=PyByteArray_Size(py_body);
    }
    else if (PyString_Check(py_body)) {
      rcontent=PyString_AS_STRING(py_body);
      rsize=PyString_Size(py_body);
    }
    if (rcontent && rsize>0) nxweb_response_append_data(resp, rcontent, rsize);
  }
  else if (py_result && PyString_Check(py_result)) {
    resp->status_code=500;
    resp->status="Internal Server Error";
    resp->content_type="text/html";
    nxweb_log_error("python call failed: %s", PyString_AS_STRING(py_result));
    nxweb_response_printf(resp, "python call failed: %H", PyString_AS_STRING(py_result));
  }
  else {
    PyErr_Print();
    nxweb_log_error("python call failed");
    nxweb_response_printf(resp, "python call failed");
  }
  Py_XDECREF(py_result);

  // Release the thread. No Python API allowed beyond this point.
  PyGILState_Release(gstate);

  nxweb_log_debug("invoke python complete");

  return NXWEB_OK;
}
Exemple #11
0
static nxweb_result start_proxy_request(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_proxy_request_data* rdata) {

  nxweb_log_debug("start_proxy_request");

  nxe_loop* loop=conn->tdata->loop;
  nxweb_handler* handler=conn->handler;
  assert(handler->idx>=0 && handler->idx<NXWEB_MAX_PROXY_POOLS);
  nxd_http_proxy* hpx=nxd_http_proxy_pool_connect(&conn->tdata->proxy_pool[handler->idx]);
  rdata->proxy_request_complete=0;
  rdata->proxy_request_error=0;
  rdata->response_sending_started=0;
  if (hpx) {
    rdata->hpx=hpx;
    nxweb_http_request* preq=nxd_http_proxy_prepare(hpx);
    if (handler->proxy_copy_host) preq->host=req->host;
    preq->method=req->method;
    preq->head_method=req->head_method;
    preq->content_length=req->content_length;
    preq->content_type=req->content_type;
    /// Do not forward Accept-Encoding header if you want to process results (eg SSI)
    // preq->accept_encoding=req->accept_encoding;
    preq->expect_100_continue=!!req->content_length;
    if (handler->uri) {
      const char* path_info=req->path_info? req->path_info : req->uri;
      if (*handler->uri) {
        char* uri=nxb_alloc_obj(conn->hsp.nxb, strlen(handler->uri)+strlen(path_info)+1);
        strcat(strcpy(uri, handler->uri), path_info);
        preq->uri=uri;
      }
      else {
        preq->uri=path_info;
      }
    }
    else {
      preq->uri=req->uri;
    }
    preq->http11=1;
    preq->keep_alive=1;
    preq->user_agent=req->user_agent;
    preq->cookie=req->cookie;
    preq->if_modified_since=req->if_modified_since + nxd_http_proxy_pool_get_backend_time_delta(hpx->pool);
    preq->x_forwarded_for=conn->remote_addr;
    preq->x_forwarded_host=req->host;
    preq->x_forwarded_ssl=nxweb_server_config.listen_config[conn->lconf_idx].secure;
    preq->uid=req->uid;
    preq->parent_req=req->parent_req;
    preq->headers=req->headers; // need to filter these???
    nxd_http_proxy_start_request(hpx, preq);
    nxe_init_subscriber(&rdata->proxy_events_sub, &nxweb_http_server_proxy_events_sub_class);
    nxe_subscribe(loop, &hpx->hcp.events_pub, &rdata->proxy_events_sub);
    nxd_rbuffer_init(&rdata->rb_resp, rdata->rbuf, NXWEB_RBUF_SIZE);
    nxe_connect_streams(loop, &hpx->hcp.resp_body_out, &rdata->rb_resp.data_in);

    if (req->content_length) { // receive body
      nxd_rbuffer_init(&rdata->rb_req, rdata->rbuf, NXWEB_RBUF_SIZE); // use same buffer area for request and response bodies, as they do not overlap in time
      conn->hsp.cls->connect_request_body_out(&conn->hsp, &rdata->rb_req.data_in);
      nxe_connect_streams(loop, &rdata->rb_req.data_out, &hpx->hcp.req_body_in);
      req->cdstate.monitor_only=1;

      conn->hsp.cls->start_receiving_request_body(&conn->hsp);
    }
    nxe_set_timer(loop, NXWEB_TIMER_BACKEND, &rdata->timer_backend);
    return NXWEB_OK;
  }
  else {
    nxweb_http_response* resp=&conn->hsp._resp;
    nxweb_send_http_error(resp, 502, "Bad Gateway");
    return NXWEB_ERROR;
  }
}