comparison src/http/modules/ngx_http_scgi_module.c @ 8021:75af96daee97

SCGI: combining headers with identical names (ticket #1724). SCGI specification explicitly forbids headers with duplicate names (section "3. Request Format"): "Duplicate names are not allowed in the headers". Further, provided headers are expected to follow CGI specification, which also requires to combine headers (RFC 3875, section "4.1.18. Protocol-Specific Meta-Variables"): "If multiple header fields with the same field-name are received then the server MUST rewrite them as a single value having the same semantics".
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 30 May 2022 21:25:28 +0300
parents 2f443cac3f1e
children 2bf7792c262e
comparison
equal deleted inserted replaced
8020:f8f6b9fee66a 8021:75af96daee97
631 631
632 static ngx_int_t 632 static ngx_int_t
633 ngx_http_scgi_create_request(ngx_http_request_t *r) 633 ngx_http_scgi_create_request(ngx_http_request_t *r)
634 { 634 {
635 off_t content_length_n; 635 off_t content_length_n;
636 u_char ch, *key, *val, *lowcase_key; 636 u_char ch, sep, *key, *val, *lowcase_key;
637 size_t len, key_len, val_len, allocated; 637 size_t len, key_len, val_len, allocated;
638 ngx_buf_t *b; 638 ngx_buf_t *b;
639 ngx_str_t content_length; 639 ngx_str_t content_length;
640 ngx_uint_t i, n, hash, skip_empty, header_params; 640 ngx_uint_t i, n, hash, skip_empty, header_params;
641 ngx_chain_t *cl, *body; 641 ngx_chain_t *cl, *body;
642 ngx_list_part_t *part; 642 ngx_list_part_t *part;
643 ngx_table_elt_t *header, **ignored; 643 ngx_table_elt_t *header, *hn, **ignored;
644 ngx_http_scgi_params_t *params; 644 ngx_http_scgi_params_t *params;
645 ngx_http_script_code_pt code; 645 ngx_http_script_code_pt code;
646 ngx_http_script_engine_t e, le; 646 ngx_http_script_engine_t e, le;
647 ngx_http_scgi_loc_conf_t *scf; 647 ngx_http_scgi_loc_conf_t *scf;
648 ngx_http_script_len_code_pt lcode; 648 ngx_http_script_len_code_pt lcode;
705 if (scf->upstream.pass_request_headers) { 705 if (scf->upstream.pass_request_headers) {
706 706
707 allocated = 0; 707 allocated = 0;
708 lowcase_key = NULL; 708 lowcase_key = NULL;
709 709
710 if (params->number) { 710 if (ngx_http_link_multi_headers(r) != NGX_OK) {
711 return NGX_ERROR;
712 }
713
714 if (params->number || r->headers_in.multi) {
711 n = 0; 715 n = 0;
712 part = &r->headers_in.headers.part; 716 part = &r->headers_in.headers.part;
713 717
714 while (part) { 718 while (part) {
715 n += part->nelts; 719 n += part->nelts;
733 } 737 }
734 738
735 part = part->next; 739 part = part->next;
736 header = part->elts; 740 header = part->elts;
737 i = 0; 741 i = 0;
742 }
743
744 for (n = 0; n < header_params; n++) {
745 if (&header[i] == ignored[n]) {
746 goto next_length;
747 }
738 } 748 }
739 749
740 if (params->number) { 750 if (params->number) {
741 if (allocated < header[i].key.len) { 751 if (allocated < header[i].key.len) {
742 allocated = header[i].key.len + 16; 752 allocated = header[i].key.len + 16;
768 } 778 }
769 } 779 }
770 780
771 len += sizeof("HTTP_") - 1 + header[i].key.len + 1 781 len += sizeof("HTTP_") - 1 + header[i].key.len + 1
772 + header[i].value.len + 1; 782 + header[i].value.len + 1;
783
784 for (hn = header[i].next; hn; hn = hn->next) {
785 len += hn->value.len + 2;
786 ignored[header_params++] = hn;
787 }
788
789 next_length:
790
791 continue;
773 } 792 }
774 } 793 }
775 794
776 /* netstring: "length:" + packet + "," */ 795 /* netstring: "length:" + packet + "," */
777 796
867 i = 0; 886 i = 0;
868 } 887 }
869 888
870 for (n = 0; n < header_params; n++) { 889 for (n = 0; n < header_params; n++) {
871 if (&header[i] == ignored[n]) { 890 if (&header[i] == ignored[n]) {
872 goto next; 891 goto next_value;
873 } 892 }
874 } 893 }
875 894
876 key = b->last; 895 key = b->last;
877 b->last = ngx_cpymem(key, "HTTP_", sizeof("HTTP_") - 1); 896 b->last = ngx_cpymem(key, "HTTP_", sizeof("HTTP_") - 1);
891 910
892 *b->last++ = (u_char) 0; 911 *b->last++ = (u_char) 0;
893 912
894 val = b->last; 913 val = b->last;
895 b->last = ngx_copy(val, header[i].value.data, header[i].value.len); 914 b->last = ngx_copy(val, header[i].value.data, header[i].value.len);
915
916 if (header[i].next) {
917
918 if (header[i].key.len == sizeof("Cookie") - 1
919 && ngx_strncasecmp(header[i].key.data, (u_char *) "Cookie",
920 sizeof("Cookie") - 1)
921 == 0)
922 {
923 sep = ';';
924
925 } else {
926 sep = ',';
927 }
928
929 for (hn = header[i].next; hn; hn = hn->next) {
930 *b->last++ = sep;
931 *b->last++ = ' ';
932 b->last = ngx_copy(b->last, hn->value.data, hn->value.len);
933 }
934 }
935
896 *b->last++ = (u_char) 0; 936 *b->last++ = (u_char) 0;
897 937
898 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 938 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
899 "scgi param: \"%s: %s\"", key, val); 939 "scgi param: \"%s: %s\"", key, val);
900 940
901 next: 941 next_value:
902 942
903 continue; 943 continue;
904 } 944 }
905 } 945 }
906 946