Fri, 30 Dec 2011 15:19:16 +0100
Fixed response header bug
/* * 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 "nsapi.h" #include "pool.h" #include "pblock.h" #include "io.h" #include "util.h" #include "httprequest.h" #include "conf.h" #include "vserver.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 = conf_get_default_vs(); /* Pass request line as "clf-request" */ 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 /* Pass any query as "query" in reqpb */ // TODO: query /* Get abs_path part of request URI, and canonicalize the path */ sstr_t absPath = request->uri; // TODO: get abs_path absPath.ptr = util_canonicalize_uri( request->pool, absPath.ptr, absPath.length, (int*)&absPath.length); /* Decode the abs_path */ // TODO: decode abs_path /* Pass the abs_path as "uri" in reqpb */ // TODO: pass abs_path to reqpb // TODO: replace this code 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; } 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(sizeof(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: { 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"); 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); return r; } int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) { // TODO: free memory close(sn->sys_fd); return 0; } 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]; printf("execute [%s]\n", d->func->name); 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.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_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; 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]; 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; } return ret; } } } return ret; } /* * 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)) { printf("name is %s -> add object %s\n", name, obj->name); objset_add_object(sn->sn.pool, os, obj); } } return REQ_PROCEED; }