| 906 | | stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; |
| 907 | | |
| 908 | | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, |
| 909 | | "skipping http2 DATA frame, reason: %d", |
| 910 | | stream->skip_data); |
| | 906 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, |
| | 907 | "skipping http2 DATA frame"); |
| 919 | | } |
| 920 | | |
| 921 | | r = stream->request; |
| 922 | | |
| 923 | | if (r->request_body == NULL |
| 924 | | && ngx_http_v2_init_request_body(r) != NGX_OK) |
| 925 | | { |
| 926 | | stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR; |
| 927 | | return ngx_http_v2_state_skip_padded(h2c, pos, end); |
| 928 | | } |
| 929 | | |
| 930 | | fc = r->connection; |
| 931 | | rb = r->request_body; |
| 932 | | tf = rb->temp_file; |
| 933 | | buf = rb->buf; |
| 934 | | |
| 935 | | if (size) { |
| 936 | | rb->rest += size; |
| 937 | | |
| 938 | | if (r->headers_in.content_length_n != -1 |
| 939 | | && r->headers_in.content_length_n < rb->rest) |
| 940 | | { |
| 941 | | ngx_log_error(NGX_LOG_INFO, fc->log, 0, |
| 942 | | "client intended to send body data " |
| 943 | | "larger than declared"); |
| 944 | | |
| 945 | | stream->skip_data = NGX_HTTP_V2_DATA_ERROR; |
| 946 | | goto error; |
| 947 | | |
| 948 | | } else { |
| 949 | | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| 950 | | |
| 951 | | if (clcf->client_max_body_size |
| 952 | | && clcf->client_max_body_size < rb->rest) |
| 953 | | { |
| 954 | | ngx_log_error(NGX_LOG_ERR, fc->log, 0, |
| 955 | | "client intended to send " |
| 956 | | "too large chunked body: %O bytes", rb->rest); |
| 957 | | |
| 958 | | stream->skip_data = NGX_HTTP_V2_DATA_ERROR; |
| 959 | | goto error; |
| 960 | | } |
| 961 | | } |
| 962 | | |
| 963 | | h2c->state.length -= size; |
| 964 | | |
| 965 | | if (tf) { |
| 966 | | buf->start = pos; |
| 967 | | buf->pos = pos; |
| 968 | | |
| 969 | | pos += size; |
| 970 | | |
| 971 | | buf->end = pos; |
| 972 | | buf->last = pos; |
| 973 | | |
| 974 | | n = ngx_write_chain_to_temp_file(tf, rb->bufs); |
| 975 | | |
| 976 | | /* TODO: n == 0 or not complete and level event */ |
| 977 | | |
| 978 | | if (n == NGX_ERROR) { |
| 979 | | stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR; |
| 980 | | goto error; |
| 981 | | } |
| 982 | | |
| 983 | | tf->offset += n; |
| 984 | | |
| 985 | | } else { |
| 986 | | buf->last = ngx_cpymem(buf->last, pos, size); |
| 987 | | pos += size; |
| 988 | | } |
| 989 | | |
| 990 | | r->request_length += size; |
| 991 | | } |
| | 916 | last = stream->in_closed; |
| | 917 | |
| | 918 | } else { |
| | 919 | last = 0; |
| | 920 | } |
| | 921 | |
| | 922 | rc = ngx_http_v2_process_request_body(stream->request, pos, size, last); |
| | 923 | |
| | 924 | if (rc != NGX_OK) { |
| | 925 | stream->skip_data = 1; |
| | 926 | ngx_http_finalize_request(stream->request, rc); |
| | 927 | } |
| | 928 | |
| | 929 | pos += size; |
| | 930 | h2c->state.length -= size; |
| 1003 | | if (h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG) { |
| 1004 | | stream->in_closed = 1; |
| 1005 | | |
| 1006 | | if (r->headers_in.content_length_n < 0) { |
| 1007 | | r->headers_in.content_length_n = rb->rest; |
| 1008 | | |
| 1009 | | } else if (r->headers_in.content_length_n != rb->rest) { |
| 1010 | | ngx_log_error(NGX_LOG_INFO, fc->log, 0, |
| 1011 | | "client prematurely closed stream: " |
| 1012 | | "only %O out of %O bytes of request body received", |
| 1013 | | rb->rest, r->headers_in.content_length_n); |
| 1014 | | |
| 1015 | | stream->skip_data = NGX_HTTP_V2_DATA_ERROR; |
| 1016 | | goto error; |
| 1017 | | } |
| 1018 | | |
| 1019 | | if (tf) { |
| 1020 | | ngx_memzero(buf, sizeof(ngx_buf_t)); |
| 1021 | | |
| 1022 | | buf->in_file = 1; |
| 1023 | | buf->file_last = tf->file.offset; |
| 1024 | | buf->file = &tf->file; |
| 1025 | | |
| 1026 | | rb->buf = NULL; |
| 1027 | | } |
| 1028 | | |
| 1029 | | if (rb->post_handler) { |
| 1030 | | if (fc->read->timer_set) { |
| 1031 | | ngx_del_timer(fc->read); |
| 1032 | | } |
| 1033 | | |
| 1034 | | r->read_event_handler = ngx_http_block_reading; |
| 1035 | | rb->post_handler(r); |
| 1036 | | } |
| 1037 | | |
| 1038 | | } else if (rb->post_handler) { |
| 1039 | | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| 1040 | | ngx_add_timer(fc->read, clcf->client_body_timeout); |
| 1041 | | } |
| 1042 | | |
| 3528 | | static ngx_int_t |
| 3529 | | ngx_http_v2_init_request_body(ngx_http_request_t *r) |
| 3530 | | { |
| 3531 | | ngx_buf_t *buf; |
| 3532 | | ngx_temp_file_t *tf; |
| 3533 | | ngx_http_request_body_t *rb; |
| 3534 | | ngx_http_core_loc_conf_t *clcf; |
| 3535 | | |
| 3536 | | rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
| 3537 | | if (rb == NULL) { |
| 3538 | | return NGX_ERROR; |
| 3539 | | } |
| 3540 | | |
| 3541 | | r->request_body = rb; |
| 3542 | | |
| 3543 | | if (r->stream->in_closed) { |
| 3544 | | return NGX_OK; |
| 3545 | | } |
| 3546 | | |
| 3547 | | rb->rest = r->headers_in.content_length_n; |
| 3548 | | |
| 3549 | | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| 3550 | | |
| 3551 | | if (r->request_body_in_file_only |
| 3552 | | || rb->rest > (off_t) clcf->client_body_buffer_size |
| 3553 | | || rb->rest < 0) |
| 3554 | | { |
| 3555 | | tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); |
| 3556 | | if (tf == NULL) { |
| 3557 | | return NGX_ERROR; |
| 3558 | | } |
| 3559 | | |
| 3560 | | tf->file.fd = NGX_INVALID_FILE; |
| 3561 | | tf->file.log = r->connection->log; |
| 3562 | | tf->path = clcf->client_body_temp_path; |
| 3563 | | tf->pool = r->pool; |
| 3564 | | tf->warn = "a client request body is buffered to a temporary file"; |
| 3565 | | tf->log_level = r->request_body_file_log_level; |
| 3566 | | tf->persistent = r->request_body_in_persistent_file; |
| 3567 | | tf->clean = r->request_body_in_clean_file; |
| 3568 | | |
| 3569 | | if (r->request_body_file_group_access) { |
| 3570 | | tf->access = 0660; |
| 3571 | | } |
| 3572 | | |
| 3573 | | rb->temp_file = tf; |
| 3574 | | |
| 3575 | | if (r->stream->in_closed |
| 3576 | | && ngx_create_temp_file(&tf->file, tf->path, tf->pool, |
| 3577 | | tf->persistent, tf->clean, tf->access) |
| 3578 | | != NGX_OK) |
| 3579 | | { |
| 3580 | | return NGX_ERROR; |
| 3581 | | } |
| 3582 | | |
| 3583 | | buf = ngx_calloc_buf(r->pool); |
| 3584 | | if (buf == NULL) { |
| 3585 | | return NGX_ERROR; |
| 3586 | | } |
| 3587 | | |
| 3588 | | } else { |
| 3589 | | |
| 3590 | | if (rb->rest == 0) { |
| 3591 | | return NGX_OK; |
| 3592 | | } |
| 3593 | | |
| 3594 | | buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest); |
| 3595 | | if (buf == NULL) { |
| 3596 | | return NGX_ERROR; |
| 3597 | | } |
| 3598 | | } |
| 3599 | | |
| 3600 | | rb->buf = buf; |
| 3601 | | |
| 3602 | | rb->bufs = ngx_alloc_chain_link(r->pool); |
| 3603 | | if (rb->bufs == NULL) { |
| 3604 | | return NGX_ERROR; |
| 3605 | | } |
| 3606 | | |
| 3607 | | rb->bufs->buf = buf; |
| 3608 | | rb->bufs->next = NULL; |
| 3609 | | |
| 3610 | | rb->rest = 0; |
| 3611 | | |
| 3612 | | return NGX_OK; |
| 3613 | | } |
| 3614 | | |
| 3615 | | |
| 3633 | | |
| 3634 | | case NGX_HTTP_V2_DATA_ERROR: |
| | 3417 | } |
| | 3418 | |
| | 3419 | rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
| | 3420 | if (rb == NULL) { |
| | 3421 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| | 3422 | } |
| | 3423 | |
| | 3424 | /* |
| | 3425 | * set by ngx_pcalloc(): |
| | 3426 | * |
| | 3427 | * rb->bufs = NULL; |
| | 3428 | * rb->buf = NULL; |
| | 3429 | * rb->received = 0; |
| | 3430 | * rb->free = NULL; |
| | 3431 | * rb->busy = NULL; |
| | 3432 | */ |
| | 3433 | |
| | 3434 | rb->rest = 1; |
| | 3435 | rb->post_handler = post_handler; |
| | 3436 | |
| | 3437 | r->request_body = rb; |
| | 3438 | |
| | 3439 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| | 3440 | |
| | 3441 | len = r->headers_in.content_length_n; |
| | 3442 | |
| | 3443 | if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size |
| | 3444 | && !r->request_body_in_file_only) |
| | 3445 | { |
| | 3446 | rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); |
| | 3447 | |
| | 3448 | } else { |
| | 3449 | rb->buf = ngx_calloc_buf(r->pool); |
| | 3450 | |
| | 3451 | if (rb->buf != NULL) { |
| | 3452 | rb->buf->sync = 1; |
| | 3453 | } |
| | 3454 | } |
| | 3455 | |
| | 3456 | if (rb->buf == NULL) { |
| | 3457 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| | 3458 | } |
| | 3459 | |
| | 3460 | if (stream->in_closed) { |
| | 3461 | return ngx_http_v2_process_request_body(r, NULL, 0, 1); |
| | 3462 | } |
| | 3463 | |
| | 3464 | stream->no_flow_control = 1; |
| | 3465 | |
| | 3466 | stream->recv_window = NGX_HTTP_V2_MAX_WINDOW; |
| | 3467 | |
| | 3468 | if (ngx_http_v2_send_window_update(stream->connection, stream->node->id, |
| | 3469 | stream->recv_window) |
| | 3470 | == NGX_ERROR) |
| | 3471 | { |
| | 3472 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| | 3473 | } |
| | 3474 | |
| | 3475 | ngx_add_timer(r->connection->read, clcf->client_body_timeout); |
| | 3476 | |
| | 3477 | r->read_event_handler = ngx_http_v2_read_client_request_body_handler; |
| | 3478 | r->write_event_handler = ngx_http_request_empty_handler; |
| | 3479 | |
| | 3480 | return NGX_AGAIN; |
| | 3481 | } |
| | 3482 | |
| | 3483 | |
| | 3484 | static ngx_int_t |
| | 3485 | ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, |
| | 3486 | size_t size, ngx_uint_t last) |
| | 3487 | { |
| | 3488 | ngx_buf_t *buf; |
| | 3489 | ngx_int_t rc; |
| | 3490 | ngx_connection_t *fc; |
| | 3491 | ngx_http_request_body_t *rb; |
| | 3492 | ngx_http_core_loc_conf_t *clcf; |
| | 3493 | |
| | 3494 | rb = r->request_body; |
| | 3495 | |
| | 3496 | if (rb == NULL) { |
| | 3497 | return NGX_OK; |
| | 3498 | } |
| | 3499 | |
| | 3500 | fc = r->connection; |
| | 3501 | buf = rb->buf; |
| | 3502 | |
| | 3503 | if (size) { |
| | 3504 | if (buf->sync) { |
| | 3505 | buf->pos = buf->start = pos; |
| | 3506 | buf->last = buf->end = pos + size; |
| | 3507 | |
| | 3508 | } else { |
| | 3509 | if (size > (size_t) (buf->end - buf->last)) { |
| | 3510 | ngx_log_error(NGX_LOG_INFO, fc->log, 0, |
| | 3511 | "client intended to send body data " |
| | 3512 | "larger than declared"); |
| | 3513 | |
| | 3514 | return NGX_HTTP_BAD_REQUEST; |
| | 3515 | } |
| | 3516 | |
| | 3517 | buf->last = ngx_cpymem(buf->last, pos, size); |
| | 3518 | } |
| | 3519 | } |
| | 3520 | |
| | 3521 | if (last) { |
| | 3522 | rb->rest = 0; |
| | 3523 | |
| | 3524 | if (fc->read->timer_set) { |
| | 3525 | ngx_del_timer(fc->read); |
| | 3526 | } |
| | 3527 | |
| | 3528 | rc = ngx_http_v2_filter_request_body(r); |
| | 3529 | |
| | 3530 | if (rc != NGX_OK) { |
| | 3531 | return rc; |
| | 3532 | } |
| | 3533 | |
| | 3534 | if (buf->sync) { |
| | 3535 | /* prevent reusing this buffer in the upstream module */ |
| | 3536 | rb->buf = NULL; |
| | 3537 | } |
| | 3538 | |
| 3636 | | return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; |
| | 3540 | r->headers_in.content_length_n = rb->received; |
| | 3541 | } |
| | 3542 | |
| | 3543 | r->read_event_handler = ngx_http_block_reading; |
| | 3544 | rb->post_handler(r); |
| | 3545 | |
| | 3546 | return NGX_OK; |
| | 3547 | } |
| | 3548 | |
| | 3549 | if (size == 0) { |
| | 3550 | return NGX_OK; |
| | 3551 | } |
| | 3552 | |
| | 3553 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| | 3554 | ngx_add_timer(fc->read, clcf->client_body_timeout); |
| | 3555 | |
| | 3556 | if (buf->sync) { |
| | 3557 | return ngx_http_v2_filter_request_body(r); |
| | 3558 | } |
| | 3559 | |
| | 3560 | return NGX_OK; |
| | 3561 | } |
| | 3562 | |
| | 3563 | |
| | 3564 | static ngx_int_t |
| | 3565 | ngx_http_v2_filter_request_body(ngx_http_request_t *r) |
| | 3566 | { |
| | 3567 | ngx_buf_t *b, *buf; |
| | 3568 | ngx_int_t rc; |
| | 3569 | ngx_chain_t *cl; |
| | 3570 | ngx_http_request_body_t *rb; |
| | 3571 | ngx_http_core_loc_conf_t *clcf; |
| | 3572 | |
| | 3573 | rb = r->request_body; |
| | 3574 | buf = rb->buf; |
| | 3575 | |
| | 3576 | cl = ngx_chain_get_free_buf(r->pool, &rb->free); |
| | 3577 | if (cl == NULL) { |
| | 3578 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| | 3579 | } |
| | 3580 | |
| | 3581 | b = cl->buf; |
| | 3582 | |
| | 3583 | ngx_memzero(b, sizeof(ngx_buf_t)); |
| | 3584 | |
| | 3585 | if (buf->pos != buf->last) { |
| | 3586 | r->request_length += buf->last - buf->pos; |
| | 3587 | rb->received += buf->last - buf->pos; |
| | 3588 | |
| | 3589 | if (r->headers_in.content_length_n != -1) { |
| | 3590 | if (r->headers_in.content_length_n < rb->received) { |
| | 3591 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, |
| | 3592 | "client intended to send body data " |
| | 3593 | "larger than declared"); |
| | 3594 | |
| | 3595 | return NGX_HTTP_BAD_REQUEST; |
| | 3596 | } |
| | 3597 | |
| | 3599 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| | 3600 | |
| | 3601 | if (clcf->client_max_body_size |
| | 3602 | && clcf->client_max_body_size < rb->received) |
| | 3603 | { |
| | 3604 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
| | 3605 | "client intended to send too large chunked body: " |
| | 3606 | "%O bytes", rb->received); |
| | 3607 | |
| | 3608 | return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; |
| | 3609 | } |
| | 3610 | } |
| | 3611 | |
| | 3612 | b->temporary = 1; |
| | 3613 | b->pos = buf->pos; |
| | 3614 | b->last = buf->last; |
| | 3615 | b->start = b->pos; |
| | 3616 | b->end = b->last; |
| | 3617 | |
| | 3618 | buf->pos = buf->last; |
| | 3619 | } |
| | 3620 | |
| | 3621 | if (!rb->rest) { |
| | 3622 | if (r->headers_in.content_length_n != -1 |
| | 3623 | && r->headers_in.content_length_n != rb->received) |
| | 3624 | { |
| | 3625 | ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, |
| | 3626 | "client prematurely closed stream: " |
| | 3627 | "only %O out of %O bytes of request body received", |
| | 3628 | rb->received, r->headers_in.content_length_n); |
| | 3629 | |
| 3641 | | case NGX_HTTP_V2_DATA_INTERNAL_ERROR: |
| 3642 | | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| 3643 | | } |
| 3644 | | |
| 3645 | | if (!r->request_body && ngx_http_v2_init_request_body(r) != NGX_OK) { |
| 3646 | | stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR; |
| 3647 | | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
| 3648 | | } |
| 3649 | | |
| 3650 | | if (stream->in_closed) { |
| 3651 | | post_handler(r); |
| 3652 | | return NGX_OK; |
| 3653 | | } |
| 3654 | | |
| 3655 | | r->request_body->post_handler = post_handler; |
| 3656 | | |
| 3657 | | r->read_event_handler = ngx_http_v2_read_client_request_body_handler; |
| 3658 | | r->write_event_handler = ngx_http_request_empty_handler; |
| 3659 | | |
| 3660 | | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| 3661 | | ngx_add_timer(r->connection->read, clcf->client_body_timeout); |
| 3662 | | |
| 3663 | | return NGX_AGAIN; |
| | 3633 | b->last_buf = 1; |
| | 3634 | } |
| | 3635 | |
| | 3636 | b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body; |
| | 3637 | b->flush = r->request_body_no_buffering; |
| | 3638 | |
| | 3639 | rc = ngx_http_top_request_body_filter(r, cl); |
| | 3640 | |
| | 3641 | ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl, |
| | 3642 | (ngx_buf_tag_t) &ngx_http_v2_filter_request_body); |
| | 3643 | |
| | 3644 | return rc; |
| 3747 | | if (!stream->out_closed) { |
| 3748 | | if (ngx_http_v2_send_rst_stream(h2c, node->id, |
| 3749 | | fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR |
| 3750 | | : NGX_HTTP_V2_INTERNAL_ERROR) |
| 3751 | | != NGX_OK) |
| 3752 | | { |
| 3753 | | h2c->connection->error = 1; |
| | 3728 | if (!stream->rst_sent && !h2c->connection->error) { |
| | 3729 | |
| | 3730 | if (!stream->out_closed) { |
| | 3731 | if (ngx_http_v2_send_rst_stream(h2c, node->id, |
| | 3732 | fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR |
| | 3733 | : NGX_HTTP_V2_INTERNAL_ERROR) |
| | 3734 | != NGX_OK) |
| | 3735 | { |
| | 3736 | h2c->connection->error = 1; |
| | 3737 | } |
| | 3738 | |
| | 3739 | } else if (!stream->in_closed) { |
| | 3740 | if (ngx_http_v2_send_rst_stream(h2c, node->id, NGX_HTTP_V2_NO_ERROR) |
| | 3741 | != NGX_OK) |
| | 3742 | { |
| | 3743 | h2c->connection->error = 1; |
| | 3744 | } |