Changeset 7e47bd8ddd24e8c8c76a3f1a7ae94710d5bc4664
- Timestamp:
- 09/11/09 06:06:38 (4 years ago)
- git-parent:
- Files:
-
- src/Makefile.in (modified) (1 diff)
- src/noit_http.c (modified) (13 diffs)
- src/noit_http.h (modified) (4 diffs)
- src/noit_rest.c (added)
- src/noit_rest.h (added)
- src/noitd.c (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
src/Makefile.in
r69aafdd r7e47bd8 40 40 noit_module.o noit_conf.o noit_conf_checks.o noit_tokenizer.o \ 41 41 noit_capabilities_listener.o noit_xml.o \ 42 noit_jlog_listener.o noit_livestream_listener.o noit_filters.o 42 noit_jlog_listener.o noit_livestream_listener.o noit_filters.o \ 43 noit_http.o noit_rest.o 43 44 44 45 STRATCON_OBJS=stratcond.o noit_listener.o \ src/noit_http.c
r88a7178 r7e47bd8 42 42 #define REQ_PAT "\r\n\r\n" 43 43 #define REQ_PATSIZE 4 44 #define HEADER_CONTENT_LENGTH "content-length" 45 #define HEADER_EXPECT "expect" 44 46 45 47 #define CTX_ADD_HEADER(a,b) \ … … 66 68 } \ 67 69 } while(0) 68 struct bchain *bchain_from_data( void *d, size_t size) {70 struct bchain *bchain_from_data(const void *d, size_t size) { 69 71 struct bchain *n; 70 72 n = bchain_alloc(size); … … 86 88 case 'H': 87 89 if(!strcasecmp(s, "HEAD")) return NOIT_HTTP_HEAD; 90 break; 91 case 'P': 92 if(!strcasecmp(s, "POST")) return NOIT_HTTP_POST; 88 93 break; 89 94 default: … … 177 182 178 183 if(!ctx->conn.e) return 0; 184 #if 0 179 185 if(ctx->res.output_started == noit_false) return EVENTER_EXCEPTION; 186 #endif 180 187 if(!b) { 181 188 if(ctx->res.closed) ctx->res.complete = noit_true; … … 213 220 } 214 221 static noit_boolean 215 noit_http_request_finalize (noit_http_request *req, noit_boolean *err) {222 noit_http_request_finalize_headers(noit_http_request *req, noit_boolean *err) { 216 223 int start; 224 void *vval; 217 225 const char *mstr, *last_name = NULL; 218 226 struct bchain *b; 219 227 228 if(req->first_input && req->first_input->size) { 229 noitL(noit_error, "HTTP REQ (headers): ===>%.*s<===\n", 230 req->first_input->size, req->first_input->buff + req->first_input->start); 231 } 232 if(req->state != NOIT_HTTP_REQ_HEADERS) return noit_false; 220 233 if(!req->current_input) req->current_input = req->first_input; 221 234 if(!req->current_input) return noit_false; … … 265 278 req->current_input->start + req->current_offset, 266 279 req->first_input->size); 267 if(req->last_input == req->current_ request_chain)280 if(req->last_input == req->current_input) 268 281 req->last_input = req->first_input; 269 282 } … … 347 360 } while(next_str); 348 361 } 362 363 /* headers are done... we could need to read a payload */ 364 if(noit_hash_retrieve(&req->headers, 365 HEADER_CONTENT_LENGTH, 366 sizeof(HEADER_CONTENT_LENGTH)-1, &vval)) { 367 const char *val = vval; 368 req->has_payload = noit_true; 369 req->content_length = strtoll(val, NULL, 10); 370 } 371 if(noit_hash_retrieve(&req->headers, HEADER_EXPECT, 372 sizeof(HEADER_EXPECT)-1, &vval)) { 373 const char *val = vval; 374 if(strncmp(val, "100-", 4) || /* Bad expect header */ 375 req->has_payload == noit_false) /* expect, but no content length */ 376 FAIL; 377 /* We need to tell the client to "go-ahead" -- HTTP sucks */ 378 req->state = NOIT_HTTP_REQ_EXPECT; 379 return noit_false; 380 } 381 if(req->content_length > 0) { 382 /* switch modes... let's go read the payload */ 383 req->state = NOIT_HTTP_REQ_PAYLOAD; 384 return noit_false; 385 } 386 349 387 req->complete = noit_true; 350 388 return noit_true; 389 } 390 static noit_boolean 391 noit_http_request_finalize_payload(noit_http_request *req, noit_boolean *err) { 392 req->complete = noit_true; 393 return noit_true; 394 } 395 static noit_boolean 396 noit_http_request_finalize(noit_http_request *req, noit_boolean *err) { 397 if(req->state == NOIT_HTTP_REQ_HEADERS) 398 if(noit_http_request_finalize_headers(req, err)) return noit_true; 399 if(req->state == NOIT_HTTP_REQ_EXPECT) return noit_false; 400 if(req->state == NOIT_HTTP_REQ_PAYLOAD) 401 if(noit_http_request_finalize_payload(req, err)) return noit_true; 402 return noit_false; 351 403 } 352 404 int … … 391 443 in->allocd - in->size - in->start, 392 444 &mask, ctx->conn.e); 445 noitL(noit_error, "HTTP REQ (drive read %d)\n", len); 393 446 if(len == -1 && errno == EAGAIN) return mask; 394 447 if(len <= 0) goto full_error; 395 448 if(len > 0) in->size += len; 396 449 rv = noit_http_request_finalize(&ctx->req, &err); 397 if(len == -1 || err == noit_true) { 398 goto full_error; 399 } 400 if(rv == noit_true) return EVENTER_WRITE | EVENTER_EXCEPTION; 450 if(len == -1 || err == noit_true) goto full_error; 451 if(ctx->req.state == NOIT_HTTP_REQ_EXPECT) { 452 const char *expect; 453 ctx->req.state = NOIT_HTTP_REQ_PAYLOAD; 454 assert(ctx->res.leader == NULL); 455 expect = "HTTP/1.1 100 Continue\r\n\r\n"; 456 ctx->res.leader = bchain_from_data(expect, strlen(expect)); 457 _http_perform_write(ctx, &mask); 458 if(ctx->res.leader != NULL) return mask; 459 } 460 if(rv == noit_true) return mask | EVENTER_WRITE | EVENTER_EXCEPTION; 401 461 } 402 462 return EVENTER_READ | EVENTER_EXCEPTION; 403 463 } 464 noit_boolean 465 noit_http_session_prime_input(noit_http_session_ctx *ctx, 466 const void *data, size_t len) { 467 if(ctx->req.first_input != NULL) return noit_false; 468 if(len > DEFAULT_BCHAINSIZE) return noit_false; 469 ctx->req.first_input = ctx->req.last_input = 470 bchain_alloc(DEFAULT_BCHAINSIZE); 471 memcpy(ctx->req.first_input->buff, data, len); 472 ctx->req.first_input->size = len; 473 return noit_true; 474 } 475 404 476 void 405 477 noit_http_request_release(noit_http_session_ctx *ctx) { 406 478 noit_hash_destroy(&ctx->req.headers, NULL, NULL); 407 RELEASE_BCHAIN(ctx->req.first_input); 479 /* If we expected a payload, we expect a trailing \r\n */ 480 if(ctx->req.has_payload) { 481 int drained, mask; 482 ctx->drainage = ctx->req.content_length - ctx->req.content_length_read; 483 /* best effort, we'll drain it before the next request anyway */ 484 drained = noit_http_session_req_consume(ctx, NULL, ctx->drainage, &mask); 485 ctx->drainage -= drained; 486 } 487 RELEASE_BCHAIN(ctx->req.current_request_chain); 408 488 memset(&ctx->req, 0, sizeof(ctx->req)); 409 489 } … … 426 506 } 427 507 int 508 noit_http_session_req_consume(noit_http_session_ctx *ctx, 509 void *buf, size_t len, int *mask) { 510 size_t bytes_read = 0; 511 /* We attempt to consume from the first_input */ 512 struct bchain *in, *tofree; 513 len = MIN(len, ctx->req.content_length - ctx->req.content_length_read); 514 while(bytes_read < len) { 515 int crlen = 0; 516 in = ctx->req.first_input; 517 while(in && bytes_read < len) { 518 int partial_len = MIN(in->size, len - bytes_read); 519 bytes_read += partial_len; 520 if(buf) memcpy(buf+bytes_read, in->buff+in->start, partial_len); 521 noitL(noit_error, "HTTP REQ (consume) : ===>%.*s<===\n", 522 partial_len, in->buff+in->start); 523 ctx->req.content_length_read += partial_len; 524 in->start += partial_len; 525 in->size -= partial_len; 526 if(in->size == 0) { 527 tofree = in; 528 ctx->req.first_input = in = in->next; 529 tofree->next = NULL; 530 RELEASE_BCHAIN(tofree); 531 if(in == NULL) { 532 ctx->req.last_input = NULL; 533 return bytes_read; 534 } 535 } 536 } 537 while(bytes_read + crlen < len) { 538 int rlen; 539 in = ctx->req.last_input; 540 if(!in) 541 in = ctx->req.first_input = ctx->req.last_input = 542 bchain_alloc(DEFAULT_BCHAINSIZE); 543 else if(in->start + in->size >= in->allocd) { 544 in->next = bchain_alloc(DEFAULT_BCHAINSIZE); 545 in = ctx->req.last_input = in->next; 546 } 547 /* pull next chunk */ 548 rlen = ctx->conn.e->opset->read(ctx->conn.e->fd, 549 in->buff + in->start + in->size, 550 in->allocd - in->size - in->start, 551 mask, ctx->conn.e); 552 noitL(noit_error, "HTTP REQ (consume read %d)\n", rlen); 553 if(rlen == -1 && errno == EAGAIN) { 554 /* We'd block to read more, but we have data, 555 * so do a short read */ 556 if(ctx->req.first_input->size) break; 557 /* We've got nothing... */ 558 return -1; 559 } 560 if(rlen <= 0) return -1; 561 in->size += rlen; 562 crlen += rlen; 563 } 564 } 565 /* NOT REACHED */ 566 return bytes_read; 567 } 568 int 428 569 noit_http_session_drive(eventer_t e, int origmask, void *closure, 429 570 struct timeval *now) { … … 431 572 int rv; 432 573 int mask = origmask; 574 575 /* Drainage -- this is as nasty as it sounds 576 * The last request could have unread upload content, we would have 577 * noted that in noit_http_request_release. 578 */ 579 while(ctx->drainage > 0) { 580 int len; 581 len = noit_http_session_req_consume(ctx, NULL, ctx->drainage, &mask); 582 if(len == -1 && errno == EAGAIN) return mask; 583 if(len <= 0) goto abort_drive; 584 ctx->drainage -= len; 585 } 586 433 587 next_req: 434 588 if(ctx->req.complete != noit_true) { 589 int maybe_write_mask; 435 590 mask = noit_http_complete_request(ctx, origmask); 436 if(ctx->req.complete != noit_true) return mask; 591 _http_perform_write(ctx, &maybe_write_mask); 592 if(ctx->req.complete != noit_true) return mask | maybe_write_mask; 437 593 } 438 594 … … 444 600 ctx->conn.e && 445 601 ctx->conn.needs_close == noit_true) { 602 abort_drive: 446 603 ctx->conn.e->opset->close(ctx->conn.e->fd, &mask, ctx->conn.e); 447 604 ctx->conn.e = NULL; … … 455 612 if(ctx->req.complete == noit_false) goto next_req; 456 613 if(ctx->conn.e) { 457 return mask ;614 return mask | rv; 458 615 } 459 616 return 0; … … 684 841 struct bchain *in) { 685 842 struct bchain *out; 686 int ilen, hexlen;843 int ilen, maxlen, hexlen; 687 844 int opts = ctx->res.output_options; 688 845 689 846 /* a chunked header looks like: hex*\r\ndata\r\n */ 690 847 /* let's assume that content never gets "larger" */ 848 if(opts & NOIT_HTTP_GZIP) maxlen = deflateBound(NULL, in->size); 849 else if(opts & NOIT_HTTP_DEFLATE) maxlen = compressBound(in->size); 850 691 851 /* So, the link size is the len(data) + 4 + ceil(log(len(data))/log(16)) */ 692 ilen = in->size;852 ilen = maxlen; 693 853 hexlen = 0; 694 854 while(ilen) { ilen >>= 4; hexlen++; } 695 855 if(hexlen == 0) hexlen = 1; 696 856 697 ilen = in->size; 698 if(opts & NOIT_HTTP_GZIP) ilen = deflateBound(NULL, ilen); 699 else if(opts & NOIT_HTTP_DEFLATE) ilen = compressBound(ilen); 700 out = bchain_alloc(hexlen + 4 + ilen); 857 out = bchain_alloc(hexlen + 4 + maxlen); 701 858 /* if we're chunked, let's give outselved hexlen + 2 prefix space */ 702 859 if(opts & NOIT_HTTP_CHUNKED) out->start = hexlen + 2; src/noit_http.h
r88a7178 r7e47bd8 40 40 41 41 typedef enum { 42 NOIT_HTTP_OTHER, NOIT_HTTP_GET, NOIT_HTTP_HEAD 42 NOIT_HTTP_OTHER, NOIT_HTTP_GET, NOIT_HTTP_HEAD, NOIT_HTTP_POST 43 43 } noit_http_method; 44 44 typedef enum { … … 79 79 size_t current_offset; /* analyzing. */ 80 80 81 enum { NOIT_HTTP_REQ_HEADERS = 0, 82 NOIT_HTTP_REQ_EXPECT, 83 NOIT_HTTP_REQ_PAYLOAD } state; 81 84 struct bchain *current_request_chain; 85 noit_boolean has_payload; 86 int64_t content_length; 87 int64_t content_length_read; 82 88 char *method_str; 83 89 char *uri_str; … … 113 119 typedef struct noit_http_session_ctx { 114 120 noit_atomic32_t ref_cnt; 121 int64_t drainage; 115 122 noit_http_connection conn; 116 123 noit_http_request req; … … 130 137 131 138 API_EXPORT(noit_boolean) 139 noit_http_session_prime_input(noit_http_session_ctx *, const void *, size_t); 140 API_EXPORT(int) 141 noit_http_session_req_consume(noit_http_session_ctx *ctx, 142 void *buf, size_t len, int *mask); 143 API_EXPORT(noit_boolean) 132 144 noit_http_response_status_set(noit_http_session_ctx *, int, const char *); 133 145 API_EXPORT(noit_boolean) src/noitd.c
rb9a4230 r7e47bd8 52 52 #include "noit_console.h" 53 53 #include "noit_jlog_listener.h" 54 #include "noit_rest.h" 54 55 #include "noit_livestream_listener.h" 55 56 #include "noit_capabilities_listener.h" … … 181 182 noit_capabilities_listener_init(); 182 183 noit_jlog_listener_init(); 184 noit_http_rest_init(); 183 185 noit_livestream_listener_init(); 184 186
