view h2_request_body_extra.t @ 1836:74cffa9d4c43

Tests: enabled session reuse via TLS session tickets. This fixes tests with TLSv1.3 enabled when using BoringSSL, since for TLSv1.3 it only supports session reuse via TLS session tickets, and not server-side session cache.
author Maxim Dounin <mdounin@mdounin.ru>
date Thu, 23 Mar 2023 19:50:02 +0300
parents f66266cc82c8
children 236d038dc04a
line wrap: on
line source

#!/usr/bin/perl

# (C) Maxim Dounin
# (C) Nginx, Inc.

# Tests for HTTP/2 protocol with request body, additional tests.

###############################################################################

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/);

$t->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;
        server_name  localhost;

        client_header_buffer_size 1k;
        client_body_buffer_size 2k;

        location / {
            add_header X-Body $request_body;
            add_header X-Body-File $request_body_file;
            proxy_pass http://127.0.0.1:8082;
        }

        location /file {
            client_body_in_file_only on;
            add_header X-Body "$request_body";
            add_header X-Body-File "$request_body_file";
            proxy_pass http://127.0.0.1:8082;
        }

        location /single {
            client_body_in_single_buffer on;
            add_header X-Body "$request_body";
            add_header X-Body-File "$request_body_file";
            proxy_pass http://127.0.0.1:8082;
        }

        location /large {
            client_max_body_size 1k;
            proxy_pass http://127.0.0.1:8082;
        }

        location /unbuf/ {
            add_header X-Unbuf-File "$request_body_file";
            proxy_pass http://127.0.0.1:8081/;
            proxy_request_buffering off;
            proxy_http_version 1.1;
        }
    }

    server {
        listen       127.0.0.1:8082;
        server_name  localhost;
        return 204;
    }
}

EOF

plan(skip_all => 'not yet') unless $t->has_version('1.21.2');
$t->plan(50);

$t->run();

###############################################################################

# below are basic body tests from body.t, slightly
# adapted to HTTP/2, repeated multiple times with variations:
#
# buffered vs. non-buffered, length vs. chunked,
# single frame vs. multiple frames
#
# some does not make sense in HTTP/2 (such as "body in two buffers"), but
# preserved for consistency and due to the fact that proxying via HTTP/1.1
# is used in unbuffered tests

unlike(http2_get('/'), qr/x-body:/ms, 'no body');

