summaryrefslogtreecommitdiffstats
path: root/debian/vendor-h2o/t/50mruby
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/vendor-h2o/t/50mruby-acl.t39
-rw-r--r--debian/vendor-h2o/t/50mruby-dos-detector.t111
-rw-r--r--debian/vendor-h2o/t/50mruby-htpasswd.t48
-rw-r--r--debian/vendor-h2o/t/50mruby-http-request.t269
-rw-r--r--debian/vendor-h2o/t/50mruby.t541
-rw-r--r--debian/vendor-h2o/t/50mruby/hello.rb3
-rw-r--r--debian/vendor-h2o/t/50mruby/index.html1
7 files changed, 1012 insertions, 0 deletions
diff --git a/debian/vendor-h2o/t/50mruby-acl.t b/debian/vendor-h2o/t/50mruby-acl.t
new file mode 100644
index 0000000..0c84a68
--- /dev/null
+++ b/debian/vendor-h2o/t/50mruby-acl.t
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+use Test::More;
+use Test::Exception;
+use t::Util;
+
+plan skip_all => 'mruby support is off'
+ unless server_features()->{mruby};
+
+subtest "invalid configuration 1" => sub {
+ throws_ok sub {
+ spawn_h2o(<< 'EOT');
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ acl { respond(403) }
+ acl { respond(200) }
+EOT
+ }, qr/server failed to start/, 'acl cannot be called more than once';
+};
+
+subtest "invalid configuration 2" => sub {
+ throws_ok sub {
+ spawn_h2o(<< 'EOT');
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ acl { respond(403) }
+ proc {|env| [200, {}, []]}
+EOT
+ }, qr/server failed to start/, 'acl configuration is ignored';
+};
+
+done_testing();
diff --git a/debian/vendor-h2o/t/50mruby-dos-detector.t b/debian/vendor-h2o/t/50mruby-dos-detector.t
new file mode 100644
index 0000000..6b7cdc5
--- /dev/null
+++ b/debian/vendor-h2o/t/50mruby-dos-detector.t
@@ -0,0 +1,111 @@
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+use Test::More;
+use t::Util;
+
+plan skip_all => 'mruby support is off'
+ unless server_features()->{mruby};
+
+plan skip_all => 'curl not found'
+ unless prog_exists('curl');
+
+subtest "default callback" => sub {
+ my $server = spawn_h2o(<< 'EOT');
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ require "share/h2o/mruby/dos_detector.rb"
+ DoSDetector.new({
+ :strategy => DoSDetector::CountingStrategy.new({ :period => 1000, :threshold => 2, :ban_period => 1 << 32 }),
+ })
+ mruby.handler:
+ Proc.new do |env|
+ [200, {}, []]
+ end
+EOT
+ subtest "forbidden" => sub {
+ {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }s, "status";
+ }
+ {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 403 }s, "status";
+ is $body, "Forbidden", "content";
+ }
+ };
+};
+
+subtest "fallthrough callback" => sub {
+ my $server = spawn_h2o(<< 'EOT');
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ require "share/h2o/mruby/dos_detector.rb"
+ DoSDetector.new({
+ :strategy => DoSDetector::CountingStrategy.new({ :period => 1000, :threshold => 2, :ban_period => 1 << 32 }),
+ :callback => DoSDetector.fallthrough_callback,
+ })
+ mruby.handler:
+ Proc.new do |env|
+ [200, {}, ["DOS_COUNT is ", env["DOS_COUNT"], ", DOS_IP is ", env["DOS_IP"]]]
+ end
+EOT
+ subtest "success" => sub {
+ {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }s, "status";
+ }
+ {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }s, "status";
+ is $body, "DOS_COUNT is 2, DOS_IP is 127.0.0.1", "content";
+ }
+ };
+};
+
+subtest "customized callback" => sub {
+ my $server = spawn_h2o(<< 'EOT');
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ require "share/h2o/mruby/dos_detector.rb"
+ DoSDetector.new({
+ :strategy => DoSDetector::CountingStrategy.new({ :period => 1000, :threshold => 2, :ban_period => 1 << 32 }),
+ :callback => Proc.new do |env, detected, ip|
+ if detected
+ [503, {}, ["Service Unavailable"]]
+ else
+ [399, {}, []]
+ end
+ end
+ })
+ mruby.handler:
+ Proc.new do |env|
+ [200, {}, []]
+ end
+EOT
+ subtest "success" => sub {
+ {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }s, "status";
+ }
+ {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 503 }s, "status";
+ is $body, "Service Unavailable", "content";
+ }
+ };
+};
+
+done_testing();
diff --git a/debian/vendor-h2o/t/50mruby-htpasswd.t b/debian/vendor-h2o/t/50mruby-htpasswd.t
new file mode 100644
index 0000000..31cbf59
--- /dev/null
+++ b/debian/vendor-h2o/t/50mruby-htpasswd.t
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+use Test::More;
+use t::Util;
+
+plan skip_all => 'mruby support is off'
+ unless server_features()->{mruby};
+
+plan skip_all => 'curl not found'
+ unless prog_exists('curl');
+
+subtest "basic" => sub {
+ my $server = spawn_h2o(<< 'EOT');
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ require "share/h2o/mruby/htpasswd.rb"
+ Htpasswd.new("t/assets/.htpasswd", "protected space")
+ mruby.handler:
+ Proc.new do |env|
+ [200, {}, ["hello ", env["REMOTE_USER"], "\n"]]
+ end
+EOT
+ subtest "no-auth" => sub {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 401 }s, "status";
+ like $headers, qr{\r\nwww-authenticate: basic realm="protected space"\r}is, "www-authenticate header";
+ unlike $body, qr/hello/;
+ };
+
+ subtest "fail" => sub {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://aaa:aaa\@127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 401 }s, "status";
+ like $headers, qr{\r\nwww-authenticate: basic realm="protected space"\r}is, "www-authenticate header";
+ unlike $body, qr/hello/;
+ };
+
+ subtest "success" => sub {
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://dankogai:kogaidan\@127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }s, "status";
+ is $body, "hello dankogai\n", "content";
+ };
+};
+
+done_testing();
diff --git a/debian/vendor-h2o/t/50mruby-http-request.t b/debian/vendor-h2o/t/50mruby-http-request.t
new file mode 100644
index 0000000..991d34b
--- /dev/null
+++ b/debian/vendor-h2o/t/50mruby-http-request.t
@@ -0,0 +1,269 @@
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+use Net::EmptyPort qw(empty_port check_port);
+use Test::More;
+use t::Util;
+
+plan skip_all => 'mruby support is off'
+ unless server_features()->{mruby};
+
+plan skip_all => 'curl not found'
+ unless prog_exists('curl');
+
+plan skip_all => 'plackup not found'
+ unless prog_exists('plackup');
+
+plan skip_all => 'Starlet not found'
+ unless system('perl -MStarlet /dev/null > /dev/null 2>&1') == 0;
+
+my $upstream_hostport = "127.0.0.1:@{[empty_port()]}";
+
+sub create_upstream {
+ my @args = (
+ qw(plackup -s Starlet --keepalive-timeout 100 --access-log /dev/null --listen),
+ $upstream_hostport,
+ ASSETS_DIR . "/upstream.psgi",
+ );
+ spawn_server(
+ argv => \@args,
+ is_ready => sub {
+ $upstream_hostport =~ /:([0-9]+)$/s
+ or die "failed to extract port number";
+ check_port($1);
+ },
+ );
+};
+
+my $server = spawn_h2o(sub {
+ my ($port, $tls_port) = @_;
+ return << "EOT";
+proxy.timeout.io: 1000
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ headers = {}
+ env.each do |key, value|
+ if /^HTTP_/.match(key)
+ headers[\$'] = value
+ end
+ end
+ headers["x-h2o-mruby"] = "1"
+ http_request("http://$upstream_hostport#{env["PATH_INFO"]}#{env["QUERY_STRING"]}", {
+ method: env["REQUEST_METHOD"],
+ headers: headers,
+ body: env["rack.input"],
+ }).join
+ end
+ /as_str:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {}, [http_request("http://$upstream_hostport/index.txt").join[2].join]]
+ end
+ /cl:
+ mruby.handler: |
+ Proc.new do |env|
+ if !/^\\/([0-9]+)/.match(env["PATH_INFO"])
+ raise "failed to parse PATH_INFO"
+ end
+ cl = \$1
+ body = ["abc", "def", "ghi", "jkl", "mno"]
+ if \$'.length != 0
+ class T
+ def initialize(a)
+ \@a = a
+ end
+ def each(&b)
+ \@a.each(&b)
+ end
+ end
+ body = T.new(body)
+ end
+ [200, {"content-length" => cl}, body]
+ end
+ /esi:
+ mruby.handler: |
+ class ESIResponse
+ def initialize(input)
+ \@parts = input.split /(<esi:include +src=".*?" *\\/>)/
+ \@parts.each_with_index do |part, index|
+ if /^<esi:include +src=" *(.*?) *"/.match(part)
+ \@parts[index] = http_request("http://$upstream_hostport/#{\$1}")
+ end
+ end
+ end
+ def each(&block)
+ \@parts.each do |part|
+ if part.kind_of? String
+ block.call(part)
+ else
+ part.join[2].each(&block)
+ end
+ end
+ end
+ end
+ Proc.new do |env|
+ resp = http_request("http://$upstream_hostport/esi.html").join
+ resp[2] = ESIResponse.new(resp[2].join)
+ resp
+ end
+ /fast-path-partial:
+ mruby.handler: |
+ Proc.new do |env|
+ resp = http_request("http://$upstream_hostport/streaming-body").join
+ resp[2].each do |x|
+ break
+ end
+ resp
+ end
+ /async-delegate:
+ mruby.handler: |
+ Proc.new do |env|
+ resp = http_request("http://$upstream_hostport#{env["PATH_INFO"]}").join
+ if resp[0] != 200
+ resp = [399, {}, []]
+ end
+ resp
+ end
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {}, ["delegated!"]]
+ end
+EOT
+});
+
+run_with_curl($server, sub {
+ my ($proto, $port, $curl_cmd) = @_;
+ $curl_cmd .= ' --silent --dump-header /dev/stderr';
+ subtest "connection-error" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/index.txt");
+ like $headers, qr{HTTP/[^ ]+ 500\s}is;
+ };
+ my $upstream = create_upstream();
+ subtest "get" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/index.txt");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, "hello\n";
+ };
+ subtest "headers" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/echo-headers");
+ like $headers, qr{^HTTP/[^ ]+ 200\s}is;
+ like $body, qr{^host: $upstream_hostport$}im;
+ unlike $body, qr{^host: 127.0.0.1:$port$}im;
+ like $body, qr{^user-agent: *curl/}im;
+ like $body, qr{^accept: *\*/\*$}im;
+ like $body, qr{^x-h2o-mruby:}im;
+ };
+ subtest "post" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd --data 'hello world' $proto://127.0.0.1:$port/echo");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, 'hello world';
+ };
+ subtest "slow-chunked" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/streaming-body");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, (join "", 1..30);
+ };
+ subtest "as_str" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/as_str/");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, "hello\n";
+ };
+ subtest "content-length" => sub {
+ subtest "non-chunked" => sub {
+ for my $i (0..15) {
+ subtest "cl=$i" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/cl/$i");
+ like $headers, qr{^HTTP/[^ ]+ 200\s.*\ncontent-length:\s*$i\r}is;
+ is $body, substr "abcdefghijklmno", 0, $i;
+ }
+ };
+ for my $i (16..30) {
+ subtest "cl=$i" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/cl/$i");
+ like $headers, qr{^HTTP/[^ ]+ 200\s.*\ncontent-length:\s*15\r}is;
+ is $body, "abcdefghijklmno";
+ }
+ };
+ };
+ subtest "chunked" => sub {
+ for my $i (0..30) {
+ subtest "cl=$i" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/cl/$i/chunked");
+ like $headers, qr{^HTTP/[^ ]+ 200\s.*\ncontent-length:\s*$i\r}is;
+ is $body, substr "abcdefghijklmno", 0, $i;
+ }
+ };
+ };
+ };
+ subtest "esi" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/esi/");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, "Hello to the world, from H2O!\n";
+ };
+ subtest "fast-path-partial" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/fast-path-partial/");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, join "", 2..30;
+ };
+ subtest "async-delegate" => sub {
+ subtest "non-delegated" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/async-delegate/index.txt");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, "hello\n";
+ };
+ subtest "delegated" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd $proto://127.0.0.1:$port/async-delegate/notfound");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, "delegated!";
+ };
+ };
+});
+
+subtest 'empty body' => sub {
+ my $upstream = create_upstream();
+ my $server = spawn_h2o(sub {
+ my ($port, $tls_port) = @_;
+ return << "EOT";
+hosts:
+ default:
+ paths:
+ /no-content:
+ mruby.handler: |
+ proc {|env|
+ resp = http_request("http://$upstream_hostport/no-content").join
+ resp[2] = [resp[2].join]
+ resp
+ }
+ /head:
+ mruby.handler: |
+ proc {|env|
+ resp = http_request("http://$upstream_hostport/index.txt", { :method => 'HEAD' }).join
+ resp[2] = [resp[2].join]
+ resp
+ }
+EOT
+ });
+
+ run_with_curl($server, sub {
+ my ($proto, $port, $curl_cmd) = @_;
+ $curl_cmd .= ' --silent --dump-header /dev/stderr';
+
+ subtest "no content" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd -m 1 $proto://127.0.0.1:$port/no-content");
+ like $headers, qr{HTTP/[^ ]+ 204\s}is;
+ is $body, "";
+ };
+
+ subtest "head" => sub {
+ my ($headers, $body) = run_prog("$curl_cmd -m 1 $proto://127.0.0.1:$port/head");
+ like $headers, qr{HTTP/[^ ]+ 200\s}is;
+ is $body, "";
+ };
+ });
+};
+
+done_testing();
diff --git a/debian/vendor-h2o/t/50mruby.t b/debian/vendor-h2o/t/50mruby.t
new file mode 100644
index 0000000..d93f17e
--- /dev/null
+++ b/debian/vendor-h2o/t/50mruby.t
@@ -0,0 +1,541 @@
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+use File::Temp qw(tempdir);
+use Test::More;
+use t::Util;
+
+plan skip_all => 'mruby support is off'
+ unless server_features()->{mruby};
+
+plan skip_all => 'curl not found'
+ unless prog_exists('curl');
+
+subtest "handler-file" => sub {
+ my $server = spawn_h2o(<< 'EOT');
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler-file: t/50mruby/hello.rb
+EOT
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ is $body, "hello from h2o_mruby\n";
+ like $headers, qr{^HTTP/1\.1 200 OK\r\n}s;
+ like $headers, qr{^content-type: text/plain; charset=utf-8\r$}im;
+};
+
+subtest "basic" => sub {
+ my $server = spawn_h2o(<< 'EOT');
+hosts:
+ default:
+ paths:
+ /inline:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {}, ["hello from h2o_mruby\n"]]
+ end
+ /return-404:
+ mruby.handler: |
+ Proc.new do |env|
+ [404, {}, ["not found"]]
+ end
+ file.dir: examples/doc_root
+ /fallthru:
+ mruby.handler: |
+ Proc.new do |env|
+ [399, {}, []]
+ end
+ file.dir: t/50mruby/
+ /echo:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {}, [JSON.generate(env)]]
+ end
+ /headers:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {"foo" => "123\n456", "bar" => "baz"}, []]
+ end
+ /headers-each:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, [["content-type", "text/plain"], ["hello", "world"]], []]
+ end
+EOT
+ my $fetch = sub {
+ my $path = shift;
+ run_prog("curl --silent -A h2o_mruby_test --dump-header /dev/stderr http://127.0.0.1:$server->{port}$path");
+ };
+ my ($headers, $body) = $fetch->("/inline/");
+ is $body, "hello from h2o_mruby\n", "inline";
+ subtest "return-404" => sub {
+ ($headers, $body) = $fetch->("/return-404/");
+ like $headers, qr{^HTTP/1\.1 404 }is;
+ is $body, "not found";
+ };
+ subtest "fallthru" => sub {
+ ($headers, $body) = $fetch->("/fallthru/");
+ like $headers, qr{^HTTP/1\.1 200 OK\r\n}is;
+ is md5_hex($body), md5_file("t/50mruby/index.html");
+ };
+ subtest "echo" => sub {
+ ($headers, $body) = $fetch->("/echo/abc?def");
+ like $body, qr{"REQUEST_METHOD":"GET"}, "REQUEST_METHOD";
+ like $body, qr{"SCRIPT_NAME":"/echo"}, "SCRIPT_NAME";
+ like $body, qr{"PATH_INFO":"/abc"}, "PATH_INFO";
+ like $body, qr{"QUERY_STRING":"def"}, "QUERY_STRING";
+ like $body, qr{"SERVER_NAME":"default"}, "SERVER_NAME";
+ like $body, qr{"SERVER_ADDR":"127.0.0.1"}, "SERVER_ADDR";
+ like $body, qr{"SERVER_PORT":"$server->{port}"}, "SERVER_PORT";
+ like $body, qr{"HTTP_HOST":"127.0.0.1:$server->{port}"}, "HTTP_HOST";
+ like $body, qr{"SERVER_ADDR":"127.0.0.1"}, "REMOTE_ADDR";
+ like $body, qr{"SERVER_PORT":"[0-9]+"}, "REMOTE_PORT";
+ like $body, qr{"HTTP_USER_AGENT":"h2o_mruby_test"}, "HTTP_USER_AGENT";
+ like $body, qr{"rack.url_scheme":"http"}, "url_scheme";
+ like $body, qr{"SERVER_SOFTWARE":"h2o/[0-9]+\.[0-9]+\.[0-9]+}, "SERVER_SOFTWARE";
+ };
+ subtest "protocol" => sub {
+ run_with_curl($server, sub {
+ my ($proto, $port, $curl) = @_;
+ my $content = `$curl --silent --show-error $proto://127.0.0.1:$port/echo`;
+ if ($curl =~ /http2/) {
+ like $content, qr{"SERVER_PROTOCOL":"HTTP/2"}, "SERVER_PROTOCOL";
+ } else {
+ like $content, qr{"SERVER_PROTOCOL":"HTTP/1\.1"}, "SERVER_PROTOCOL";
+ }
+ });
+ };
+ subtest "headers" => sub {
+ ($headers, $body) = $fetch->("/headers/");
+ like $headers, qr{^foo: 123\r$}mi;
+ like $headers, qr{^foo: 456\r$}mi;
+ like $headers, qr{^bar: baz\r$}mi;
+ };
+ subtest "headers-each" => sub {
+ ($headers, $body) = $fetch->("/headers-each/");
+ like $headers, qr{^content-type: text/plain\r$}mi;
+ like $headers, qr{^hello: world\r$}mi;
+ };
+};
+
+subtest "reprocess_request" => sub {
+ my $server = spawn_h2o(<< "EOT");
+hosts:
+ default:
+ reproxy: ON
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {"x-reproxy-url" => "http://default/dest#{env["PATH_INFO"]}"}, ["should never see this"]]
+ end
+ /307:
+ mruby.handler: |
+ Proc.new do |env|
+ [307, {"x-reproxy-url" => "http://default/dest#{env["PATH_INFO"]}"}, ["should never see this"]]
+ end
+ /dest:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {}, ["#{env["SCRIPT_NAME"]}#{env["PATH_INFO"]};#{env["CONTENT_LENGTH"]}"]]
+ end
+EOT
+ my ($stderr, $stdout) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ is $stdout, "/dest/;";
+ ($stderr, $stdout) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/hoge");
+ is $stdout, "/dest/hoge;";
+ subtest "preserve-method" => sub {
+ ($stderr, $stdout) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/307/");
+ is $stdout, "/dest/;";
+ ($stderr, $stdout) = run_prog("curl --data hello --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/307/");
+ is $stdout, "/dest/;5";
+ ($stderr, $stdout) = run_prog("curl --data hello --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ is $stdout, "/dest/;";
+ };
+};
+
+subtest "server-push" => sub {
+ plan skip_all => 'nghttp not found'
+ unless prog_exists('nghttp');
+ my $server = spawn_h2o(<< "EOT");
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ push_paths = []
+ if env["PATH_INFO"] == "/index.txt"
+ push_paths << "/index.js"
+ end
+ [399, push_paths.empty? ? {} : {"link" => push_paths.map{|p| "<#{p}>; rel=preload"}.join()}, []]
+ end
+ file.dir: t/assets/doc_root
+EOT
+ my $resp = `nghttp -n --stat https://127.0.0.1:$server->{tls_port}/index.txt`;
+ like $resp, qr{\nid\s*responseEnd\s.*\s/index\.js\n.*\s/index\.txt}is, "receives index.js then /index.txt";
+};
+
+subtest "server-push / nopush" => sub {
+ plan skip_all => 'nghttp not found'
+ unless prog_exists('nghttp');
+ my $server = spawn_h2o(<< "EOT");
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ push_paths = []
+ if env["PATH_INFO"] == "/index.txt"
+ push_paths << "/index.js"
+ end
+ [399, push_paths.empty? ? {} : {"link" => push_paths.map{|p| "<#{p}>; rel=preload; nopush"}.join()}, []]
+ end
+ file.dir: t/assets/doc_root
+EOT
+ my $resp = `nghttp -n --stat https://127.0.0.1:$server->{tls_port}/index.txt`;
+ unlike $resp, qr{/index\.js}is, "receives only /index.txt";
+ like $resp, qr{/index\.txt}is, "receives only /index.txt";
+};
+
+subtest "infinite-reprocess" => sub {
+ my $server = spawn_h2o(sub {
+ my ($port, $tls_port) = @_;
+ return << "EOT";
+hosts:
+ "127.0.0.1:$port":
+ paths:
+ /:
+ reproxy: ON
+ mruby.handler: |
+ Proc.new do |env|
+ [200,{"x-reproxy-url" => "http://127.0.0.1:$port/"},[]]
+ end
+EOT
+ });
+ my ($stderr, $stdout) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $stderr, qr{^HTTP\/1.1 502 }s, "502 response";
+ like $stdout, qr{too many internal delegations}, "reason";
+};
+
+subtest "send-file" => sub {
+ my $server = spawn_h2o(<< "EOT");
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ [200,{}, File::open("t/50mruby/index.html")]
+ end
+EOT
+ my ($headers, $body) = run_prog("curl --silent -A h2o_mruby_test --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 OK\r\n}is;
+ is md5_hex($body), md5_file("t/50mruby/index.html");
+};
+
+subtest "exception" => sub {
+ my $server = spawn_h2o(<< 'EOT');
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ cnt = 0
+ Proc.new do |env|
+ cnt += 1
+ if cnt % 2 != 0
+ [200, {}, ["hello\n"]]
+ else
+ raise "error from rack"
+ end
+ end
+EOT
+ my $fetch = sub {
+ run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}");
+ };
+ for (1..3) {
+ my ($headers, $body) = $fetch->();
+ like $headers, qr{^HTTP/1\.1 200 }is;
+ is $body, "hello\n";
+ ($headers, $body) = $fetch->();
+ like $headers, qr{^HTTP/1\.1 500 }is;
+ }
+};
+
+subtest "post" => sub {
+ my $server = spawn_h2o(<< "EOT");
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ body = []
+ 3.times do
+ env["rack.input"].rewind
+ body << env["rack.input"].read
+ body << "\\n"
+ end
+ [200, {}, body]
+ end
+EOT
+ my ($headers, $body) = run_prog("curl --silent --data 'hello' --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 OK\r\n}is;
+ is $body, "hello\n" x 3;
+};
+
+subtest "InputStream#read-after-close" => sub {
+ my $server = spawn_h2o(<< "EOT");
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ prev_input = nil
+ Proc.new do |env|
+ if !prev_input
+ prev_input = env["rack.input"]
+ resp = "not cached"
+ else
+ begin
+ prev_input.read
+ resp = "must not seed this"
+ rescue IOError => e
+ resp = "got IOError"
+ end
+ end
+ [200, {}, [resp]]
+ end
+EOT
+ my ($headers, $body) = run_prog("curl --silent --data 'hello' --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 OK\r\n}is;
+ is $body, "not cached";
+ ($headers, $body) = run_prog("curl --silent --data 'hello' --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 OK\r\n}is;
+ is $body, "got IOError";
+};
+
+subtest "header-concat" => sub {
+ my $server = spawn_h2o(<< "EOT");
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ [200, {}, [env["HTTP_COOKIE"]]]
+ end
+EOT
+ run_with_curl($server, sub {
+ my ($proto, $port, $curl) = @_;
+ my ($headers, $body) = run_prog("$curl --silent -H 'cookie: a=b' -H 'cookie: c=d' --dump-header /dev/stderr $proto://127.0.0.1:$port/");
+ like $headers, qr{^HTTP/\S+ 200}is;
+ like $body, qr{^a=b;\s*c=d$}is;
+ });
+};
+
+subtest "close-called" => sub {
+ my $server = spawn_h2o(<< "EOT");
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ is_open = false
+ lambda do |env|
+ if is_open
+ return [500, {}, ["close not called"]]
+ end
+ is_open = true
+ return [
+ 200,
+ {},
+ Class.new do
+ def each
+ yield "hello"
+ end
+ define_method(:close) do
+ is_open = false
+ end
+ end.new,
+ ]
+ end
+EOT
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }is;
+ is $body, "hello";
+ ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }is;
+ is $body, "hello";
+};
+
+subtest "close-called-on-exception" => sub {
+ my $server = spawn_h2o(<< "EOT");
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ is_open = false
+ lambda do |env|
+ if is_open
+ return [500, {}, ["close not called"]]
+ end
+ is_open = true
+ return [
+ 200,
+ {},
+ Class.new do
+ def each
+ yield "hello"
+ raise "yeah!"
+ end
+ define_method(:close) do
+ is_open = false
+ end
+ end.new,
+ ]
+ end
+EOT
+ my ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }is;
+ is $body, "hello";
+ ($headers, $body) = run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 200 }is;
+ is $body, "hello";
+};
+
+subtest "log lineno" => sub {
+ my $tester = sub {
+ my ($name, $conf, $expected) = @_;
+
+ subtest $name => sub {
+ my $tempdir = tempdir(CLEANUP => 1);
+ unlink "$tempdir/error_log";
+ my $server = spawn_h2o(<< "EOT");
+$conf
+error-log: $tempdir/error_log
+EOT
+ run_prog("curl --silent --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ my @log = do {
+ open my $fh, "<", "$tempdir/error_log"
+ or die "failed to open error_log:$!";
+ map { my $l = $_; chomp $l; $l } <$fh>;
+ };
+ @log = grep { $_ =~ /^\[h2o_mruby\]/ } @log;
+ like $log[$#log], qr{\[h2o_mruby\] in request:/:mruby raised: @{[$server->{conf_file}]}:$expected:\s*hoge \(RuntimeError\)};
+ };
+ };
+ $tester->("flow style", <<"EOT", 5);
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: Proc.new do |env| raise "hoge" end
+EOT
+ $tester->("block style", <<"EOT", 7);
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ Proc.new do |env|
+ raise "hoge"
+ end
+EOT
+};
+
+subtest 'response with specific statuses should not contain content-length header' => sub {
+ my $server = spawn_h2o(<< "EOT");
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ proc {|env|
+ [204, {}, []]
+ }
+EOT
+ my ($headers, $body) = run_prog("curl --silent --data 'hello' --dump-header /dev/stderr http://127.0.0.1:$server->{port}/");
+ like $headers, qr{^HTTP/1\.1 204 OK\r\n}is;
+ unlike $headers, qr{^content-length:}im;
+};
+
+subtest 'PATH_INFO and SCRIPT_NAME' => sub {
+ plan skip_all => "nc not found"
+ unless prog_exists("nc");
+
+ my $server = spawn_h2o(<< "EOT");
+num-threads: 1
+hosts:
+ default:
+ paths:
+ /:
+ mruby.handler: |
+ proc {|env|
+ [200, {}, ['handler1, ' + env['SCRIPT_NAME'] + ', ' + env['PATH_INFO']]]
+ }
+ /abc:
+ mruby.handler: |
+ proc {|env|
+ [200, {}, ['handler2, ' + env['SCRIPT_NAME'] + ', ' + env['PATH_INFO']]]
+ }
+ "/foo bar":
+ mruby.handler: |
+ proc {|env|
+ [200, {}, ['handler3, ' + env['SCRIPT_NAME'] + ', ' + env['PATH_INFO']]]
+ }
+EOT
+ my $nc = sub {
+ my $path = shift;
+ my $cmd = "echo 'GET $path HTTP/1.1\\r\\nHost: 127.0.0.1\\r\\n\\r' | nc 127.0.0.1 $server->{port}";
+ (undef, my $r) = run_prog($cmd);
+ split(/\r\n\r\n/, $r, 2);
+ };
+
+ my $body;
+ (undef, $body) = $nc->('/abc/def%20ghi');
+ is $body, 'handler2, /abc, /def%20ghi', 'should be kept undecoded';
+
+ (undef, $body) = $nc->('/abc/def/../ghi/../jhk');
+ is $body, 'handler2, /abc, /def/../ghi/../jhk', 'https://github.com/h2o/h2o/pull/1480#issuecomment-339614160';
+
+ (undef, $body) = $nc->('/123/../abc/def/../ghi');
+ is $body, 'handler2, /abc, /def/../ghi', 'https://github.com/h2o/h2o/pull/1480#issuecomment-339658134';
+
+ (undef, $body) = $nc->('/foo%20bar/baz');
+ is $body, 'handler3, /foo bar, /baz', 'paths should be decoded';
+
+ (undef, $body) = $nc->('/xxx/../hoge');
+ is $body, 'handler1, , /xxx/../hoge', 'string size is too big issue 1';
+
+ (undef, $body) = $nc->('/../abc');
+ is $body, 'handler2, /abc, ', 'string size is too big issue 2';
+
+ (undef, $body) = $nc->('abc');
+ is $body, 'handler2, /abc, ', 'no leading slash 1';
+
+ (undef, $body) = $nc->('abc/def');
+ is $body, 'handler2, /abc, /def', 'no leading slash 2';
+
+ (undef, $body) = $nc->('123/../abc/def/../ghi');
+ is $body, 'handler2, /abc, /def/../ghi', 'no leading slash 3';
+
+ (undef, $body) = $nc->('xyz');
+ is $body, 'handler1, , xyz', 'no leading slash 4';
+
+ (undef, $body) = $nc->('');
+ is $body, 'handler1, , ', 'empty path';
+};
+
+done_testing();
diff --git a/debian/vendor-h2o/t/50mruby/hello.rb b/debian/vendor-h2o/t/50mruby/hello.rb
new file mode 100644
index 0000000..34f3214
--- /dev/null
+++ b/debian/vendor-h2o/t/50mruby/hello.rb
@@ -0,0 +1,3 @@
+Proc.new do |env|
+ [200, {"content-type" => "text/plain; charset=utf-8"}, ["hello from h2o_mruby\n"]]
+end
diff --git a/debian/vendor-h2o/t/50mruby/index.html b/debian/vendor-h2o/t/50mruby/index.html
new file mode 100644
index 0000000..d0287e6
--- /dev/null
+++ b/debian/vendor-h2o/t/50mruby/index.html
@@ -0,0 +1 @@
+I'm index.html