Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2.c @ 6496:887cca40ba6a
HTTP/2: rewritten handling of request body.
There are two improvements:
1. Support for request body filters;
2. Receiving of request body is started only after
the ngx_http_read_client_request_body() call.
The last one fixes the problem when the client_max_body_size value might not be
respected from the right location if the location was changed either during the
process of receiving body or after the whole body had been received.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Fri, 01 Apr 2016 15:56:03 +0300 |
parents | 92464ebace8e |
children | 9d66d7ed2abb |
comparison
equal
deleted
inserted
replaced
6495:92464ebace8e | 6496:887cca40ba6a |
---|---|
48 | 48 |
49 #define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) | 49 #define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) |
50 | 50 |
51 #define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) | 51 #define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) |
52 #define NGX_HTTP_V2_DEFAULT_WINDOW 65535 | 52 #define NGX_HTTP_V2_DEFAULT_WINDOW 65535 |
53 | |
54 #define NGX_HTTP_V2_INITIAL_WINDOW 0 | |
53 | 55 |
54 #define NGX_HTTP_V2_ROOT (void *) -1 | 56 #define NGX_HTTP_V2_ROOT (void *) -1 |
55 | 57 |
56 | 58 |
57 static void ngx_http_v2_read_handler(ngx_event_t *rev); | 59 static void ngx_http_v2_read_handler(ngx_event_t *rev); |
161 static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); | 163 static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); |
162 static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, | 164 static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, |
163 ngx_http_v2_header_t *header); | 165 ngx_http_v2_header_t *header); |
164 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); | 166 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); |
165 static void ngx_http_v2_run_request(ngx_http_request_t *r); | 167 static void ngx_http_v2_run_request(ngx_http_request_t *r); |
166 static ngx_int_t ngx_http_v2_init_request_body(ngx_http_request_t *r); | 168 static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, |
169 u_char *pos, size_t size, ngx_uint_t last); | |
170 static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r); | |
167 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r); | 171 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r); |
168 | 172 |
169 static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, | 173 static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, |
170 ngx_http_v2_stream_t *stream, ngx_uint_t status); | 174 ngx_http_v2_stream_t *stream, ngx_uint_t status); |
171 static void ngx_http_v2_close_stream_handler(ngx_event_t *ev); | 175 static void ngx_http_v2_close_stream_handler(ngx_event_t *ev); |
760 if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { | 764 if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { |
761 | 765 |
762 if (h2c->state.length == 0) { | 766 if (h2c->state.length == 0) { |
763 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | 767 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
764 "client sent padded DATA frame " | 768 "client sent padded DATA frame " |
765 "with incorrect length: %uz", | 769 "with incorrect length: 0"); |
766 h2c->state.length); | |
767 | 770 |
768 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | 771 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); |
769 } | 772 } |
770 | 773 |
771 if (end - pos == 0) { | 774 if (end - pos == 0) { |
843 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 846 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
844 } | 847 } |
845 | 848 |
846 stream->recv_window -= h2c->state.length; | 849 stream->recv_window -= h2c->state.length; |
847 | 850 |
848 if (stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) { | 851 if (stream->no_flow_control |
849 | 852 && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) |
853 { | |
850 if (ngx_http_v2_send_window_update(h2c, node->id, | 854 if (ngx_http_v2_send_window_update(h2c, node->id, |
851 NGX_HTTP_V2_MAX_WINDOW | 855 NGX_HTTP_V2_MAX_WINDOW |
852 - stream->recv_window) | 856 - stream->recv_window) |
853 == NGX_ERROR) | 857 == NGX_ERROR) |
854 { | 858 { |
873 } | 877 } |
874 | 878 |
875 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 879 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
876 } | 880 } |
877 | 881 |
882 stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; | |
883 | |
878 h2c->state.stream = stream; | 884 h2c->state.stream = stream; |
879 | 885 |
880 return ngx_http_v2_state_read_data(h2c, pos, end); | 886 return ngx_http_v2_state_read_data(h2c, pos, end); |
881 } | 887 } |
882 | 888 |
883 | 889 |
884 static u_char * | 890 static u_char * |
885 ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, | 891 ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, |
886 u_char *end) | 892 u_char *end) |
887 { | 893 { |
888 size_t size; | 894 size_t size; |
889 ssize_t n; | 895 ngx_int_t rc; |
890 ngx_buf_t *buf; | 896 ngx_uint_t last; |
891 ngx_int_t rc; | 897 ngx_http_v2_stream_t *stream; |
892 ngx_temp_file_t *tf; | |
893 ngx_connection_t *fc; | |
894 ngx_http_request_t *r; | |
895 ngx_http_v2_stream_t *stream; | |
896 ngx_http_request_body_t *rb; | |
897 ngx_http_core_loc_conf_t *clcf; | |
898 | 898 |
899 stream = h2c->state.stream; | 899 stream = h2c->state.stream; |
900 | 900 |
901 if (stream == NULL) { | 901 if (stream == NULL) { |
902 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 902 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
903 } | 903 } |
904 | 904 |
905 if (stream->skip_data) { | 905 if (stream->skip_data) { |
906 stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; | 906 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, |
907 | 907 "skipping http2 DATA frame"); |
908 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | |
909 "skipping http2 DATA frame, reason: %d", | |
910 stream->skip_data); | |
911 | 908 |
912 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 909 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
913 } | 910 } |
914 | 911 |
915 size = end - pos; | 912 size = end - pos; |
916 | 913 |
917 if (size > h2c->state.length) { | 914 if (size >= h2c->state.length) { |
918 size = h2c->state.length; | 915 size = h2c->state.length; |
919 } | 916 last = stream->in_closed; |
920 | 917 |
921 r = stream->request; | 918 } else { |
922 | 919 last = 0; |
923 if (r->request_body == NULL | 920 } |
924 && ngx_http_v2_init_request_body(r) != NGX_OK) | 921 |
925 { | 922 rc = ngx_http_v2_process_request_body(stream->request, pos, size, last); |
926 stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR; | 923 |
927 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 924 if (rc != NGX_OK) { |
928 } | 925 stream->skip_data = 1; |
929 | 926 ngx_http_finalize_request(stream->request, rc); |
930 fc = r->connection; | 927 } |
931 rb = r->request_body; | 928 |
932 tf = rb->temp_file; | 929 pos += size; |
933 buf = rb->buf; | 930 h2c->state.length -= size; |
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 } | |
992 | 931 |
993 if (h2c->state.length) { | 932 if (h2c->state.length) { |
994 if (rb->post_handler) { | |
995 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
996 ngx_add_timer(fc->read, clcf->client_body_timeout); | |
997 } | |
998 | |
999 return ngx_http_v2_state_save(h2c, pos, end, | 933 return ngx_http_v2_state_save(h2c, pos, end, |
1000 ngx_http_v2_state_read_data); | 934 ngx_http_v2_state_read_data); |
1001 } | 935 } |
1002 | 936 |
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 | |
1043 if (h2c->state.padding) { | 937 if (h2c->state.padding) { |
1044 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 938 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
1045 } | 939 } |
1046 | 940 |
1047 return ngx_http_v2_state_complete(h2c, pos, end); | 941 return ngx_http_v2_state_complete(h2c, pos, end); |
1048 | |
1049 error: | |
1050 | |
1051 if (rb->post_handler) { | |
1052 if (fc->read->timer_set) { | |
1053 ngx_del_timer(fc->read); | |
1054 } | |
1055 | |
1056 if (stream->skip_data == NGX_HTTP_V2_DATA_ERROR) { | |
1057 rc = (r->headers_in.content_length_n == -1) | |
1058 ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE : NGX_HTTP_BAD_REQUEST; | |
1059 | |
1060 } else { | |
1061 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
1062 } | |
1063 | |
1064 ngx_http_finalize_request(r, rc); | |
1065 } | |
1066 | |
1067 return ngx_http_v2_state_skip_padded(h2c, pos, end); | |
1068 } | 942 } |
1069 | 943 |
1070 | 944 |
1071 static u_char * | 945 static u_char * |
1072 ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, | 946 ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, |
2554 h2scf->concurrent_streams); | 2428 h2scf->concurrent_streams); |
2555 | 2429 |
2556 buf->last = ngx_http_v2_write_uint16(buf->last, | 2430 buf->last = ngx_http_v2_write_uint16(buf->last, |
2557 NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); | 2431 NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); |
2558 buf->last = ngx_http_v2_write_uint32(buf->last, | 2432 buf->last = ngx_http_v2_write_uint32(buf->last, |
2559 NGX_HTTP_V2_MAX_WINDOW); | 2433 NGX_HTTP_V2_INITIAL_WINDOW); |
2560 | 2434 |
2561 buf->last = ngx_http_v2_write_uint16(buf->last, | 2435 buf->last = ngx_http_v2_write_uint16(buf->last, |
2562 NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); | 2436 NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); |
2563 buf->last = ngx_http_v2_write_uint32(buf->last, | 2437 buf->last = ngx_http_v2_write_uint32(buf->last, |
2564 NGX_HTTP_V2_MAX_FRAME_SIZE); | 2438 NGX_HTTP_V2_MAX_FRAME_SIZE); |
2876 | 2750 |
2877 stream->request = r; | 2751 stream->request = r; |
2878 stream->connection = h2c; | 2752 stream->connection = h2c; |
2879 | 2753 |
2880 stream->send_window = h2c->init_window; | 2754 stream->send_window = h2c->init_window; |
2881 stream->recv_window = NGX_HTTP_V2_MAX_WINDOW; | 2755 stream->recv_window = NGX_HTTP_V2_INITIAL_WINDOW; |
2882 | 2756 |
2883 h2c->processing++; | 2757 h2c->processing++; |
2884 | 2758 |
2885 return stream; | 2759 return stream; |
2886 } | 2760 } |
3513 | 3387 |
3514 if (r->headers_in.content_length_n > 0 && r->stream->in_closed) { | 3388 if (r->headers_in.content_length_n > 0 && r->stream->in_closed) { |
3515 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | 3389 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, |
3516 "client prematurely closed stream"); | 3390 "client prematurely closed stream"); |
3517 | 3391 |
3518 r->stream->skip_data = NGX_HTTP_V2_DATA_ERROR; | 3392 r->stream->skip_data = 1; |
3519 | 3393 |
3520 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | 3394 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); |
3521 return; | 3395 return; |
3522 } | 3396 } |
3523 | 3397 |
3524 ngx_http_process_request(r); | 3398 ngx_http_process_request(r); |
3525 } | |
3526 | |
3527 | |
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 } | 3399 } |
3614 | 3400 |
3615 | 3401 |
3616 ngx_int_t | 3402 ngx_int_t |
3617 ngx_http_v2_read_request_body(ngx_http_request_t *r, | 3403 ngx_http_v2_read_request_body(ngx_http_request_t *r, |
3618 ngx_http_client_body_handler_pt post_handler) | 3404 ngx_http_client_body_handler_pt post_handler) |
3619 { | 3405 { |
3406 off_t len; | |
3620 ngx_http_v2_stream_t *stream; | 3407 ngx_http_v2_stream_t *stream; |
3408 ngx_http_request_body_t *rb; | |
3621 ngx_http_core_loc_conf_t *clcf; | 3409 ngx_http_core_loc_conf_t *clcf; |
3622 | 3410 |
3623 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
3624 "http2 read request body"); | |
3625 | |
3626 stream = r->stream; | 3411 stream = r->stream; |
3627 | 3412 |
3628 switch (stream->skip_data) { | 3413 if (stream->skip_data) { |
3629 | 3414 r->request_body_no_buffering = 0; |
3630 case NGX_HTTP_V2_DATA_DISCARD: | |
3631 post_handler(r); | 3415 post_handler(r); |
3632 return NGX_OK; | 3416 return NGX_OK; |
3633 | 3417 } |
3634 case NGX_HTTP_V2_DATA_ERROR: | 3418 |
3635 if (r->headers_in.content_length_n == -1) { | 3419 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
3636 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; | 3420 if (rb == NULL) { |
3637 } else { | |
3638 return NGX_HTTP_BAD_REQUEST; | |
3639 } | |
3640 | |
3641 case NGX_HTTP_V2_DATA_INTERNAL_ERROR: | |
3642 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 3421 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
3643 } | 3422 } |
3644 | 3423 |
3645 if (!r->request_body && ngx_http_v2_init_request_body(r) != NGX_OK) { | 3424 /* |
3646 stream->skip_data = NGX_HTTP_V2_DATA_INTERNAL_ERROR; | 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) { | |
3647 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 3457 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
3648 } | 3458 } |
3649 | 3459 |
3650 if (stream->in_closed) { | 3460 if (stream->in_closed) { |
3651 post_handler(r); | 3461 return ngx_http_v2_process_request_body(r, NULL, 0, 1); |
3652 return NGX_OK; | 3462 } |
3653 } | 3463 |
3654 | 3464 stream->no_flow_control = 1; |
3655 r->request_body->post_handler = post_handler; | 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); | |
3656 | 3476 |
3657 r->read_event_handler = ngx_http_v2_read_client_request_body_handler; | 3477 r->read_event_handler = ngx_http_v2_read_client_request_body_handler; |
3658 r->write_event_handler = ngx_http_request_empty_handler; | 3478 r->write_event_handler = ngx_http_request_empty_handler; |
3659 | 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 | |
3539 if (r->headers_in.content_length_n == -1) { | |
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 | |
3660 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | 3553 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
3661 ngx_add_timer(r->connection->read, clcf->client_body_timeout); | 3554 ngx_add_timer(fc->read, clcf->client_body_timeout); |
3662 | 3555 |
3663 return NGX_AGAIN; | 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 (rb->received > r->headers_in.content_length_n) { | |
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 | |
3598 } else { | |
3599 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
3600 | |
3601 if (clcf->client_max_body_size | |
3602 && rb->received > clcf->client_max_body_size) | |
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 | |
3630 return NGX_HTTP_BAD_REQUEST; | |
3631 } | |
3632 | |
3633 b->last_buf = 1; | |
3634 } | |
3635 | |
3636 b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body; | |
3637 | |
3638 rc = ngx_http_top_request_body_filter(r, cl); | |
3639 | |
3640 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl, | |
3641 (ngx_buf_tag_t) &ngx_http_v2_filter_request_body); | |
3642 | |
3643 return rc; | |
3664 } | 3644 } |
3665 | 3645 |
3666 | 3646 |
3667 static void | 3647 static void |
3668 ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) | 3648 ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) |
3676 | 3656 |
3677 if (fc->read->timedout) { | 3657 if (fc->read->timedout) { |
3678 ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out"); | 3658 ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out"); |
3679 | 3659 |
3680 fc->timedout = 1; | 3660 fc->timedout = 1; |
3681 r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD; | 3661 r->stream->skip_data = 1; |
3682 | 3662 |
3683 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); | 3663 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); |
3684 return; | 3664 return; |
3685 } | 3665 } |
3686 | 3666 |
3687 if (fc->error) { | 3667 if (fc->error) { |
3688 ngx_log_error(NGX_LOG_INFO, fc->log, 0, | 3668 ngx_log_error(NGX_LOG_INFO, fc->log, 0, |
3689 "client prematurely closed stream"); | 3669 "client prematurely closed stream"); |
3690 | 3670 |
3691 r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD; | 3671 r->stream->skip_data = 1; |
3692 | 3672 |
3693 ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); | 3673 ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); |
3694 return; | 3674 return; |
3695 } | 3675 } |
3696 } | 3676 } |