2 Star 0 Fork 0

mirrors_nginx/nginx-tests

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
h2_headers.t 45.58 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
#!/usr/bin/perl
# (C) Sergey Kandaurov
# (C) Nginx, Inc.
# Tests for HTTP/2 headers.
###############################################################################
use warnings;
use strict;
use Test::More;
BEGIN { use FindBin; chdir($FindBin::Bin); }
use lib 'lib';
use Test::Nginx;
use Test::Nginx::HTTP2;
###############################################################################
select STDERR; $| = 1;
select STDOUT; $| = 1;
my $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite/)->plan(103)
->write_file_expand('nginx.conf', <<'EOF');
%%TEST_GLOBALS%%
daemon off;
events {
}
http {
%%TEST_GLOBALS_HTTP%%
server {
listen 127.0.0.1:8080 http2;
listen 127.0.0.1:8081;
listen 127.0.0.1:8082 http2 sndbuf=128;
server_name localhost;
large_client_header_buffers 2 64k;
location / {
add_header X-Sent-Foo $http_x_foo;
add_header X-Referer $http_referer;
return 200;
}
location /frame_size {
add_header X-LongHeader $arg_h;
add_header X-LongHeader $arg_h;
add_header X-LongHeader $arg_h;
alias %%TESTDIR%%/t2.html;
}
location /continuation {
add_header X-LongHeader $arg_h;
add_header X-LongHeader $arg_h;
add_header X-LongHeader $arg_h;
return 200 body;
location /continuation/204 {
return 204;
}
}
location /proxy/ {
add_header X-UC-a $upstream_cookie_a;
add_header X-UC-c $upstream_cookie_c;
proxy_pass http://127.0.0.1:8083/;
proxy_set_header X-Cookie-a $cookie_a;
proxy_set_header X-Cookie-c $cookie_c;
}
location /proxy2/ {
proxy_pass http://127.0.0.1:8081/;
}
location /set-cookie {
add_header Set-Cookie a=b;
add_header Set-Cookie c=d;
return 200;
}
location /cookie {
add_header X-Cookie $http_cookie;
add_header X-Cookie-a $cookie_a;
add_header X-Cookie-c $cookie_c;
return 200;
}
}
server {
listen 127.0.0.1:8084 http2;
server_name localhost;
large_client_header_buffers 4 512;
}
server {
listen 127.0.0.1:8085 http2;
server_name localhost;
large_client_header_buffers 1 512;
}
server {
listen 127.0.0.1:8086 http2;
server_name localhost;
underscores_in_headers on;
add_header X-Sent-Foo $http_x_foo always;
}
server {
listen 127.0.0.1:8087 http2;
server_name localhost;
ignore_invalid_headers off;
add_header X-Sent-Foo $http_x_foo always;
}
}
EOF
$t->run_daemon(\&http_daemon);
# suppress deprecation warning
open OLDERR, ">&", \*STDERR; close STDERR;
$t->run();
open STDERR, ">&", \*OLDERR;
$t->waitforsocket('127.0.0.1:' . port(8083));
# file size is slightly beyond initial window size: 2**16 + 80 bytes
$t->write_file('t1.html',
join('', map { sprintf "X%04dXXX", $_ } (1 .. 8202)));
$t->write_file('t2.html', 'SEE-THIS');
###############################################################################
# 6.1. Indexed Header Field Representation
my $s = Test::Nginx::HTTP2->new();
my $sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 }]});
my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'indexed header field');
# 6.2.1. Literal Header Field with Incremental Indexing
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 1, huff => 0 },
{ name => ':scheme', value => 'http', mode => 1, huff => 0 },
{ name => ':path', value => '/', mode => 1, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 1, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal with indexing');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 1, huff => 1 },
{ name => ':scheme', value => 'http', mode => 1, huff => 1 },
{ name => ':path', value => '/', mode => 1, huff => 1 },
{ name => ':authority', value => 'localhost', mode => 1, huff => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal with indexing - huffman');
# 6.2.1. Literal Header Field with Incremental Indexing -- New Name
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 2, huff => 0 },
{ name => ':scheme', value => 'http', mode => 2, huff => 0 },
{ name => ':path', value => '/', mode => 2, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 2, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal with indexing - new');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 2, huff => 1 },
{ name => ':scheme', value => 'http', mode => 2, huff => 1 },
{ name => ':path', value => '/', mode => 2, huff => 1 },
{ name => ':authority', value => 'localhost', mode => 2, huff => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal with indexing - new huffman');
# 6.2.2. Literal Header Field without Indexing
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 3, huff => 0 },
{ name => ':scheme', value => 'http', mode => 3, huff => 0 },
{ name => ':path', value => '/', mode => 3, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 3, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal without indexing');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 3, huff => 1 },
{ name => ':scheme', value => 'http', mode => 3, huff => 1 },
{ name => ':path', value => '/', mode => 3, huff => 1 },
{ name => ':authority', value => 'localhost', mode => 3, huff => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal without indexing - huffman');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 3, huff => 0 },
{ name => ':scheme', value => 'http', mode => 3, huff => 0 },
{ name => ':path', value => '/', mode => 3, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 3, huff => 0 },
{ name => 'referer', value => 'foo', mode => 3, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200,
'literal without indexing - multibyte index');
is($frame->{headers}->{'x-referer'}, 'foo',
'literal without indexing - multibyte index value');
# 6.2.2. Literal Header Field without Indexing -- New Name
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 4, huff => 0 },
{ name => ':scheme', value => 'http', mode => 4, huff => 0 },
{ name => ':path', value => '/', mode => 4, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 4, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal without indexing - new');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 4, huff => 1 },
{ name => ':scheme', value => 'http', mode => 4, huff => 1 },
{ name => ':path', value => '/', mode => 4, huff => 1 },
{ name => ':authority', value => 'localhost', mode => 4, huff => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200,
'literal without indexing - new huffman');
# 6.2.3. Literal Header Field Never Indexed
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 5, huff => 0 },
{ name => ':scheme', value => 'http', mode => 5, huff => 0 },
{ name => ':path', value => '/', mode => 5, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 5, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal never indexed');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 5, huff => 1 },
{ name => ':scheme', value => 'http', mode => 5, huff => 1 },
{ name => ':path', value => '/', mode => 5, huff => 1 },
{ name => ':authority', value => 'localhost', mode => 5, huff => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal never indexed - huffman');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 5, huff => 0 },
{ name => ':scheme', value => 'http', mode => 5, huff => 0 },
{ name => ':path', value => '/', mode => 5, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 5, huff => 0 },
{ name => 'referer', value => 'foo', mode => 5, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200,
'literal never indexed - multibyte index');
is($frame->{headers}->{'x-referer'}, 'foo',
'literal never indexed - multibyte index value');
# 6.2.3. Literal Header Field Never Indexed -- New Name
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 6, huff => 0 },
{ name => ':scheme', value => 'http', mode => 6, huff => 0 },
{ name => ':path', value => '/', mode => 6, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 6, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal never indexed - new');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 6, huff => 1 },
{ name => ':scheme', value => 'http', mode => 6, huff => 1 },
{ name => ':path', value => '/', mode => 6, huff => 1 },
{ name => ':authority', value => 'localhost', mode => 6, huff => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 200, 'literal never indexed - new huffman');
# reuse literal with multibyte indexing
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'referer', value => 'foo', mode => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-referer'}, 'foo', 'value with indexing - new');
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 0 },
{ name => 'referer', value => 'foo', mode => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-referer'}, 'foo', 'value with indexing - indexed');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x-foo', value => 'X-Bar', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, 'X-Bar', 'name with indexing - new');
# reuse literal with multibyte indexing - reused name
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 0 },
{ name => 'x-foo', value => 'X-Bar', mode => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, 'X-Bar', 'name with indexing - indexed');
# reuse literal with multibyte indexing - reused name only
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 0 },
{ name => 'x-foo', value => 'X-Baz', mode => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, 'X-Baz',
'name with indexing - indexed name');
# response header field with characters not suitable for huffman encoding
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x-foo', value => '{{{{{', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, '{{{{{', 'rare chars');
like($s->{headers}, qr/\Q{{{{{/, 'rare chars - no huffman encoding');
# response header field with huffman encoding
# NB: implementation detail, not obligated
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x-foo', value => 'aaaaa', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, 'aaaaa', 'well known chars');
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);
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x-foo', value => $field, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, $field, 'all chars');
unlike($s->{headers}, qr/abcde/, 'all chars - huffman encoding');
# 6.3. Dynamic Table Size Update
# remove some indexed headers from the dynamic table
# by maintaining dynamic table space only for index 0
# 'x-foo' has index 0, and 'referer' has index 1
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'referer', value => 'foo', mode => 1 },
{ name => 'x-foo', value => 'X-Bar', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
$sid = $s->new_stream({ table_size => 61, headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => 'x-foo', value => 'X-Bar', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
isnt($frame, undef, 'updated table size - remaining index');
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'referer', value => 'foo', mode => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "GOAWAY" } @$frames;
is($frame->{code}, 0x9, 'invalid index');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'unknown', value => 'foo', mode => 3 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "GOAWAY" } @$frames;
is($frame->{code}, 0x9, 'invalid index in literal header field');
# 5.4.1. Connection Error Handling
# An endpoint that encounters a connection error SHOULD first send a
# GOAWAY frame <..>
($frame) = grep { $_->{type} eq "GOAWAY" } @$frames;
ok($frame, 'invalid index - GOAWAY');
# RFC 7541, 2.3.3. Index Address Space
# Indices strictly greater than the sum of the lengths of both tables
# MUST be treated as a decoding error.
# 4.3. Header Compression and Decompression
# A decoding error in a header block MUST be treated
# as a connection error of type COMPRESSION_ERROR.
is($frame->{last_sid}, $sid, 'invalid index - GOAWAY last stream');
is($frame->{code}, 9, 'invalid index - GOAWAY COMPRESSION_ERROR');
# HPACK zero index
# RFC 7541, 6.1 Indexed Header Field Representation
# The index value of 0 is not used. It MUST be treated as a decoding
# error if found in an indexed header field representation.
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => '', value => '', mode => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
ok($frame, 'zero index - GOAWAY');
is($frame->{code}, 9, 'zero index - GOAWAY COMPRESSION_ERROR');
# invalid table size update
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ table_size => 4097, headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => 'x-foo', value => 'X-Bar', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "GOAWAY" } @$frames;
ok($frame, 'invalid table size - GOAWAY');
is($frame->{last_sid}, $sid, 'invalid table size - GOAWAY last stream');
is($frame->{code}, 9, 'invalid table size - GOAWAY COMPRESSION_ERROR');
# request header field with multiple values
# 8.1.2.5. Compressing the Cookie Header Field
# To allow for better compression efficiency, the Cookie header field
# MAY be split into separate header fields <..>.
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/cookie', mode => 2 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'cookie', value => 'a=b', mode => 2},
{ name => 'cookie', value => 'c=d', mode => 2}]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-cookie-a'}, 'b',
'multiple request header fields - cookie');
is($frame->{headers}->{'x-cookie-c'}, 'd',
'multiple request header fields - cookie 2');
is($frame->{headers}->{'x-cookie'}, 'a=b; c=d',
'multiple request header fields - semi-colon');
# request header field with multiple values to HTTP backend
# 8.1.2.5. Compressing the Cookie Header Field
# these MUST be concatenated into a single octet string
# using the two-octet delimiter of 0x3B, 0x20 (the ASCII string "; ")
# before being passed into a non-HTTP/2 context, such as an HTTP/1.1
# connection <..>
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/proxy/cookie', mode => 2 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'cookie', value => 'a=b', mode => 2 },
{ name => 'cookie', value => 'c=d', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-cookie'}, 'a=b; c=d',
'multiple request header fields proxied - semi-colon');
is($frame->{headers}->{'x-sent-cookie2'}, '',
'multiple request header fields proxied - dublicate cookie');
is($frame->{headers}->{'x-sent-cookie-a'}, 'b',
'multiple request header fields proxied - cookie 1');
is($frame->{headers}->{'x-sent-cookie-c'}, 'd',
'multiple request header fields proxied - cookie 2');
# response header field with multiple values
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ path => '/set-cookie' });
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'set-cookie'}[0], 'a=b',
'multiple response header fields - cookie');
is($frame->{headers}->{'set-cookie'}[1], 'c=d',
'multiple response header fields - cookie 2');
# response header field with multiple values from HTTP backend
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ path => '/proxy/set-cookie' });
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'set-cookie'}[0], 'a=b',
'multiple response header proxied - cookie');
is($frame->{headers}->{'set-cookie'}[1], 'c=d',
'multiple response header proxied - cookie 2');
is($frame->{headers}->{'x-uc-a'}, 'b',
'multiple response header proxied - upstream cookie');
is($frame->{headers}->{'x-uc-c'}, 'd',
'multiple response header proxied - upstream cookie 2');
# CONTINUATION in response
# put three long header fields (not less than SETTINGS_MAX_FRAME_SIZE/2)
# to break header block into separate frames, one such field per frame
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ path => '/continuation?h=' . 'x' x 2**13 });
$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);
my @data = grep { $_->{type} =~ "HEADERS|CONTINUATION" } @$frames;
my $data = $data[-1];
is(@{$data->{headers}{'x-longheader'}}, 3,
'response CONTINUATION - headers');
is($data->{headers}{'x-longheader'}[0], 'x' x 2**13,
'response CONTINUATION - header 1');
is($data->{headers}{'x-longheader'}[1], 'x' x 2**13,
'response CONTINUATION - header 2');
is($data->{headers}{'x-longheader'}[2], 'x' x 2**13,
'response CONTINUATION - header 3');
@data = sort { $a <=> $b } map { $_->{length} } @data;
cmp_ok($data[-1], '<=', 2**14, 'response CONTINUATION - max frame size');
# same but without response DATA frames
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ path => '/continuation/204?h=' . 'x' x 2**13 });
$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);
@data = grep { $_->{type} =~ "HEADERS|CONTINUATION" } @$frames;
$data = $data[-1];
is(@{$data->{headers}{'x-longheader'}}, 3,
'no body CONTINUATION - headers');
is($data->{headers}{'x-longheader'}[0], 'x' x 2**13,
'no body CONTINUATION - header 1');
is($data->{headers}{'x-longheader'}[1], 'x' x 2**13,
'no body CONTINUATION - header 2');
is($data->{headers}{'x-longheader'}[2], 'x' x 2**13,
'no body CONTINUATION - header 3');
@data = sort { $a <=> $b } map { $_->{length} } @data;
cmp_ok($data[-1], '<=', 2**14, 'no body CONTINUATION - max frame size');
# response header block is always split by SETTINGS_MAX_FRAME_SIZE
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ path => '/continuation?h=' . 'x' x 2**15 });
$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);
@data = grep { $_->{type} =~ "HEADERS|CONTINUATION" } @$frames;
@data = sort { $a <=> $b } map { $_->{length} } @data;
cmp_ok($data[-1], '<=', 2**14, 'response header frames limited');
# response header frame sent in parts
$s = Test::Nginx::HTTP2->new(port(8082));
$s->h2_settings(0, 0x5 => 2**17);
$sid = $s->new_stream({ path => '/frame_size?h=' . 'x' x 2**15 });
$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
ok($frame, 'response header - parts');
SKIP: {
skip 'response header failed', 1 unless $frame;
is(length join('', @{$frame->{headers}->{'x-longheader'}}), 98304,
'response header - headers');
}
# response header block split and sent in parts
$s = Test::Nginx::HTTP2->new(port(8082));
$sid = $s->new_stream({ path => '/continuation?h=' . 'x' x 2**15 });
$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);
@data = grep { $_->{type} =~ "HEADERS|CONTINUATION" } @$frames;
ok(@data, 'response header split');
SKIP: {
skip 'response header split failed', 2 unless @data;
my ($lengths) = sort { $b <=> $a } map { $_->{length} } @data;
cmp_ok($lengths, '<=', 16384, 'response header split - max size');
is(length join('', @{$data[-1]->{headers}->{'x-longheader'}}), 98304,
'response header split - headers');
}
# max_field_size - header field name
$s = Test::Nginx::HTTP2->new(port(8084));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x' x 511, value => 'value', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'field name size less');
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x' x 511, value => 'value', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'field name size second');
$s = Test::Nginx::HTTP2->new(port(8084));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x' x 512, value => 'value', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'field name size equal');
$s = Test::Nginx::HTTP2->new(port(8084));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x' x 513, value => 'value', mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
is($frame, undef, 'field name size greater');
# max_field_size - header field value
$s = Test::Nginx::HTTP2->new(port(8084));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'name', value => 'x' x 511, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'field value size less');
$s = Test::Nginx::HTTP2->new(port(8084));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'name', value => 'x' x 511, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'field value size equal');
$s = Test::Nginx::HTTP2->new(port(8084));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'name', value => 'x' x 513, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
is($frame, undef, 'field value size greater');
# max_header_size
$s = Test::Nginx::HTTP2->new(port(8085));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'longname', value => 'x' x 450, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'header size less');
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'longname', value => 'x' x 450, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'header size second');
$s = Test::Nginx::HTTP2->new(port(8085));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'longname', value => 'x' x 451, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'header size equal');
$s = Test::Nginx::HTTP2->new(port(8085));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'longname', value => 'x' x 452, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
is($frame, undef, 'header size greater');
# header size is based on (decompressed) header list
# two extra 1-byte indices would otherwise fit in max_header_size
$s = Test::Nginx::HTTP2->new(port(8085));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'longname', value => 'x' x 400, mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'header size new index');
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'longname', value => 'x' x 400, mode => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'DATA' } @$frames;
ok($frame, 'header size indexed');
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/t2.html', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'longname', value => 'x' x 400, mode => 0 },
{ name => 'longname', value => 'x' x 400, mode => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;
is($frame->{code}, 0xb, 'header size indexed greater');
# HPACK table boundary
$s = Test::Nginx::HTTP2->new();
$s->read(all => [{ sid => $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => '', mode => 0 },
{ name => 'x' x 2016, value => 'x' x 2048, mode => 2 }]}), fin => 1 }]);
$frames = $s->read(all => [{ sid => $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => '', mode => 0 },
{ name => 'x' x 2016, value => 'x' x 2048, mode => 0 }]}), fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
ok($frame, 'HPACK table boundary');
$s->read(all => [{ sid => $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => '', mode => 0 },
{ name => 'x' x 33, value => 'x' x 4031, mode => 2 }]}), fin => 1 }]);
$frames = $s->read(all => [{ sid => $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => '', mode => 0 },
{ name => 'x' x 33, value => 'x' x 4031, mode => 0 }]}), fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
ok($frame, 'HPACK table boundary - header field name');
$s->read(all => [{ sid => $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => '', mode => 0 },
{ name => 'x', value => 'x' x 64, mode => 2 }]}), fin => 1 }]);
$frames = $s->read(all => [{ sid => $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => '', mode => 0 },
{ name => 'x', value => 'x' x 64, mode => 0 }]}), fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
ok($frame, 'HPACK table boundary - header field value');
# ensure that request header field value with newline doesn't get split
#
# 10.3. Intermediary Encapsulation Attacks
# Any request or response that contains a character not permitted
# in a header field value MUST be treated as malformed.
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/proxy2/', mode => 1 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x-foo', value => "x-bar\r\nreferer:see-this", mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
# 10.3. Intermediary Encapsulation Attacks
# An intermediary therefore cannot translate an HTTP/2 request or response
# containing an invalid field name into an HTTP/1.1 message.
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
isnt($frame->{headers}->{'x-referer'}, 'see-this', 'newline in request header');
TODO: {
local $TODO = 'not yet' unless $t->has_version('1.23.4');
is($frame->{headers}->{':status'}, 400, 'newline in request header - bad request');
}
# invalid header name as seen with underscore should not lead to ignoring rest
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x_foo', value => "x-bar", mode => 2 },
{ name => 'referer', value => "see-this", mode => 1 }]});
$frames = $s->read(all => [{ type => 'HEADERS' }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-referer'}, 'see-this', 'after invalid header name');
# other invalid header name characters as seen with ':'
TODO: {
local $TODO = 'not yet' unless $t->has_version('1.23.4');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x:foo', value => "x-bar", mode => 2 },
{ name => 'referer', value => "see-this", mode => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 400, 'colon in header name');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x foo', value => "bar", mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 400, 'space in header name');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => "foo\x02", value => "bar", mode => 2 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 400, 'control in header name');
}
# header name with underscore - underscores_in_headers on
$s = Test::Nginx::HTTP2->new(port(8086));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x_foo', value => "x-bar", mode => 2 },
{ name => 'referer', value => "see-this", mode => 1 }]});
$frames = $s->read(all => [{ type => 'HEADERS' }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, 'x-bar',
'underscore in header name - underscores_in_headers');
# header name with underscore - ignore_invalid_headers off
$s = Test::Nginx::HTTP2->new(port(8087));
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 },
{ name => 'x_foo', value => "x-bar", mode => 2 },
{ name => 'referer', value => "see-this", mode => 1 }]});
$frames = $s->read(all => [{ type => 'HEADERS' }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-sent-foo'}, 'x-bar',
'underscore in header name - ignore_invalid_headers');
# missing mandatory request header ':scheme'
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => 'localhost', mode => 1 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 400, 'incomplete headers');
# empty request header ':authority'
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ headers => [
{ name => ':method', value => 'GET', mode => 0 },
{ name => ':scheme', value => 'http', mode => 0 },
{ name => ':path', value => '/', mode => 0 },
{ name => ':authority', value => '', mode => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 400, 'empty authority');
# client sent invalid :path header
$sid = $s->new_stream({ path => 't1.html' });
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 400, 'invalid path');
$sid = $s->new_stream({ path => "/t1.html\x02" });
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{':status'}, 400, 'invalid path control');
# ngx_http_v2_parse_int() error handling
# NGX_ERROR
$s = Test::Nginx::HTTP2->new();
{
local $SIG{PIPE} = 'IGNORE';
syswrite($s->{socket}, pack("x2C3NC", 1, 0x1, 5, 1, 0xff));
}
$frames = $s->read(all => [{ type => "GOAWAY" }]);
($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;
is($frame->{code}, 0x6, 'invalid index length');
$s = Test::Nginx::HTTP2->new();
{
local $SIG{PIPE} = 'IGNORE';
syswrite($s->{socket}, pack("x2C3NC2", 2, 0x1, 5, 1, 0x42, 0xff));
}
$frames = $s->read(all => [{ type => "GOAWAY" }]);
($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;
is($frame->{code}, 0x6, 'invalid literal length');
# NGX_DECLINED
$s = Test::Nginx::HTTP2->new();
{
local $SIG{PIPE} = 'IGNORE';
syswrite($s->{socket}, pack("x2C3NN", 5, 0x1, 5, 1, 0xffffffff));
}
$frames = $s->read(all => [{ type => "GOAWAY" }]);
($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;
is($frame->{code}, 0x9, 'too long index');
$s = Test::Nginx::HTTP2->new();
{
local $SIG{PIPE} = 'IGNORE';
syswrite($s->{socket}, pack("x2C3NCN", 6, 0x1, 5, 1, 0x42, 0xffffffff));
}
$frames = $s->read(all => [{ type => "GOAWAY" }]);
($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;
is($frame->{code}, 0x9, 'too long literal');
# NGX_AGAIN
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ split => [35], split_delay => 1.1, headers => [
{ name => ':method', value => 'GET', mode => 3, huff => 0 },
{ name => ':scheme', value => 'http', mode => 3, huff => 0 },
{ name => ':path', value => '/', mode => 3, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 3, huff => 0 },
{ name => 'referer', value => 'foo', mode => 3, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-referer'}, 'foo', 'header split index');
$s = Test::Nginx::HTTP2->new();
$sid = $s->new_stream({ split => [37], split_delay => 1.1, headers => [
{ name => ':method', value => 'GET', mode => 3, huff => 0 },
{ name => ':scheme', value => 'http', mode => 3, huff => 0 },
{ name => ':path', value => '/', mode => 3, huff => 0 },
{ name => ':authority', value => 'localhost', mode => 3, huff => 0 },
{ name => 'referer', value => '1234' x 32, mode => 3, huff => 0 }]});
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
is($frame->{headers}->{'x-referer'}, '1234' x 32, 'header split field length');
###############################################################################
sub http_daemon {
my $server = IO::Socket::INET->new(
Proto => 'tcp',
LocalHost => '127.0.0.1',
LocalPort => port(8083),
Listen => 5,
Reuse => 1
)
or die "Can't create listening socket: $!\n";
local $SIG{PIPE} = 'IGNORE';
while (my $client = $server->accept()) {
$client->autoflush(1);
my $headers = '';
my $uri = '';
while (<$client>) {
$headers .= $_;
last if (/^\x0d?\x0a?$/);
}
next if $headers eq '';
$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
if ($uri eq '/cookie') {
my ($cookie, $cookie2) = $headers =~ /Cookie: (.+)/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;
print $client <<EOF;
HTTP/1.1 200 OK
Connection: close
X-Sent-Cookie: $cookie
X-Sent-Cookie2: $cookie2
X-Sent-Cookie-a: $cookie_a
X-Sent-Cookie-c: $cookie_c
EOF
} elsif ($uri eq '/set-cookie') {
print $client <<EOF;
HTTP/1.1 200 OK
Connection: close
Set-Cookie: a=b
Set-Cookie: c=d
EOF
}
} continue {
close $client;
}
}
###############################################################################
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors_nginx/nginx-tests.git
git@gitee.com:mirrors_nginx/nginx-tests.git
mirrors_nginx
nginx-tests
nginx-tests
master

搜索帮助