]> Sergey Matveev's repositories - public-inbox.git/blob - t/httpd.t
githttpbackend: clamp to one smart HTTP request at-a-time
[public-inbox.git] / t / httpd.t
1 # Copyright (C) 2016 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 use strict;
4 use warnings;
5 use Test::More;
6
7 foreach my $mod (qw(Plack::Util Plack::Request Plack::Builder Danga::Socket
8                         HTTP::Date HTTP::Status)) {
9         eval "require $mod";
10         plan skip_all => "$mod missing for httpd.t" if $@;
11 }
12 use File::Temp qw/tempdir/;
13 use Cwd qw/getcwd/;
14 use IO::Socket;
15 use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD);
16 use Socket qw(SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
17 use IPC::Run;
18
19 # FIXME: too much setup
20 my $tmpdir = tempdir('pi-httpd-XXXXXX', TMPDIR => 1, CLEANUP => 1);
21 my $home = "$tmpdir/pi-home";
22 my $err = "$tmpdir/stderr.log";
23 my $out = "$tmpdir/stdout.log";
24 my $pi_home = "$home/.public-inbox";
25 my $pi_config = "$pi_home/config";
26 my $maindir = "$tmpdir/main.git";
27 my $main_bin = getcwd()."/t/main-bin";
28 my $main_path = "$main_bin:$ENV{PATH}"; # for spamc ham mock
29 my $group = 'test-httpd';
30 my $addr = $group . '@example.com';
31 my $cfgpfx = "publicinbox.$group";
32 my $failbox = "$home/fail.mbox";
33 local $ENV{PI_EMERGENCY} = $failbox;
34 my $mda = 'blib/script/public-inbox-mda';
35 my $httpd = 'blib/script/public-inbox-httpd';
36 my $init = 'blib/script/public-inbox-init';
37
38 my %opts = (
39         LocalAddr => '127.0.0.1',
40         ReuseAddr => 1,
41         Proto => 'tcp',
42         Type => SOCK_STREAM,
43         Listen => 1024,
44 );
45 my $sock = IO::Socket::INET->new(%opts);
46 my $pid;
47 END { kill 'TERM', $pid if defined $pid };
48 {
49         local $ENV{HOME} = $home;
50         ok(!system($init, $group, $maindir, 'http://example.com/', $addr),
51                 'init ran properly');
52
53         # ensure successful message delivery
54         {
55                 local $ENV{ORIGINAL_RECIPIENT} = $addr;
56                 my $in = <<EOF;
57 From: Me <me\@example.com>
58 To: You <you\@example.com>
59 Cc: $addr
60 Message-Id: <nntp\@example.com>
61 Subject: hihi
62 Date: Thu, 01 Jan 1970 06:06:06 +0000
63
64 nntp
65 EOF
66                 local $ENV{PATH} = $main_path;
67                 IPC::Run::run([$mda], \$in);
68                 is(0, $?, 'ran MDA correctly');
69         }
70         ok($sock, 'sock created');
71         $! = 0;
72         my $fl = fcntl($sock, F_GETFD, 0);
73         ok(! $!, 'no error from fcntl(F_GETFD)');
74         is($fl, FD_CLOEXEC, 'cloexec set by default (Perl behavior)');
75         $pid = fork;
76         if ($pid == 0) {
77                 use POSIX qw(dup2);
78                 # pretend to be systemd
79                 fcntl($sock, F_SETFD, $fl &= ~FD_CLOEXEC);
80                 dup2(fileno($sock), 3) or die "dup2 failed: $!\n";
81                 $ENV{LISTEN_PID} = $$;
82                 $ENV{LISTEN_FDS} = 1;
83                 exec $httpd, "--stdout=$out", "--stderr=$err";
84                 die "FAIL: $!\n";
85         }
86         ok(defined $pid, 'forked httpd process successfully');
87         $! = 0;
88         fcntl($sock, F_SETFD, $fl |= FD_CLOEXEC);
89         ok(! $!, 'no error from fcntl(F_SETFD)');
90         my $host = $sock->sockhost;
91         my $port = $sock->sockport;
92         my $conn = IO::Socket::INET->new(PeerAddr => $host,
93                                 PeerPort => $port,
94                                 Proto => 'tcp',
95                                 Type => SOCK_STREAM);
96         ok($conn, 'connected');
97         ok($conn->write("GET / HTTP/1.0\r\n\r\n"), 'wrote data to socket');
98         {
99                 my $buf;
100                 ok($conn->read($buf, 4096), 'read some bytes');
101                 like($buf, qr!\AHTTP/1\.[01] 404\b!, 'got 404 response');
102                 is($conn->read($buf, 1), 0, "EOF");
103         }
104
105         is(system(qw(git clone -q --mirror),
106                         "http://$host:$port/$group", "$tmpdir/clone.git"),
107                 0, 'smart clone successful');
108
109         # ensure dumb cloning works, too:
110         is(system('git', "--git-dir=$maindir",
111                 qw(config http.uploadpack false)),
112                 0, 'disable http.uploadpack');
113         is(system(qw(git clone -q --mirror),
114                         "http://$host:$port/$group", "$tmpdir/dumb.git"),
115                 0, 'clone successful');
116
117         ok(kill('TERM', $pid), 'killed httpd');
118         $pid = undef;
119         waitpid(-1, 0);
120
121         is(system('git', "--git-dir=$tmpdir/clone.git",
122                   qw(fsck --no-verbose)), 0,
123                 'fsck on cloned directory successful');
124 }
125
126 done_testing();
127
128 1;