comparison src/http/v3/ngx_http_v3_request.c @ 8689: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
8688:a346905c359f 8689: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 }