From 078f637c80cc33c7d29973b95fdc16205ad7bb32 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 1 Jan 2020 10:38:59 +0000 Subject: wwwstatic: add directory listing + index.html support It's now possible to use WwwStatic as a standalone PSGI app to serve static files and recreate the award-winning web design of https://public-inbox.org/ :> --- t/www_static.t | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 t/www_static.t (limited to 't') diff --git a/t/www_static.t b/t/www_static.t new file mode 100644 index 00000000..5f2e3380 --- /dev/null +++ b/t/www_static.t @@ -0,0 +1,96 @@ +# Copyright (C) 2019 all contributors +# License: AGPL-3.0+ +use strict; +use warnings; +use Test::More; +use PublicInbox::TestCommon; +my ($tmpdir, $for_destroy) = tmpdir(); +my @mods = qw(HTTP::Request::Common Plack::Test URI::Escape); +require_mods(@mods); +use_ok $_ foreach @mods; +use_ok 'PublicInbox::WwwStatic'; + +my $app = sub { + my $ws = PublicInbox::WwwStatic->new(docroot => $tmpdir, @_); + sub { $ws->call(shift) }; +}; + +test_psgi($app->(), sub { + my $cb = shift; + my $res = $cb->(GET('/')); + is($res->code, 404, '404 on "/" by default'); + open my $fh, '>', "$tmpdir/index.html" or die; + print $fh 'hi' or die; + close $fh or die; + $res = $cb->(GET('/')); + is($res->code, 200, '200 with index.html'); + is($res->content, 'hi', 'default index.html returned'); + $res = $cb->(HEAD('/')); + is($res->code, 200, '200 on HEAD /'); + is($res->content, '', 'no content'); + is($res->header('Content-Length'), '2', 'content-length set'); + like($res->header('Content-Type'), qr!^text/html\b!, + 'content-type is html'); +}); + +test_psgi($app->(autoindex => 1, index => []), sub { + my $cb = shift; + my $res = $cb->(GET('/')); + my $updir = 'href="../">../'; + is($res->code, 200, '200 with autoindex default'); + my $ls = $res->content; + like($ls, qr/index\.html/, 'got listing with index.html'); + ok(index($ls, $updir) < 0, 'no updir at /'); + mkdir("$tmpdir/dir") or die; + rename("$tmpdir/index.html", "$tmpdir/dir/index.html") or die; + + $res = $cb->(GET('/dir/')); + is($res->code, 200, '200 with autoindex for dir/'); + $ls = $res->content; + ok(index($ls, $updir) > 0, 'updir at /dir/'); + + for my $up (qw(/../ .. /dir/.. /dir/../)) { + is($cb->(GET($up))->code, 403, "`$up' traversal rejected"); + } + + $res = $cb->(GET('/dir')); + is($res->code, 302, '302 w/o slash'); + like($res->header('Location'), qr!://[^/]+/dir/\z!, + 'redirected w/ slash'); + + rename("$tmpdir/dir/index.html", "$tmpdir/dir/foo") or die; + link("$tmpdir/dir/foo", "$tmpdir/dir/foo.gz") or die; + $res = $cb->(GET('/dir/')); + unlike($res->content, qr/>foo\.gzcontent, qr/>foo(GET('/dir/foo/bar')); + is($res->code, 404, 'using file as dir fails'); + + unlink("$tmpdir/dir/foo") or die; + $res = $cb->(GET('/dir/')); + like($res->content, qr/>foo\.gz', "$tmpdir/dir/foo" or die; + print $fh "uncompressed\n" or die; + close $fh or die; + utime(0, 0, "$tmpdir/dir/foo") or die; + $res = $cb->(GET('/dir/')); + my $html = $res->content; + like($html, qr/>foofoo\.gz(GET('/dir/foo')); + is($res->content, "uncompressed\n", + 'got uncompressed on mtime mismatch'); + + utime(0, 0, "$tmpdir/dir/foo.gz") or die; + my $get = GET('/dir/foo'); + $get->header('Accept-Encoding' => 'gzip'); + $res = $cb->($get); + is($res->content, "hi", 'got compressed on mtime match'); +}); + +done_testing(); -- cgit v1.2.3-24-ge0c7