UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 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 sstr_t http_request_get_abspath(HTTPRequest *req) { 68 sstr_t uri = req->uri; 69 70 int i = 0; 71 if(uri.ptr[0] == '/') { 72 return uri; 73 } else if(sstrprefix(uri, S("http://"))) { 74 i = 7; 75 } else if(sstrprefix(uri, S("https://"))) { 76 i = 8; 77 } else if(!sstrcmp(uri, S("*"))) { 78 return uri; 79 } 80 81 for(;i<uri.length;i++) { 82 if(uri.ptr[i] == '/') { 83 return sstrsubs(uri, i); 84 } 85 } 86 87 return S("/"); 88 } 89 90 int handle_request(HTTPRequest *request, threadpool_t *thrpool, EventHandler *ev) { 91 // handle nsapi request 92 93 // create pool 94 pool_handle_t *pool = pool_create(); 95 96 // create nsapi data structures 97 NSAPISession *sn = pool_malloc(pool, sizeof(NSAPISession)); 98 if(sn == NULL) { 99 /* TODO: error */ 100 } 101 ZERO(sn, sizeof(NSAPISession)); 102 NSAPIRequest *rq = pool_malloc(pool, sizeof(NSAPIRequest)); 103 if(rq == NULL) { 104 /* TODO: error */ 105 } 106 ZERO(rq, sizeof(NSAPIRequest)); 107 rq->rq.req_start = request->req_start; 108 rq->phase = NSAPIAuthTrans; 109 110 // fill session structure 111 sn->connection = request->connection; 112 sn->netbuf = request->netbuf; 113 sn->sn.pool = pool; 114 SessionHandler *sh = request->connection->session_handler; 115 WSBool ssl; 116 IOStream *io = sh->create_iostream(sh, request->connection, pool, &ssl); 117 sn->sn.csd = httpstream_new(pool, io); 118 sn->sn.ssl = ssl; 119 120 sn->sn.client = pblock_create_pool(sn->sn.pool, 8); 121 sn->sn.next = NULL; 122 sn->sn.fill = 1; 123 sn->sn.subject = NULL; 124 125 if(!ev) { 126 ev = ev_instance(get_default_event_handler()); 127 } 128 sn->sn.ev = ev; 129 130 // the session needs the current server configuration 131 sn->config = request->connection->listener->cfg; 132 133 // add ip to sn->client pblock 134 char ip_str[INET_ADDRSTRLEN]; 135 if(inet_ntop( 136 AF_INET, 137 &request->connection->address.sin_addr, 138 ip_str, 139 INET_ADDRSTRLEN) != NULL) 140 { 141 pblock_kvinsert(pb_key_ip, ip_str, INET_ADDRSTRLEN, sn->sn.client); 142 } 143 144 // init NSAPI request structure 145 if(request_initialize(pool, request, rq) != 0) { 146 log_ereport(LOG_FAILURE, "Cannot initialize request structure"); 147 pool_destroy(pool); 148 return 1; 149 } 150 151 // set default virtual server 152 rq->vs = request->connection->listener->default_vs.vs; 153 154 // Pass request line as "clf-request" 155 // remove \r\n 156 sstr_t clfreq = request->request_line; 157 while(clfreq.length > 0 && clfreq.ptr[clfreq.length - 1] < 33) { 158 clfreq.length--; 159 } 160 pblock_kvinsert( 161 pb_key_clf_request, 162 clfreq.ptr, 163 clfreq.length, 164 rq->rq.reqpb); 165 166 // Pass method as "method" in reqpb, and also as method_num 167 pblock_kvinsert( 168 pb_key_method, 169 request->method.ptr, 170 request->method.length, 171 rq->rq.reqpb); 172 // TODO: method num 173 //rqRq.rq.method_num = rqHdr->GetMethodNumber(); 174 //PR_ASSERT(rqRq.rq.method_num != -1 || iStatus); 175 176 // Pass protocol as "protocol" in reqpb, and also in protv_num 177 pblock_kvinsert( 178 pb_key_protocol, 179 request->httpv.ptr, 180 request->httpv.length, 181 rq->rq.reqpb); 182 183 if(!sstrcmp(request->httpv, S("HTTP/1.1"))) { 184 rq->rq.protv_num = PROTOCOL_VERSION_HTTP11; 185 } else if(!sstrcmp(request->httpv, S("HTTP/1.0"))) { 186 rq->rq.protv_num = PROTOCOL_VERSION_HTTP10; 187 } else { 188 // invalid protocol version - abort 189 log_ereport( 190 LOG_FAILURE, 191 "invalid protocol version: %.*s", 192 (int)request->httpv.length, 193 request->httpv.ptr); 194 pool_destroy(pool); 195 return 1; 196 } 197 198 /* 199 * get absolute path and query of the request uri 200 */ 201 // TODO: check for '#' 202 sstr_t absPath = http_request_get_abspath(request); 203 if(!absPath.ptr) { 204 // TODO: error msg 205 return 1; 206 } else if(absPath.ptr[0] == '*') { 207 // TODO: implement global OPTIONS 208 return 1; 209 } 210 211 sstr_t query; 212 query.length = 0; 213 214 for(int i=0;i<request->uri.length;i++) { 215 if(request->uri.ptr[i] == '?') { 216 /* split uri in path and query */ 217 if(absPath.length > i + 2) { 218 query.length = absPath.length - i - 1; 219 query.ptr = absPath.ptr + i + 1; 220 } 221 absPath.length = i; 222 223 // Pass any query as 'query' in reqpb 224 pblock_kvinsert( 225 pb_key_query, 226 query.ptr, 227 query.length, 228 rq->rq.reqpb); 229 230 break; 231 } 232 } 233 234 // Get abs_path part of request URI, and canonicalize the path 235 sstr_t orig_path = absPath; 236 absPath.ptr = util_canonicalize_uri( 237 pool, 238 absPath.ptr, 239 absPath.length, 240 (int*)&absPath.length); 241 if(!absPath.ptr) { 242 log_ereport( 243 LOG_WARN, 244 "invalid request path: {%.*s}", 245 (int)orig_path.length, 246 orig_path.ptr); 247 pool_destroy(pool); 248 // TODO: 400 bad request 249 return 1; 250 } 251 252 // Decode the abs_path 253 if(util_uri_unescape_strict(absPath.ptr)) { 254 // Pass the abs_path as 'uri' in reqpb 255 pblock_kvinsert( 256 pb_key_uri, 257 absPath.ptr, 258 absPath.length, 259 rq->rq.reqpb); 260 } else { 261 // TODO: log error 262 log_ereport( 263 LOG_WARN, 264 "uri unescape failed: {%.*s}", 265 (int)absPath.length, 266 absPath.ptr); 267 // TODO: 400 bad request 268 pblock_kvinsert(pb_key_uri, "/", 1, rq->rq.reqpb); 269 } 270 271 // pass http header to the NSAPI request structure 272 int hlen = request->headers->len; 273 HeaderArray *ha = request->headers; 274 for(int i=0;i<=hlen;i++) { 275 if(i == hlen) { 276 ha = ha->next; 277 if(ha == NULL) { 278 break; 279 } 280 i = 0; 281 hlen = ha->len; 282 } 283 284 Header header = ha->headers[i]; 285 286 if(header.name.ptr[0] < 90) { 287 header.name.ptr[0] += 32; 288 } 289 290 // change to lower case 291 for(int j=0;j<header.name.length;j++) { 292 if(header.name.ptr[j] > 64 && header.name.ptr[j] < 97) { 293 header.name.ptr[j] += 32; 294 } 295 } 296 297 pblock_nvlinsert( 298 header.name.ptr, 299 header.name.length, 300 header.value.ptr, 301 header.value.length, 302 rq->rq.headers); 303 } 304 305 // get host and port 306 char *hosthdr = pblock_findkeyval(pb_key_host, rq->rq.headers); 307 if(hosthdr) { 308 char *host = pool_strdup(pool, hosthdr); 309 char *portstr = strchr(host, ':'); 310 if(portstr) { 311 *portstr = '\0'; 312 } 313 rq->host = host; 314 } else { 315 rq->host = NULL; // TODO: value from listener 316 } 317 rq->port = request->connection->listener->port; 318 319 if(rq->host) { 320 VirtualServer *vs = ucx_map_cstr_get(sn->config->host_vs, rq->host); 321 if(vs) { 322 rq->vs = vs; 323 } else { 324 log_ereport( 325 LOG_VERBOSE, 326 "Unkown host ''%s'': using default virtual server", 327 rq->host); 328 } 329 } 330 331 // parse connection header 332 rq->rq.rq_attr.keep_alive = (rq->rq.protv_num >= PROTOCOL_VERSION_HTTP11); 333 char *conn_str = pblock_findkeyval(pb_key_connection, rq->rq.headers); 334 if(conn_str) { 335 if(!strcasecmp(conn_str, "keep-alive")) { 336 rq->rq.rq_attr.keep_alive = 1; 337 } else if(!strcasecmp(conn_str, "close")) { 338 rq->rq.rq_attr.keep_alive = 0; 339 } 340 } 341 342 // check for request body and prepare input buffer 343 char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers); 344 if(ctlen_str) { 345 int ctlen = atoi(ctlen_str); 346 347 //printf("request body length: %d\n", ctlen); 348 349 netbuf *nb = request->netbuf; 350 351 // create new netbuf 352 HttpStream *net_io = (HttpStream*)httpstream_new(pool, io); 353 net_io->max_read = ctlen; 354 355 sn->sn.inbuf = pool_malloc(pool, sizeof(netbuf)); 356 sn->sn.inbuf->sd = net_io; 357 sn->sn.inbuf->pos = 0; 358 359 // prepare buffer 360 int cur_input_len = nb->cursize - nb->pos; 361 362 if(cur_input_len >= ctlen) { 363 /* 364 * all data is already in the primary input buffer 365 * just link the new netbuf to the primary buffer 366 */ 367 sn->sn.inbuf->maxsize = ctlen; 368 sn->sn.inbuf->cursize = ctlen; 369 sn->sn.inbuf->inbuf = nb->inbuf + nb->pos; 370 } else { 371 sn->sn.inbuf->maxsize = (ctlen > 2048) ? (2048) : (ctlen); 372 sn->sn.inbuf->inbuf = pool_malloc(pool, sn->sn.inbuf->maxsize); 373 374 if(cur_input_len > 0) { 375 // we have read a part of the request body -> copy to netbuf 376 memcpy(sn->sn.inbuf->inbuf, nb->inbuf+nb->pos, cur_input_len); 377 } 378 379 sn->sn.inbuf->cursize = cur_input_len; 380 } 381 } else { 382 sn->sn.inbuf = NULL; 383 } 384 385 // 386 // Send the request to the NSAPI system 387 // 388 389 // compare current and listener thread pool 390 threadpool_t *lstp = request->connection->listener->threadpool; 391 sn->defaultpool = lstp; 392 if(lstp == thrpool) { 393 sn->currentpool = thrpool; 394 nsapi_handle_request(sn, rq); 395 } else { 396 // execute nsapi functions on a different thread pool 397 nsapi_change_threadpool(sn, rq, lstp); 398 } 399 400 return 0; 401 } 402 403 404 405 void header_add(HeaderArray *hd, sstr_t name, sstr_t value) { 406 while(hd->len >= hd->alloc) { 407 if(hd->next == NULL) { 408 HeaderArray *block = malloc(sizeof(HeaderArray)); 409 block->next = NULL; 410 block->len = 0; 411 block->headers = calloc(16, sizeof(Header)); 412 block->alloc = 16; 413 hd->next = block; 414 } 415 hd = hd->next; 416 } 417 hd->headers[hd->len].name = name; 418 hd->headers[hd->len].value = value; 419 hd->len++; 420 } 421 422 void header_array_free(HeaderArray *hd) { 423 HeaderArray *next; 424 while(hd) { 425 next = hd->next; 426 free(hd->headers); 427 free(hd); 428 hd = next; 429 } 430 } 431 432 433 /* 434 * NSAPI Processing 435 */ 436 437 int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq) { 438 int r = REQ_NOACTION; 439 do { 440 switch(rq->phase) { 441 case NSAPIAuthTrans: { 442 r = nsapi_authtrans(sn, rq); 443 if(r != REQ_PROCEED) { 444 break; 445 } 446 rq->phase++; 447 nsapi_context_next_stage(&rq->context); 448 } 449 case NSAPINameTrans: { 450 //printf(">>> NameTrans\n"); 451 r = nsapi_nametrans(sn, rq); 452 if(r != REQ_PROCEED) { 453 break; 454 } 455 rq->phase++; 456 nsapi_context_next_stage(&rq->context); 457 } 458 case NSAPIPathCheck: { 459 //printf(">>> PathCheck\n"); 460 r = nsapi_pathcheck(sn, rq); 461 if(r != REQ_PROCEED) { 462 break; 463 } 464 rq->phase++; 465 nsapi_context_next_stage(&rq->context); 466 } 467 case NSAPIObjectType: { 468 //printf(">>> ObjectType\n"); 469 r = nsapi_objecttype(sn, rq); 470 if(r != REQ_PROCEED) { 471 break; 472 } 473 rq->phase++; 474 nsapi_context_next_stage(&rq->context); 475 } 476 case NSAPIService: { 477 //printf(">>> Service\n"); 478 r = nsapi_service(sn, rq); 479 if(r != REQ_PROCEED) { 480 break; 481 } 482 rq->phase = NSAPIAddLog; // skip NSAPIError 483 nsapi_context_next_stage(&rq->context); 484 } 485 case NSAPIAddLog: { 486 //printf(">>> AddLog\n"); 487 r = nsapi_addlog(sn, rq); 488 if(r == REQ_PROCESSING) { 489 break; 490 } 491 // finish request 492 r = REQ_PROCEED; 493 break; 494 } 495 default: // should not happen, but when it does, finish the req 496 case REQ_FINISH: { 497 //printf(">>> Finish\n"); 498 //r = nsapi_finish_request(sn, rq); 499 r = REQ_PROCEED; 500 break; 501 } 502 case NSAPIError: { 503 //printf(">>> Error\n"); 504 r = nsapi_error(sn, rq); 505 if(r == REQ_PROCEED) { 506 // restart the loop to switch to AddLog directive 507 r = REQ_RESTART; 508 rq->phase = NSAPIAddLog; 509 nsapi_context_next_stage(&rq->context); 510 } else { 511 /* 512 * an error occured while handling an error 513 * leave loop to finish the request (without AddLog) 514 */ 515 r = REQ_PROCEED; 516 break; 517 } 518 } 519 } 520 521 if(r == REQ_ABORTED) { 522 // switch to NSAPIError 523 rq->phase = NSAPIError; 524 nsapi_context_next_stage(&rq->context); 525 r = REQ_RESTART; 526 } 527 528 } while (r == REQ_RESTART); 529 530 if(r != REQ_PROCESSING) { 531 r = nsapi_finish_request(sn, rq); 532 } 533 534 return r; 535 } 536 537 int nsapi_finish_request(NSAPISession *sn, NSAPIRequest *rq) { 538 if(rq->rq.rq_attr.keep_alive) { 539 SessionHandler *sh = sn->connection->session_handler; 540 sh->keep_alive(sh, sn->connection); 541 /* 542 * keep the connection object 543 * the sn->config is referenced by the connection, so we don't 544 * unref it 545 */ 546 } else { 547 connection_destroy(sn->connection); 548 cfg_unref(sn->config); 549 } 550 551 // free all memory 552 free(sn->netbuf->inbuf); 553 free(sn->netbuf); 554 555 pool_destroy(sn->sn.pool); 556 557 return 0; 558 } 559 560 int nsapi_authtrans(NSAPISession *sn, NSAPIRequest *rq) { 561 HTTPObjectConfig *objconf = rq->vs->objects; 562 httpd_object *obj = objconf->objects[0]; 563 dtable *dt = object_get_dtable(obj, NSAPIAuthTrans); 564 565 int ret = rq->context.last_req_code; 566 for(int i=NCX_DI(rq);i<dt->ndir;i++) { 567 if(ret == REQ_NOACTION) { 568 directive *d = dt->dirs[i]; 569 ret = nsapi_exec(d, sn, rq); 570 } 571 572 if(ret != REQ_NOACTION) { 573 /* 574 * if a saf is still processing, we need to save the context, to 575 * process this object at a later time 576 */ 577 if(ret == REQ_PROCESSING) { 578 // save nsapi context 579 // add +1 to start next round with next function 580 rq->context.dtable_index = i + 1; 581 } 582 583 return ret; 584 } 585 } 586 587 588 return REQ_PROCEED; 589 } 590 591 int nsapi_nametrans(NSAPISession *sn, NSAPIRequest *rq) { 592 HTTPObjectConfig *objconf = rq->vs->objects; 593 //printf("nsapi_nametrans\n"); 594 httpd_objset *objset = objset_create(sn->sn.pool); 595 rq->rq.os = objset; 596 // first object in objconf is the default object TODO: make sure it is 597 objset_add_object(sn->sn.pool, objset, objconf->objects[0]); 598 599 httpd_object *obj = objset->obj[0]; // nametrans only in default object 600 dtable *dt = object_get_dtable(obj, NSAPINameTrans); 601 602 // execute directives 603 int ret = rq->context.last_req_code; 604 char *name = NULL; 605 char *ppath = NULL; 606 for(int i=NCX_DI(rq);i<dt->ndir;i++) { 607 if(ret == REQ_NOACTION) { 608 directive *d = dt->dirs[i]; 609 ret = nsapi_exec(d, sn, rq); 610 } 611 612 // check for name or ppath 613 name = pblock_findkeyval(pb_key_name, rq->rq.vars); 614 ppath = pblock_findkeyval(pb_key_ppath, rq->rq.vars); 615 616 // add additional objects to the objset 617 if(add_objects(objconf, objset, sn, rq, name, ppath) == REQ_ABORTED) { 618 log_ereport(LOG_FAILURE, "add_objects failed"); 619 return REQ_ABORTED; 620 } 621 622 if(ret != REQ_NOACTION) { 623 /* 624 * if a saf is still processing, we need to save the context, to 625 * process this object at a later time 626 */ 627 if(ret == REQ_PROCESSING) { 628 // save nsapi context 629 // add +1 to start next round with next function 630 rq->context.dtable_index = i + 1; 631 } else if(ret == REQ_PROCEED) { 632 char *pp = pblock_findkeyval(pb_key_ppath, rq->rq.vars); 633 pblock_kvinsert(pb_key_path, pp, strlen(pp), rq->rq.vars); 634 } 635 636 return ret; 637 } 638 } 639 640 // if no function has set the ppath var, translate it to docroot 641 if(ret == REQ_NOACTION && ppath == NULL) { 642 sstr_t docroot = rq->vs->document_root; 643 if(docroot.length < 1) { 644 log_ereport( 645 LOG_WARN, 646 "VirtualServer(%.*s) docroot too short", 647 (int)rq->vs->name.length, 648 rq->vs->name.ptr); 649 return REQ_ABORTED; // docroot too short 650 } 651 652 // if there is a trailing '/', remove it 653 /* 654 if(docroot.ptr[docroot.length - 1] == '/') { 655 docroot.length--; 656 } 657 658 sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); 659 660 sstr_t translated; 661 translated.length = docroot.length + uri.length; 662 translated.ptr = alloca(translated.length + 1); 663 translated = sstrncat(translated, 2, docroot, uri); 664 665 pblock_kvinsert( 666 pb_key_ppath, 667 translated.ptr, 668 translated.length, 669 rq->rq.vars); 670 */ 671 sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); 672 request_set_path(docroot, uri, rq->rq.vars); 673 } 674 675 // TODO: remove ppath 676 char *pp = pblock_findkeyval(pb_key_ppath, rq->rq.vars); 677 pblock_kvinsert(pb_key_path, pp, strlen(pp), rq->rq.vars); 678 679 return REQ_PROCEED; 680 } 681 682 int nsapi_pathcheck(NSAPISession *sn, NSAPIRequest *rq) { 683 //printf("nsapi_pathcheck\n"); 684 httpd_objset *objset = rq->rq.os; 685 686 if(NCX_OI(rq) == -1) { 687 NCX_OI(rq) = objset->pos - 1; 688 } 689 690 int ret = rq->context.last_req_code; 691 for(int i=NCX_OI(rq);i>=0;i--) { 692 httpd_object *obj = objset->obj[i]; 693 dtable *dt = object_get_dtable(obj, NSAPIPathCheck); 694 695 // execute directives 696 for(int j=NCX_DI(rq);j<dt->ndir;j++) { 697 if(ret == REQ_NOACTION || ret == REQ_PROCEED) { 698 directive *d = dt->dirs[j]; 699 ret = nsapi_exec(d, sn, rq); 700 } else { 701 if(ret == REQ_PROCESSING) { 702 // save nsapi context 703 rq->context.objset_index = i; 704 705 // add +1 to start next round with next function 706 rq->context.dtable_index = j + 1; 707 } 708 709 return ret; 710 } 711 } 712 } 713 714 return REQ_PROCEED; 715 } 716 717 int nsapi_objecttype(NSAPISession *sn, NSAPIRequest *rq) { 718 //printf("nsapi_objecttype\n"); 719 httpd_objset *objset = rq->rq.os; 720 721 if(NCX_OI(rq) == -1) { 722 // object index is undefined -> define correct object index 723 NCX_OI(rq) = objset->pos - 1; 724 } 725 726 int ret = rq->context.last_req_code; 727 for(int i=NCX_OI(rq);i>=0;i--) { 728 httpd_object *obj = objset->obj[i]; 729 dtable *dt = object_get_dtable(obj, NSAPIObjectType); 730 731 // execute directives 732 for(int j=NCX_DI(rq);j<dt->ndir;j++) { 733 if(ret == REQ_NOACTION) { 734 directive *d = dt->dirs[j]; 735 ret = nsapi_exec(d, sn, rq); 736 } 737 738 switch(ret) { 739 case REQ_PROCEED: { 740 char *type = pblock_findkeyval( 741 pb_key_content_type, 742 rq->rq.srvhdrs); 743 if(type == NULL) { 744 ret = REQ_NOACTION; 745 break; 746 } 747 return ret; 748 } 749 case REQ_PROCESSING: { 750 // save nsapi context 751 rq->context.objset_index = i; 752 753 // add +1 to start next round with next function 754 rq->context.dtable_index = j + 1; 755 return ret; 756 } 757 case REQ_NOACTION: { 758 break; 759 } 760 default: { 761 return ret; 762 } 763 } 764 } 765 } 766 767 /* 768 * No function returned with REQ_PROCEED, but we need a content type. 769 * If the path ends with a '/', we set the content type to 770 * 'internal/directory' so that 'index-common' can serve the content. 771 * Otherwise we set the content type to text/plain 772 */ 773 sstr_t path = sstr(pblock_findkeyval(pb_key_ppath, rq->rq.vars)); 774 sstr_t ct; 775 if(path.ptr[path.length - 1] == '/') { 776 // directory 777 ct = sstrn("internal/directory", 18); 778 } else { 779 ct = sstrn("text/plain", 10); 780 } 781 pblock_kvinsert(pb_key_content_type, ct.ptr, ct.length, rq->rq.srvhdrs); 782 783 return REQ_PROCEED; 784 } 785 786 int nsapi_service(NSAPISession *sn, NSAPIRequest *rq) { 787 //printf("nsapi_service\n"); 788 httpd_objset *objset = rq->rq.os; 789 790 if(NCX_OI(rq) == -1) { 791 NCX_OI(rq) = objset->pos - 1; 792 } 793 794 int ret = rq->context.last_req_code; 795 char *content_type = NULL; 796 char *method = NULL; 797 for(int i=NCX_OI(rq);i>=0;i--) { 798 httpd_object *obj = objset->obj[i]; 799 dtable *dt = object_get_dtable(obj, NSAPIService); 800 801 // execute directives 802 for(int j=NCX_DI(rq);j<dt->ndir;j++) { 803 if(ret == REQ_NOACTION) { 804 directive *d = dt->dirs[j]; 805 806 // check type parameter 807 char *dtp = pblock_findkeyval(pb_key_type, d->param); 808 if(dtp) { 809 // type parameter for directive 810 if(!content_type) { 811 content_type = pblock_findkeyval( 812 pb_key_content_type, 813 rq->rq.srvhdrs); 814 } 815 // compare types 816 if(!contenttype_match(sstr(dtp), sstr(content_type))) { 817 continue; 818 } 819 } 820 821 // check method parameter 822 char *dmt = pblock_findkeyval(pb_key_method, d->param); 823 if(dmt) { 824 if(!method) { 825 method = pblock_findkeyval(pb_key_method, rq->rq.reqpb); 826 } 827 828 if(!method_match(dmt, method)) { 829 continue; 830 } 831 } 832 833 // execute the saf 834 ret = nsapi_exec(d, sn, rq); 835 } 836 837 if(ret != REQ_NOACTION) { 838 if(ret == REQ_PROCEED) { 839 /* 840 * flush buffer and add termination if chunked encoding 841 * is enabled 842 */ 843 net_finish(sn->sn.csd); 844 } else if(ret == REQ_PROCESSING) { 845 // save nsapi context 846 rq->context.objset_index = i; 847 848 // add +1 to start next round with next function 849 rq->context.dtable_index = j + 1; 850 } 851 852 return ret; 853 } 854 } 855 } 856 857 return ret; 858 } 859 860 int nsapi_error(NSAPISession *sn, NSAPIRequest *rq) { 861 //printf("nsapi_error\n"); 862 httpd_objset *objset = rq->rq.os; 863 864 if(NCX_OI(rq) == -1) { 865 NCX_OI(rq) = objset->pos - 1; 866 } 867 868 int ret = rq->context.last_req_code; 869 for(int i=NCX_OI(rq);i>=0;i--) { 870 httpd_object *obj = objset->obj[i]; 871 dtable *dt = object_get_dtable(obj, NSAPIError); 872 873 // execute directives 874 for(int j=NCX_DI(rq);j<dt->ndir;j++) { 875 if(ret == REQ_NOACTION) { 876 directive *d = dt->dirs[j]; 877 878 // check status code parameter 879 char *status = pblock_findkeyval(pb_key_type, d->param); 880 if(status) { 881 int statuscode = atoi(status); 882 if(statuscode != rq->rq.status_num) { 883 continue; 884 } 885 } 886 887 // execute the saf 888 ret = nsapi_exec(d, sn, rq); 889 } 890 891 if(ret == REQ_ABORTED) { 892 // if an error directive fails, we use the default 893 // error handler 894 break; 895 } 896 if(ret != REQ_NOACTION) { 897 if(ret == REQ_PROCEED) { 898 /* 899 * flush buffer and add termination if chunked encoding 900 * is enabled 901 */ 902 net_finish(sn->sn.csd); 903 } else if(ret == REQ_PROCESSING) { 904 // save nsapi context 905 rq->context.objset_index = i; 906 907 // add +1 to start next round with next function 908 rq->context.dtable_index = j + 1; 909 } 910 return ret; 911 } 912 } 913 } 914 915 if(ret != REQ_PROCEED) { 916 // default error handler 917 nsapi_error_request((Session*)sn, (Request*)rq); 918 } 919 920 return ret; 921 } 922 923 int nsapi_addlog(NSAPISession *sn, NSAPIRequest *rq) { 924 //printf("nsapi_addlog\n"); 925 httpd_objset *objset = rq->rq.os; 926 927 if(NCX_OI(rq) == -1) { 928 NCX_OI(rq) = objset->pos - 1; 929 } 930 931 int ret = rq->context.last_req_code; 932 for(int i=NCX_OI(rq);i>=0;i--) { 933 httpd_object *obj = objset->obj[i]; 934 dtable *dt = object_get_dtable(obj, NSAPIAddLog); 935 936 // execute directives 937 for(int j=NCX_DI(rq);j<dt->ndir;j++) { 938 if(ret == REQ_NOACTION) { 939 directive *d = dt->dirs[j]; 940 ret = nsapi_exec(d, sn, rq); 941 } 942 943 if(ret != REQ_NOACTION) { 944 if(ret == REQ_PROCESSING) { 945 // save nsapi context 946 rq->context.objset_index = i; 947 948 // add +1 to start next round with next function 949 rq->context.dtable_index = j + 1; 950 } 951 952 return ret; 953 } 954 } 955 } 956 957 return REQ_PROCEED; 958 } 959 960 struct _tpd_data { 961 NSAPISession *sn; 962 NSAPIRequest *rq; 963 directive *directive; 964 threadpool_t *threadpool; 965 }; 966 967 int nsapi_exec(directive *d, NSAPISession *sn, NSAPIRequest *rq) { 968 // TODO: condition 969 if(d->cond) { 970 if(!condition_evaluate(d->cond, (Session*)sn, (Request*)rq)) { 971 return REQ_NOACTION; 972 } 973 } 974 975 char *poolname = pblock_findkeyval(pb_key_pool, d->param); 976 if(poolname) { 977 threadpool_t *pool = get_threadpool(sstr(poolname)); 978 if(pool && pool != sn->currentpool) { 979 // execute directive in different thread pool 980 return nsapi_exec_tp(d, sn, rq, pool); 981 } 982 } else if(sn->currentpool != sn->defaultpool) { 983 // if no pool is set, use always the default thread pool 984 return nsapi_exec_tp(d, sn, rq, sn->defaultpool); 985 } 986 987 return SAF_EXEC(d->func, d->param, (Session*)sn, (Request*)rq); 988 } 989 990 int nsapi_exec_tp( 991 directive *d, 992 NSAPISession *sn, 993 NSAPIRequest *rq, 994 threadpool_t *pool) 995 { 996 struct _tpd_data *data = malloc(sizeof(struct _tpd_data)); 997 if(data == NULL) { 998 // TODO: set error 999 return REQ_ABORTED; 1000 } 1001 data->sn = sn; 1002 data->rq = rq; 1003 data->directive = d; 1004 data->threadpool = pool; 1005 1006 sn->currentpool = pool; 1007 threadpool_run(pool, thrpool_exec, data); 1008 1009 return REQ_PROCESSING; 1010 } 1011 1012 void nsapi_function_return(Session *session, Request *request, int ret) { 1013 NSAPISession *sn = (NSAPISession*)session; 1014 NSAPIRequest *rq = (NSAPIRequest*)request; 1015 1016 rq->context.last_req_code = ret; 1017 1018 if(sn->currentpool != sn->defaultpool) { 1019 nsapi_change_threadpool(sn, rq, sn->defaultpool); 1020 } else { 1021 nsapi_handle_request(sn, rq); 1022 } 1023 } 1024 1025 void nsapi_change_threadpool( 1026 NSAPISession *sn, 1027 NSAPIRequest *rq, 1028 threadpool_t *thrpool) 1029 { 1030 struct _tpd_data *data = malloc(sizeof(struct _tpd_data)); 1031 data->sn = sn; 1032 data->rq = rq; 1033 data->threadpool = thrpool; 1034 1035 threadpool_run(thrpool, thrpool_change, data); 1036 } 1037 1038 void* thrpool_exec(void *d) { 1039 struct _tpd_data *data = d; 1040 1041 data->sn->currentpool = data->threadpool; 1042 int r = SAF_EXEC( 1043 data->directive->func, 1044 data->directive->param, 1045 (Session*)data->sn, 1046 (Request*)data->rq); 1047 1048 nsapi_function_return((Session*)data->sn, (Request*)data->rq, r); 1049 free(data); 1050 1051 return NULL; 1052 } 1053 1054 void* thrpool_change(void *d) { 1055 struct _tpd_data *data = d; 1056 1057 data->sn->currentpool = data->threadpool; 1058 nsapi_handle_request(data->sn, data->rq); 1059 1060 free(data); 1061 return NULL; 1062 } 1063 1064 1065 /* 1066 * checks if the method matches the cmp string 1067 * the format of cmp is a single method string or a list of methods 1068 * (method1|method2|method3|...) 1069 */ 1070 int method_match(char *cmp, char *method) { 1071 if(cmp[0] != '(') { 1072 /* not a list of methods, so just compare the 2 strings */ 1073 if(!strcmp(cmp, method)) { 1074 return 1; 1075 } 1076 } else if(cmp[0] == 0) { 1077 // empty string 1078 log_ereport( 1079 LOG_WARN, 1080 "Skipped service saf with empty method parameter"); 1081 return 0; 1082 } 1083 1084 size_t method_len = strlen(method); 1085 size_t last_pos = 0; 1086 cmp++; /* skip '(' */ 1087 for(int i=0;cmp[i]!=0;i++) { 1088 char c = cmp[i]; 1089 if(c == '|' || c == ')') { 1090 size_t len = i - last_pos; 1091 if(len == method_len) { 1092 char *cp = cmp + last_pos; 1093 if(!memcmp(cp, method, len)) { 1094 return 1; 1095 } 1096 } 1097 last_pos = i + 1; 1098 } 1099 } 1100 1101 return 0; 1102 } 1103 1104 /* 1105 * checks if the content type matches the cmp string 1106 * the format of cmp is a single string with wildcards or a list 1107 * of types (also with wildcard support) 1108 * (type1|type2*) 1109 */ 1110 int contenttype_match(sstr_t cmp, sstr_t ctype) { 1111 if(cmp.ptr[0] != '(') { 1112 if(cmp.ptr[0] == '*') { 1113 cmp.ptr++; 1114 cmp.length--; 1115 return sstrsuffix(ctype, cmp); 1116 } else if(cmp.ptr[cmp.length-1] == '*') { 1117 cmp.length--; 1118 return sstrprefix(ctype, cmp); 1119 } else { 1120 return !sstrcmp(cmp, ctype); 1121 } 1122 } else if(cmp.ptr[0] == 0) { 1123 log_ereport(LOG_WARN, "Skipped service saf with empty type parameter"); 1124 return 0; 1125 } 1126 1127 cmp = sstrsubsl(cmp, 1, cmp.length - 2); 1128 1129 int begin = 0; 1130 for(int i=0;i<cmp.length;i++) { 1131 if(cmp.ptr[i] == '|') { 1132 if(contenttype_match(sstrsubsl(cmp, begin, i-begin), ctype)) { 1133 return 1; 1134 } 1135 begin = i + 1; 1136 } 1137 } 1138 return contenttype_match(sstrsubs(cmp, begin), ctype); 1139 } 1140 1141 /* 1142 * adds objects with specific name or path to the httpd_objset 1143 */ 1144 int add_objects( 1145 HTTPObjectConfig *objs, 1146 httpd_objset *os, 1147 NSAPISession *sn, 1148 NSAPIRequest *rq, 1149 char *name, 1150 char *path) 1151 { 1152 /* first, add all objects with a matching path */ 1153 /* TODO */ 1154 1155 1156 /* add object with object with matching name */ 1157 if(name == NULL) { 1158 return REQ_PROCEED; 1159 } 1160 1161 for(int i=0;i<objs->nobj;i++) { 1162 httpd_object *obj = objs->objects[i]; 1163 1164 if(obj->name && !strcmp(name, obj->name)) { 1165 objset_add_object(sn->sn.pool, os, obj); 1166 } 1167 } 1168 1169 return REQ_PROCEED; 1170 } 1171