diff -r 1fdbf4170ef4 -r b8bf95b39952 src/server/daemon/httprequest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/httprequest.c Sat Jan 14 13:53:44 2012 +0100 @@ -0,0 +1,532 @@ +/* + * 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 +#include + + +#include "../public/nsapi.h" +#include "../util/pool.h" +#include "../util/pblock.h" +#include "../util/io.h" +#include "../util/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);indir;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.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;jndir;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; + 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;jndir;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; + } + } + + 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;inobj;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; +}