like(http2_get_body('/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body');
like(http2_get_body('/', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body in two buffers');
like(http2_get_body('/', '0123456789' x 512),
	qr/x-body-file/ms, 'body in file');
like(read_body_file(http2_get_body('/file', '0123456789' x 512)),
	qr/^(0123456789){512}$/s, 'body in file only');
like(http2_get_body('/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body in single buffer');
like(http2_get_body('/large', '0123456789' x 128),
	qr/:status: 413/, 'body too large');

# without Content-Length header

like(http2_get_body_nolen('/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body nolen');
like(http2_get_body_nolen('/', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body nolen in two buffers');
like(http2_get_body_nolen('/', '0123456789' x 512),
	qr/x-body-file/ms, 'body nolen in file');
like(read_body_file(http2_get_body_nolen('/file', '0123456789' x 512)),
	qr/^(0123456789){512}$/s, 'body nolen in file only');
like(http2_get_body_nolen('/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body nolen in single buffer');
like(http2_get_body_nolen('/large', '0123456789' x 128),
	qr/:status: 413/, 'body nolen too large');

# with multiple frames

like(http2_get_body_multi('/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body multi');
like(http2_get_body_multi('/', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body multi in two buffers');
like(http2_get_body_multi('/', '0123456789' x 512),
	qr/x-body-file/ms, 'body multi in file');
like(read_body_file(http2_get_body_multi('/file', '0123456789' x 512)),
	qr/^(0123456789){512}$/s, 'body multi in file only');
like(http2_get_body_multi('/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body multi in single buffer');
like(http2_get_body_multi('/large', '0123456789' x 128),
	qr/:status: 413/, 'body multi too large');

# with multiple frames and without Content-Length header

like(http2_get_body_multi_nolen('/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body multi nolen');
like(http2_get_body_multi_nolen('/', '0123456789' x 128),
	qr/x-body: (0123456789){128}/ms, 'body multi nolen in two buffers');
like(http2_get_body_multi_nolen('/', '0123456789' x 512),
	qr/x-body-file/ms, 'body multi nolen in file');
like(read_body_file(http2_get_body_multi_nolen('/file', '0123456789' x 512)),
	qr/^(0123456789){512}$/s, 'body multi nolen in file only');
like(http2_get_body_multi_nolen('/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body multi nolen in single buffer');
like(http2_get_body_multi_nolen('/large', '0123456789' x 128),
	qr/:status: 413/, 'body multi nolen too large');

# unbuffered

unlike(http2_get('/unbuf/'), qr/x-body:/ms, 'no body unbuf');

like(http2_get_body('/unbuf/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body unbuf');
like(http2_get_body('/unbuf/', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body unbuf in two buffers');
like(http2_get_body('/unbuf/', '0123456789' x 512),
	qr/(?!.*x-unbuf-file.*)x-body-file/ms, 'body unbuf in file');
like(read_body_file(http2_get_body('/unbuf/file', '0123456789' x 512)),
	qr/^(0123456789){512}$/s, 'body unbuf in file only');
like(http2_get_body('/unbuf/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body unbuf in single buffer');
like(http2_get_body('/unbuf/large', '0123456789' x 128),
	qr/:status: 413/, 'body unbuf too large');

# unbuffered without Content-Length

like(http2_get_body_nolen('/unbuf/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body unbuf nolen');
like(http2_get_body_nolen('/unbuf/', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body unbuf nolen in two buffers');
like(http2_get_body_nolen('/unbuf/', '0123456789' x 512),
	qr/(?!.*x-unbuf-file.*)x-body-file/ms, 'body unbuf nolen in file');
like(read_body_file(http2_get_body_nolen('/unbuf/file', '0123456789' x 512)),
	qr/^(0123456789){512}$/s, 'body unbuf nolen in file only');
like(http2_get_body_nolen('/unbuf/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body unbuf nolen in single buffer');
like(http2_get_body_nolen('/unbuf/large', '0123456789' x 128),
	qr/:status: 413/, 'body unbuf nolen too large');

# unbuffered with multiple frames

like(http2_get_body_multi('/unbuf/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body unbuf multi');
like(http2_get_body_multi('/unbuf/', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body unbuf multi in two buffers');
like(http2_get_body_multi('/unbuf/', '0123456789' x 512),
	qr/(?!.*x-unbuf-file.*)x-body-file/ms, 'body unbuf multi in file');
like(read_body_file(http2_get_body_multi('/unbuf/file', '0123456789' x 512)),
	qr/^(0123456789){512}$/s, 'body unbuf multi in file only');
like(http2_get_body_multi('/unbuf/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms, 'body unbuf multi in single buffer');
like(http2_get_body_multi('/unbuf/large', '0123456789' x 128),
	qr/:status: 413/, 'body unbuf multi too large');

# unbuffered with multiple frames and without Content-Length

like(http2_get_body_multi_nolen('/unbuf/', '0123456789'),
	qr/x-body: 0123456789$/ms, 'body unbuf multi nolen');
like(http2_get_body_multi_nolen('/unbuf/', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms,
	'body unbuf multi nolen in two buffers');
like(http2_get_body_multi_nolen('/unbuf/', '0123456789' x 512),
	qr/(?!.*x-unbuf-file.*)x-body-file/ms,
        'body unbuf multi nolen in file');
like(read_body_file(http2_get_body_multi_nolen('/unbuf/file',
	'0123456789' x 512)), qr/^(0123456789){512}$/s,
	'body unbuf multi nolen in file only');
like(http2_get_body_multi_nolen('/unbuf/single', '0123456789' x 128),
	qr/x-body: (0123456789){128}$/ms,
	'body unbuf multi nolen in single buffer');
like(http2_get_body_multi_nolen('/unbuf/large', '0123456789' x 128),
	qr/:status: 413/, 'body unbuf multi nolen too large');

###############################################################################

sub http2_get {
	my ($uri) = @_;

	my $s = Test::Nginx::HTTP2->new();
	my $sid = $s->new_stream({ path => $uri });
	my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);

	my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;

	return join("\n", map { "$_: " . $frame->{headers}->{$_}; }
		keys %{$frame->{headers}});
}

sub http2_get_body {
	my ($uri, $body) = @_;

	my $s = Test::Nginx::HTTP2->new();
	my $sid = $s->new_stream({ path => $uri, body => $body });
	my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);

	my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;

	return join("\n", map { "$_: " . $frame->{headers}->{$_}; }
			keys %{$frame->{headers}});
}

sub http2_get_body_nolen {
	my ($uri, $body) = @_;

	my $s = Test::Nginx::HTTP2->new();
	my $sid = $s->new_stream({ path => $uri, body_more => 1 });
	$s->h2_body($body);
	my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);

	my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;

	return join("\n", map { "$_: " . $frame->{headers}->{$_}; }
			keys %{$frame->{headers}});
}

sub http2_get_body_multi {
	my ($uri, $body) = @_;

	my $s = Test::Nginx::HTTP2->new();
	my $sid = $s->new_stream({
		headers => [
			{ name => ':method', value => 'GET' },
			{ name => ':scheme', value => 'http' },
			{ name => ':path', value => $uri },
			{ name => ':authority', value => 'localhost' },
			{ name => 'content-length', value => length $body },
		],
		body_more => 1
	});
	for my $b (split //, $body, 10) {
		$s->h2_body($b, { body_more => 1 });
	}
	select undef, undef, undef, 0.1;
	$s->h2_body('');
	my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);

	my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;

	return join("\n", map { "$_: " . $frame->{headers}->{$_}; }
			keys %{$frame->{headers}});
}

sub http2_get_body_multi_nolen {
	my ($uri, $body) = @_;

	my $s = Test::Nginx::HTTP2->new();
	my $sid = $s->new_stream({ path => $uri, body_more => 1 });
	for my $b (split //, $body, 10) {
		$s->h2_body($b, { body_more => 1 });
	}
	select undef, undef, undef, 0.1;
	$s->h2_body('');
	my $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);

	my ($frame) = grep { $_->{type} eq "HEADERS" } @$frames;

	return join("\n", map { "$_: " . $frame->{headers}->{$_}; }
			keys %{$frame->{headers}});
}

sub read_body_file {
	my ($r) = @_;
	return '' unless $r =~ m/x-body-file: (.*)/;
	open FILE, $1
		or return "$!";
	local $/;
	my $content = <FILE>;
	close FILE;
	return $content;
}

###############################################################################