Wed, 16 May 2012 12:47:28 +0200
added event handler
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2011 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 <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" HTTPRequest *http_request_new() { HTTPRequest *req = malloc(sizeof(HTTPRequest)); 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; return req; } int handle_request(HTTPRequest *request) { // handle nsapi request // create pool request->pool = pool_create(); // create nsapi data structures NSAPISession *sn = malloc(sizeof(NSAPISession)); NSAPIRequest *rq = malloc(sizeof(NSAPIRequest)); request->rq = rq; rq->phase = NSAPIAuthTrans; // fill session structure sn->sys_fd = request->connection->fd; sn->sn.pool = pool_create(); sn->sn.csd = stream_new_from_fd(request->connection->fd); sn->sn.client = pblock_create_pool(sn->sn.pool, 8); sn->sn.next = NULL; sn->sn.fill = 1; sn->sn.subject = NULL; /* 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(request->pool, request, rq) != 0) { printf("Cannot initialize request structure\n"); return 1; } // set default virtual server rq->vs = request->connection->listener->default_vs.vs; /* Pass request line as "clf-request" */ /* TODO: with or without \r\n ? */ 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); // TODO: protocol num /* * get absolute path and query of the request uri */ // TODO: check for '#' sstr_t absPath = request->uri; 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 */ absPath.ptr = util_canonicalize_uri( request->pool, absPath.ptr, absPath.length, (int*)&absPath.length); /* Decode the abs_path */ // TODO: decode abs_path (done?) /* Pass the abs_path as 'uri' in reqpb */ pblock_kvinsert( pb_key_uri, absPath.ptr, absPath.length, 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; } if(ha->headers[i].name[0] < 90) { ha->headers[i].name[0] += 32; } /* change to lower case */ char *header_name = ha->headers[i].name; for(int i=0;header_name[i]!=0;i++) { if(header_name[i] > 64 && header_name[i] < 97) { header_name[i] += 32; } } pblock_nvinsert( ha->headers[i].name, ha->headers[i].value, rq->rq.headers); } /* 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 */ NetIOStream *net_io = (NetIOStream*)net_stream_from_fd( request->connection->fd); net_io->max_read = ctlen; sn->sn.inbuf = malloc(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 = malloc(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 nsapi_handle_request(sn, rq); return 0; } void header_add(HeaderArray *hd, char *name, char *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++; } /* * NSAPI Processing * TODO: add this to new file */ int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq) { // TODO: threadpool 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++; nsapi_context_next_stage(&rq->context); } case NSAPIAddLog: { //printf(">>> AddLog\n"); rq->phase++; nsapi_context_next_stage(&rq->context); } case REQ_FINISH: { //printf(">>> Finish\n"); r = nsapi_finish_request(sn, rq); } } } while (r == REQ_RESTART); r = nsapi_finish_request(sn, rq); return r; } int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) { // TODO: free memory close(sn->sys_fd); 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++) { directive *d = dt->dirs[i]; ret = d->func->func(d->param, (Session*)sn, (Request*)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++) { directive *d = dt->dirs[i]; ret = d->func->func(d->param, (Session*)sn, (Request*)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) { fprintf(stderr, "add_objects failed\n"); 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; } 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) { printf("docroot too short\n"); return REQ_ABORTED; /* docroot too short */ } /* if there is a trailing '/', remove it */ if(docroot.ptr[docroot.length - 1] == '/') { docroot.ptr[docroot.length - 1] = 0; // TODO: can I do this? 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(2, translated, docroot, uri); pblock_kvinsert( pb_key_ppath, translated.ptr, translated.length, 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=0;j<dt->ndir;j++) { directive *d = dt->dirs[j]; /* execute the saf */ ret = d->func->func(d->param, (Session*)sn, (Request*)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; } else if(ret == REQ_ABORTED) { if(rq->rq.status_num == PROTOCOL_UNAUTHORIZED) { // TODO: unify error handling protocol_start_response((Session*)sn, (Request*)rq); } } 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=0;j<dt->ndir;j++) { directive *d = dt->dirs[j]; ret = d->func->func(d->param, (Session*)sn, (Request*)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=0;j<dt->ndir;j++) { 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(strcmp(dtp, content_type) != 0) { 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 = d->func->func(d->param, (Session*)sn, (Request*)rq); if(ret != REQ_PROCEED) { fprintf(stderr, "saf not proceed\n"); } 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 ret; } /* * 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 */ 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; } /* * 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; }