static nxweb_result curtime_generate_cache_key(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp) { if (!req->get_method || req->content_length) return NXWEB_OK; // do not cache POST requests, etc. char* key=nxb_alloc_obj(req->nxb, strlen(req->uri)+2); *key=' '; strcpy(key+1, req->uri); resp->cache_key=key; resp->cache_key_root_len=1; return NXWEB_OK; }
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; }
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; }
static nxweb_result nxweb_http_proxy_handler_on_headers(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_response* resp) { nxweb_log_debug("nxweb_http_proxy_handler_on_headers"); nxweb_http_proxy_request_data* rdata=nxb_alloc_obj(conn->hsp.nxb, sizeof(nxweb_http_proxy_request_data)); nxe_loop* loop=conn->tdata->loop; memset(rdata, 0, sizeof(nxweb_http_proxy_request_data)); rdata->conn=conn; conn->hsp.req_data=rdata; conn->hsp.req_finalize=nxweb_http_proxy_request_finalize; rdata->rbuf=nxp_alloc(conn->tdata->free_rbuf_pool); rdata->timer_backend.super.cls.timer_cls=&timer_backend_class; return start_proxy_request(conn, req, rdata); }
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; }
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; } }