Mercurial > hg > nginx-quic
comparison src/http/v3/ngx_http_v3_request.c @ 8282:6bd8ed493b85 quic
HTTP/3: refactored request body parser.
The change reduces diff to the default branch for
src/http/ngx_http_request_body.c.
Also, client Content-Length, if present, is now checked against the real body
size sent by client.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Mon, 25 Jan 2021 16:16:47 +0300 |
parents | dbe33ef9cd9a |
children | a9034b10dacc |
comparison
equal
deleted
inserted
replaced
8281:a346905c359f | 8282:6bd8ed493b85 |
---|---|
17 ngx_str_t *name, ngx_str_t *value); | 17 ngx_str_t *name, ngx_str_t *value); |
18 static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, | 18 static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, |
19 ngx_str_t *name, ngx_str_t *value); | 19 ngx_str_t *name, ngx_str_t *value); |
20 static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); | 20 static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); |
21 static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r); | 21 static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r); |
22 static void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r); | |
23 static ngx_int_t ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r); | |
24 static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r, | |
25 ngx_chain_t *in); | |
22 | 26 |
23 | 27 |
24 static const struct { | 28 static const struct { |
25 ngx_str_t name; | 29 ngx_str_t name; |
26 ngx_uint_t method; | 30 ngx_uint_t method; |
623 | 627 |
624 | 628 |
625 static ngx_int_t | 629 static ngx_int_t |
626 ngx_http_v3_process_request_header(ngx_http_request_t *r) | 630 ngx_http_v3_process_request_header(ngx_http_request_t *r) |
627 { | 631 { |
632 ssize_t n; | |
633 ngx_buf_t *b; | |
634 ngx_connection_t *c; | |
635 | |
636 c = r->connection; | |
637 | |
628 if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { | 638 if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { |
629 return NGX_ERROR; | 639 return NGX_ERROR; |
630 } | 640 } |
631 | 641 |
632 if (r->headers_in.server.len == 0) { | 642 if (r->headers_in.server.len == 0) { |
633 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | 643 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
634 "client sent neither \":authority\" nor \"Host\" header"); | 644 "client sent neither \":authority\" nor \"Host\" header"); |
635 goto failed; | 645 goto failed; |
636 } | 646 } |
637 | 647 |
638 if (r->headers_in.host) { | 648 if (r->headers_in.host) { |
640 || ngx_memcmp(r->headers_in.host->value.data, | 650 || ngx_memcmp(r->headers_in.host->value.data, |
641 r->headers_in.server.data, | 651 r->headers_in.server.data, |
642 r->headers_in.server.len) | 652 r->headers_in.server.len) |
643 != 0) | 653 != 0) |
644 { | 654 { |
645 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | 655 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
646 "client sent \":authority\" and \"Host\" headers " | 656 "client sent \":authority\" and \"Host\" headers " |
647 "with different values"); | 657 "with different values"); |
648 goto failed; | 658 goto failed; |
649 } | 659 } |
650 } | 660 } |
653 r->headers_in.content_length_n = | 663 r->headers_in.content_length_n = |
654 ngx_atoof(r->headers_in.content_length->value.data, | 664 ngx_atoof(r->headers_in.content_length->value.data, |
655 r->headers_in.content_length->value.len); | 665 r->headers_in.content_length->value.len); |
656 | 666 |
657 if (r->headers_in.content_length_n == NGX_ERROR) { | 667 if (r->headers_in.content_length_n == NGX_ERROR) { |
658 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | 668 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
659 "client sent invalid \"Content-Length\" header"); | 669 "client sent invalid \"Content-Length\" header"); |
660 goto failed; | 670 goto failed; |
671 } | |
672 | |
673 } else { | |
674 b = r->header_in; | |
675 n = b->last - b->pos; | |
676 | |
677 if (n == 0) { | |
678 n = c->recv(c, b->start, b->end - b->start); | |
679 | |
680 if (n == NGX_ERROR) { | |
681 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
682 return NGX_ERROR; | |
683 } | |
684 | |
685 if (n > 0) { | |
686 b->pos = b->start; | |
687 b->last = b->start + n; | |
688 } | |
689 } | |
690 | |
691 if (n != 0) { | |
692 r->headers_in.chunked = 1; | |
661 } | 693 } |
662 } | 694 } |
663 | 695 |
664 return NGX_OK; | 696 return NGX_OK; |
665 | 697 |
669 return NGX_ERROR; | 701 return NGX_ERROR; |
670 } | 702 } |
671 | 703 |
672 | 704 |
673 ngx_int_t | 705 ngx_int_t |
674 ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b, | 706 ngx_http_v3_read_request_body(ngx_http_request_t *r) |
675 ngx_http_chunked_t *ctx) | |
676 { | 707 { |
708 size_t preread; | |
677 ngx_int_t rc; | 709 ngx_int_t rc; |
710 ngx_chain_t *cl, out; | |
711 ngx_http_request_body_t *rb; | |
712 ngx_http_core_loc_conf_t *clcf; | |
713 | |
714 rb = r->request_body; | |
715 | |
716 preread = r->header_in->last - r->header_in->pos; | |
717 | |
718 if (preread) { | |
719 | |
720 /* there is the pre-read part of the request body */ | |
721 | |
722 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
723 "http3 client request body preread %uz", preread); | |
724 | |
725 out.buf = r->header_in; | |
726 out.next = NULL; | |
727 cl = &out; | |
728 | |
729 } else { | |
730 cl = NULL; | |
731 } | |
732 | |
733 rc = ngx_http_v3_request_body_filter(r, cl); | |
734 if (rc != NGX_OK) { | |
735 return rc; | |
736 } | |
737 | |
738 if (rb->rest == 0) { | |
739 /* the whole request body was pre-read */ | |
740 r->request_body_no_buffering = 0; | |
741 rb->post_handler(r); | |
742 return NGX_OK; | |
743 } | |
744 | |
745 if (rb->rest < 0) { | |
746 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
747 "negative request body rest"); | |
748 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
749 } | |
750 | |
751 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
752 | |
753 rb->buf = ngx_create_temp_buf(r->pool, clcf->client_body_buffer_size); | |
754 if (rb->buf == NULL) { | |
755 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
756 } | |
757 | |
758 r->read_event_handler = ngx_http_v3_read_client_request_body_handler; | |
759 r->write_event_handler = ngx_http_request_empty_handler; | |
760 | |
761 return ngx_http_v3_do_read_client_request_body(r); | |
762 } | |
763 | |
764 | |
765 static void | |
766 ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r) | |
767 { | |
768 ngx_int_t rc; | |
769 | |
770 if (r->connection->read->timedout) { | |
771 r->connection->timedout = 1; | |
772 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); | |
773 return; | |
774 } | |
775 | |
776 rc = ngx_http_v3_do_read_client_request_body(r); | |
777 | |
778 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { | |
779 ngx_http_finalize_request(r, rc); | |
780 } | |
781 } | |
782 | |
783 | |
784 ngx_int_t | |
785 ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r) | |
786 { | |
787 ngx_int_t rc; | |
788 | |
789 if (r->connection->read->timedout) { | |
790 r->connection->timedout = 1; | |
791 return NGX_HTTP_REQUEST_TIME_OUT; | |
792 } | |
793 | |
794 rc = ngx_http_v3_do_read_client_request_body(r); | |
795 | |
796 if (rc == NGX_OK) { | |
797 r->reading_body = 0; | |
798 } | |
799 | |
800 return rc; | |
801 } | |
802 | |
803 | |
804 static ngx_int_t | |
805 ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) | |
806 { | |
807 off_t rest; | |
808 size_t size; | |
809 ssize_t n; | |
810 ngx_int_t rc; | |
811 ngx_chain_t out; | |
678 ngx_connection_t *c; | 812 ngx_connection_t *c; |
813 ngx_http_request_body_t *rb; | |
814 ngx_http_core_loc_conf_t *clcf; | |
815 | |
816 c = r->connection; | |
817 rb = r->request_body; | |
818 | |
819 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
820 "http3 read client request body"); | |
821 | |
822 for ( ;; ) { | |
823 for ( ;; ) { | |
824 if (rb->buf->last == rb->buf->end) { | |
825 | |
826 /* update chains */ | |
827 | |
828 rc = ngx_http_v3_request_body_filter(r, NULL); | |
829 | |
830 if (rc != NGX_OK) { | |
831 return rc; | |
832 } | |
833 | |
834 if (rb->busy != NULL) { | |
835 if (r->request_body_no_buffering) { | |
836 if (c->read->timer_set) { | |
837 ngx_del_timer(c->read); | |
838 } | |
839 | |
840 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { | |
841 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
842 } | |
843 | |
844 return NGX_AGAIN; | |
845 } | |
846 | |
847 ngx_log_error(NGX_LOG_ALERT, c->log, 0, | |
848 "busy buffers after request body flush"); | |
849 | |
850 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
851 } | |
852 | |
853 rb->buf->pos = rb->buf->start; | |
854 rb->buf->last = rb->buf->start; | |
855 } | |
856 | |
857 size = rb->buf->end - rb->buf->last; | |
858 rest = rb->rest - (rb->buf->last - rb->buf->pos); | |
859 | |
860 if ((off_t) size > rest) { | |
861 size = (size_t) rest; | |
862 } | |
863 | |
864 n = c->recv(c, rb->buf->last, size); | |
865 | |
866 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
867 "http3 client request body recv %z", n); | |
868 | |
869 if (n == NGX_AGAIN) { | |
870 break; | |
871 } | |
872 | |
873 if (n == 0) { | |
874 rb->buf->last_buf = 1; | |
875 } | |
876 | |
877 if (n == NGX_ERROR) { | |
878 c->error = 1; | |
879 return NGX_HTTP_BAD_REQUEST; | |
880 } | |
881 | |
882 rb->buf->last += n; | |
883 | |
884 /* pass buffer to request body filter chain */ | |
885 | |
886 out.buf = rb->buf; | |
887 out.next = NULL; | |
888 | |
889 rc = ngx_http_v3_request_body_filter(r, &out); | |
890 | |
891 if (rc != NGX_OK) { | |
892 return rc; | |
893 } | |
894 | |
895 if (rb->rest == 0) { | |
896 break; | |
897 } | |
898 | |
899 if (rb->buf->last < rb->buf->end) { | |
900 break; | |
901 } | |
902 } | |
903 | |
904 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
905 "http3 client request body rest %O", rb->rest); | |
906 | |
907 if (rb->rest == 0) { | |
908 break; | |
909 } | |
910 | |
911 if (!c->read->ready) { | |
912 | |
913 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
914 ngx_add_timer(c->read, clcf->client_body_timeout); | |
915 | |
916 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { | |
917 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
918 } | |
919 | |
920 return NGX_AGAIN; | |
921 } | |
922 } | |
923 | |
924 if (c->read->timer_set) { | |
925 ngx_del_timer(c->read); | |
926 } | |
927 | |
928 if (!r->request_body_no_buffering) { | |
929 r->read_event_handler = ngx_http_block_reading; | |
930 rb->post_handler(r); | |
931 } | |
932 | |
933 return NGX_OK; | |
934 } | |
935 | |
936 | |
937 static ngx_int_t | |
938 ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
939 { | |
940 off_t max; | |
941 size_t size; | |
942 ngx_int_t rc; | |
943 ngx_buf_t *b; | |
944 ngx_uint_t last; | |
945 ngx_chain_t *cl, *out, *tl, **ll; | |
946 ngx_http_request_body_t *rb; | |
947 ngx_http_core_loc_conf_t *clcf; | |
948 ngx_http_core_srv_conf_t *cscf; | |
679 ngx_http_v3_parse_data_t *st; | 949 ngx_http_v3_parse_data_t *st; |
680 enum { | 950 |
681 sw_start = 0, | 951 rb = r->request_body; |
682 sw_skip | 952 st = r->h3_parse; |
683 }; | 953 |
684 | 954 if (rb->rest == -1) { |
685 c = r->connection; | 955 |
686 st = ctx->h3_parse; | 956 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
687 | 957 "http3 request body filter"); |
688 if (st == NULL) { | 958 |
689 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | 959 st = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_data_t)); |
690 "http3 parse request body"); | |
691 | |
692 st = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_parse_data_t)); | |
693 if (st == NULL) { | 960 if (st == NULL) { |
694 goto failed; | 961 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
695 } | 962 } |
696 | 963 |
697 ctx->h3_parse = st; | 964 r->h3_parse = st; |
698 } | 965 |
699 | 966 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); |
700 while (b->pos < b->last && ctx->size == 0) { | 967 |
701 | 968 rb->rest = cscf->large_client_header_buffers.size; |
702 rc = ngx_http_v3_parse_data(c, st, *b->pos++); | 969 } |
703 | 970 |
704 if (rc > 0) { | 971 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
705 ngx_http_v3_finalize_connection(c, rc, | 972 |
706 "could not parse request body"); | 973 max = r->headers_in.content_length_n; |
707 goto failed; | 974 |
708 } | 975 if (max == -1 && clcf->client_max_body_size) { |
709 | 976 max = clcf->client_max_body_size; |
710 if (rc == NGX_ERROR) { | 977 } |
711 goto failed; | 978 |
712 } | 979 out = NULL; |
713 | 980 ll = &out; |
714 if (rc == NGX_AGAIN) { | 981 last = 0; |
715 ctx->state = sw_skip; | 982 |
716 continue; | 983 for (cl = in; cl; cl = cl->next) { |
717 } | 984 |
718 | 985 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, |
719 if (rc == NGX_DONE) { | 986 "http3 body buf " |
720 return NGX_DONE; | 987 "t:%d f:%d %p, pos %p, size: %z file: %O, size: %O", |
721 } | 988 cl->buf->temporary, cl->buf->in_file, |
722 | 989 cl->buf->start, cl->buf->pos, |
723 /* rc == NGX_OK */ | 990 cl->buf->last - cl->buf->pos, |
724 | 991 cl->buf->file_pos, |
725 ctx->size = st->length; | 992 cl->buf->file_last - cl->buf->file_pos); |
726 ctx->state = sw_start; | 993 |
727 } | 994 if (cl->buf->last_buf) { |
728 | 995 last = 1; |
729 if (ctx->state == sw_skip) { | 996 } |
730 ctx->length = 1; | 997 |
731 return NGX_AGAIN; | 998 b = NULL; |
732 } | 999 |
733 | 1000 while (cl->buf->pos < cl->buf->last) { |
734 if (b->pos == b->last) { | 1001 |
735 ctx->length = ctx->size; | 1002 if (st->length == 0) { |
736 return NGX_AGAIN; | 1003 r->request_length++; |
737 } | 1004 |
738 | 1005 rc = ngx_http_v3_parse_data(r->connection, st, *cl->buf->pos++); |
739 return NGX_OK; | 1006 |
740 | 1007 if (rc == NGX_AGAIN) { |
741 failed: | 1008 continue; |
742 | 1009 } |
743 return NGX_ERROR; | 1010 |
1011 if (rc == NGX_DONE) { | |
1012 last = 1; | |
1013 goto done; | |
1014 } | |
1015 | |
1016 if (rc > 0) { | |
1017 ngx_http_v3_finalize_connection(r->connection, rc, | |
1018 "client sent invalid body"); | |
1019 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1020 "client sent invalid body"); | |
1021 return NGX_HTTP_BAD_REQUEST; | |
1022 } | |
1023 | |
1024 if (rc == NGX_ERROR) { | |
1025 ngx_http_v3_finalize_connection(r->connection, | |
1026 NGX_HTTP_V3_ERR_INTERNAL_ERROR, | |
1027 "internal error"); | |
1028 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
1029 } | |
1030 | |
1031 /* rc == NGX_OK */ | |
1032 } | |
1033 | |
1034 if (max != -1 && (uint64_t) (max - rb->received) < st->length) { | |
1035 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1036 "client intended to send too large " | |
1037 "body: %O+%uL bytes", | |
1038 rb->received, st->length); | |
1039 | |
1040 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; | |
1041 } | |
1042 | |
1043 if (b | |
1044 && st->length <= 128 | |
1045 && (uint64_t) (cl->buf->last - cl->buf->pos) >= st->length) | |
1046 { | |
1047 rb->received += st->length; | |
1048 r->request_length += st->length; | |
1049 | |
1050 if (st->length < 8) { | |
1051 | |
1052 while (st->length) { | |
1053 *b->last++ = *cl->buf->pos++; | |
1054 st->length--; | |
1055 } | |
1056 | |
1057 } else { | |
1058 ngx_memmove(b->last, cl->buf->pos, st->length); | |
1059 b->last += st->length; | |
1060 cl->buf->pos += st->length; | |
1061 st->length = 0; | |
1062 } | |
1063 | |
1064 continue; | |
1065 } | |
1066 | |
1067 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
1068 if (tl == NULL) { | |
1069 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
1070 } | |
1071 | |
1072 b = tl->buf; | |
1073 | |
1074 ngx_memzero(b, sizeof(ngx_buf_t)); | |
1075 | |
1076 b->temporary = 1; | |
1077 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; | |
1078 b->start = cl->buf->pos; | |
1079 b->pos = cl->buf->pos; | |
1080 b->last = cl->buf->last; | |
1081 b->end = cl->buf->end; | |
1082 b->flush = r->request_body_no_buffering; | |
1083 | |
1084 *ll = tl; | |
1085 ll = &tl->next; | |
1086 | |
1087 size = cl->buf->last - cl->buf->pos; | |
1088 | |
1089 if (size > st->length) { | |
1090 cl->buf->pos += (size_t) st->length; | |
1091 rb->received += st->length; | |
1092 r->request_length += st->length; | |
1093 st->length = 0; | |
1094 | |
1095 } else { | |
1096 st->length -= size; | |
1097 rb->received += size; | |
1098 r->request_length += size; | |
1099 cl->buf->pos = cl->buf->last; | |
1100 } | |
1101 | |
1102 b->last = cl->buf->pos; | |
1103 } | |
1104 } | |
1105 | |
1106 done: | |
1107 | |
1108 if (last) { | |
1109 | |
1110 if (st->length > 0) { | |
1111 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
1112 "client prematurely closed stream"); | |
1113 r->connection->error = 1; | |
1114 return NGX_HTTP_BAD_REQUEST; | |
1115 } | |
1116 | |
1117 if (r->headers_in.content_length_n == -1) { | |
1118 r->headers_in.content_length_n = rb->received; | |
1119 | |
1120 } else if (r->headers_in.content_length_n != rb->received) { | |
1121 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
1122 "client sent less body data than expected: " | |
1123 "%O out of %O bytes of request body received", | |
1124 rb->received, r->headers_in.content_length_n); | |
1125 return NGX_HTTP_BAD_REQUEST; | |
1126 } | |
1127 | |
1128 rb->rest = 0; | |
1129 | |
1130 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
1131 if (tl == NULL) { | |
1132 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
1133 } | |
1134 | |
1135 b = tl->buf; | |
1136 | |
1137 ngx_memzero(b, sizeof(ngx_buf_t)); | |
1138 | |
1139 b->last_buf = 1; | |
1140 | |
1141 *ll = tl; | |
1142 ll = &tl->next; | |
1143 | |
1144 } else { | |
1145 | |
1146 /* set rb->rest, amount of data we want to see next time */ | |
1147 | |
1148 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
1149 | |
1150 rb->rest = (off_t) cscf->large_client_header_buffers.size; | |
1151 } | |
1152 | |
1153 rc = ngx_http_top_request_body_filter(r, out); | |
1154 | |
1155 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, | |
1156 (ngx_buf_tag_t) &ngx_http_read_client_request_body); | |
1157 | |
1158 return rc; | |
744 } | 1159 } |