--- a/src/server/daemon/httprequest.c Tue Aug 13 22:14:32 2019 +0200 +++ b/src/server/daemon/httprequest.c Sat Sep 24 16:26:10 2022 +0200 @@ -87,6 +87,48 @@ return S("/"); } +NSAPISession* nsapisession_create(pool_handle_t *pool) { + NSAPISession *sn = pool_malloc(pool, sizeof(NSAPISession)); + if(!sn) { + return NULL; + } + + ZERO(sn, sizeof(NSAPISession)); + + sn->sn.pool = pool; + sn->allocator = util_pool_allocator(pool); + + sn->sn.client = pblock_create_pool(sn->sn.pool, 8); + if(!sn->sn.client) { + pool_free(pool, sn); + return NULL; + } + sn->sn.fill = 1; + + return sn; +} + +int nsapisession_setconnection(NSAPISession *sn, Connection *conn, netbuf *inbuf, IOStream **io) { + SessionHandler *sh = conn->session_handler; + WSBool ssl; + IOStream *sio = sh->create_iostream(sh, conn, sn->sn.pool, &ssl); + if(!sio) { + return 1; + } + *io = sio; + IOStream *http = httpstream_new(sn->sn.pool, sio); + if(!http) { + return 1; + } + sn->connection = conn; + sn->netbuf = inbuf; + sn->sn.csd = http; + sn->sn.ssl = ssl; + sn->sn.inbuf = inbuf; + sn->sn.inbuf->sd = http; + return 0; +} + int handle_request(HTTPRequest *request, threadpool_t *thrpool, EventHandler *ev) { // handle nsapi request @@ -94,11 +136,11 @@ pool_handle_t *pool = pool_create(); // create nsapi data structures - NSAPISession *sn = pool_malloc(pool, sizeof(NSAPISession)); + NSAPISession *sn = nsapisession_create(pool); if(sn == NULL) { /* TODO: error */ } - ZERO(sn, sizeof(NSAPISession)); + NSAPIRequest *rq = pool_malloc(pool, sizeof(NSAPIRequest)); if(rq == NULL) { /* TODO: error */ @@ -108,25 +150,16 @@ rq->phase = NSAPIAuthTrans; // fill session structure - sn->connection = request->connection; - sn->netbuf = request->netbuf; - sn->sn.pool = pool; - SessionHandler *sh = request->connection->session_handler; - WSBool ssl; - IOStream *io = sh->create_iostream(sh, request->connection, pool, &ssl); - sn->sn.csd = httpstream_new(pool, io); - sn->sn.ssl = ssl; - - sn->sn.client = pblock_create_pool(sn->sn.pool, 8); - sn->sn.next = NULL; - sn->sn.fill = 1; - sn->sn.subject = NULL; + IOStream *io = NULL; + if(nsapisession_setconnection(sn, request->connection, request->netbuf, &io)) { + // TODO: error + } if(!ev) { ev = ev_instance(get_default_event_handler()); } sn->sn.ev = ev; - + // the session needs the current server configuration sn->config = request->connection->listener->cfg; @@ -198,13 +231,13 @@ /* * get absolute path and query of the request uri */ - // TODO: check for '#' + // TODO: check for '#' #72 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 + // TODO: implement global OPTIONS #71 return 1; } @@ -342,44 +375,42 @@ // 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); + int64_t ctlen; + if(util_strtoint(ctlen_str, &ctlen)) { + netbuf *nb = sn->netbuf; + HttpStream *net_io = (HttpStream*)sn->sn.csd; + net_io->read_eof = WS_FALSE; - netbuf *nb = request->netbuf; + // how many bytes are already read and in the buffer + int cur_input_available = nb->cursize - nb->pos; - // 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); + if(cur_input_available >= ctlen) { + // we have the whole request body in the buffer and + // maybe even more + // no more read from the socket is necessary to get the body, + // therefore disable it + net_io->max_read = 0; + } else { + // read still required to get the complete request body + net_io->max_read = ctlen - cur_input_available; } - - sn->sn.inbuf->cursize = cur_input_len; + //printf("request body length: %d\n", ctlen); + } // else: should we abort? + } + char *transfer_encoding = pblock_findkeyval(pb_key_transfer_encoding, rq->rq.headers); + if(transfer_encoding) { + if(!strcmp(transfer_encoding, "chunked")) { + netbuf *nb = sn->netbuf; + sn->buffer = (char*)nb->inbuf; + sn->pos = nb->pos; + sn->cursize = nb->cursize; + + if(httpstream_enable_chunked_read(sn->sn.csd, sn->buffer, nb->maxsize, &sn->cursize, &sn->pos)) { + pool_destroy(pool); + // TODO: error 500 + return 1; + } } - } else { - sn->sn.inbuf = NULL; } // @@ -535,6 +566,33 @@ } int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) { + request_free_resources(sn, rq); + + WSBool read_stream_eof = httpstream_eof(sn->sn.csd); + if(!read_stream_eof) { + log_ereport(LOG_WARN, "request input stream not closed"); + // TODO: clean stream + rq->rq.rq_attr.keep_alive = 0; // workaround + } + if(sn->pos < sn->cursize) { + log_ereport(LOG_WARN, "nsapi_finish_request: TODO: remaining bytes in buffer"); + // TODO: reuse buffer in next request + rq->rq.rq_attr.keep_alive = 0; // workaround + } + + if(rq->rq.senthdrs) { + // flush buffer and add termination if chunked encoding + // is enabled + net_finish(sn->sn.csd); + } else { + // why was no response sent? + // something must have gone wrong + // terminate the session + char *clf_req = pblock_findkeyval(pb_key_clf_request, rq->rq.reqpb); + log_ereport(LOG_WARN, "nsapi_finish_request: no response header: request: %s", clf_req); + rq->rq.rq_attr.keep_alive = 0; + } + if(rq->rq.rq_attr.keep_alive) { SessionHandler *sh = sn->connection->session_handler; sh->keep_alive(sh, sn->connection); @@ -557,6 +615,16 @@ return 0; } +void request_free_resources(NSAPISession *sn, NSAPIRequest *rq) { + if(!rq->resources) return; + + UcxMapIterator i = ucx_map_iterator(rq->resources); + ResourceData *resource; + UCX_MAP_FOREACH(key, resource, i) { + resourcepool_free(&sn->sn, &rq->rq, resource); + } +} + int nsapi_authtrans(NSAPISession *sn, NSAPIRequest *rq) { HTTPObjectConfig *objconf = rq->vs->objects; httpd_object *obj = objconf->objects[0]; @@ -835,12 +903,12 @@ } if(ret != REQ_NOACTION) { - if(ret == REQ_PROCEED) { - /* - * flush buffer and add termination if chunked encoding - * is enabled - */ - net_finish(sn->sn.csd); + if(ret == REQ_PROCEED && !rq->rq.senthdrs) { + // a service SAF must send a response + // senthdrs == 0 indicators something has gone + // wrong + protocol_status(&sn->sn, &rq->rq, 500, NULL); + ret = REQ_ABORTED; } else if(ret == REQ_PROCESSING) { // save nsapi context rq->context.objset_index = i; @@ -875,11 +943,18 @@ if(ret == REQ_NOACTION) { directive *d = dt->dirs[j]; - // check status code parameter + // check status code parameter + // Error SAFs can specify, for which status code they should + // be executed char *status = pblock_findkeyval(pb_key_type, d->param); if(status) { - int statuscode = atoi(status); - if(statuscode != rq->rq.status_num) { + int64_t statuscode = -1; + if(!util_strtoint(status, &statuscode)) { + log_ereport( + LOG_WARN, + "nsapi_error: directive '%s' ignored: invalid type parameter: integer status code expected", + d->func->name); + } else if(statuscode != rq->rq.status_num) { continue; } } @@ -895,10 +970,8 @@ } if(ret != REQ_NOACTION) { if(ret == REQ_PROCEED) { - /* - * flush buffer and add termination if chunked encoding - * is enabled - */ + // flush buffer and add termination if chunked encoding + // is enabled net_finish(sn->sn.csd); } else if(ret == REQ_PROCESSING) { // save nsapi context @@ -914,7 +987,9 @@ if(ret != REQ_PROCEED) { // default error handler - nsapi_error_request((Session*)sn, (Request*)rq); + if(!rq->rq.senthdrs) { + nsapi_error_request((Session*)sn, (Request*)rq); + } } return ret;