about summary refs log tree commit homepage
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/httpd-corner.t27
-rw-r--r--t/httpd-unix.t105
2 files changed, 130 insertions, 2 deletions
diff --git a/t/httpd-corner.t b/t/httpd-corner.t
index 198a7e90..19564074 100644
--- a/t/httpd-corner.t
+++ b/t/httpd-corner.t
@@ -16,6 +16,7 @@ use Digest::SHA qw(sha1_hex);
 use File::Temp qw/tempdir/;
 use Cwd qw/getcwd/;
 use IO::Socket;
+use IO::Socket::UNIX;
 use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD :seek);
 use Socket qw(SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
 use POSIX qw(dup2 mkfifo :sys_wait_h);
@@ -34,20 +35,32 @@ my %opts = (
         Listen => 1024,
 );
 my $sock = IO::Socket::INET->new(%opts);
+my $upath = "$tmpdir/s";
+my $unix = IO::Socket::UNIX->new(
+        Listen => 1024,
+        Type => SOCK_STREAM,
+        Local => $upath
+);
+ok($unix, 'UNIX socket created');
 my $pid;
 END { kill 'TERM', $pid if defined $pid };
 my $spawn_httpd = sub {
         my (@args) = @_;
+        $! = 0;
         my $fl = fcntl($sock, F_GETFD, 0);
         ok(! $!, 'no error from fcntl(F_GETFD)');
         is($fl, FD_CLOEXEC, 'cloexec set by default (Perl behavior)');
         $pid = fork;
         if ($pid == 0) {
                 # pretend to be systemd
-                fcntl($sock, F_SETFD, $fl &= ~FD_CLOEXEC);
                 dup2(fileno($sock), 3) or die "dup2 failed: $!\n";
+                dup2(fileno($unix), 4) or die "dup2 failed: $!\n";
+                $sock = IO::Handle->new_from_fd(3, 'r');
+                $sock->fcntl(F_SETFD, 0);
+                $unix = IO::Handle->new_from_fd(4, 'r');
+                $unix->fcntl(F_SETFD, 0);
                 $ENV{LISTEN_PID} = $$;
-                $ENV{LISTEN_FDS} = 1;
+                $ENV{LISTEN_FDS} = 2;
                 exec $httpd, @args, "--stdout=$out", "--stderr=$err", $psgi;
                 die "FAIL: $!\n";
         }
@@ -63,6 +76,16 @@ my $spawn_httpd = sub {
         $spawn_httpd->('-W0');
 }
 
+# Unix domain sockets
+{
+        my $u = IO::Socket::UNIX->new(Type => SOCK_STREAM, Peer => $upath);
+        ok($u, 'unix socket connected');
+        $u->write("GET /host-port HTTP/1.0\r\n\r\n");
+        $u->read(my $buf, 4096);
+        like($buf, qr!\r\n\r\n127\.0\.0\.1:0\z!,
+                'set REMOTE_ADDR and REMOTE_PORT for Unix socket');
+}
+
 sub conn_for {
         my ($sock, $msg) = @_;
         my $conn = IO::Socket::INET->new(
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();