#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 "event.h"
#include "httplistener.h"
#include "func.h"
#include "error.h"
void http_request_init(HTTPRequest *req) {
memset(req,
0,
sizeof(HTTPRequest));
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);
}
cxmutstr http_request_get_abspath(HTTPRequest *req) {
cxmutstr uri = req->uri;
int i =
0;
if(uri.ptr[
0] ==
'/') {
return uri;
}
else if(cx_strprefix(cx_strcast(uri), (cxstring)
CX_STR(
"http://"))) {
i =
7;
}
else if(cx_strprefix(cx_strcast(uri), (cxstring)
CX_STR(
"https://"))) {
i =
8;
}
else if(!cx_strcmp(cx_strcast(uri), (cxstring)
CX_STR(
"*"))) {
return uri;
}
for(;i<uri.length;i++) {
if(uri.ptr[i] ==
'/') {
return cx_strsubs_m(uri, i);
}
}
return (cxmutstr){
"/",
1 };
}
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->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) {
pool_handle_t *pool = pool_create();
if(!pool) {
log_ereport(
LOG_FAILURE,
"cannot create new memory pool for http request");
return 1;
}
int ret = nsapi_start_request(request, thrpool, ev, pool);
if(ret) {
pool_destroy(pool);
}
return ret;
}
int nsapi_start_request(HTTPRequest *request,
threadpool_t *thrpool, EventHandler *ev,
pool_handle_t *pool) {
log_ereport(
LOG_DEBUG,
"trace reqid: %016llx nsapi_start_request", (
unsigned long long int)request->connection->id);
NSAPISession *sn = nsapisession_create(pool);
if(sn ==
NULL) {
return 1;
}
NSAPIRequest *rq = pool_malloc(pool,
sizeof(NSAPIRequest));
if(rq ==
NULL) {
return 1;
}
ZERO(rq,
sizeof(NSAPIRequest));
rq->rq.req_start = request->req_start;
rq->phase = NSAPIAuthTrans;
IOStream *io =
NULL;
if(nsapisession_setconnection(sn, request->connection, request->netbuf, &io)) {
return 1;
}
if(!ev) {
ev = ev_instance(get_default_event_handler());
}
sn->sn.ev = ev;
sn->config = request->connection->listener->cfg;
if(request->connection->addr_type ==
CONN_ADDR_IPV4) {
char ip_str[
INET_ADDRSTRLEN];
if(inet_ntop(
AF_INET,
&request->connection->address.address_v4.sin_addr,
ip_str,
INET_ADDRSTRLEN) !=
NULL)
{
pblock_kvinsert(pb_key_ip, ip_str,
INET_ADDRSTRLEN, sn->sn.client);
}
}
else if(request->connection->addr_type ==
CONN_ADDR_IPV6) {
char ip_str[
INET6_ADDRSTRLEN];
if(inet_ntop(
AF_INET6,
&request->connection->address.address_v6.sin6_addr,
ip_str,
INET6_ADDRSTRLEN) !=
NULL)
{
pblock_kvinsert(pb_key_ip, ip_str,
INET6_ADDRSTRLEN, sn->sn.client);
}
}
if(request_initialize(pool, request, rq) !=
0) {
log_ereport(
LOG_FAILURE,
"Cannot initialize request structure");
return 1;
}
rq->vs = request->connection->listener->default_vs.vs;
cxmutstr clfreq = request->request_line;
while(clfreq.length >
0 && clfreq.ptr[clfreq.length -
1] <
33) {
clfreq.length--;
}
pblock_kvinsert(
pb_key_clf_request,
clfreq.ptr,
clfreq.length,
rq->rq.reqpb);
pblock_kvinsert(
pb_key_method,
request->method.ptr,
request->method.length,
rq->rq.reqpb);
pblock_kvinsert(
pb_key_protocol,
request->httpv.ptr,
request->httpv.length,
rq->rq.reqpb);
if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)
CX_STR(
"HTTP/1.1"))) {
rq->rq.protv_num =
PROTOCOL_VERSION_HTTP11;
}
else if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)
CX_STR(
"HTTP/1.0"))) {
rq->rq.protv_num =
PROTOCOL_VERSION_HTTP10;
}
else {
log_ereport(
LOG_FAILURE,
"invalid protocol version: %.*s",
(
int)request->httpv.length,
request->httpv.ptr);
return 1;
}
cxmutstr absPath = http_request_get_abspath(request);
if(!absPath.ptr) {
return 1;
}
else if(absPath.ptr[
0] ==
'*') {
return 1;
}
for(
int i=
0;i<request->uri.length;i++) {
if(request->uri.ptr[i] ==
'?') {
cxmutstr query;
query.ptr =
NULL;
query.length =
0;
if(absPath.length > i +
2) {
query.length = absPath.length - i -
1;
query.ptr = absPath.ptr + i +
1;
}
absPath.length = i;
if(query.length >
0) {
pblock_kvinsert(
pb_key_query,
query.ptr,
query.length,
rq->rq.reqpb);
}
break;
}
}
cxmutstr 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);
return 1;
}
if(util_uri_unescape_strict(absPath.ptr)) {
pblock_kvinsert(
pb_key_uri,
absPath.ptr,
absPath.length,
rq->rq.reqpb);
}
else {
log_ereport(
LOG_WARN,
"uri unescape failed: {%.*s}",
(
int)absPath.length,
absPath.ptr);
pblock_kvinsert(pb_key_uri,
"/",
1, rq->rq.reqpb);
}
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;
}
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);
}
char *hosthdr = pblock_findkeyval(pb_key_host, rq->rq.headers);
if(hosthdr) {
char *host = pool_strdup(pool, hosthdr);
char *portstr =
NULL;
if(host[
0] !=
'[') {
portstr = strchr(host,
':');
}
else {
char *v6end = strchr(host,
']');
if(v6end) {
portstr = strchr(v6end,
':');
}
}
if(portstr) {
*portstr =
'\0';
}
rq->host = host;
}
else {
rq->host =
NULL;
}
rq->port = request->connection->listener->port;
if(rq->host) {
VirtualServer *vs = cxMapGet(sn->config->host_vs, cx_hash_key_str(rq->host));
if(vs) {
rq->vs = vs;
}
else {
log_ereport(
LOG_VERBOSE,
"Unkown host ''%s'': using default virtual server",
rq->host);
}
}
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;
}
}
char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers);
if(ctlen_str) {
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;
int cur_input_available = nb->cursize - nb->pos;
if(cur_input_available >= ctlen) {
net_io->max_read =
0;
}
else {
net_io->max_read = ctlen - cur_input_available;
}
}
}
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 = pool_malloc(pool, nb->maxsize);
if(!sn->buffer) {
return 1;
}
if(nb->cursize - nb->pos >
0) {
memcpy(sn->buffer, nb->inbuf, nb->cursize);
}
sn->pos = nb->pos;
sn->cursize = nb->cursize;
nb->pos =
0;
nb->cursize =
0;
if(httpstream_enable_chunked_read(sn->sn.csd, sn->buffer, nb->maxsize, &sn->cursize, &sn->pos)) {
return 1;
}
}
}
threadpool_t *lstp = request->connection->listener->threadpool;
sn->defaultpool = lstp;
if(lstp == thrpool) {
sn->currentpool = thrpool;
nsapi_handle_request(sn, rq);
}
else {
nsapi_change_threadpool(sn, rq, lstp);
}
return 0;
}
void header_add(HeaderArray *hd, cxmutstr name, cxmutstr 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;
}
}
int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq) {
log_ereport(
LOG_DEBUG,
"trace reqid: %016llx nsapi_handle_request %d", (
unsigned long long int)sn->connection->id, rq->phase);
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: {
r = nsapi_nametrans(sn, rq);
if(r !=
REQ_PROCEED) {
break;
}
rq->phase++;
nsapi_context_next_stage(&rq->context);
}
case NSAPIPathCheck: {
r = nsapi_pathcheck(sn, rq);
if(r !=
REQ_PROCEED) {
break;
}
rq->phase++;
nsapi_context_next_stage(&rq->context);
}
case NSAPIObjectType: {
r = nsapi_objecttype(sn, rq);
if(r !=
REQ_PROCEED) {
break;
}
rq->phase++;
nsapi_context_next_stage(&rq->context);
}
case NSAPIService: {
r = nsapi_service(sn, rq);
if(r !=
REQ_PROCEED) {
break;
}
rq->phase = NSAPIAddLog;
nsapi_context_next_stage(&rq->context);
}
case NSAPIAddLog: {
r = nsapi_addlog(sn, rq);
if(r ==
REQ_PROCESSING) {
break;
}
r =
REQ_PROCEED;
break;
}
default:
case REQ_FINISH: {
r =
REQ_PROCEED;
break;
}
case NSAPIError: {
r = nsapi_error(sn, rq);
if(r ==
REQ_PROCEED) {
r =
REQ_RESTART;
rq->phase = NSAPIAddLog;
nsapi_context_next_stage(&rq->context);
}
else {
r =
REQ_PROCEED;
break;
}
}
}
if(r ==
REQ_ABORTED) {
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) {
rq->finished =
TRUE;
request_free_resources(sn, rq);
log_ereport(
LOG_DEBUG,
"trace reqid: %016llx nsapi_finish_request", (
unsigned long long int)sn->connection->id);
WSBool read_stream_eof = httpstream_eof(sn->sn.csd);
if(!read_stream_eof) {
log_ereport(
LOG_WARN,
"request input stream not closed");
rq->rq.rq_attr.keep_alive =
0;
}
if(sn->pos < sn->cursize) {
log_ereport(
LOG_WARN,
"nsapi_finish_request: TODO: remaining bytes in buffer");
rq->rq.rq_attr.keep_alive =
0;
}
if(rq->rq.senthdrs) {
net_finish(sn->sn.csd);
}
else {
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;
}
char *response_content_length = pblock_findkeyval(pb_key_content_length, rq->rq.srvhdrs);
int64_t response_ctlen;
if(response_content_length && util_strtoint(response_content_length, &response_ctlen)) {
int64_t w = httpstream_written(sn->sn.csd);
if(w != response_ctlen) {
log_ereport(
LOG_WARN,
"nsapi_finish_request: content-length != number of bytes written");
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);
}
else {
log_ereport(
LOG_DEBUG,
"trace reqid: %016llx connection: close", (
unsigned long long int)sn->connection->id);
connection_destroy(sn->connection);
}
free(sn->netbuf->inbuf);
free(sn->netbuf);
pool_destroy(sn->sn.pool);
return 0;
}
void request_free_resources(NSAPISession *sn, NSAPIRequest *rq) {
if(!rq->resources)
return;
CxIterator i = cxMapIteratorValues(rq->resources);
cx_foreach(ResourceData *, 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];
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(ret ==
REQ_PROCESSING) {
rq->context.dtable_index = i +
1;
}
return ret;
}
}
return REQ_PROCEED;
}
int nsapi_nametrans(NSAPISession *sn, NSAPIRequest *rq) {
HTTPObjectConfig *objconf = rq->vs->objects;
httpd_objset *objset = objset_create(sn->sn.pool);
rq->rq.os = objset;
objset_add_object(sn->sn.pool, objset, objconf->objects[
0]);
httpd_object *obj = objset->obj[
0];
dtable *dt = object_get_dtable(obj, NSAPINameTrans);
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);
}
name = pblock_findkeyval(pb_key_name, rq->rq.vars);
ppath = pblock_findkeyval(pb_key_ppath, rq->rq.vars);
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(ret ==
REQ_PROCESSING) {
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(ret ==
REQ_NOACTION && ppath ==
NULL) {
cxmutstr 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;
}
cxstring uri = cx_str(pblock_findkeyval(pb_key_uri, rq->rq.reqpb));
request_set_path(cx_strcast(docroot), uri, rq->rq.vars);
}
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) {
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);
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) {
rq->context.objset_index = i;
rq->context.dtable_index = j +
1;
}
return ret;
}
}
}
return REQ_PROCEED;
}
int nsapi_objecttype(NSAPISession *sn, NSAPIRequest *rq) {
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, NSAPIObjectType);
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: {
rq->context.objset_index = i;
rq->context.dtable_index = j +
1;
return ret;
}
case REQ_NOACTION: {
break;
}
default: {
return ret;
}
}
}
}
cxstring path = cx_str(pblock_findkeyval(pb_key_ppath, rq->rq.vars));
cxstring ct;
if(path.ptr[path.length -
1] ==
'/') {
ct = (cxstring)
CX_STR(
"internal/directory");
}
else {
ct = (cxstring)
CX_STR(
"text/plain");
}
pblock_kvinsert(pb_key_content_type, ct.ptr, ct.length, rq->rq.srvhdrs);
return REQ_PROCEED;
}
int nsapi_service(NSAPISession *sn, NSAPIRequest *rq) {
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);
for(
int j=
NCX_DI(rq);j<dt->ndir;j++) {
if(ret ==
REQ_NOACTION) {
directive *d = dt->dirs[j];
char *dtp = pblock_findkeyval(pb_key_type, d->param);
if(dtp) {
if(!content_type) {
content_type = pblock_findkeyval(
pb_key_content_type,
rq->rq.srvhdrs);
}
if(!contenttype_match(cx_str(dtp), cx_str(content_type))) {
continue;
}
}
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;
}
}
ret = nsapi_exec(d, sn, rq);
}
if(ret !=
REQ_NOACTION) {
if(ret ==
REQ_PROCEED && !rq->rq.senthdrs) {
protocol_status(&sn->sn, &rq->rq,
500,
NULL);
ret =
REQ_ABORTED;
}
else if(ret ==
REQ_PROCESSING) {
rq->context.objset_index = i;
rq->context.dtable_index = j +
1;
}
return ret;
}
}
}
return ret;
}
int nsapi_error(NSAPISession *sn, NSAPIRequest *rq) {
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);
for(
int j=
NCX_DI(rq);j<dt->ndir;j++) {
if(ret ==
REQ_NOACTION) {
directive *d = dt->dirs[j];
char *status = pblock_findkeyval(pb_key_type, d->param);
if(status) {
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;
}
}
ret = nsapi_exec(d, sn, rq);
}
if(ret ==
REQ_ABORTED) {
break;
}
if(ret !=
REQ_NOACTION) {
if(ret ==
REQ_PROCEED) {
net_finish(sn->sn.csd);
}
else if(ret ==
REQ_PROCESSING) {
rq->context.objset_index = i;
rq->context.dtable_index = j +
1;
}
return ret;
}
}
}
if(ret !=
REQ_PROCEED) {
if(!rq->rq.senthdrs) {
nsapi_error_request((Session*)sn, (Request*)rq);
}
}
return ret;
}
int nsapi_addlog(NSAPISession *sn, NSAPIRequest *rq) {
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);
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) {
rq->context.objset_index = i;
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) {
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(cx_str(poolname));
if(pool && pool != sn->currentpool) {
return nsapi_exec_tp(d, sn, rq, pool);
}
}
else if(sn->currentpool != sn->defaultpool) {
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) {
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_saf_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_function_return(Session *session, Request *request,
int ret) {
ev_saf_return(session->ev, session, request, ret);
}
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;
}
int method_match(
char *cmp,
char *method) {
if(cmp[
0] !=
'(') {
if(!strcmp(cmp, method)) {
return 1;
}
}
else if(cmp[
0] ==
0) {
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++;
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;
}
int contenttype_match(cxstring cmp, cxstring ctype) {
if(cmp.ptr[
0] !=
'(') {
if(cmp.ptr[
0] ==
'*') {
cmp.ptr++;
cmp.length--;
return cx_strsuffix(ctype, cmp);
}
else if(cmp.ptr[cmp.length-
1] ==
'*') {
cmp.length--;
return cx_strprefix(ctype, cmp);
}
else {
return !cx_strcmp(cmp, ctype);
}
}
else if(cmp.ptr[
0] ==
0) {
log_ereport(
LOG_WARN,
"Skipped service saf with empty type parameter");
return 0;
}
cmp = cx_strsubsl(cmp,
1, cmp.length -
2);
int begin =
0;
for(
int i=
0;i<cmp.length;i++) {
if(cmp.ptr[i] ==
'|') {
if(contenttype_match(cx_strsubsl(cmp, begin, i-begin), ctype)) {
return 1;
}
begin = i +
1;
}
}
return contenttype_match(cx_strsubs(cmp, begin), ctype);
}
int add_objects(
HTTPObjectConfig *objs,
httpd_objset *os,
NSAPISession *sn,
NSAPIRequest *rq,
char *name,
char *path)
{
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;
}