Annotation of embedaddon/nginx/src/http/ngx_http_request_body.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (C) Igor Sysoev
! 4: * Copyright (C) Nginx, Inc.
! 5: */
! 6:
! 7:
! 8: #include <ngx_config.h>
! 9: #include <ngx_core.h>
! 10: #include <ngx_http.h>
! 11:
! 12:
! 13: static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
! 14: static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
! 15: static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
! 16: static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
! 17: static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
! 18: ngx_buf_t *b);
! 19: static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
! 20:
! 21: static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
! 22: ngx_chain_t *in);
! 23: static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
! 24: ngx_chain_t *in);
! 25: static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
! 26: ngx_chain_t *in);
! 27: static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
! 28: ngx_chain_t *in);
! 29:
! 30:
! 31: ngx_int_t
! 32: ngx_http_read_client_request_body(ngx_http_request_t *r,
! 33: ngx_http_client_body_handler_pt post_handler)
! 34: {
! 35: size_t preread;
! 36: ssize_t size;
! 37: ngx_int_t rc;
! 38: ngx_buf_t *b;
! 39: ngx_chain_t out, *cl;
! 40: ngx_http_request_body_t *rb;
! 41: ngx_http_core_loc_conf_t *clcf;
! 42:
! 43: r->main->count++;
! 44:
! 45: #if (NGX_HTTP_SPDY)
! 46: if (r->spdy_stream) {
! 47: rc = ngx_http_spdy_read_request_body(r, post_handler);
! 48: goto done;
! 49: }
! 50: #endif
! 51:
! 52: if (r != r->main || r->request_body || r->discard_body) {
! 53: post_handler(r);
! 54: return NGX_OK;
! 55: }
! 56:
! 57: if (ngx_http_test_expect(r) != NGX_OK) {
! 58: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 59: goto done;
! 60: }
! 61:
! 62: rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
! 63: if (rb == NULL) {
! 64: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 65: goto done;
! 66: }
! 67:
! 68: /*
! 69: * set by ngx_pcalloc():
! 70: *
! 71: * rb->bufs = NULL;
! 72: * rb->buf = NULL;
! 73: * rb->free = NULL;
! 74: * rb->busy = NULL;
! 75: * rb->chunked = NULL;
! 76: */
! 77:
! 78: rb->rest = -1;
! 79: rb->post_handler = post_handler;
! 80:
! 81: r->request_body = rb;
! 82:
! 83: if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
! 84: post_handler(r);
! 85: return NGX_OK;
! 86: }
! 87:
! 88: preread = r->header_in->last - r->header_in->pos;
! 89:
! 90: if (preread) {
! 91:
! 92: /* there is the pre-read part of the request body */
! 93:
! 94: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 95: "http client request body preread %uz", preread);
! 96:
! 97: out.buf = r->header_in;
! 98: out.next = NULL;
! 99:
! 100: rc = ngx_http_request_body_filter(r, &out);
! 101:
! 102: if (rc != NGX_OK) {
! 103: goto done;
! 104: }
! 105:
! 106: r->request_length += preread - (r->header_in->last - r->header_in->pos);
! 107:
! 108: if (!r->headers_in.chunked
! 109: && rb->rest > 0
! 110: && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
! 111: {
! 112: /* the whole request body may be placed in r->header_in */
! 113:
! 114: b = ngx_calloc_buf(r->pool);
! 115: if (b == NULL) {
! 116: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 117: goto done;
! 118: }
! 119:
! 120: b->temporary = 1;
! 121: b->start = r->header_in->pos;
! 122: b->pos = r->header_in->pos;
! 123: b->last = r->header_in->last;
! 124: b->end = r->header_in->end;
! 125:
! 126: rb->buf = b;
! 127:
! 128: r->read_event_handler = ngx_http_read_client_request_body_handler;
! 129: r->write_event_handler = ngx_http_request_empty_handler;
! 130:
! 131: rc = ngx_http_do_read_client_request_body(r);
! 132: goto done;
! 133: }
! 134:
! 135: } else {
! 136: /* set rb->rest */
! 137:
! 138: if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
! 139: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 140: goto done;
! 141: }
! 142: }
! 143:
! 144: if (rb->rest == 0) {
! 145: /* the whole request body was pre-read */
! 146:
! 147: if (r->request_body_in_file_only) {
! 148: if (ngx_http_write_request_body(r) != NGX_OK) {
! 149: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 150: goto done;
! 151: }
! 152:
! 153: cl = ngx_chain_get_free_buf(r->pool, &rb->free);
! 154: if (cl == NULL) {
! 155: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 156: }
! 157:
! 158: b = cl->buf;
! 159:
! 160: ngx_memzero(b, sizeof(ngx_buf_t));
! 161:
! 162: b->in_file = 1;
! 163: b->file_last = rb->temp_file->file.offset;
! 164: b->file = &rb->temp_file->file;
! 165:
! 166: rb->bufs = cl;
! 167: }
! 168:
! 169: post_handler(r);
! 170:
! 171: return NGX_OK;
! 172: }
! 173:
! 174: if (rb->rest < 0) {
! 175: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
! 176: "negative request body rest");
! 177: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 178: goto done;
! 179: }
! 180:
! 181: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
! 182:
! 183: size = clcf->client_body_buffer_size;
! 184: size += size >> 2;
! 185:
! 186: /* TODO: honor r->request_body_in_single_buf */
! 187:
! 188: if (!r->headers_in.chunked && rb->rest < size) {
! 189: size = (ssize_t) rb->rest;
! 190:
! 191: if (r->request_body_in_single_buf) {
! 192: size += preread;
! 193: }
! 194:
! 195: } else {
! 196: size = clcf->client_body_buffer_size;
! 197: }
! 198:
! 199: rb->buf = ngx_create_temp_buf(r->pool, size);
! 200: if (rb->buf == NULL) {
! 201: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 202: goto done;
! 203: }
! 204:
! 205: r->read_event_handler = ngx_http_read_client_request_body_handler;
! 206: r->write_event_handler = ngx_http_request_empty_handler;
! 207:
! 208: rc = ngx_http_do_read_client_request_body(r);
! 209:
! 210: done:
! 211:
! 212: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
! 213: r->main->count--;
! 214: }
! 215:
! 216: return rc;
! 217: }
! 218:
! 219:
! 220: static void
! 221: ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
! 222: {
! 223: ngx_int_t rc;
! 224:
! 225: if (r->connection->read->timedout) {
! 226: r->connection->timedout = 1;
! 227: ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
! 228: return;
! 229: }
! 230:
! 231: rc = ngx_http_do_read_client_request_body(r);
! 232:
! 233: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
! 234: ngx_http_finalize_request(r, rc);
! 235: }
! 236: }
! 237:
! 238:
! 239: static ngx_int_t
! 240: ngx_http_do_read_client_request_body(ngx_http_request_t *r)
! 241: {
! 242: off_t rest;
! 243: size_t size;
! 244: ssize_t n;
! 245: ngx_int_t rc;
! 246: ngx_buf_t *b;
! 247: ngx_chain_t *cl, out;
! 248: ngx_connection_t *c;
! 249: ngx_http_request_body_t *rb;
! 250: ngx_http_core_loc_conf_t *clcf;
! 251:
! 252: c = r->connection;
! 253: rb = r->request_body;
! 254:
! 255: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
! 256: "http read client request body");
! 257:
! 258: for ( ;; ) {
! 259: for ( ;; ) {
! 260: if (rb->buf->last == rb->buf->end) {
! 261:
! 262: /* pass buffer to request body filter chain */
! 263:
! 264: out.buf = rb->buf;
! 265: out.next = NULL;
! 266:
! 267: rc = ngx_http_request_body_filter(r, &out);
! 268:
! 269: if (rc != NGX_OK) {
! 270: return rc;
! 271: }
! 272:
! 273: /* write to file */
! 274:
! 275: if (ngx_http_write_request_body(r) != NGX_OK) {
! 276: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 277: }
! 278:
! 279: /* update chains */
! 280:
! 281: rc = ngx_http_request_body_filter(r, NULL);
! 282:
! 283: if (rc != NGX_OK) {
! 284: return rc;
! 285: }
! 286:
! 287: if (rb->busy != NULL) {
! 288: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 289: }
! 290:
! 291: rb->buf->pos = rb->buf->start;
! 292: rb->buf->last = rb->buf->start;
! 293: }
! 294:
! 295: size = rb->buf->end - rb->buf->last;
! 296: rest = rb->rest - (rb->buf->last - rb->buf->pos);
! 297:
! 298: if ((off_t) size > rest) {
! 299: size = (size_t) rest;
! 300: }
! 301:
! 302: n = c->recv(c, rb->buf->last, size);
! 303:
! 304: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
! 305: "http client request body recv %z", n);
! 306:
! 307: if (n == NGX_AGAIN) {
! 308: break;
! 309: }
! 310:
! 311: if (n == 0) {
! 312: ngx_log_error(NGX_LOG_INFO, c->log, 0,
! 313: "client prematurely closed connection");
! 314: }
! 315:
! 316: if (n == 0 || n == NGX_ERROR) {
! 317: c->error = 1;
! 318: return NGX_HTTP_BAD_REQUEST;
! 319: }
! 320:
! 321: rb->buf->last += n;
! 322: r->request_length += n;
! 323:
! 324: if (n == rest) {
! 325: /* pass buffer to request body filter chain */
! 326:
! 327: out.buf = rb->buf;
! 328: out.next = NULL;
! 329:
! 330: rc = ngx_http_request_body_filter(r, &out);
! 331:
! 332: if (rc != NGX_OK) {
! 333: return rc;
! 334: }
! 335: }
! 336:
! 337: if (rb->rest == 0) {
! 338: break;
! 339: }
! 340:
! 341: if (rb->buf->last < rb->buf->end) {
! 342: break;
! 343: }
! 344: }
! 345:
! 346: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
! 347: "http client request body rest %O", rb->rest);
! 348:
! 349: if (rb->rest == 0) {
! 350: break;
! 351: }
! 352:
! 353: if (!c->read->ready) {
! 354: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
! 355: ngx_add_timer(c->read, clcf->client_body_timeout);
! 356:
! 357: if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
! 358: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 359: }
! 360:
! 361: return NGX_AGAIN;
! 362: }
! 363: }
! 364:
! 365: if (c->read->timer_set) {
! 366: ngx_del_timer(c->read);
! 367: }
! 368:
! 369: if (rb->temp_file || r->request_body_in_file_only) {
! 370:
! 371: /* save the last part */
! 372:
! 373: if (ngx_http_write_request_body(r) != NGX_OK) {
! 374: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 375: }
! 376:
! 377: cl = ngx_chain_get_free_buf(r->pool, &rb->free);
! 378: if (cl == NULL) {
! 379: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 380: }
! 381:
! 382: b = cl->buf;
! 383:
! 384: ngx_memzero(b, sizeof(ngx_buf_t));
! 385:
! 386: b->in_file = 1;
! 387: b->file_last = rb->temp_file->file.offset;
! 388: b->file = &rb->temp_file->file;
! 389:
! 390: rb->bufs = cl;
! 391: }
! 392:
! 393: r->read_event_handler = ngx_http_block_reading;
! 394:
! 395: rb->post_handler(r);
! 396:
! 397: return NGX_OK;
! 398: }
! 399:
! 400:
! 401: static ngx_int_t
! 402: ngx_http_write_request_body(ngx_http_request_t *r)
! 403: {
! 404: ssize_t n;
! 405: ngx_chain_t *cl;
! 406: ngx_temp_file_t *tf;
! 407: ngx_http_request_body_t *rb;
! 408: ngx_http_core_loc_conf_t *clcf;
! 409:
! 410: rb = r->request_body;
! 411:
! 412: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 413: "http write client request body, bufs %p", rb->bufs);
! 414:
! 415: if (rb->temp_file == NULL) {
! 416: tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
! 417: if (tf == NULL) {
! 418: return NGX_ERROR;
! 419: }
! 420:
! 421: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
! 422:
! 423: tf->file.fd = NGX_INVALID_FILE;
! 424: tf->file.log = r->connection->log;
! 425: tf->path = clcf->client_body_temp_path;
! 426: tf->pool = r->pool;
! 427: tf->warn = "a client request body is buffered to a temporary file";
! 428: tf->log_level = r->request_body_file_log_level;
! 429: tf->persistent = r->request_body_in_persistent_file;
! 430: tf->clean = r->request_body_in_clean_file;
! 431:
! 432: if (r->request_body_file_group_access) {
! 433: tf->access = 0660;
! 434: }
! 435:
! 436: rb->temp_file = tf;
! 437:
! 438: if (rb->bufs == NULL) {
! 439: /* empty body with r->request_body_in_file_only */
! 440:
! 441: if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
! 442: tf->persistent, tf->clean, tf->access)
! 443: != NGX_OK)
! 444: {
! 445: return NGX_ERROR;
! 446: }
! 447:
! 448: return NGX_OK;
! 449: }
! 450: }
! 451:
! 452: if (rb->bufs == NULL) {
! 453: return NGX_OK;
! 454: }
! 455:
! 456: n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
! 457:
! 458: /* TODO: n == 0 or not complete and level event */
! 459:
! 460: if (n == NGX_ERROR) {
! 461: return NGX_ERROR;
! 462: }
! 463:
! 464: rb->temp_file->offset += n;
! 465:
! 466: /* mark all buffers as written */
! 467:
! 468: for (cl = rb->bufs; cl; cl = cl->next) {
! 469: cl->buf->pos = cl->buf->last;
! 470: }
! 471:
! 472: rb->bufs = NULL;
! 473:
! 474: return NGX_OK;
! 475: }
! 476:
! 477:
! 478: ngx_int_t
! 479: ngx_http_discard_request_body(ngx_http_request_t *r)
! 480: {
! 481: ssize_t size;
! 482: ngx_int_t rc;
! 483: ngx_event_t *rev;
! 484:
! 485: #if (NGX_HTTP_SPDY)
! 486: if (r->spdy_stream && r == r->main) {
! 487: r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD;
! 488: return NGX_OK;
! 489: }
! 490: #endif
! 491:
! 492: if (r != r->main || r->discard_body || r->request_body) {
! 493: return NGX_OK;
! 494: }
! 495:
! 496: if (ngx_http_test_expect(r) != NGX_OK) {
! 497: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 498: }
! 499:
! 500: rev = r->connection->read;
! 501:
! 502: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
! 503:
! 504: if (rev->timer_set) {
! 505: ngx_del_timer(rev);
! 506: }
! 507:
! 508: if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
! 509: return NGX_OK;
! 510: }
! 511:
! 512: size = r->header_in->last - r->header_in->pos;
! 513:
! 514: if (size || r->headers_in.chunked) {
! 515: rc = ngx_http_discard_request_body_filter(r, r->header_in);
! 516:
! 517: if (rc != NGX_OK) {
! 518: return rc;
! 519: }
! 520:
! 521: if (r->headers_in.content_length_n == 0) {
! 522: return NGX_OK;
! 523: }
! 524: }
! 525:
! 526: rc = ngx_http_read_discarded_request_body(r);
! 527:
! 528: if (rc == NGX_OK) {
! 529: r->lingering_close = 0;
! 530: return NGX_OK;
! 531: }
! 532:
! 533: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
! 534: return rc;
! 535: }
! 536:
! 537: /* rc == NGX_AGAIN */
! 538:
! 539: r->read_event_handler = ngx_http_discarded_request_body_handler;
! 540:
! 541: if (ngx_handle_read_event(rev, 0) != NGX_OK) {
! 542: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 543: }
! 544:
! 545: r->count++;
! 546: r->discard_body = 1;
! 547:
! 548: return NGX_OK;
! 549: }
! 550:
! 551:
! 552: void
! 553: ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
! 554: {
! 555: ngx_int_t rc;
! 556: ngx_msec_t timer;
! 557: ngx_event_t *rev;
! 558: ngx_connection_t *c;
! 559: ngx_http_core_loc_conf_t *clcf;
! 560:
! 561: c = r->connection;
! 562: rev = c->read;
! 563:
! 564: if (rev->timedout) {
! 565: c->timedout = 1;
! 566: c->error = 1;
! 567: ngx_http_finalize_request(r, NGX_ERROR);
! 568: return;
! 569: }
! 570:
! 571: if (r->lingering_time) {
! 572: timer = (ngx_msec_t) (r->lingering_time - ngx_time());
! 573:
! 574: if (timer <= 0) {
! 575: r->discard_body = 0;
! 576: r->lingering_close = 0;
! 577: ngx_http_finalize_request(r, NGX_ERROR);
! 578: return;
! 579: }
! 580:
! 581: } else {
! 582: timer = 0;
! 583: }
! 584:
! 585: rc = ngx_http_read_discarded_request_body(r);
! 586:
! 587: if (rc == NGX_OK) {
! 588: r->discard_body = 0;
! 589: r->lingering_close = 0;
! 590: ngx_http_finalize_request(r, NGX_DONE);
! 591: return;
! 592: }
! 593:
! 594: if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
! 595: c->error = 1;
! 596: ngx_http_finalize_request(r, NGX_ERROR);
! 597: return;
! 598: }
! 599:
! 600: /* rc == NGX_AGAIN */
! 601:
! 602: if (ngx_handle_read_event(rev, 0) != NGX_OK) {
! 603: c->error = 1;
! 604: ngx_http_finalize_request(r, NGX_ERROR);
! 605: return;
! 606: }
! 607:
! 608: if (timer) {
! 609:
! 610: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
! 611:
! 612: timer *= 1000;
! 613:
! 614: if (timer > clcf->lingering_timeout) {
! 615: timer = clcf->lingering_timeout;
! 616: }
! 617:
! 618: ngx_add_timer(rev, timer);
! 619: }
! 620: }
! 621:
! 622:
! 623: static ngx_int_t
! 624: ngx_http_read_discarded_request_body(ngx_http_request_t *r)
! 625: {
! 626: size_t size;
! 627: ssize_t n;
! 628: ngx_int_t rc;
! 629: ngx_buf_t b;
! 630: u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
! 631:
! 632: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 633: "http read discarded body");
! 634:
! 635: ngx_memzero(&b, sizeof(ngx_buf_t));
! 636:
! 637: b.temporary = 1;
! 638:
! 639: for ( ;; ) {
! 640: if (r->headers_in.content_length_n == 0) {
! 641: r->read_event_handler = ngx_http_block_reading;
! 642: return NGX_OK;
! 643: }
! 644:
! 645: if (!r->connection->read->ready) {
! 646: return NGX_AGAIN;
! 647: }
! 648:
! 649: size = (size_t) ngx_min(r->headers_in.content_length_n,
! 650: NGX_HTTP_DISCARD_BUFFER_SIZE);
! 651:
! 652: n = r->connection->recv(r->connection, buffer, size);
! 653:
! 654: if (n == NGX_ERROR) {
! 655: r->connection->error = 1;
! 656: return NGX_OK;
! 657: }
! 658:
! 659: if (n == NGX_AGAIN) {
! 660: return NGX_AGAIN;
! 661: }
! 662:
! 663: if (n == 0) {
! 664: return NGX_OK;
! 665: }
! 666:
! 667: b.pos = buffer;
! 668: b.last = buffer + n;
! 669:
! 670: rc = ngx_http_discard_request_body_filter(r, &b);
! 671:
! 672: if (rc != NGX_OK) {
! 673: return rc;
! 674: }
! 675: }
! 676: }
! 677:
! 678:
! 679: static ngx_int_t
! 680: ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
! 681: {
! 682: size_t size;
! 683: ngx_int_t rc;
! 684: ngx_http_request_body_t *rb;
! 685:
! 686: if (r->headers_in.chunked) {
! 687:
! 688: rb = r->request_body;
! 689:
! 690: if (rb == NULL) {
! 691:
! 692: rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
! 693: if (rb == NULL) {
! 694: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 695: }
! 696:
! 697: rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
! 698: if (rb->chunked == NULL) {
! 699: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 700: }
! 701:
! 702: r->request_body = rb;
! 703: }
! 704:
! 705: for ( ;; ) {
! 706:
! 707: rc = ngx_http_parse_chunked(r, b, rb->chunked);
! 708:
! 709: if (rc == NGX_OK) {
! 710:
! 711: /* a chunk has been parsed successfully */
! 712:
! 713: size = b->last - b->pos;
! 714:
! 715: if ((off_t) size > rb->chunked->size) {
! 716: b->pos += rb->chunked->size;
! 717: rb->chunked->size = 0;
! 718:
! 719: } else {
! 720: rb->chunked->size -= size;
! 721: b->pos = b->last;
! 722: }
! 723:
! 724: continue;
! 725: }
! 726:
! 727: if (rc == NGX_DONE) {
! 728:
! 729: /* a whole response has been parsed successfully */
! 730:
! 731: r->headers_in.content_length_n = 0;
! 732: break;
! 733: }
! 734:
! 735: if (rc == NGX_AGAIN) {
! 736:
! 737: /* set amount of data we want to see next time */
! 738:
! 739: r->headers_in.content_length_n = rb->chunked->length;
! 740: break;
! 741: }
! 742:
! 743: /* invalid */
! 744:
! 745: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 746: "client sent invalid chunked body");
! 747:
! 748: return NGX_HTTP_BAD_REQUEST;
! 749: }
! 750:
! 751: } else {
! 752: size = b->last - b->pos;
! 753:
! 754: if ((off_t) size > r->headers_in.content_length_n) {
! 755: b->pos += r->headers_in.content_length_n;
! 756: r->headers_in.content_length_n = 0;
! 757:
! 758: } else {
! 759: b->pos = b->last;
! 760: r->headers_in.content_length_n -= size;
! 761: }
! 762: }
! 763:
! 764: return NGX_OK;
! 765: }
! 766:
! 767:
! 768: static ngx_int_t
! 769: ngx_http_test_expect(ngx_http_request_t *r)
! 770: {
! 771: ngx_int_t n;
! 772: ngx_str_t *expect;
! 773:
! 774: if (r->expect_tested
! 775: || r->headers_in.expect == NULL
! 776: || r->http_version < NGX_HTTP_VERSION_11)
! 777: {
! 778: return NGX_OK;
! 779: }
! 780:
! 781: r->expect_tested = 1;
! 782:
! 783: expect = &r->headers_in.expect->value;
! 784:
! 785: if (expect->len != sizeof("100-continue") - 1
! 786: || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
! 787: sizeof("100-continue") - 1)
! 788: != 0)
! 789: {
! 790: return NGX_OK;
! 791: }
! 792:
! 793: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 794: "send 100 Continue");
! 795:
! 796: n = r->connection->send(r->connection,
! 797: (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
! 798: sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
! 799:
! 800: if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
! 801: return NGX_OK;
! 802: }
! 803:
! 804: /* we assume that such small packet should be send successfully */
! 805:
! 806: return NGX_ERROR;
! 807: }
! 808:
! 809:
! 810: static ngx_int_t
! 811: ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
! 812: {
! 813: if (r->headers_in.chunked) {
! 814: return ngx_http_request_body_chunked_filter(r, in);
! 815:
! 816: } else {
! 817: return ngx_http_request_body_length_filter(r, in);
! 818: }
! 819: }
! 820:
! 821:
! 822: static ngx_int_t
! 823: ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
! 824: {
! 825: size_t size;
! 826: ngx_int_t rc;
! 827: ngx_buf_t *b;
! 828: ngx_chain_t *cl, *tl, *out, **ll;
! 829: ngx_http_request_body_t *rb;
! 830:
! 831: rb = r->request_body;
! 832:
! 833: if (rb->rest == -1) {
! 834: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 835: "http request body content length filter");
! 836:
! 837: rb->rest = r->headers_in.content_length_n;
! 838: }
! 839:
! 840: out = NULL;
! 841: ll = &out;
! 842:
! 843: for (cl = in; cl; cl = cl->next) {
! 844:
! 845: tl = ngx_chain_get_free_buf(r->pool, &rb->free);
! 846: if (tl == NULL) {
! 847: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 848: }
! 849:
! 850: b = tl->buf;
! 851:
! 852: ngx_memzero(b, sizeof(ngx_buf_t));
! 853:
! 854: b->temporary = 1;
! 855: b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
! 856: b->start = cl->buf->pos;
! 857: b->pos = cl->buf->pos;
! 858: b->last = cl->buf->last;
! 859: b->end = cl->buf->end;
! 860:
! 861: size = cl->buf->last - cl->buf->pos;
! 862:
! 863: if ((off_t) size < rb->rest) {
! 864: cl->buf->pos = cl->buf->last;
! 865: rb->rest -= size;
! 866:
! 867: } else {
! 868: cl->buf->pos += rb->rest;
! 869: rb->rest = 0;
! 870: b->last = cl->buf->pos;
! 871: b->last_buf = 1;
! 872: }
! 873:
! 874: *ll = tl;
! 875: ll = &tl->next;
! 876: }
! 877:
! 878: rc = ngx_http_request_body_save_filter(r, out);
! 879:
! 880: ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
! 881: (ngx_buf_tag_t) &ngx_http_read_client_request_body);
! 882:
! 883: return rc;
! 884: }
! 885:
! 886:
! 887: static ngx_int_t
! 888: ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
! 889: {
! 890: size_t size;
! 891: ngx_int_t rc;
! 892: ngx_buf_t *b;
! 893: ngx_chain_t *cl, *out, *tl, **ll;
! 894: ngx_http_request_body_t *rb;
! 895: ngx_http_core_loc_conf_t *clcf;
! 896:
! 897: rb = r->request_body;
! 898:
! 899: if (rb->rest == -1) {
! 900:
! 901: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
! 902: "http request body chunked filter");
! 903:
! 904: rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
! 905: if (rb->chunked == NULL) {
! 906: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 907: }
! 908:
! 909: r->headers_in.content_length_n = 0;
! 910: rb->rest = 3;
! 911: }
! 912:
! 913: out = NULL;
! 914: ll = &out;
! 915:
! 916: for (cl = in; cl; cl = cl->next) {
! 917:
! 918: for ( ;; ) {
! 919:
! 920: ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
! 921: "http body chunked buf "
! 922: "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
! 923: cl->buf->temporary, cl->buf->in_file,
! 924: cl->buf->start, cl->buf->pos,
! 925: cl->buf->last - cl->buf->pos,
! 926: cl->buf->file_pos,
! 927: cl->buf->file_last - cl->buf->file_pos);
! 928:
! 929: rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
! 930:
! 931: if (rc == NGX_OK) {
! 932:
! 933: /* a chunk has been parsed successfully */
! 934:
! 935: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
! 936:
! 937: if (clcf->client_max_body_size
! 938: && clcf->client_max_body_size
! 939: < r->headers_in.content_length_n + rb->chunked->size)
! 940: {
! 941: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 942: "client intended to send too large chunked "
! 943: "body: %O bytes",
! 944: r->headers_in.content_length_n
! 945: + rb->chunked->size);
! 946:
! 947: r->lingering_close = 1;
! 948:
! 949: return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
! 950: }
! 951:
! 952: tl = ngx_chain_get_free_buf(r->pool, &rb->free);
! 953: if (tl == NULL) {
! 954: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 955: }
! 956:
! 957: b = tl->buf;
! 958:
! 959: ngx_memzero(b, sizeof(ngx_buf_t));
! 960:
! 961: b->temporary = 1;
! 962: b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
! 963: b->start = cl->buf->pos;
! 964: b->pos = cl->buf->pos;
! 965: b->last = cl->buf->last;
! 966: b->end = cl->buf->end;
! 967:
! 968: *ll = tl;
! 969: ll = &tl->next;
! 970:
! 971: size = cl->buf->last - cl->buf->pos;
! 972:
! 973: if ((off_t) size > rb->chunked->size) {
! 974: cl->buf->pos += rb->chunked->size;
! 975: r->headers_in.content_length_n += rb->chunked->size;
! 976: rb->chunked->size = 0;
! 977:
! 978: } else {
! 979: rb->chunked->size -= size;
! 980: r->headers_in.content_length_n += size;
! 981: cl->buf->pos = cl->buf->last;
! 982: }
! 983:
! 984: b->last = cl->buf->pos;
! 985:
! 986: continue;
! 987: }
! 988:
! 989: if (rc == NGX_DONE) {
! 990:
! 991: /* a whole response has been parsed successfully */
! 992:
! 993: rb->rest = 0;
! 994:
! 995: tl = ngx_chain_get_free_buf(r->pool, &rb->free);
! 996: if (tl == NULL) {
! 997: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 998: }
! 999:
! 1000: b = tl->buf;
! 1001:
! 1002: ngx_memzero(b, sizeof(ngx_buf_t));
! 1003:
! 1004: b->last_buf = 1;
! 1005:
! 1006: *ll = tl;
! 1007: ll = &tl->next;
! 1008:
! 1009: break;
! 1010: }
! 1011:
! 1012: if (rc == NGX_AGAIN) {
! 1013:
! 1014: /* set rb->rest, amount of data we want to see next time */
! 1015:
! 1016: rb->rest = rb->chunked->length;
! 1017:
! 1018: break;
! 1019: }
! 1020:
! 1021: /* invalid */
! 1022:
! 1023: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
! 1024: "client sent invalid chunked body");
! 1025:
! 1026: return NGX_HTTP_BAD_REQUEST;
! 1027: }
! 1028: }
! 1029:
! 1030: rc = ngx_http_request_body_save_filter(r, out);
! 1031:
! 1032: ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
! 1033: (ngx_buf_tag_t) &ngx_http_read_client_request_body);
! 1034:
! 1035: return rc;
! 1036: }
! 1037:
! 1038:
! 1039: static ngx_int_t
! 1040: ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
! 1041: {
! 1042: #if (NGX_DEBUG)
! 1043: ngx_chain_t *cl;
! 1044: #endif
! 1045: ngx_http_request_body_t *rb;
! 1046:
! 1047: rb = r->request_body;
! 1048:
! 1049: #if (NGX_DEBUG)
! 1050:
! 1051: for (cl = rb->bufs; cl; cl = cl->next) {
! 1052: ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
! 1053: "http body old buf t:%d f:%d %p, pos %p, size: %z "
! 1054: "file: %O, size: %z",
! 1055: cl->buf->temporary, cl->buf->in_file,
! 1056: cl->buf->start, cl->buf->pos,
! 1057: cl->buf->last - cl->buf->pos,
! 1058: cl->buf->file_pos,
! 1059: cl->buf->file_last - cl->buf->file_pos);
! 1060: }
! 1061:
! 1062: for (cl = in; cl; cl = cl->next) {
! 1063: ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
! 1064: "http body new buf t:%d f:%d %p, pos %p, size: %z "
! 1065: "file: %O, size: %z",
! 1066: cl->buf->temporary, cl->buf->in_file,
! 1067: cl->buf->start, cl->buf->pos,
! 1068: cl->buf->last - cl->buf->pos,
! 1069: cl->buf->file_pos,
! 1070: cl->buf->file_last - cl->buf->file_pos);
! 1071: }
! 1072:
! 1073: #endif
! 1074:
! 1075: /* TODO: coalesce neighbouring buffers */
! 1076:
! 1077: if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
! 1078: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 1079: }
! 1080:
! 1081: return NGX_OK;
! 1082: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>