1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <limits.h>
32
33 #include <arpa/inet.h>
34
35 #include "../public/nsapi.h"
36 #include "../util/pool.h"
37 #include "../util/pblock.h"
38 #include "../util/io.h"
39 #include "../util/util.h"
40 #include "httprequest.h"
41 #include "config.h"
42 #include "vserver.h"
43 #include "event.h"
44 #include "httplistener.h"
45 #include "func.h"
46 #include "error.h"
47
48 void http_request_init(HTTPRequest *req) {
49 memset(req,
0,
sizeof(HTTPRequest));
50
51 HeaderArray *hd = malloc(
sizeof(HeaderArray));
52 hd->next =
NULL;
53 hd->len =
0;
54 hd->headers = calloc(
16,
sizeof(Header));
55 hd->alloc =
16;
56
57 req->headers = hd;
58
59 req->req_start = time(
NULL);
60 }
61
62 void http_request_cleanup(HTTPRequest *req) {
63 header_array_free(req->headers);
64 free(req);
65 }
66
67 cxmutstr http_request_get_abspath(HTTPRequest *req) {
68 cxmutstr uri = req->uri;
69
70 int i =
0;
71 if(uri.ptr[
0] ==
'/') {
72 return uri;
73 }
else if(cx_strprefix(cx_strcast(uri), (cxstring)
CX_STR(
"http://"))) {
74 i =
7;
75 }
else if(cx_strprefix(cx_strcast(uri), (cxstring)
CX_STR(
"https://"))) {
76 i =
8;
77 }
else if(!cx_strcmp(cx_strcast(uri), (cxstring)
CX_STR(
"*"))) {
78 return uri;
79 }
80
81 for(;i<uri.length;i++) {
82 if(uri.ptr[i] ==
'/') {
83 return cx_strsubs_m(uri, i);
84 }
85 }
86
87 return (cxmutstr){
"/",
1 };
88 }
89
90 NSAPISession* nsapisession_create(
pool_handle_t *pool) {
91 NSAPISession *sn = pool_malloc(pool,
sizeof(NSAPISession));
92 if(!sn) {
93 return NULL;
94 }
95
96 ZERO(sn,
sizeof(NSAPISession));
97
98 sn->sn.pool = pool;
99
100 sn->sn.client = pblock_create_pool(sn->sn.pool,
8);
101 if(!sn->sn.client) {
102 pool_free(pool, sn);
103 return NULL;
104 }
105 sn->sn.fill =
1;
106
107 return sn;
108 }
109
110 int nsapisession_setconnection(NSAPISession *sn, Connection *conn, netbuf *inbuf, IOStream **io) {
111 SessionHandler *sh = conn->session_handler;
112 WSBool ssl;
113 IOStream *sio = sh->create_iostream(sh, conn, sn->sn.pool, &ssl);
114 if(!sio) {
115 return 1;
116 }
117 *io = sio;
118 IOStream *http = httpstream_new(sn->sn.pool, sio);
119 if(!http) {
120 return 1;
121 }
122 sn->connection = conn;
123 sn->netbuf = inbuf;
124 sn->sn.csd = http;
125 sn->sn.ssl = ssl;
126 sn->sn.inbuf = inbuf;
127 sn->sn.inbuf->sd = http;
128 return 0;
129 }
130
131 int handle_request(HTTPRequest *request,
threadpool_t *thrpool, EventHandler *ev) {
132
133
134
135 pool_handle_t *pool = pool_create();
136 if(!pool) {
137 log_ereport(
LOG_FAILURE,
"cannot create new memory pool for http request");
138 return 1;
139 }
140
141 int ret = nsapi_start_request(request, thrpool, ev, pool);
142 if(ret) {
143 pool_destroy(pool);
144 }
145 return ret;
146 }
147
148 int nsapi_start_request(HTTPRequest *request,
threadpool_t *thrpool, EventHandler *ev,
pool_handle_t *pool) {
149 log_ereport(
LOG_DEBUG,
"trace reqid: %016llx nsapi_start_request", (
unsigned long long int)request->connection->id);
150
151
152 NSAPISession *sn = nsapisession_create(pool);
153 if(sn ==
NULL) {
154
155 return 1;
156 }
157
158 NSAPIRequest *rq = pool_malloc(pool,
sizeof(NSAPIRequest));
159 if(rq ==
NULL) {
160
161 return 1;
162 }
163 ZERO(rq,
sizeof(NSAPIRequest));
164 rq->rq.req_start = request->req_start;
165 rq->phase = NSAPIAuthTrans;
166
167
168 IOStream *io =
NULL;
169 if(nsapisession_setconnection(sn, request->connection, request->netbuf, &io)) {
170
171 return 1;
172 }
173
174 if(!ev) {
175 ev = ev_instance(get_default_event_handler());
176 }
177 sn->sn.ev = ev;
178
179
180 sn->config = request->connection->listener->cfg;
181
182
183 if(request->connection->addr_type ==
CONN_ADDR_IPV4) {
184 char ip_str[
INET_ADDRSTRLEN];
185 if(inet_ntop(
186 AF_INET,
187 &request->connection->address.address_v4.sin_addr,
188 ip_str,
189 INET_ADDRSTRLEN) !=
NULL)
190 {
191 pblock_kvinsert(pb_key_ip, ip_str,
INET_ADDRSTRLEN, sn->sn.client);
192 }
193 }
else if(request->connection->addr_type ==
CONN_ADDR_IPV6) {
194 char ip_str[
INET6_ADDRSTRLEN];
195 if(inet_ntop(
196 AF_INET6,
197 &request->connection->address.address_v6.sin6_addr,
198 ip_str,
199 INET6_ADDRSTRLEN) !=
NULL)
200 {
201 pblock_kvinsert(pb_key_ip, ip_str,
INET6_ADDRSTRLEN, sn->sn.client);
202 }
203 }
204
205
206 if(request_initialize(pool, request, rq) !=
0) {
207 log_ereport(
LOG_FAILURE,
"Cannot initialize request structure");
208 return 1;
209 }
210
211
212 rq->vs = request->connection->listener->default_vs.vs;
213
214
215
216 cxmutstr clfreq = request->request_line;
217 while(clfreq.length >
0 && clfreq.ptr[clfreq.length -
1] <
33) {
218 clfreq.length--;
219 }
220 pblock_kvinsert(
221 pb_key_clf_request,
222 clfreq.ptr,
223 clfreq.length,
224 rq->rq.reqpb);
225
226
227 pblock_kvinsert(
228 pb_key_method,
229 request->method.ptr,
230 request->method.length,
231 rq->rq.reqpb);
232
233
234
235
236
237 pblock_kvinsert(
238 pb_key_protocol,
239 request->httpv.ptr,
240 request->httpv.length,
241 rq->rq.reqpb);
242
243 if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)
CX_STR(
"HTTP/1.1"))) {
244 rq->rq.protv_num =
PROTOCOL_VERSION_HTTP11;
245 }
else if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)
CX_STR(
"HTTP/1.0"))) {
246 rq->rq.protv_num =
PROTOCOL_VERSION_HTTP10;
247 }
else {
248
249 log_ereport(
250 LOG_FAILURE,
251 "invalid protocol version: %.*s",
252 (
int)request->httpv.length,
253 request->httpv.ptr);
254 return 1;
255 }
256
257
258
259
260
261 cxmutstr absPath = http_request_get_abspath(request);
262 if(!absPath.ptr) {
263
264 return 1;
265 }
else if(absPath.ptr[
0] ==
'*') {
266
267 return 1;
268 }
269
270 for(
int i=
0;i<request->uri.length;i++) {
271 if(request->uri.ptr[i] ==
'?') {
272 cxmutstr query;
273 query.ptr =
NULL;
274 query.length =
0;
275
276
277 if(absPath.length > i +
2) {
278 query.length = absPath.length - i -
1;
279 query.ptr = absPath.ptr + i +
1;
280 }
281 absPath.length = i;
282
283
284 if(query.length >
0) {
285 pblock_kvinsert(
286 pb_key_query,
287 query.ptr,
288 query.length,
289 rq->rq.reqpb);
290 }
291
292 break;
293 }
294 }
295
296
297 cxmutstr orig_path = absPath;
298 absPath.ptr = util_canonicalize_uri(
299 pool,
300 absPath.ptr,
301 absPath.length,
302 (
int*)&absPath.length);
303 if(!absPath.ptr) {
304 log_ereport(
305 LOG_WARN,
306 "invalid request path: {%.*s}",
307 (
int)orig_path.length,
308 orig_path.ptr);
309
310 return 1;
311 }
312
313
314 if(util_uri_unescape_strict(absPath.ptr)) {
315
316 pblock_kvinsert(
317 pb_key_uri,
318 absPath.ptr,
319 absPath.length,
320 rq->rq.reqpb);
321 }
else {
322 log_ereport(
323 LOG_WARN,
324 "uri unescape failed: {%.*s}",
325 (
int)absPath.length,
326 absPath.ptr);
327
328 pblock_kvinsert(pb_key_uri,
"/",
1, rq->rq.reqpb);
329 }
330
331
332 int hlen = request->headers->len;
333 HeaderArray *ha = request->headers;
334 for(
int i=
0;i<=hlen;i++) {
335 if(i == hlen) {
336 ha = ha->next;
337 if(ha ==
NULL) {
338 break;
339 }
340 i =
0;
341 hlen = ha->len;
342 }
343
344 Header header = ha->headers[i];
345
346 if(header.name.ptr[
0] <
90) {
347 header.name.ptr[
0] +=
32;
348 }
349
350
351 for(
int j=
0;j<header.name.length;j++) {
352 if(header.name.ptr[j] >
64 && header.name.ptr[j] <
97) {
353 header.name.ptr[j] +=
32;
354 }
355 }
356
357 pblock_nvlinsert(
358 header.name.ptr,
359 header.name.length,
360 header.value.ptr,
361 header.value.length,
362 rq->rq.headers);
363 }
364
365
366 char *hosthdr = pblock_findkeyval(pb_key_host, rq->rq.headers);
367 if(hosthdr) {
368 char *host = pool_strdup(pool, hosthdr);
369 char *portstr =
NULL;
370 if(host[
0] !=
'[') {
371 portstr = strchr(host,
':');
372 }
else {
373 char *v6end = strchr(host,
']');
374 if(v6end) {
375 portstr = strchr(v6end,
':');
376 }
377 }
378
379 if(portstr) {
380 *portstr =
'\0';
381 }
382 rq->host = host;
383 }
else {
384 rq->host =
NULL;
385 }
386 rq->port = request->connection->listener->port;
387
388 if(rq->host) {
389 VirtualServer *vs = cxMapGet(sn->config->host_vs, cx_hash_key_str(rq->host));
390 if(vs) {
391 rq->vs = vs;
392 }
else {
393 log_ereport(
394 LOG_VERBOSE,
395 "Unkown host ''%s'': using default virtual server",
396 rq->host);
397 }
398 }
399
400
401 rq->rq.rq_attr.keep_alive = (rq->rq.protv_num >=
PROTOCOL_VERSION_HTTP11);
402 char *conn_str = pblock_findkeyval(pb_key_connection, rq->rq.headers);
403 if(conn_str) {
404 if(!strcasecmp(conn_str,
"keep-alive")) {
405 rq->rq.rq_attr.keep_alive =
1;
406 }
else if(!strcasecmp(conn_str,
"close")) {
407 rq->rq.rq_attr.keep_alive =
0;
408 }
409 }
410
411
412 char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers);
413 if(ctlen_str) {
414 int64_t ctlen;
415 if(util_strtoint(ctlen_str, &ctlen)) {
416 netbuf *nb = sn->netbuf;
417 HttpStream *net_io = (HttpStream*)sn->sn.csd;
418 net_io->read_eof =
WS_FALSE;
419
420
421 int cur_input_available = nb->cursize - nb->pos;
422
423 if(cur_input_available >= ctlen) {
424
425
426
427
428 net_io->max_read =
0;
429 }
else {
430
431 net_io->max_read = ctlen - cur_input_available;
432 }
433
434 }
435 }
436 char *transfer_encoding = pblock_findkeyval(pb_key_transfer_encoding, rq->rq.headers);
437 if(transfer_encoding) {
438 if(!strcmp(transfer_encoding,
"chunked")) {
439 netbuf *nb = sn->netbuf;
440
441 sn->buffer = pool_malloc(pool, nb->maxsize);
442 if(!sn->buffer) {
443
444 return 1;
445 }
446
447
448 if(nb->cursize - nb->pos >
0) {
449 memcpy(sn->buffer, nb->inbuf, nb->cursize);
450 }
451
452 sn->pos = nb->pos;
453 sn->cursize = nb->cursize;
454
455
456 nb->pos =
0;
457 nb->cursize =
0;
458
459 if(httpstream_enable_chunked_read(sn->sn.csd, sn->buffer, nb->maxsize, &sn->cursize, &sn->pos)) {
460
461 return 1;
462 }
463 }
464 }
465
466
467
468
469
470
471 threadpool_t *lstp = request->connection->listener->threadpool;
472 sn->defaultpool = lstp;
473 if(lstp == thrpool) {
474 sn->currentpool = thrpool;
475 nsapi_handle_request(sn, rq);
476 }
else {
477
478 nsapi_change_threadpool(sn, rq, lstp);
479 }
480
481 return 0;
482 }
483
484
485
486 void header_add(HeaderArray *hd, cxmutstr name, cxmutstr value) {
487 while(hd->len >= hd->alloc) {
488 if(hd->next ==
NULL) {
489 HeaderArray *block = malloc(
sizeof(HeaderArray));
490 block->next =
NULL;
491 block->len =
0;
492 block->headers = calloc(
16,
sizeof(Header));
493 block->alloc =
16;
494 hd->next = block;
495 }
496 hd = hd->next;
497 }
498 hd->headers[hd->len].name = name;
499 hd->headers[hd->len].value = value;
500 hd->len++;
501 }
502
503 void header_array_free(HeaderArray *hd) {
504 HeaderArray *next;
505 while(hd) {
506 next = hd->next;
507 free(hd->headers);
508 free(hd);
509 hd = next;
510 }
511 }
512
513
514
515
516
517
518 int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq) {
519 log_ereport(
LOG_DEBUG,
"trace reqid: %016llx nsapi_handle_request %d", (
unsigned long long int)sn->connection->id, rq->phase);
520
521 int r =
REQ_NOACTION;
522 do {
523 switch(rq->phase) {
524 case NSAPIAuthTrans: {
525 r = nsapi_authtrans(sn, rq);
526 if(r !=
REQ_PROCEED) {
527 break;
528 }
529 rq->phase++;
530 nsapi_context_next_stage(&rq->context);
531 }
532 case NSAPINameTrans: {
533
534 r = nsapi_nametrans(sn, rq);
535 if(r !=
REQ_PROCEED) {
536 break;
537 }
538 rq->phase++;
539 nsapi_context_next_stage(&rq->context);
540 }
541 case NSAPIPathCheck: {
542
543 r = nsapi_pathcheck(sn, rq);
544 if(r !=
REQ_PROCEED) {
545 break;
546 }
547 rq->phase++;
548 nsapi_context_next_stage(&rq->context);
549 }
550 case NSAPIObjectType: {
551
552 r = nsapi_objecttype(sn, rq);
553 if(r !=
REQ_PROCEED) {
554 break;
555 }
556 rq->phase++;
557 nsapi_context_next_stage(&rq->context);
558 }
559 case NSAPIService: {
560
561 r = nsapi_service(sn, rq);
562 if(r !=
REQ_PROCEED) {
563 break;
564 }
565 rq->phase = NSAPIAddLog;
566 nsapi_context_next_stage(&rq->context);
567 }
568 case NSAPIAddLog: {
569
570 r = nsapi_addlog(sn, rq);
571 if(r ==
REQ_PROCESSING) {
572 break;
573 }
574
575 r =
REQ_PROCEED;
576 break;
577 }
578 default:
579 case REQ_FINISH: {
580
581
582 r =
REQ_PROCEED;
583 break;
584 }
585 case NSAPIError: {
586
587 r = nsapi_error(sn, rq);
588 if(r ==
REQ_PROCEED) {
589
590 r =
REQ_RESTART;
591 rq->phase = NSAPIAddLog;
592 nsapi_context_next_stage(&rq->context);
593 }
else {
594
595
596
597
598 r =
REQ_PROCEED;
599 break;
600 }
601 }
602 }
603
604 if(r ==
REQ_ABORTED) {
605
606 rq->phase = NSAPIError;
607 nsapi_context_next_stage(&rq->context);
608 r =
REQ_RESTART;
609 }
610
611 }
while (r ==
REQ_RESTART);
612
613 if(r !=
REQ_PROCESSING) {
614 r = nsapi_finish_request(sn, rq);
615 }
616
617 return r;
618 }
619
620 int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) {
621 rq->finished =
TRUE;
622 request_free_resources(sn, rq);
623
624 log_ereport(
LOG_DEBUG,
"trace reqid: %016llx nsapi_finish_request", (
unsigned long long int)sn->connection->id);
625
626 WSBool read_stream_eof = httpstream_eof(sn->sn.csd);
627 if(!read_stream_eof) {
628 log_ereport(
LOG_WARN,
"request input stream not closed");
629
630 rq->rq.rq_attr.keep_alive =
0;
631 }
632 if(sn->pos < sn->cursize) {
633 log_ereport(
LOG_WARN,
"nsapi_finish_request: TODO: remaining bytes in buffer");
634
635 rq->rq.rq_attr.keep_alive =
0;
636 }
637
638 if(rq->rq.senthdrs) {
639
640
641 net_finish(sn->sn.csd);
642 }
else {
643
644
645
646 char *clf_req = pblock_findkeyval(pb_key_clf_request, rq->rq.reqpb);
647 log_ereport(
LOG_WARN,
"nsapi_finish_request: no response header: request: %s", clf_req);
648 rq->rq.rq_attr.keep_alive =
0;
649 }
650
651 char *response_content_length = pblock_findkeyval(pb_key_content_length, rq->rq.srvhdrs);
652 int64_t response_ctlen;
653 if(response_content_length && util_strtoint(response_content_length, &response_ctlen)) {
654 int64_t w = httpstream_written(sn->sn.csd);
655 if(w != response_ctlen) {
656 log_ereport(
LOG_WARN,
"nsapi_finish_request: content-length != number of bytes written");
657 rq->rq.rq_attr.keep_alive =
0;
658 }
659 }
660
661 if(rq->rq.rq_attr.keep_alive) {
662 SessionHandler *sh = sn->connection->session_handler;
663 sh->keep_alive(sh, sn->connection);
664
665
666
667
668
669 }
else {
670 log_ereport(
LOG_DEBUG,
"trace reqid: %016llx connection: close", (
unsigned long long int)sn->connection->id);
671 connection_destroy(sn->connection);
672 }
673
674
675 free(sn->netbuf->inbuf);
676 free(sn->netbuf);
677
678 pool_destroy(sn->sn.pool);
679
680 return 0;
681 }
682
683 void request_free_resources(NSAPISession *sn, NSAPIRequest *rq) {
684 if(!rq->resources)
return;
685
686 CxIterator i = cxMapIteratorValues(rq->resources);
687 cx_foreach(ResourceData *, resource, i) {
688 resourcepool_free(&sn->sn, &rq->rq, resource);
689 }
690 }
691
692 int nsapi_authtrans(NSAPISession *sn, NSAPIRequest *rq) {
693 HTTPObjectConfig *objconf = rq->vs->objects;
694 httpd_object *obj = objconf->objects[
0];
695 dtable *dt = object_get_dtable(obj, NSAPIAuthTrans);
696
697 int ret = rq->context.last_req_code;
698 for(
int i=
NCX_DI(rq);i<dt->ndir;i++) {
699 if(ret ==
REQ_NOACTION) {
700 directive *d = dt->dirs[i];
701 ret = nsapi_exec(d, sn, rq);
702 }
703
704 if(ret !=
REQ_NOACTION) {
705
706
707
708
709 if(ret ==
REQ_PROCESSING) {
710
711
712 rq->context.dtable_index = i +
1;
713 }
714
715 return ret;
716 }
717 }
718
719
720 return REQ_PROCEED;
721 }
722
723 int nsapi_nametrans(NSAPISession *sn, NSAPIRequest *rq) {
724 HTTPObjectConfig *objconf = rq->vs->objects;
725
726 httpd_objset *objset = objset_create(sn->sn.pool);
727 rq->rq.os = objset;
728
729 objset_add_object(sn->sn.pool, objset, objconf->objects[
0]);
730
731 httpd_object *obj = objset->obj[
0];
732 dtable *dt = object_get_dtable(obj, NSAPINameTrans);
733
734
735 int ret = rq->context.last_req_code;
736 char *name =
NULL;
737 char *ppath =
NULL;
738 for(
int i=
NCX_DI(rq);i<dt->ndir;i++) {
739 if(ret ==
REQ_NOACTION) {
740 directive *d = dt->dirs[i];
741 ret = nsapi_exec(d, sn, rq);
742 }
743
744
745 name = pblock_findkeyval(pb_key_name, rq->rq.vars);
746 ppath = pblock_findkeyval(pb_key_ppath, rq->rq.vars);
747
748
749 if(add_objects(objconf, objset, sn, rq, name, ppath) ==
REQ_ABORTED) {
750 log_ereport(
LOG_FAILURE,
"add_objects failed");
751 return REQ_ABORTED;
752 }
753
754 if(ret !=
REQ_NOACTION) {
755
756
757
758
759 if(ret ==
REQ_PROCESSING) {
760
761
762 rq->context.dtable_index = i +
1;
763 }
else if(ret ==
REQ_PROCEED) {
764 char *pp = pblock_findkeyval(pb_key_ppath, rq->rq.vars);
765 pblock_kvinsert(pb_key_path, pp, strlen(pp), rq->rq.vars);
766 }
767
768 return ret;
769 }
770 }
771
772
773 if(ret ==
REQ_NOACTION && ppath ==
NULL) {
774 cxmutstr docroot = rq->vs->document_root;
775 if(docroot.length <
1) {
776 log_ereport(
777 LOG_WARN,
778 "VirtualServer(%.*s) docroot too short",
779 (
int)rq->vs->name.length,
780 rq->vs->name.ptr);
781 return REQ_ABORTED;
782 }
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803 cxstring uri = cx_str(pblock_findkeyval(pb_key_uri, rq->rq.reqpb));
804 request_set_path(cx_strcast(docroot), uri, rq->rq.vars);
805 }
806
807
808 char *pp = pblock_findkeyval(pb_key_ppath, rq->rq.vars);
809 pblock_kvinsert(pb_key_path, pp, strlen(pp), rq->rq.vars);
810
811 return REQ_PROCEED;
812 }
813
814 int nsapi_pathcheck(NSAPISession *sn, NSAPIRequest *rq) {
815
816 httpd_objset *objset = rq->rq.os;
817
818 if(
NCX_OI(rq) == -
1) {
819 NCX_OI(rq) = objset->pos -
1;
820 }
821
822 int ret = rq->context.last_req_code;
823 for(
int i=
NCX_OI(rq);i>=
0;i--) {
824 httpd_object *obj = objset->obj[i];
825 dtable *dt = object_get_dtable(obj, NSAPIPathCheck);
826
827
828 for(
int j=
NCX_DI(rq);j<dt->ndir;j++) {
829 if(ret ==
REQ_NOACTION || ret ==
REQ_PROCEED) {
830 directive *d = dt->dirs[j];
831 ret = nsapi_exec(d, sn, rq);
832 }
else {
833 if(ret ==
REQ_PROCESSING) {
834
835 rq->context.objset_index = i;
836
837
838 rq->context.dtable_index = j +
1;
839 }
840
841 return ret;
842 }
843 }
844 }
845
846 return REQ_PROCEED;
847 }
848
849 int nsapi_objecttype(NSAPISession *sn, NSAPIRequest *rq) {
850
851 httpd_objset *objset = rq->rq.os;
852
853 if(
NCX_OI(rq) == -
1) {
854
855 NCX_OI(rq) = objset->pos -
1;
856 }
857
858 int ret = rq->context.last_req_code;
859 for(
int i=
NCX_OI(rq);i>=
0;i--) {
860 httpd_object *obj = objset->obj[i];
861 dtable *dt = object_get_dtable(obj, NSAPIObjectType);
862
863
864 for(
int j=
NCX_DI(rq);j<dt->ndir;j++) {
865 if(ret ==
REQ_NOACTION) {
866 directive *d = dt->dirs[j];
867 ret = nsapi_exec(d, sn, rq);
868 }
869
870 switch(ret) {
871 case REQ_PROCEED: {
872 char *type = pblock_findkeyval(
873 pb_key_content_type,
874 rq->rq.srvhdrs);
875 if(type ==
NULL) {
876 ret =
REQ_NOACTION;
877 break;
878 }
879 return ret;
880 }
881 case REQ_PROCESSING: {
882
883 rq->context.objset_index = i;
884
885
886 rq->context.dtable_index = j +
1;
887 return ret;
888 }
889 case REQ_NOACTION: {
890 break;
891 }
892 default: {
893 return ret;
894 }
895 }
896 }
897 }
898
899
900
901
902
903
904
905 cxstring path = cx_str(pblock_findkeyval(pb_key_ppath, rq->rq.vars));
906 cxstring ct;
907 if(path.ptr[path.length -
1] ==
'/') {
908
909 ct = (cxstring)
CX_STR(
"internal/directory");
910 }
else {
911 ct = (cxstring)
CX_STR(
"text/plain");
912 }
913 pblock_kvinsert(pb_key_content_type, ct.ptr, ct.length, rq->rq.srvhdrs);
914
915 return REQ_PROCEED;
916 }
917
918 int nsapi_service(NSAPISession *sn, NSAPIRequest *rq) {
919
920 httpd_objset *objset = rq->rq.os;
921
922 if(
NCX_OI(rq) == -
1) {
923 NCX_OI(rq) = objset->pos -
1;
924 }
925
926 int ret = rq->context.last_req_code;
927 char *content_type =
NULL;
928 char *method =
NULL;
929 for(
int i=
NCX_OI(rq);i>=
0;i--) {
930 httpd_object *obj = objset->obj[i];
931 dtable *dt = object_get_dtable(obj, NSAPIService);
932
933
934 for(
int j=
NCX_DI(rq);j<dt->ndir;j++) {
935 if(ret ==
REQ_NOACTION) {
936 directive *d = dt->dirs[j];
937
938
939 char *dtp = pblock_findkeyval(pb_key_type, d->param);
940 if(dtp) {
941
942 if(!content_type) {
943 content_type = pblock_findkeyval(
944 pb_key_content_type,
945 rq->rq.srvhdrs);
946 }
947
948 if(!contenttype_match(cx_str(dtp), cx_str(content_type))) {
949 continue;
950 }
951 }
952
953
954 char *dmt = pblock_findkeyval(pb_key_method, d->param);
955 if(dmt) {
956 if(!method) {
957 method = pblock_findkeyval(pb_key_method, rq->rq.reqpb);
958 }
959
960 if(!method_match(dmt, method)) {
961 continue;
962 }
963 }
964
965
966 ret = nsapi_exec(d, sn, rq);
967 }
968
969 if(ret !=
REQ_NOACTION) {
970 if(ret ==
REQ_PROCEED && !rq->rq.senthdrs) {
971
972
973
974 protocol_status(&sn->sn, &rq->rq,
500,
NULL);
975 ret =
REQ_ABORTED;
976 }
else if(ret ==
REQ_PROCESSING) {
977
978 rq->context.objset_index = i;
979
980
981 rq->context.dtable_index = j +
1;
982 }
983
984 return ret;
985 }
986 }
987 }
988
989 return ret;
990 }
991
992 int nsapi_error(NSAPISession *sn, NSAPIRequest *rq) {
993
994 httpd_objset *objset = rq->rq.os;
995
996 if(
NCX_OI(rq) == -
1) {
997 NCX_OI(rq) = objset->pos -
1;
998 }
999
1000 int ret = rq->context.last_req_code;
1001 for(
int i=
NCX_OI(rq);i>=
0;i--) {
1002 httpd_object *obj = objset->obj[i];
1003 dtable *dt = object_get_dtable(obj, NSAPIError);
1004
1005
1006 for(
int j=
NCX_DI(rq);j<dt->ndir;j++) {
1007 if(ret ==
REQ_NOACTION) {
1008 directive *d = dt->dirs[j];
1009
1010
1011
1012
1013 char *status = pblock_findkeyval(pb_key_type, d->param);
1014 if(status) {
1015 int64_t statuscode = -
1;
1016 if(!util_strtoint(status, &statuscode)) {
1017 log_ereport(
1018 LOG_WARN,
1019 "nsapi_error: directive ''%s'' ignored: invalid type parameter: integer status code expected",
1020 d->func->name);
1021 }
else if(statuscode != rq->rq.status_num) {
1022 continue;
1023 }
1024 }
1025
1026
1027 ret = nsapi_exec(d, sn, rq);
1028 }
1029
1030 if(ret ==
REQ_ABORTED) {
1031
1032
1033 break;
1034 }
1035 if(ret !=
REQ_NOACTION) {
1036 if(ret ==
REQ_PROCEED) {
1037
1038
1039 net_finish(sn->sn.csd);
1040 }
else if(ret ==
REQ_PROCESSING) {
1041
1042 rq->context.objset_index = i;
1043
1044
1045 rq->context.dtable_index = j +
1;
1046 }
1047 return ret;
1048 }
1049 }
1050 }
1051
1052 if(ret !=
REQ_PROCEED) {
1053
1054 if(!rq->rq.senthdrs) {
1055 nsapi_error_request((Session*)sn, (Request*)rq);
1056 }
1057 }
1058
1059 return ret;
1060 }
1061
1062 int nsapi_addlog(NSAPISession *sn, NSAPIRequest *rq) {
1063
1064 httpd_objset *objset = rq->rq.os;
1065
1066 if(
NCX_OI(rq) == -
1) {
1067 NCX_OI(rq) = objset->pos -
1;
1068 }
1069
1070 int ret = rq->context.last_req_code;
1071 for(
int i=
NCX_OI(rq);i>=
0;i--) {
1072 httpd_object *obj = objset->obj[i];
1073 dtable *dt = object_get_dtable(obj, NSAPIAddLog);
1074
1075
1076 for(
int j=
NCX_DI(rq);j<dt->ndir;j++) {
1077 if(ret ==
REQ_NOACTION) {
1078 directive *d = dt->dirs[j];
1079 ret = nsapi_exec(d, sn, rq);
1080 }
1081
1082 if(ret !=
REQ_NOACTION) {
1083 if(ret ==
REQ_PROCESSING) {
1084
1085 rq->context.objset_index = i;
1086
1087
1088 rq->context.dtable_index = j +
1;
1089 }
1090
1091 return ret;
1092 }
1093 }
1094 }
1095
1096 return REQ_PROCEED;
1097 }
1098
1099 struct _tpd_data {
1100 NSAPISession *sn;
1101 NSAPIRequest *rq;
1102 directive *directive;
1103 threadpool_t *threadpool;
1104 };
1105
1106 int nsapi_exec(directive *d, NSAPISession *sn, NSAPIRequest *rq) {
1107
1108 if(d->cond) {
1109 if(!condition_evaluate(d->cond, (Session*)sn, (Request*)rq)) {
1110 return REQ_NOACTION;
1111 }
1112 }
1113
1114 char *poolname = pblock_findkeyval(pb_key_pool, d->param);
1115 if(poolname) {
1116 threadpool_t *pool = get_threadpool(cx_str(poolname));
1117 if(pool && pool != sn->currentpool) {
1118
1119 return nsapi_exec_tp(d, sn, rq, pool);
1120 }
1121 }
else if(sn->currentpool != sn->defaultpool) {
1122
1123 return nsapi_exec_tp(d, sn, rq, sn->defaultpool);
1124 }
1125
1126 return SAF_EXEC(d->func, d->param, (Session*)sn, (Request*)rq);
1127 }
1128
1129 int nsapi_exec_tp(
1130 directive *d,
1131 NSAPISession *sn,
1132 NSAPIRequest *rq,
1133 threadpool_t *pool)
1134 {
1135 struct _tpd_data *data = malloc(
sizeof(
struct _tpd_data));
1136 if(data ==
NULL) {
1137
1138 return REQ_ABORTED;
1139 }
1140 data->sn = sn;
1141 data->rq = rq;
1142 data->directive = d;
1143 data->threadpool = pool;
1144
1145 sn->currentpool = pool;
1146 threadpool_run(pool, thrpool_exec, data);
1147
1148 return REQ_PROCESSING;
1149 }
1150
1151 void nsapi_saf_return(Session *session, Request *request,
int ret) {
1152 NSAPISession *sn = (NSAPISession*)session;
1153 NSAPIRequest *rq = (NSAPIRequest*)request;
1154
1155 rq->context.last_req_code = ret;
1156
1157 if(sn->currentpool != sn->defaultpool) {
1158 nsapi_change_threadpool(sn, rq, sn->defaultpool);
1159 }
else {
1160 nsapi_handle_request(sn, rq);
1161 }
1162 }
1163
1164 void nsapi_function_return(Session *session, Request *request,
int ret) {
1165 ev_saf_return(session->ev, session, request, ret);
1166 }
1167
1168 void nsapi_change_threadpool(
1169 NSAPISession *sn,
1170 NSAPIRequest *rq,
1171 threadpool_t *thrpool)
1172 {
1173 struct _tpd_data *data = malloc(
sizeof(
struct _tpd_data));
1174 data->sn = sn;
1175 data->rq = rq;
1176 data->threadpool = thrpool;
1177
1178 threadpool_run(thrpool, thrpool_change, data);
1179 }
1180
1181 void* thrpool_exec(
void *d) {
1182 struct _tpd_data *data = d;
1183
1184 data->sn->currentpool = data->threadpool;
1185 int r =
SAF_EXEC(
1186 data->directive->func,
1187 data->directive->param,
1188 (Session*)data->sn,
1189 (Request*)data->rq);
1190
1191 nsapi_function_return((Session*)data->sn, (Request*)data->rq, r);
1192 free(data);
1193
1194 return NULL;
1195 }
1196
1197 void* thrpool_change(
void *d) {
1198 struct _tpd_data *data = d;
1199
1200 data->sn->currentpool = data->threadpool;
1201 nsapi_handle_request(data->sn, data->rq);
1202
1203 free(data);
1204 return NULL;
1205 }
1206
1207
1208
1209
1210
1211
1212
1213 int method_match(
char *cmp,
char *method) {
1214 if(cmp[
0] !=
'(') {
1215
1216 if(!strcmp(cmp, method)) {
1217 return 1;
1218 }
1219 }
else if(cmp[
0] ==
0) {
1220
1221 log_ereport(
1222 LOG_WARN,
1223 "Skipped service saf with empty method parameter");
1224 return 0;
1225 }
1226
1227 size_t method_len = strlen(method);
1228 size_t last_pos =
0;
1229 cmp++;
1230 for(
int i=
0;cmp[i]!=
0;i++) {
1231 char c = cmp[i];
1232 if(c ==
'|' || c ==
')') {
1233 size_t len = i - last_pos;
1234 if(len == method_len) {
1235 char *cp = cmp + last_pos;
1236 if(!memcmp(cp, method, len)) {
1237 return 1;
1238 }
1239 }
1240 last_pos = i +
1;
1241 }
1242 }
1243
1244 return 0;
1245 }
1246
1247
1248
1249
1250
1251
1252
1253 int contenttype_match(cxstring cmp, cxstring ctype) {
1254 if(cmp.ptr[
0] !=
'(') {
1255 if(cmp.ptr[
0] ==
'*') {
1256 cmp.ptr++;
1257 cmp.length--;
1258 return cx_strsuffix(ctype, cmp);
1259 }
else if(cmp.ptr[cmp.length-
1] ==
'*') {
1260 cmp.length--;
1261 return cx_strprefix(ctype, cmp);
1262 }
else {
1263 return !cx_strcmp(cmp, ctype);
1264 }
1265 }
else if(cmp.ptr[
0] ==
0) {
1266 log_ereport(
LOG_WARN,
"Skipped service saf with empty type parameter");
1267 return 0;
1268 }
1269
1270 cmp = cx_strsubsl(cmp,
1, cmp.length -
2);
1271
1272 int begin =
0;
1273 for(
int i=
0;i<cmp.length;i++) {
1274 if(cmp.ptr[i] ==
'|') {
1275 if(contenttype_match(cx_strsubsl(cmp, begin, i-begin), ctype)) {
1276 return 1;
1277 }
1278 begin = i +
1;
1279 }
1280 }
1281 return contenttype_match(cx_strsubs(cmp, begin), ctype);
1282 }
1283
1284
1285
1286
1287 int add_objects(
1288 HTTPObjectConfig *objs,
1289 httpd_objset *os,
1290 NSAPISession *sn,
1291 NSAPIRequest *rq,
1292 char *name,
1293 char *path)
1294 {
1295
1296
1297
1298
1299
1300 if(name ==
NULL) {
1301 return REQ_PROCEED;
1302 }
1303
1304 for(
int i=
0;i<objs->nobj;i++) {
1305 httpd_object *obj = objs->objects[i];
1306
1307 if(obj->name && !strcmp(name, obj->name)) {
1308 objset_add_object(sn->sn.pool, os, obj);
1309 }
1310 }
1311
1312 return REQ_PROCEED;
1313 }
1314