Mon, 06 Mar 2017 17:32:26 +0100
merge srvctrl into default branch
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2013 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include <limits.h> #include <arpa/inet.h> #include "../public/nsapi.h" #include "../util/pool.h" #include "../util/pblock.h" #include "../util/io.h" #include "../util/util.h" #include "httprequest.h" #include "config.h" #include "vserver.h" #include "httplistener.h" #include "func.h" #include "error.h" void http_request_init(HTTPRequest *req) { req->connection = NULL; req->uri.ptr = NULL; HeaderArray *hd = malloc(sizeof(HeaderArray)); hd->next = NULL; hd->len = 0; hd->headers = calloc(16, sizeof(Header)); hd->alloc = 16; req->headers = hd; req->req_start = time(NULL); } void http_request_cleanup(HTTPRequest *req) { header_array_free(req->headers); free(req); } sstr_t http_request_get_abspath(HTTPRequest *req) { sstr_t uri = req->uri; int i = 0; if(uri.ptr[0] == '/') { return uri; } else if(sstrprefix(uri, S("http://"))) { i = 7; } else if(sstrprefix(uri, S("https://"))) { i = 8; } else if(!sstrcmp(uri, S("*"))) { return uri; } for(;i<uri.length;i++) { if(uri.ptr[i] == '/') { return sstrsubs(uri, i); } } return S("/"); } int handle_request(HTTPRequest *request, threadpool_t *thrpool) { // handle nsapi request // create pool pool_handle_t *pool = pool_create(); // create nsapi data structures NSAPISession *sn = pool_malloc(pool, sizeof(NSAPISession)); if(sn == NULL) { /* TODO: error */ } ZERO(sn, sizeof(NSAPISession)); NSAPIRequest *rq = pool_malloc(pool, sizeof(NSAPIRequest)); if(rq == NULL) { /* TODO: error */ } ZERO(rq, sizeof(NSAPIRequest)); rq->rq.req_start = request->req_start; rq->phase = NSAPIAuthTrans; // fill session structure sn->connection = request->connection; sn->netbuf = request->netbuf; sn->sn.pool = pool; //sn->sn.csd = stream_new_from_fd(pool, request->connection->fd); //sn->sn.csd = net_stream_from_fd(pool, request->connection->fd); IOStream *io; if(request->connection->ssl) { io = sslstream_new(pool, request->connection->ssl); sn->sn.ssl = 1; } else { io = sysstream_new(pool, request->connection->fd); } sn->sn.csd = httpstream_new(pool, io); sn->sn.client = pblock_create_pool(sn->sn.pool, 8); sn->sn.next = NULL; sn->sn.fill = 1; sn->sn.subject = NULL; // the session needs the current server configuration sn->config = request->connection->listener->cfg; // add ip to sn->client pblock char ip_str[INET_ADDRSTRLEN]; if(inet_ntop( AF_INET, &request->connection->address.sin_addr, ip_str, INET_ADDRSTRLEN) != NULL) { pblock_kvinsert(pb_key_ip, ip_str, INET_ADDRSTRLEN, sn->sn.client); } // init NSAPI request structure if(request_initialize(pool, request, rq) != 0) { log_ereport(LOG_FAILURE, "Cannot initialize request structure"); pool_destroy(pool); return 1; } // set default virtual server rq->vs = request->connection->listener->default_vs.vs; // Pass request line as "clf-request" // remove \r\n sstr_t clfreq = request->request_line; while(clfreq.ptr[clfreq.length - 1] < 33) { clfreq.length--; } request->request_line = clfreq; pblock_kvinsert( pb_key_clf_request, request->request_line.ptr, request->request_line.length, rq->rq.reqpb); // Pass method as "method" in reqpb, and also as method_num pblock_kvinsert( pb_key_method, request->method.ptr, request->method.length, rq->rq.reqpb); // TODO: method num //rqRq.rq.method_num = rqHdr->GetMethodNumber(); //PR_ASSERT(rqRq.rq.method_num != -1 || iStatus); // Pass protocol as "protocol" in reqpb, and also in protv_num pblock_kvinsert( pb_key_protocol, request->httpv.ptr, request->httpv.length, rq->rq.reqpb); if(!sstrcmp(request->httpv, S("HTTP/1.1"))) { rq->rq.protv_num = PROTOCOL_VERSION_HTTP11; } else if(!sstrcmp(request->httpv, S("HTTP/1.0"))) { rq->rq.protv_num = PROTOCOL_VERSION_HTTP10; } else { // invalid protocol version - abort log_ereport( LOG_FAILURE, "invalid protocol version: %.*s", (int)request->httpv.length, request->httpv.ptr); pool_destroy(pool); return 1; } /* * get absolute path and query of the request uri */ // TODO: check for '#' sstr_t absPath = http_request_get_abspath(request); if(!absPath.ptr) { // TODO: error msg return 1; } else if(absPath.ptr[0] == '*') { // TODO: implement global OPTIONS return 1; } sstr_t query; query.length = 0; for(int i=0;i<request->uri.length;i++) { if(request->uri.ptr[i] == '?') { /* split uri in path and query */ if(absPath.length > i + 2) { query.length = absPath.length - i - 1; query.ptr = absPath.ptr + i + 1; } absPath.length = i; // Pass any query as 'query' in reqpb pblock_kvinsert( pb_key_query, query.ptr, query.length, rq->rq.reqpb); break; } } // Get abs_path part of request URI, and canonicalize the path sstr_t orig_path = absPath; absPath.ptr = util_canonicalize_uri( pool, absPath.ptr, absPath.length, (int*)&absPath.length); if(!absPath.ptr) { log_ereport( LOG_WARN, "invalid request path: {%.*s}", (int)orig_path.length, orig_path.ptr); pool_destroy(pool); // TODO: 400 bad request return 1; } // Decode the abs_path if(util_uri_unescape_strict(absPath.ptr)) { // Pass the abs_path as 'uri' in reqpb pblock_kvinsert( pb_key_uri, absPath.ptr, absPath.length, rq->rq.reqpb); } else { // TODO: log error log_ereport( LOG_WARN, "uri unescape failed: {%.*s}", (int)absPath.length, absPath.ptr); // TODO: 400 bad request pblock_kvinsert(pb_key_uri, "/", 1, rq->rq.reqpb); } // pass http header to the NSAPI request structure int hlen = request->headers->len; HeaderArray *ha = request->headers; for(int i=0;i<=hlen;i++) { if(i == hlen) { ha = ha->next; if(ha == NULL) { break; } i = 0; hlen = ha->len; } Header header = ha->headers[i]; if(header.name.ptr[0] < 90) { header.name.ptr[0] += 32; } // change to lower case for(int j=0;j<header.name.length;j++) { if(header.name.ptr[j] > 64 && header.name.ptr[j] < 97) { header.name.ptr[j] += 32; } } pblock_nvlinsert( header.name.ptr, header.name.length, header.value.ptr, header.value.length, rq->rq.headers); } // get host and port char *hosthdr = pblock_findkeyval(pb_key_host, rq->rq.headers); if(hosthdr) { char *host = pool_strdup(pool, hosthdr); char *portstr = strchr(host, ':'); if(portstr) { *portstr = '\0'; } rq->host = host; } else { rq->host = NULL; // TODO: value from listener } rq->port = request->connection->listener->port; if(rq->host) { VirtualServer *vs = ucx_map_cstr_get(sn->config->host_vs, rq->host); if(vs) { rq->vs = vs; } else { log_ereport( LOG_VERBOSE, "Unkown host '%s': using default virtual server", rq->host); } } // parse connection header rq->rq.rq_attr.keep_alive = (rq->rq.protv_num >= PROTOCOL_VERSION_HTTP11); char *conn_str = pblock_findkeyval(pb_key_connection, rq->rq.headers); if(conn_str) { if(!strcasecmp(conn_str, "keep-alive")) { rq->rq.rq_attr.keep_alive = 1; } else if(!strcasecmp(conn_str, "close")) { rq->rq.rq_attr.keep_alive = 0; } } // check for request body and prepare input buffer char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers); if(ctlen_str) { int ctlen = atoi(ctlen_str); //printf("request body length: %d\n", ctlen); netbuf *nb = request->netbuf; // create new netbuf HttpStream *net_io = (HttpStream*)httpstream_new(pool, io); net_io->max_read = ctlen; sn->sn.inbuf = pool_malloc(pool, sizeof(netbuf)); sn->sn.inbuf->sd = net_io; sn->sn.inbuf->pos = 0; // prepare buffer int cur_input_len = nb->cursize - nb->pos; if(cur_input_len >= ctlen) { /* * all data is already in the primary input buffer * just link the new netbuf to the primary buffer */ sn->sn.inbuf->maxsize = ctlen; sn->sn.inbuf->cursize = ctlen; sn->sn.inbuf->inbuf = nb->inbuf + nb->pos; } else { sn->sn.inbuf->maxsize = (ctlen > 2048) ? (2048) : (ctlen); sn->sn.inbuf->inbuf = pool_malloc(pool, sn->sn.inbuf->maxsize); if(cur_input_len > 0) { // we have read a part of the request body -> copy to netbuf memcpy(sn->sn.inbuf->inbuf, nb->inbuf+nb->pos, cur_input_len); } sn->sn.inbuf->cursize = cur_input_len; } } else { sn->sn.inbuf = NULL; } // // Send the request to the NSAPI system // // compare current and listener thread pool threadpool_t *lstp = request->connection->listener->threadpool; sn->defaultpool = lstp; if(lstp == thrpool) { sn->currentpool = thrpool; nsapi_handle_request(sn, rq); } else { // execute nsapi functions on a different thread pool nsapi_change_threadpool(sn, rq, lstp); } return 0; } void header_add(HeaderArray *hd, sstr_t name, sstr_t value) { while(hd->len >= hd->alloc) { if(hd->next == NULL) { HeaderArray *block = malloc(sizeof(HeaderArray)); block->next = NULL; block->len = 0; block->headers = calloc(16, sizeof(Header)); block->alloc = 16; hd->next = block; } hd = hd->next; } hd->headers[hd->len].name = name; hd->headers[hd->len].value = value; hd->len++; } void header_array_free(HeaderArray *hd) { HeaderArray *next; while(hd) { next = hd->next; free(hd->headers); free(hd); hd = next; } } /* * NSAPI Processing */ int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq) { int r = REQ_NOACTION; do { switch(rq->phase) { case NSAPIAuthTrans: { r = nsapi_authtrans(sn, rq); if(r != REQ_PROCEED) { break; } rq->phase++; nsapi_context_next_stage(&rq->context); } case NSAPINameTrans: { //printf(">>> NameTrans\n"); r = nsapi_nametrans(sn, rq); if(r != REQ_PROCEED) { break; } rq->phase++; nsapi_context_next_stage(&rq->context); } case NSAPIPathCheck: { //printf(">>> PathCheck\n"); r = nsapi_pathcheck(sn, rq); if(r != REQ_PROCEED) { break; } rq->phase++; nsapi_context_next_stage(&rq->context); } case NSAPIObjectType: { //printf(">>> ObjectType\n"); r = nsapi_objecttype(sn, rq); if(r != REQ_PROCEED) { break; } rq->phase++; nsapi_context_next_stage(&rq->context); } case NSAPIService: { //printf(">>> Service\n"); r = nsapi_service(sn, rq); if(r != REQ_PROCEED) { break; } rq->phase = NSAPIAddLog; // skip NSAPIError nsapi_context_next_stage(&rq->context); } case NSAPIAddLog: { //printf(">>> AddLog\n"); r = nsapi_addlog(sn, rq); if(r == REQ_PROCESSING) { break; } // finish request r = REQ_PROCEED; break; } default: // should not happen, but when it does, finish the req case REQ_FINISH: { //printf(">>> Finish\n"); //r = nsapi_finish_request(sn, rq); r = REQ_PROCEED; break; } case NSAPIError: { //printf(">>> Error\n"); r = nsapi_error(sn, rq); if(r == REQ_PROCEED) { // restart the loop to switch to AddLog directive r = REQ_RESTART; rq->phase = NSAPIAddLog; nsapi_context_next_stage(&rq->context); } else { /* * an error occured while handling an error * leave loop to finish the request (without AddLog) */ r = REQ_PROCEED; break; } } } if(r == REQ_ABORTED) { // switch to NSAPIError rq->phase = NSAPIError; nsapi_context_next_stage(&rq->context); r = REQ_RESTART; } } while (r == REQ_RESTART); if(r != REQ_PROCESSING) { r = nsapi_finish_request(sn, rq); } return r; } int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) { if(rq->rq.rq_attr.keep_alive) { SessionHandler *sh = sn->connection->session_handler; sh->keep_alive(sh, sn->connection); /* * keep the connection object * the sn->config is referenced by the connection, so we don't * unref it */ } else { connection_destroy(sn->connection); cfg_unref(sn->config); } // free all memory free(sn->netbuf->inbuf); free(sn->netbuf); pool_destroy(sn->sn.pool); return 0; } int nsapi_authtrans(NSAPISession *sn, NSAPIRequest *rq) { HTTPObjectConfig *objconf = rq->vs->objects; httpd_object *obj = objconf->objects[0]; dtable *dt = object_get_dtable(obj, NSAPIAuthTrans); int ret = rq->context.last_req_code; for(int i=NCX_DI(rq);i<dt->ndir;i++) { if(ret == REQ_NOACTION) { directive *d = dt->dirs[i]; ret = nsapi_exec(d, sn, rq); } if(ret != REQ_NOACTION) { /* * if a saf is still processing, we need to save the context, to * process this object at a later time */ if(ret == REQ_PROCESSING) { // save nsapi context // add +1 to start next round with next function rq->context.dtable_index = i + 1; } return ret; } } return REQ_PROCEED; } int nsapi_nametrans(NSAPISession *sn, NSAPIRequest *rq) { HTTPObjectConfig *objconf = rq->vs->objects; //printf("nsapi_nametrans\n"); httpd_objset *objset = objset_create(sn->sn.pool); rq->rq.os = objset; // first object in objconf is the default object TODO: make sure it is objset_add_object(sn->sn.pool, objset, objconf->objects[0]); httpd_object *obj = objset->obj[0]; // nametrans only in default object dtable *dt = object_get_dtable(obj, NSAPINameTrans); // execute directives int ret = rq->context.last_req_code; char *name = NULL; char *ppath = NULL; for(int i=NCX_DI(rq);i<dt->ndir;i++) { if(ret == REQ_NOACTION) { directive *d = dt->dirs[i]; ret = nsapi_exec(d, sn, rq); } // check for name or ppath name = pblock_findkeyval(pb_key_name, rq->rq.vars); ppath = pblock_findkeyval(pb_key_ppath, rq->rq.vars); // add additional objects to the objset if(add_objects(objconf, objset, sn, rq, name, ppath) == REQ_ABORTED) { log_ereport(LOG_FAILURE, "add_objects failed"); return REQ_ABORTED; } if(ret != REQ_NOACTION) { /* * if a saf is still processing, we need to save the context, to * process this object at a later time */ if(ret == REQ_PROCESSING) { // save nsapi context // add +1 to start next round with next function rq->context.dtable_index = i + 1; } else if(ret == REQ_PROCEED) { char *pp = pblock_findkeyval(pb_key_ppath, rq->rq.vars); pblock_kvinsert(pb_key_path, pp, strlen(pp), rq->rq.vars); } return ret; } } // if no function has set the ppath var, translate it to docroot if(ret == REQ_NOACTION && ppath == NULL) { sstr_t docroot = rq->vs->document_root; if(docroot.length < 1) { log_ereport( LOG_WARN, "VirtualServer(%.*s) docroot too short", (int)rq->vs->name.length, rq->vs->name.ptr); return REQ_ABORTED; // docroot too short } // if there is a trailing '/', remove it /* if(docroot.ptr[docroot.length - 1] == '/') { docroot.length--; } sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); sstr_t translated; translated.length = docroot.length + uri.length; translated.ptr = alloca(translated.length + 1); translated = sstrncat(translated, 2, docroot, uri); pblock_kvinsert( pb_key_ppath, translated.ptr, translated.length, rq->rq.vars); */ sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); request_set_path(docroot, uri, rq->rq.vars); } // TODO: remove ppath char *pp = pblock_findkeyval(pb_key_ppath, rq->rq.vars); pblock_kvinsert(pb_key_path, pp, strlen(pp), rq->rq.vars); return REQ_PROCEED; } int nsapi_pathcheck(NSAPISession *sn, NSAPIRequest *rq) { //printf("nsapi_pathcheck\n"); httpd_objset *objset = rq->rq.os; if(NCX_OI(rq) == -1) { NCX_OI(rq) = objset->pos - 1; } int ret = rq->context.last_req_code; for(int i=NCX_OI(rq);i>=0;i--) { httpd_object *obj = objset->obj[i]; dtable *dt = object_get_dtable(obj, NSAPIPathCheck); // execute directives for(int j=NCX_DI(rq);j<dt->ndir;j++) { if(ret == REQ_NOACTION || ret == REQ_PROCEED) { directive *d = dt->dirs[j]; ret = nsapi_exec(d, sn, rq); } else { if(ret == REQ_PROCESSING) { // save nsapi context rq->context.objset_index = i; // add +1 to start next round with next function rq->context.dtable_index = j + 1; } return ret; } } } return REQ_PROCEED; } int nsapi_objecttype(NSAPISession *sn, NSAPIRequest *rq) { //printf("nsapi_objecttype\n"); httpd_objset *objset = rq->rq.os; if(NCX_OI(rq) == -1) { // object index is undefined -> define correct object index NCX_OI(rq) = objset->pos - 1; } int ret = rq->context.last_req_code; for(int i=NCX_OI(rq);i>=0;i--) { httpd_object *obj = objset->obj[i]; dtable *dt = object_get_dtable(obj, NSAPIObjectType); // execute directives for(int j=NCX_DI(rq);j<dt->ndir;j++) { if(ret == REQ_NOACTION) { directive *d = dt->dirs[j]; ret = nsapi_exec(d, sn, rq); } switch(ret) { case REQ_PROCEED: { char *type = pblock_findkeyval( pb_key_content_type, rq->rq.srvhdrs); if(type == NULL) { ret = REQ_NOACTION; break; } return ret; } case REQ_PROCESSING: { // save nsapi context rq->context.objset_index = i; // add +1 to start next round with next function rq->context.dtable_index = j + 1; return ret; } case REQ_NOACTION: { break; } default: { return ret; } } } } /* * No function returned with REQ_PROCEED, but we need a content type. * If the path ends with a '/', we set the content type to * 'internal/directory' so that 'index-common' can serve the content. * Otherwise we set the content type to text/plain */ sstr_t path = sstr(pblock_findkeyval(pb_key_ppath, rq->rq.vars)); sstr_t ct; if(path.ptr[path.length - 1] == '/') { // directory ct = sstrn("internal/directory", 18); } else { ct = sstrn("text/plain", 10); } pblock_kvinsert(pb_key_content_type, ct.ptr, ct.length, rq->rq.srvhdrs); return REQ_PROCEED; } int nsapi_service(NSAPISession *sn, NSAPIRequest *rq) { //printf("nsapi_service\n"); httpd_objset *objset = rq->rq.os; if(NCX_OI(rq) == -1) { NCX_OI(rq) = objset->pos - 1; } int ret = rq->context.last_req_code; char *content_type = NULL; char *method = NULL; for(int i=NCX_OI(rq);i>=0;i--) { httpd_object *obj = objset->obj[i]; dtable *dt = object_get_dtable(obj, NSAPIService); // execute directives for(int j=NCX_DI(rq);j<dt->ndir;j++) { if(ret == REQ_NOACTION) { directive *d = dt->dirs[j]; // check type parameter char *dtp = pblock_findkeyval(pb_key_type, d->param); if(dtp) { // type parameter for directive if(!content_type) { content_type = pblock_findkeyval( pb_key_content_type, rq->rq.srvhdrs); } // compare types if(!contenttype_match(sstr(dtp), sstr(content_type))) { continue; } } // check method parameter char *dmt = pblock_findkeyval(pb_key_method, d->param); if(dmt) { if(!method) { method = pblock_findkeyval(pb_key_method, rq->rq.reqpb); } if(!method_match(dmt, method)) { continue; } } // execute the saf ret = nsapi_exec(d, sn, rq); } if(ret != REQ_NOACTION) { if(ret == REQ_PROCEED) { /* * flush buffer and add termination if chunked encoding * is enabled */ net_finish(sn->sn.csd); } else if(ret == REQ_PROCESSING) { // save nsapi context rq->context.objset_index = i; // add +1 to start next round with next function rq->context.dtable_index = j + 1; } return ret; } } } return ret; } int nsapi_error(NSAPISession *sn, NSAPIRequest *rq) { //printf("nsapi_error\n"); httpd_objset *objset = rq->rq.os; if(NCX_OI(rq) == -1) { NCX_OI(rq) = objset->pos - 1; } int ret = rq->context.last_req_code; for(int i=NCX_OI(rq);i>=0;i--) { httpd_object *obj = objset->obj[i]; dtable *dt = object_get_dtable(obj, NSAPIError); // execute directives for(int j=NCX_DI(rq);j<dt->ndir;j++) { if(ret == REQ_NOACTION) { directive *d = dt->dirs[j]; // check status code parameter char *status = pblock_findkeyval(pb_key_type, d->param); if(status) { int statuscode = atoi(status); if(statuscode != rq->rq.status_num) { continue; } } // execute the saf ret = nsapi_exec(d, sn, rq); } if(ret == REQ_ABORTED) { // if an error directive fails, we use the default // error handler break; } if(ret != REQ_NOACTION) { if(ret == REQ_PROCEED) { /* * flush buffer and add termination if chunked encoding * is enabled */ net_finish(sn->sn.csd); } else if(ret == REQ_PROCESSING) { // save nsapi context rq->context.objset_index = i; // add +1 to start next round with next function rq->context.dtable_index = j + 1; } return ret; } } } if(ret != REQ_PROCEED) { // default error handler nsapi_error_request((Session*)sn, (Request*)rq); } return ret; } int nsapi_addlog(NSAPISession *sn, NSAPIRequest *rq) { //printf("nsapi_addlog\n"); httpd_objset *objset = rq->rq.os; if(NCX_OI(rq) == -1) { NCX_OI(rq) = objset->pos - 1; } int ret = rq->context.last_req_code; for(int i=NCX_OI(rq);i>=0;i--) { httpd_object *obj = objset->obj[i]; dtable *dt = object_get_dtable(obj, NSAPIAddLog); // execute directives for(int j=NCX_DI(rq);j<dt->ndir;j++) { if(ret == REQ_NOACTION) { directive *d = dt->dirs[j]; ret = nsapi_exec(d, sn, rq); } if(ret != REQ_NOACTION) { if(ret == REQ_PROCESSING) { // save nsapi context rq->context.objset_index = i; // add +1 to start next round with next function rq->context.dtable_index = j + 1; } return ret; } } } return REQ_PROCEED; } struct _tpd_data { NSAPISession *sn; NSAPIRequest *rq; directive *directive; threadpool_t *threadpool; }; int nsapi_exec(directive *d, NSAPISession *sn, NSAPIRequest *rq) { // TODO: condition if(d->cond) { if(!condition_evaluate(d->cond, (Session*)sn, (Request*)rq)) { return REQ_NOACTION; } } char *poolname = pblock_findkeyval(pb_key_pool, d->param); if(poolname) { threadpool_t *pool = get_threadpool(sstr(poolname)); if(pool && pool != sn->currentpool) { // execute directive in different thread pool return nsapi_exec_tp(d, sn, rq, pool); } } else if(sn->currentpool != sn->defaultpool) { // if no pool is set, use always the default thread pool return nsapi_exec_tp(d, sn, rq, sn->defaultpool); } return SAF_EXEC(d->func, d->param, (Session*)sn, (Request*)rq); } int nsapi_exec_tp( directive *d, NSAPISession *sn, NSAPIRequest *rq, threadpool_t *pool) { struct _tpd_data *data = malloc(sizeof(struct _tpd_data)); if(data == NULL) { // TODO: set error return REQ_ABORTED; } data->sn = sn; data->rq = rq; data->directive = d; data->threadpool = pool; sn->currentpool = pool; threadpool_run(pool, thrpool_exec, data); return REQ_PROCESSING; } void nsapi_function_return(Session *session, Request *request, int ret) { NSAPISession *sn = (NSAPISession*)session; NSAPIRequest *rq = (NSAPIRequest*)request; rq->context.last_req_code = ret; if(sn->currentpool != sn->defaultpool) { nsapi_change_threadpool(sn, rq, sn->defaultpool); } else { nsapi_handle_request(sn, rq); } } void nsapi_change_threadpool( NSAPISession *sn, NSAPIRequest *rq, threadpool_t *thrpool) { struct _tpd_data *data = malloc(sizeof(struct _tpd_data)); data->sn = sn; data->rq = rq; data->threadpool = thrpool; threadpool_run(thrpool, thrpool_change, data); } void* thrpool_exec(void *d) { struct _tpd_data *data = d; data->sn->currentpool = data->threadpool; int r = SAF_EXEC( data->directive->func, data->directive->param, (Session*)data->sn, (Request*)data->rq); nsapi_function_return((Session*)data->sn, (Request*)data->rq, r); free(data); return NULL; } void* thrpool_change(void *d) { struct _tpd_data *data = d; data->sn->currentpool = data->threadpool; nsapi_handle_request(data->sn, data->rq); free(data); return NULL; } /* * checks if the method matches the cmp string * the format of cmp is a single method string or a list of methods * (method1|method2|method3|...) */ int method_match(char *cmp, char *method) { if(cmp[0] != '(') { /* not a list of methods, so just compare the 2 strings */ if(!strcmp(cmp, method)) { return 1; } } else if(cmp[0] == 0) { // empty string log_ereport( LOG_WARN, "Skipped service saf with empty method parameter"); return 0; } size_t method_len = strlen(method); size_t last_pos = 0; cmp++; /* skip '(' */ for(int i=0;cmp[i]!=0;i++) { char c = cmp[i]; if(c == '|' || c == ')') { size_t len = i - last_pos; if(len == method_len) { char *cp = cmp + last_pos; if(!memcmp(cp, method, len)) { return 1; } } last_pos = i + 1; } } return 0; } /* * checks if the content type matches the cmp string * the format of cmp is a single string with wildcards or a list * of types (also with wildcard support) * (type1|type2*) */ int contenttype_match(sstr_t cmp, sstr_t ctype) { if(cmp.ptr[0] != '(') { if(cmp.ptr[0] == '*') { cmp.ptr++; cmp.length--; return sstrsuffix(ctype, cmp); } else if(cmp.ptr[cmp.length-1] == '*') { cmp.length--; return sstrprefix(ctype, cmp); } else { return !sstrcmp(cmp, ctype); } } else if(cmp.ptr[0] == 0) { log_ereport(LOG_WARN, "Skipped service saf with empty type parameter"); return 0; } cmp = sstrsubsl(cmp, 1, cmp.length - 2); int begin = 0; for(int i=0;i<cmp.length;i++) { if(cmp.ptr[i] == '|') { if(contenttype_match(sstrsubsl(cmp, begin, i-begin), ctype)) { return 1; } begin = i + 1; } } return contenttype_match(sstrsubs(cmp, begin), ctype); } /* * adds objects with specific name or path to the httpd_objset */ int add_objects( HTTPObjectConfig *objs, httpd_objset *os, NSAPISession *sn, NSAPIRequest *rq, char *name, char *path) { /* first, add all objects with a matching path */ /* TODO */ /* add object with object with matching name */ if(name == NULL) { return REQ_PROCEED; } for(int i=0;i<objs->nobj;i++) { httpd_object *obj = objs->objects[i]; if(obj->name && !strcmp(name, obj->name)) { objset_add_object(sn->sn.pool, os, obj); } } return REQ_PROCEED; }