From 9361817ec1609f7a8fb47133a1f4a301373df5dc Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Sat, 26 Apr 2025 21:23:34 -0400 Subject: [PATCH 1/3] Do not send malformed H2 or H3 requests to NGINX A check that NGINX accepts malformed HTTP/2 or HTTP/3 requests should be specific to such malformed requests, not hidden in general tests for HTTP/2 and HTTP/3 headers. --- h2_headers.t | 8 ++++---- h3_headers.t | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/h2_headers.t b/h2_headers.t index cdf3073d..28f0c434 100644 --- a/h2_headers.t +++ b/h2_headers.t @@ -426,7 +426,7 @@ unlike($s->{headers}, qr/aaaaa/, 'well known chars - huffman encoding'); # response header field with huffman encoding - complete table mod \0, CR, LF # first saturate with short-encoded characters (NB: implementation detail) -my $field = pack "C*", ((map { 97 } (1 .. 862)), 1 .. 9, 11, 12, 14 .. 255); +my $field = pack "C*", ((map { 97 } (1 .. 862)), 9, 32 .. 126, 128 .. 255); $s = Test::Nginx::HTTP2->new(); $sid = $s->new_stream({ headers => [ @@ -1198,12 +1198,12 @@ sub http_daemon { if ($uri eq '/cookie') { - my ($cookie, $cookie2) = $headers =~ /Cookie: (.+)/ig; + my ($cookie, $cookie2) = $headers =~ /Cookie: ([\t -~]+)\r?/ig; $cookie2 = '' unless defined $cookie2; my ($cookie_a, $cookie_c) = ('', ''); - $cookie_a = $1 if $headers =~ /X-Cookie-a: (.+)/i; - $cookie_c = $1 if $headers =~ /X-Cookie-c: (.+)/i; + $cookie_a = $1 if $headers =~ /X-Cookie-a: ([\t -~]+)\r?/i; + $cookie_c = $1 if $headers =~ /X-Cookie-c: ([\t -~]+)\r?/i; print $client <{headers}, qr/aaaaa/, 'well known chars - huffman encoding'); # response header field with huffman encoding - complete table mod \0, CR, LF # first saturate with short-encoded characters (NB: implementation detail) -my $field = pack "C*", ((map { 97 } (1 .. 862)), 1 .. 9, 11, 12, 14 .. 255); +my $field = pack "C*", ((map { 97 } (1 .. 862)), 9, 32 .. 126, 128 .. 255); $s = Test::Nginx::HTTP3->new(); $sid = $s->new_stream({ headers => [ From 3da626ba5858dfa24ff0d41d8e51bae53bd41f78 Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Sat, 26 Apr 2025 21:42:37 -0400 Subject: [PATCH 2/3] Header values do not include CR Do not use a regular expression that matches CR. --- h2_headers.t | 6 +++--- h3_headers.t | 6 +++--- range_if_range.t | 2 +- slice.t | 4 ++-- stream_upstream_resolve.t | 8 ++++---- stream_upstream_resolve_reload.t | 10 ++++++---- stream_upstream_service.t | 12 ++++++------ stream_upstream_service_reload.t | 6 +++--- upstream_resolve.t | 8 ++++---- upstream_resolve_reload.t | 4 ++-- upstream_service.t | 12 ++++++------ upstream_service_reload.t | 6 +++--- userid.t | 4 ++-- 13 files changed, 45 insertions(+), 43 deletions(-) diff --git a/h2_headers.t b/h2_headers.t index 28f0c434..4c2419d7 100644 --- a/h2_headers.t +++ b/h2_headers.t @@ -1198,12 +1198,12 @@ sub http_daemon { if ($uri eq '/cookie') { - my ($cookie, $cookie2) = $headers =~ /Cookie: ([\t -~]+)\r?/ig; + my ($cookie, $cookie2) = $headers =~ /^Cookie: ([\t -~]+)\r\n/igm; $cookie2 = '' unless defined $cookie2; my ($cookie_a, $cookie_c) = ('', ''); - $cookie_a = $1 if $headers =~ /X-Cookie-a: ([\t -~]+)\r?/i; - $cookie_c = $1 if $headers =~ /X-Cookie-c: ([\t -~]+)\r?/i; + $cookie_a = $1 if $headers =~ /^X-Cookie-a: ([\t -~]+)\r\n/im; + $cookie_c = $1 if $headers =~ /^X-Cookie-c: ([\t -~]+)\r\n/im; print $client <) { $headers .= $_; last if (/^\x0d?\x0a?$/); } return 1 if $headers eq ''; - $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; - return 1 if $uri eq ''; + $uri = $1 if $headers =~ /\A[A-Z]+ ([!-~\x80-\xFF]+) HTTP\/1\.[01]\r\n/i; + die "Bad request line\n" if $uri eq ''; - $headers =~ /X-A: (.*)$/m; + $headers =~ /^X-A: ([ -~]*)\r?$/m; map { push @{$h{A}}, $_ } split(/ /, $1); - $headers =~ /X-ERROR: (.*)$/m; + $headers =~ /^X-ERROR: ([ -~]*)\r?$/m; $h{ERROR} = $1; Test::Nginx::log_core('||', "$port: response, 200"); diff --git a/stream_upstream_service.t b/stream_upstream_service.t index c7c53b42..2d240c09 100644 --- a/stream_upstream_service.t +++ b/stream_upstream_service.t @@ -550,15 +550,15 @@ sub process_name { $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; return 1 if $uri eq ''; - $headers =~ /X-A: (.*)$/m; + $headers =~ /^X-A: ([\t -~]*)\r?$/im; map { push @{$h{A}}, $_ } split(/ /, $1); - $headers =~ /X-AAAA: (.*)$/m; + $headers =~ /^X-AAAA: ([\t -~]*)\r?$/im; map { push @{$h{AAAA}}, $_ } split(/ /, $1); - $headers =~ /X-SRV: (.*)$/m; + $headers =~ /^X-SRV: ([\t -~]*)\r?$/im; map { push @{$h{SRV}}, $_ } split(/;/, $1); - $headers =~ /X-CNAME: (.+)$/m and $h{CNAME} = $1; - $headers =~ /X-ERROR: (.+)$/m and $h{ERROR} = $1; - $headers =~ /X-SERROR: (.+)$/m and $h{SERROR} = $1; + $headers =~ /^X-CNAME: ([\t -~]+)\r?$/im and $h{CNAME} = $1; + $headers =~ /^X-ERROR: ([\t -~]+)\r?$/im and $h{ERROR} = $1; + $headers =~ /^X-SERROR: ([\t -~]+)\r?$/im and $h{SERROR} = $1; Test::Nginx::log_core('||', "$port: response, 200"); print $client < Date: Sat, 26 Apr 2025 21:53:11 -0400 Subject: [PATCH 3/3] Use CRLF where that is required Lots of code used a bare LF in the HTTP protocol, but that is wrong: RFC9112 is very clear that CRLF is the HTTP line terminator. Most of the changes are just replacing \n with \r\n or adding missing \r in heredocs, but it is also necessary to update byte counts to include the extra bytes. --- access_log_variables.t | 16 +- auth_basic.t | 8 +- auth_delay.t | 8 +- auth_request.t | 12 +- dav.t | 196 ++++++++++++------------ dav_chunked.t | 42 ++--- dav_utf8.t | 76 ++++----- fastcgi.t | 6 +- fastcgi_body.t | 50 +++--- fastcgi_body2.t | 16 +- fastcgi_cache.t | 6 +- fastcgi_extra_data.t | 32 ++-- fastcgi_header_params.t | 80 +++++----- fastcgi_keepalive.t | 6 +- fastcgi_merge_params.t | 18 +-- fastcgi_merge_params2.t | 18 +-- fastcgi_request_buffering.t | 26 ++-- fastcgi_request_buffering_chunked.t | 26 ++-- fastcgi_unix.t | 6 +- geo.t | 8 +- geo_ipv6.t | 8 +- geoip.t | 8 +- grpc_trailers.t | 10 +- gzip.t | 10 +- h2_fastcgi_request_buffering.t | 6 +- h2_headers.t | 24 +-- h2_proxy_request_buffering.t | 6 +- h2_proxy_request_buffering_ssl.t | 6 +- h3_headers.t | 24 +-- h3_server_name.t | 2 +- http_absolute_redirect.t | 6 +- http_disable_symlinks.t | 6 +- http_expect_100_continue.t | 12 +- http_host.t | 12 +- http_include.t | 6 +- http_keepalive.t | 16 +- http_keepalive_shutdown.t | 12 +- http_method.t | 28 ++-- http_resolver.t | 12 +- http_resolver_aaaa.t | 6 +- http_resolver_cname.t | 6 +- ignore_invalid_headers.t | 8 +- image_filter.t | 6 +- lib/Test/Nginx.pm | 22 +-- mirror_proxy.t | 8 +- mp4_ssi.t | 8 +- msie_refresh.t | 10 +- not_modified.t | 24 +-- not_modified_finalize.t | 8 +- not_modified_proxy.t | 52 +++++-- proxy.t | 52 +++---- proxy_available.t | 6 +- proxy_cache.t | 16 +- proxy_cache_chunked.t | 12 +- proxy_cache_lock.t | 8 +- proxy_cache_lock_age.t | 8 +- proxy_cache_max_range_offset.t | 10 +- proxy_cache_range.t | 10 +- proxy_cache_use_stale.t | 10 +- proxy_cache_valid.t | 10 +- proxy_cache_vary.t | 20 +-- proxy_chunked.t | 18 +-- proxy_extra_data.t | 48 +++--- proxy_force_ranges.t | 14 +- proxy_limit_rate.t | 8 +- proxy_limit_rate2.t | 8 +- proxy_merge_headers.t | 10 +- proxy_next_upstream_tries.t | 6 +- proxy_noclose.t | 24 +-- proxy_non_idempotent.t | 6 +- proxy_pass_request.t | 20 +-- proxy_protocol.t | 6 +- proxy_protocol2.t | 6 +- proxy_protocol2_tlv.t | 6 +- proxy_request_buffering.t | 18 +-- proxy_request_buffering_chunked.t | 18 +-- proxy_request_buffering_keepalive.t | 8 +- proxy_request_buffering_ssl.t | 18 +-- proxy_ssl.t | 6 +- proxy_trailers.t | 10 +- proxy_unix.t | 20 +-- proxy_upstream_cookie.t | 10 +- proxy_variables.t | 20 +-- proxy_xar.t | 6 +- range.t | 10 +- range_charset.t | 10 +- range_clearing.t | 10 +- range_flv.t | 10 +- range_if_range.t | 20 +-- range_mp4.t | 10 +- realip.t | 54 +++---- realip_remote_addr.t | 8 +- realip_remote_port.t | 8 +- referer.t | 8 +- rewrite.t | 8 +- rewrite_set.t | 6 +- scgi.t | 80 +++++----- scgi_body.t | 42 ++--- scgi_gzip.t | 4 +- scgi_merge_params.t | 18 +-- slice.t | 28 ++-- ssi_delayed.t | 6 +- ssl.t | 6 +- ssl_ocsp.t | 10 +- ssl_proxy_protocol.t | 6 +- ssl_reject_handshake.t | 2 +- ssl_sni.t | 2 +- ssl_sni_sessions.t | 2 +- ssl_stapling.t | 8 +- stream_limit_conn.t | 6 +- stream_limit_conn_complex.t | 2 +- stream_limit_conn_dry_run.t | 8 +- stream_ssl.t | 6 +- stream_ssl_ocsp.t | 8 +- stream_ssl_session_reuse.t | 6 +- stream_ssl_stapling.t | 8 +- stream_upstream_max_conns.t | 6 +- stream_upstream_random.t | 8 +- stream_upstream_resolve.t | 22 +-- stream_upstream_resolve_reload.t | 19 +-- stream_upstream_resolver.t | 14 +- stream_upstream_service.t | 26 ++-- stream_upstream_service_reload.t | 20 +-- stub_status.t | 26 ++-- sub_filter_slice.t | 10 +- trailers.t | 16 +- upstream.t | 8 +- upstream_least_conn.t | 8 +- upstream_max_conns.t | 6 +- upstream_random.t | 8 +- upstream_resolve.t | 22 +-- upstream_resolve_reload.t | 18 +-- upstream_resolver.t | 14 +- upstream_service.t | 26 ++-- upstream_service_reload.t | 20 +-- userid.t | 8 +- uwsgi.t | 68 ++++---- uwsgi_body.t | 28 ++-- uwsgi_ssl.t | 44 +++--- worker_shutdown_timeout_proxy_upgrade.t | 20 +-- xslt.t | 10 +- 141 files changed, 1214 insertions(+), 1185 deletions(-) diff --git a/access_log_variables.t b/access_log_variables.t index 7fd5d66c..506d7a76 100644 --- a/access_log_variables.t +++ b/access_log_variables.t @@ -93,13 +93,13 @@ my $bytes_sent = length http_get('/bytes'); # pipelined requests http(<stop(); @@ -116,7 +116,7 @@ $log = $t->read_file('msec.log'); like($log, qr!/msec [\d\.]+!, 'msec'); $log = $t->read_file('request.log'); -like($log, qr!/request 200 39 [\d\.]+!, 'request'); +like($log, qr!/request 200 42 [\d\.]+!, 'request'); $log = $t->read_file('bytes.log'); is($log, "/bytes $bytes_sent 2\n", 'bytes sent'); diff --git a/auth_basic.t b/auth_basic.t index 6f7270b0..826c6896 100644 --- a/auth_basic.t +++ b/auth_basic.t @@ -133,10 +133,10 @@ sub http_get_auth { my $auth = encode_base64($user . ':' . $password, ''); return http(<Accept() >= 0) { print <run(); my $r; $r = http(<testdir() . '/file', 10, 'put file size'); $r = http(<testdir() . '/file', 0, 'put file again size'); $r = http(<testdir() . '/file', 'file deleted'); $r = http(<testdir() . '/file', 10, 'put file extra data size'); $r = http(<testdir() . '/file-moved escape', 10, 'file copied unescaped'); $t->write_file('file.exist', join '', (1 .. 42)); $r = http(<testdir() . '/file.exist', 10, 'target file truncated'); $r = http(<testdir() . '/alias', 10, 'put alias size'); # request methods with unsupported request body $r = http(<run(); my $r; $r = http(<read_file('file'), '1234567890', 'put content'); $r = http(<read_file('file'), '', 'put empty content'); my $body = ('a' . CRLF . '1234567890' . CRLF) x 1024 . '0' . CRLF . CRLF; $r = http(<has_version('1.23.0'); my $r; $r = http(<{headers}->{'x-another'}, 'bar', 'h3 trailer 2'); sub get { my ($uri) = @_; http(<{http_end} = sub { $client->write(<close; diff --git a/h2_proxy_request_buffering_ssl.t b/h2_proxy_request_buffering_ssl.t index 288b5fa5..7f920f6f 100644 --- a/h2_proxy_request_buffering_ssl.t +++ b/h2_proxy_request_buffering_ssl.t @@ -257,9 +257,9 @@ sub get_body { }; $f->{http_end} = sub { $client->write(<close; diff --git a/h3_headers.t b/h3_headers.t index c441cd54..b0955803 100644 --- a/h3_headers.t +++ b/h3_headers.t @@ -915,23 +915,23 @@ sub http_daemon { $cookie_c = $1 if $headers =~ /^X-Cookie-c: ([\t -~]+)\r\n/im; print $client < 1, SSL_hostname => $sni || $host, SSL_alpn_protocols => ['http/1.1']) diff --git a/http_absolute_redirect.t b/http_absolute_redirect.t index dac0b2c7..ec02f1b7 100644 --- a/http_absolute_redirect.t +++ b/http_absolute_redirect.t @@ -182,9 +182,9 @@ like(get('off', '/auto%20%22%23%25%3C%3E%3F%5C%5E%60%7B%7C%7D'), sub get { my ($host, $uri) = @_; http(< 1); -POST /r HTTP/1.1 -Host: localhost -Content-Length: 10 - +POST /r HTTP/1.1\r +Host: localhost\r +Content-Length: 10\r +\r EOF read_keepalive($s); @@ -175,10 +175,10 @@ sub http_keepalive { my $sleep = ($i == 1 ? $opts{sleep} : 0); http(< $s, start => 1, sleep => $sleep); -$opts{method} $url HTTP/1.1 -Host: localhost -User-Agent: $opts{ua} - +$opts{method} $url HTTP/1.1\r +Host: localhost\r +User-Agent: $opts{ua}\r +\r EOF $total .= read_keepalive($s); diff --git a/http_keepalive_shutdown.t b/http_keepalive_shutdown.t index ce5f7091..bb0ce198 100644 --- a/http_keepalive_shutdown.t +++ b/http_keepalive_shutdown.t @@ -58,12 +58,12 @@ $t->run(); # signaling on graceful shutdown to client that keepalive connection is closing my $s = http(< 1); -HEAD /test.html HTTP/1.1 -Host: localhost - -HEAD /test.html HTTP/1.1 -Host: localhost - +HEAD /test.html HTTP/1.1\r +Host: localhost\r +\r +HEAD /test.html HTTP/1.1\r +Host: localhost\r +\r EOF select undef, undef, undef, 0.1; diff --git a/http_method.t b/http_method.t index c853a933..3c9c4cf0 100644 --- a/http_method.t +++ b/http_method.t @@ -50,23 +50,23 @@ EOF ############################################################################### like(http(<read_file($uri); print $client < 1); -HEAD /big.html?e=1 HTTP/1.1 -Host: localhost - +HEAD /big.html?e=1 HTTP/1.1\r +Host: localhost\r +\r EOF my $r = http_get('/t.html', socket => $s); @@ -133,11 +133,11 @@ like($r, qr/SEE-THIS/, 'non-cacheable head - second'); sub http_get_range { my ($url, $extra) = @_; return http(<testdir())}/error.log`, qr/^$/s, 'no crit'); sub get { my ($url, $extra) = @_; return http(<print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 8\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 8\r\n\r\n"); $c->print("SEE-THIS-BUT-NOT-THIS\n"); } elsif ($uri eq '/zero') { - $c->print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 0\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 0\r\n\r\n"); $c->print("NOT-THIS\n"); } elsif ($uri eq '/short') { - $c->print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 100\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 100\r\n\r\n"); $c->print("SEE-THIS-TOO-SHORT-RESPONSE\n"); } elsif ($uri eq '/empty') { - $c->print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 100\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 100\r\n\r\n"); } elsif ($uri eq '/head/empty') { - $c->print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 8\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 8\r\n\r\n"); $c->print("SEE-THIS") unless $head; } elsif ($uri eq '/head/matching') { - $c->print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 8\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 8\r\n\r\n"); $c->print("SEE-THIS"); } elsif ($uri eq '/head/extra') { - $c->print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 8\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 8\r\n\r\n"); $c->print("SEE-THIS-BUT-NOT-THIS\n"); } elsif ($uri eq '/head/short') { - $c->print("HTTP/1.1 200 OK\n"); - $c->print("Content-Type: text/html\n"); - $c->print("Content-Length: 100\n\n"); + $c->print("HTTP/1.1 200 OK\r\n"); + $c->print("Content-Type: text/html\r\n"); + $c->print("Content-Length: 100\r\n\r\n"); $c->print("SEE-THIS\n"); } diff --git a/proxy_force_ranges.t b/proxy_force_ranges.t index 3453f655..c57fd6e5 100644 --- a/proxy_force_ranges.t +++ b/proxy_force_ranges.t @@ -98,13 +98,13 @@ like(http_get_range('/cache/t.html', 'Range: bytes=0-2,4-'), qr/^SEE.*^THIS/ms, # If-Range HTTP-date request like(http_get_range('/proxy/t.html', - "Range: bytes=4-\nIf-Range: Mon, 28 Sep 1970 06:00:00 GMT"), + "Range: bytes=4-\r\nIf-Range: Mon, 28 Sep 1970 06:00:00 GMT"), qr/^THIS/m, 'if-range last-modified proxy'); # If-Range entity-tag request like(http_get_range('/proxy/t.html', - "Range: bytes=4-\nIf-Range: \"59a5401c-8\""), + "Range: bytes=4-\r\nIf-Range: \"59a5401c-8\""), qr/^THIS/m, 'if-range etag proxy'); # range sent using chunked transfer encoding @@ -117,11 +117,11 @@ like(http_get_range('/proxy/t.html', 'Range: bytes=-2'), sub http_get_range { my ($url, $extra) = @_; return http(<read_file('pp6.log'), "2001:db8::1:123\n", 'tcp6 log'); sub pp_get { my ($url, $proxy) = @_; return http($proxy . <read_file('pp6.log'), "2001:db8::1:123\n", 'tcp6 log'); sub pp_get { my ($url, $proxy) = @_; return http($proxy . <write(<write(<run(); # as remain request body. http(<write(<{headers}->{'x-another'}, 'bar', 'h3 trailer 2'); sub get { my ($uri) = @_; http(<stop(); my $f = $t->read_file('test.log'); Test::Nginx::log_core('||', $f); -like($f, qr!^/:23:68:$l1:$l1!m, 'log - response length'); -like($f, qr!^/multi:32:77:$l2:$l2!m, 'log - response length - multi packets'); +like($f, qr!^/:23:72:$l1:$l1!m, 'log - response length'); +like($f, qr!^/multi:32:81:$l2:$l2!m, 'log - response length - multi packets'); ############################################################################### @@ -105,10 +105,10 @@ sub http_daemon { if ($uri eq '/') { print $client <<"EOF"; -HTTP/1.1 200 OK -Connection: close -X-Len: $len - +HTTP/1.1 200 OK\r +Connection: close\r +X-Len: $len\r +\r EOF print $client "TEST-OK-IF-YOU-SEE-THIS" unless $headers =~ /^HEAD/i; @@ -116,10 +116,10 @@ EOF } elsif ($uri eq '/multi') { print $client <<"EOF"; -HTTP/1.1 200 OK -Connection: close -X-Len: $len - +HTTP/1.1 200 OK\r +Connection: close\r +X-Len: $len\r +\r TEST-OK-IF-YOU-SEE-THIS EOF diff --git a/proxy_xar.t b/proxy_xar.t index 490eda6a..72e4a6c2 100644 --- a/proxy_xar.t +++ b/proxy_xar.t @@ -115,9 +115,9 @@ like(http_get('/proxy?xar=@named'), sub http_post { my ($url) = @_; http(<plan(8); ############################################################################### like(http(<new('127.0.0.1:' . port(8081)); like(http(< $s), qr/ 204 .*192.0.2.1/s, 'realip post read'); -GET / HTTP/1.0 -Host: localhost -X-Real-IP: 192.0.2.1 - +GET / HTTP/1.0\r +Host: localhost\r +X-Real-IP: 192.0.2.1\r +\r EOF ############################################################################### @@ -133,10 +133,10 @@ EOF sub http_xff { my ($uri, $xff) = @_; return http(< 1); -GET $url HTTP/1.0 -Host: localhost -X-Forwarded-For: $xff - +GET $url HTTP/1.0\r +Host: localhost\r +X-Forwarded-For: $xff\r +\r EOF return ($s->sockport(), http_end($s)); diff --git a/referer.t b/referer.t index 177c9491..63077e12 100644 --- a/referer.t +++ b/referer.t @@ -195,10 +195,10 @@ sub valid { $text = http_get($uri); } else { $text = http(<has_version('1.23.0'); my $r = http(<env->{HTTP_FOO} || ''; $request->connection()->print(<connection()->print(<env->{HTTP_X_BLAH} || ''; $request->connection()->print(< $len - 1; print < 1, PeerAddr => '127.0.0.1:' . port($port), SSL => 1, SSL_hostname => $sni, @@ -506,10 +506,10 @@ sub http_daemon { select undef, undef, undef, 0.02; $headers = <<"EOF"; -HTTP/1.1 200 OK -Connection: close -Content-Type: application/ocsp-response - +HTTP/1.1 200 OK\r +Connection: close\r +Content-Type: application/ocsp-response\r +\r EOF local $/; diff --git a/ssl_proxy_protocol.t b/ssl_proxy_protocol.t index 75cfa10b..d7276fc5 100644 --- a/ssl_proxy_protocol.t +++ b/ssl_proxy_protocol.t @@ -149,9 +149,9 @@ sub pp_get { my $s = http($proxy, start => 1); return http(< $s, SSL => 1); -GET $url HTTP/1.0 -Host: localhost - +GET $url HTTP/1.0\r +Host: localhost\r +\r EOF } diff --git a/ssl_reject_handshake.t b/ssl_reject_handshake.t index b73cc513..93dfb66c 100644 --- a/ssl_reject_handshake.t +++ b/ssl_reject_handshake.t @@ -118,7 +118,7 @@ like(get('virtual2', 8082), qr/unrecognized name/, 'virtual 2 rejected'); sub get { my ($host, $port) = @_; my $r = http( - "GET / HTTP/1.0\nHost: " . ($host || 'localhost') . "\n\n", + "GET / HTTP/1.0\r\nHost: " . ($host || 'localhost') . "\r\n\r\n", PeerAddr => '127.0.0.1:' . port($port), SSL => 1, SSL_hostname => $host diff --git a/ssl_sni.t b/ssl_sni.t index 95e418a0..edbcdf3f 100644 --- a/ssl_sni.t +++ b/ssl_sni.t @@ -149,7 +149,7 @@ sub get_cert_cn { sub get_host { my ($host, $sni) = @_; return http( - "GET / HTTP/1.0\nHost: $host\n\n", + "GET / HTTP/1.0\r\nHost: $host\r\n\r\n", SSL => 1, SSL_hostname => $sni || $host ); diff --git a/ssl_sni_sessions.t b/ssl_sni_sessions.t index fb142566..1e654a63 100644 --- a/ssl_sni_sessions.t +++ b/ssl_sni_sessions.t @@ -165,7 +165,7 @@ sub get_ssl_context { sub get { my ($host, $port, $ctx) = @_; return http( - "GET / HTTP/1.0\nHost: $host\n\n", + "GET / HTTP/1.0\r\nHost: $host\r\n\r\n", PeerAddr => '127.0.0.1:' . port($port), SSL => 1, SSL_hostname => $host, diff --git a/ssl_stapling.t b/ssl_stapling.t index 1b225ed0..51e36136 100644 --- a/ssl_stapling.t +++ b/ssl_stapling.t @@ -388,10 +388,10 @@ sub http_daemon { select undef, undef, undef, 0.02; $headers = <<"EOF"; -HTTP/1.1 200 OK -Connection: close -Content-Type: application/ocsp-response - +HTTP/1.1 200 OK\r +Connection: close\r +Content-Type: application/ocsp-response\r +\r EOF local $/; diff --git a/stream_limit_conn.t b/stream_limit_conn.t index 890f27f7..cc13d332 100644 --- a/stream_limit_conn.t +++ b/stream_limit_conn.t @@ -92,7 +92,7 @@ like(get(), qr/200 OK/, 'passed'); # same and other zones my $s = http(< 1, sleep => 0.2); -GET / HTTP/1.0 +GET / HTTP/1.0\r EOF ok($s, 'long connection'); @@ -102,8 +102,8 @@ like(get('127.0.0.1:' . port(8081)), qr/200 OK/, 'passed different zone'); like(get('127.0.0.1:' . port(8085)), qr/200 OK/, 'passed same zone unlimited'); ok(http(< $s), 'long connection closed'); -Host: localhost - +Host: localhost\r +\r EOF # zones proxy chain diff --git a/stream_limit_conn_complex.t b/stream_limit_conn_complex.t index edbef3d7..afab2337 100644 --- a/stream_limit_conn_complex.t +++ b/stream_limit_conn_complex.t @@ -71,7 +71,7 @@ $t->run()->plan(4); like(get(port(8080)), qr/200 OK/, 'passed'); my $s = http(< 1, sleep => 0.2); -GET / HTTP/1.0 +GET / HTTP/1.0\r EOF ok($s, 'long connection'); diff --git a/stream_limit_conn_dry_run.t b/stream_limit_conn_dry_run.t index ca7e5282..02993ded 100644 --- a/stream_limit_conn_dry_run.t +++ b/stream_limit_conn_dry_run.t @@ -90,14 +90,14 @@ $t->run()->plan(9); my ($p, $p1, $p2, $p3) = (port(8080), port(8081), port(8082), port(8083)); -is(stream("127.0.0.1:$p")->io("GET /\n"), 'OK', 'passed'); +is(stream("127.0.0.1:$p")->io("GET /\r\n"), 'OK', 'passed'); my $s = stream('127.0.0.1:' . port(8080)); $s->write("GET"); -is(stream("127.0.0.1:$p1")->io("GET /\n"), '', 'rejected'); -is(stream("127.0.0.1:$p2")->io("GET /\n"), 'OK', 'rejected dry run'); -is(stream("127.0.0.1:$p3")->io("GET /\n"), 'OK', 'no limit'); +is(stream("127.0.0.1:$p1")->io("GET /\r\n"), '', 'rejected'); +is(stream("127.0.0.1:$p2")->io("GET /\r\n"), 'OK', 'rejected dry run'); +is(stream("127.0.0.1:$p3")->io("GET /\r\n"), 'OK', 'no limit'); undef $s; diff --git a/stream_ssl.t b/stream_ssl.t index 787bdfe2..f8a94b47 100644 --- a/stream_ssl.t +++ b/stream_ssl.t @@ -171,9 +171,9 @@ sub http_daemon { } print $client <{ERROR} = '' unless $name->{ERROR}; my $req =<{A} -X-AAAA: $name->{AAAA} -X-CNAME: $name->{CNAME} -X-ERROR: $name->{ERROR} - +GET / HTTP/1.0\r +Host: localhost\r +X-A: $name->{A}\r +X-AAAA: $name->{AAAA}\r +X-CNAME: $name->{CNAME}\r +X-ERROR: $name->{ERROR}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -366,10 +366,10 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client <{ERROR} = '' unless $name->{ERROR}; my $req =<{A} -X-ERROR: $name->{ERROR} - +GET / HTTP/1.1\r +Host: localhost\r +X-A: $name->{A}\r +X-ERROR: $name->{ERROR}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -325,10 +325,11 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client <{SRV} = '' unless $name->{SRV}; my $req =<{A} -X-AAAA: $name->{AAAA} -X-CNAME: $name->{CNAME} -X-ERROR: $name->{ERROR} -X-SERROR: $name->{SERROR} -X-SRV: $name->{SRV} - +GET / HTTP/1.0\r +Host: localhost\r +X-A: $name->{A}\r +X-AAAA: $name->{AAAA}\r +X-CNAME: $name->{CNAME}\r +X-ERROR: $name->{ERROR}\r +X-SERROR: $name->{SERROR}\r +X-SRV: $name->{SRV}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -562,10 +562,10 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client <{SRV} = '' unless $name->{SRV}; my $req =<{A} -X-ERROR: $name->{ERROR} -X-SRV: $name->{SRV} - +GET / HTTP/1.0\r +Host: localhost\r +X-A: $name->{A}\r +X-ERROR: $name->{ERROR}\r +X-SRV: $name->{SRV}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -303,10 +303,10 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client < 1, socket => $s, sleep => 0.2); -GET /rate HTTP/1.0 +GET /rate HTTP/1.0\r EOF %status = status('/stub'); @@ -107,8 +107,8 @@ is($status{'reading'}, 1, 'reading state'); is($status{'writing'}, 1, 'reading state - not writing'); http(< 1, socket => $s, sleep => 0.2); -Host: localhost - +Host: localhost\r +\r EOF %status = status('/stub'); @@ -155,9 +155,9 @@ sub get_body { sub http_post { my ($url) = @_; return http(<{ERROR} = '' unless $name->{ERROR}; my $req =<{A} -X-AAAA: $name->{AAAA} -X-CNAME: $name->{CNAME} -X-ERROR: $name->{ERROR} - +GET / HTTP/1.0\r +Host: localhost\r +X-A: $name->{A}\r +X-AAAA: $name->{AAAA}\r +X-CNAME: $name->{CNAME}\r +X-ERROR: $name->{ERROR}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -354,10 +354,10 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client <{ERROR} = '' unless $name->{ERROR}; my $req =<{A} -X-ERROR: $name->{ERROR} - +GET / HTTP/1.0\r +Host: localhost\r +X-A: $name->{A}\r +X-ERROR: $name->{ERROR}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -293,10 +293,10 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client <{SRV} = '' unless $name->{SRV}; my $req =<{A} -X-AAAA: $name->{AAAA} -X-CNAME: $name->{CNAME} -X-ERROR: $name->{ERROR} -X-SERROR: $name->{SERROR} -X-SRV: $name->{SRV} - +GET / HTTP/1.0\r +Host: localhost\r +X-A: $name->{A}\r +X-AAAA: $name->{AAAA}\r +X-CNAME: $name->{CNAME}\r +X-ERROR: $name->{ERROR}\r +X-SERROR: $name->{SERROR}\r +X-SRV: $name->{SRV}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -515,10 +515,10 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client <{SRV} = '' unless $name->{SRV}; my $req =<{A} -X-ERROR: $name->{ERROR} -X-SRV: $name->{SRV} - +GET / HTTP/1.0\r +Host: localhost\r +X-A: $name->{A}\r +X-ERROR: $name->{ERROR}\r +X-SRV: $name->{SRV}\r +\r EOF my ($gen) = http($req, socket => sock()) =~ /X-Gen: (\d+)/; @@ -289,10 +289,10 @@ sub process_name { Test::Nginx::log_core('||', "$port: response, 200"); print $client <has_version('1.23.0'); my $r = http(<run()->waitforsocket('127.0.0.1:' . port(8081)); ############################################################################### my $s = http(< 1); -GET / HTTP/1.1 -Host: localhost -Upgrade: foo -Connection: Upgrade - +GET / HTTP/1.1\r +Host: localhost\r +Upgrade: foo\r +Connection: Upgrade\r +\r EOF my ($sel, $buf) = IO::Select->new($s); @@ -108,11 +108,11 @@ sub http_daemon { next if $headers eq ''; - print $client <<'EOF'; -HTTP/1.1 101 Switching -Upgrade: foo -Connection: Upgrade - + print $client <<"EOF"; +HTTP/1.1 101 Switching\r +Upgrade: foo\r +Connection: Upgrade\r +\r EOF } diff --git a/xslt.t b/xslt.t index b3b37d00..da95e7d5 100644 --- a/xslt.t +++ b/xslt.t @@ -124,11 +124,11 @@ like(http_get("/x5"), qr!200 OK.*param1=localhost!ms, 'params variable'); unlike(http_get("/x1"), qr!Accept-Ranges!, 'no Accept-Ranges'); like(http(<