about summary refs log tree commit homepage
path: root/t/httpd-unix.t
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2016-03-03 10:33:02 +0000
committerEric Wong <e@80x24.org>2016-03-04 00:25:43 +0000
commit8557833d769280495ababfa71f202bf131ea5512 (patch)
tree9f988d17af1e75a95badfe6d1eb083ee9ad7fdd9 /t/httpd-unix.t
parentb38de6f02fa04e36b881d2aad9c7f792beb0b6a1 (diff)
downloadpublic-inbox-8557833d769280495ababfa71f202bf131ea5512.tar.gz
Listening on Unix domain sockets can be convenient for running
behind reverse proxies, avoiding port conflicts, limiting access,
or avoiding the overhead (if any) of TCP over loopback.
Diffstat (limited to 't/httpd-unix.t')
-rw-r--r--t/httpd-unix.t105
1 files changed, 105 insertions, 0 deletions
diff --git a/t/httpd-unix.t b/t/httpd-unix.t
new file mode 100644
index 00000000..580d14d2
--- /dev/null
+++ b/t/httpd-unix.t
@@ -0,0 +1,105 @@
+# Copyright (C) 2016 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+# Tests for binding Unix domain sockets
+use strict;
+use warnings;
+use Test::More;
+
+foreach my $mod (qw(Plack::Util Plack::Request Plack::Builder Danga::Socket
+                        HTTP::Parser::XS HTTP::Date HTTP::Status)) {
+        eval "require $mod";
+        plan skip_all => "$mod missing for httpd-unix.t" if $@;
+}
+
+use File::Temp qw/tempdir/;
+use IO::Socket::UNIX;
+use Cwd qw/getcwd/;
+use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD :seek);
+my $tmpdir = tempdir('httpd-unix-XXXXXX', TMPDIR => 1, CLEANUP => 1);
+my $unix = "$tmpdir/unix.sock";
+my $httpd = 'blib/script/public-inbox-httpd';
+my $psgi = getcwd() . '/t/httpd-corner.psgi';
+my $out = "$tmpdir/out.log";
+my $err = "$tmpdir/err.log";
+
+my $pid;
+END { kill 'TERM', $pid if defined $pid };
+
+my $spawn_httpd = sub {
+        my (@args) = @_;
+        $pid = fork;
+        if ($pid == 0) {
+                exec $httpd, @args, "--stdout=$out", "--stderr=$err", $psgi;
+                die "FAIL: $!\n";
+        }
+        ok(defined $pid, 'forked httpd process successfully');
+};
+
+ok(!-S $unix, 'UNIX socket does not exist, yet');
+$spawn_httpd->("-l$unix");
+for (1..1000) {
+        last if -S $unix;
+        select undef, undef, undef, 0.02
+}
+
+ok(-S $unix, 'UNIX socket was bound by -httpd');
+sub check_sock ($) {
+        my ($unix) = @_;
+        my $sock = IO::Socket::UNIX->new(Peer => $unix, Type => SOCK_STREAM);
+        ok($sock, 'client UNIX socket connected');
+        ok($sock->write("GET /host-port HTTP/1.0\r\n\r\n"),
+                'wrote req to server');
+        ok($sock->read(my $buf, 4096), 'read response');
+        like($buf, qr!\r\n\r\n127\.0\.0\.1:0\z!,
+                'set REMOTE_ADDR and REMOTE_PORT for Unix socket');
+}
+
+check_sock($unix);
+
+{ # do not clobber existing socket
+        my $fpid = fork;
+        if ($fpid == 0) {
+                open STDOUT, '>>', "$tmpdir/1" or die "redirect failed: $!";
+                open STDERR, '>>', "$tmpdir/2" or die "redirect failed: $!";
+                exec $httpd, '-l', $unix, '-W0', $psgi;
+                die "FAIL: $!\n";
+        }
+        is($fpid, waitpid($fpid, 0), 'second httpd exits');
+        isnt($?, 0, 'httpd failed with failure to bind');
+        open my $fh, "$tmpdir/2" or die "failed to open $tmpdir/2: $!";
+        local $/;
+        my $e = <$fh>;
+        like($e, qr/no listeners bound/i, 'got error message');
+        is(-s "$tmpdir/1", 0, 'stdout was empty');
+}
+
+{
+        my $kpid = $pid;
+        $pid = undef;
+        is(kill('TERM', $kpid), 1, 'terminate existing process');
+        is(waitpid($kpid, 0), $kpid, 'existing httpd terminated');
+        is($?, 0, 'existing httpd exited successfully');
+        ok(-S $unix, 'unix socket still exists');
+}
+{
+        # wait for daemonization
+        $spawn_httpd->("-l$unix", '-D', '-P', "$tmpdir/pid");
+        my $kpid = $pid;
+        $pid = undef;
+        is(waitpid($kpid, 0), $kpid, 'existing httpd terminated');
+        check_sock($unix);
+
+        ok(-f "$tmpdir/pid", 'pid file written');
+        open my $fh, '<', "$tmpdir/pid" or die "open failed: $!";
+        my $rpid = <$fh>;
+        chomp $rpid;
+        like($rpid, qr/\A\d+\z/s, 'pid file looks like a pid');
+        is(kill('TERM', $rpid), 1, 'signalled daemonized process');
+        for (1..100) {
+                kill(0, $rpid) or last;
+                select undef, undef, undef, 0.02;
+        }
+        is(kill(0, $rpid), 0, 'daemonized process exited')
+}
+
+done_testing();