Blame


1 1ce583f0 2024-04-25 thomas.ad #!/usr/bin/env perl
2 1ce583f0 2024-04-25 thomas.ad #
3 1ce583f0 2024-04-25 thomas.ad # Copyright (c) 2024 Omar Polo <op@openbsd.org>
4 1ce583f0 2024-04-25 thomas.ad # Copyright (c) 2024 Stefan Sperling <stsp@openbsd.org>
5 1ce583f0 2024-04-25 thomas.ad #
6 1ce583f0 2024-04-25 thomas.ad # Permission to use, copy, modify, and distribute this software for any
7 1ce583f0 2024-04-25 thomas.ad # purpose with or without fee is hereby granted, provided that the above
8 1ce583f0 2024-04-25 thomas.ad # copyright notice and this permission notice appear in all copies.
9 1ce583f0 2024-04-25 thomas.ad #
10 1ce583f0 2024-04-25 thomas.ad # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1ce583f0 2024-04-25 thomas.ad # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1ce583f0 2024-04-25 thomas.ad # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1ce583f0 2024-04-25 thomas.ad # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1ce583f0 2024-04-25 thomas.ad # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1ce583f0 2024-04-25 thomas.ad # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1ce583f0 2024-04-25 thomas.ad # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1ce583f0 2024-04-25 thomas.ad
18 1ce583f0 2024-04-25 thomas.ad use v5.36;
19 1ce583f0 2024-04-25 thomas.ad use IPC::Open2;
20 1ce583f0 2024-04-25 thomas.ad use Getopt::Long qw(:config bundling);
21 1ce583f0 2024-04-25 thomas.ad use HTTP::Daemon;
22 1ce583f0 2024-04-25 thomas.ad use HTTP::Status;
23 1ce583f0 2024-04-25 thomas.ad use HTTP::Request;
24 1ce583f0 2024-04-25 thomas.ad
25 1ce583f0 2024-04-25 thomas.ad my $port = 8000;
26 1ce583f0 2024-04-25 thomas.ad
27 1ce583f0 2024-04-25 thomas.ad my $usage = "usage: $0 [-p port] repo_root_path\n";
28 1ce583f0 2024-04-25 thomas.ad GetOptions("p:i" => \$port) or die($usage);
29 1ce583f0 2024-04-25 thomas.ad
30 1ce583f0 2024-04-25 thomas.ad # $HTTP::Daemon::DEBUG = 1;
31 1ce583f0 2024-04-25 thomas.ad
32 1ce583f0 2024-04-25 thomas.ad my $server = HTTP::Daemon->new(
33 1ce583f0 2024-04-25 thomas.ad Domain => AF_INET,
34 1ce583f0 2024-04-25 thomas.ad Type => SOCK_STREAM,
35 1ce583f0 2024-04-25 thomas.ad Proto => 'tcp',
36 1ce583f0 2024-04-25 thomas.ad LocalHost => '127.0.0.1',
37 1ce583f0 2024-04-25 thomas.ad LocalPort => $port,
38 1ce583f0 2024-04-25 thomas.ad ReusePort => 1,
39 1ce583f0 2024-04-25 thomas.ad Listen => 1,
40 1ce583f0 2024-04-25 thomas.ad ) || die "Could not open socket 127.0.0.1:$port: $IO::Socket::errstr";
41 1ce583f0 2024-04-25 thomas.ad
42 1ce583f0 2024-04-25 thomas.ad $ENV{GIT_HTTP_EXPORT_ALL} = '';
43 1ce583f0 2024-04-25 thomas.ad
44 1ce583f0 2024-04-25 thomas.ad $SIG{'PIPE'} = 'IGNORE';
45 1ce583f0 2024-04-25 thomas.ad
46 1ce583f0 2024-04-25 thomas.ad my $repo_root = $ARGV[0];
47 1ce583f0 2024-04-25 thomas.ad
48 1ce583f0 2024-04-25 thomas.ad my $clientbuf;
49 1ce583f0 2024-04-25 thomas.ad my $cr;
50 1ce583f0 2024-04-25 thomas.ad my $gitbuf;
51 1ce583f0 2024-04-25 thomas.ad my $gw;
52 1ce583f0 2024-04-25 thomas.ad my $w;
53 1ce583f0 2024-04-25 thomas.ad my $bufsize = 65536;
54 1ce583f0 2024-04-25 thomas.ad my $timeout = 10;
55 1ce583f0 2024-04-25 thomas.ad my $ev;
56 1ce583f0 2024-04-25 thomas.ad my $resp;
57 1ce583f0 2024-04-25 thomas.ad my $ret;
58 1ce583f0 2024-04-25 thomas.ad my $gitpid = -1;
59 1ce583f0 2024-04-25 thomas.ad
60 1ce583f0 2024-04-25 thomas.ad sub handle_get {
61 1ce583f0 2024-04-25 thomas.ad my ($req, $client) = @_;
62 1ce583f0 2024-04-25 thomas.ad my $done = 0;
63 1ce583f0 2024-04-25 thomas.ad
64 1ce583f0 2024-04-25 thomas.ad my $path = $req->uri->path;
65 1ce583f0 2024-04-25 thomas.ad $ENV{PATH_TRANSLATED} = "/$repo_root/$path";
66 1ce583f0 2024-04-25 thomas.ad $ENV{REQUEST_METHOD} = 'GET';
67 1ce583f0 2024-04-25 thomas.ad $ENV{QUERY_STRING} = $req->uri->query;
68 1ce583f0 2024-04-25 thomas.ad
69 1ce583f0 2024-04-25 thomas.ad my $gitpid = open2(my $gitout, my $gitin, 'git', 'http-backend');
70 1ce583f0 2024-04-25 thomas.ad
71 1ce583f0 2024-04-25 thomas.ad close($gitin);
72 1ce583f0 2024-04-25 thomas.ad
73 1ce583f0 2024-04-25 thomas.ad my $headers = HTTP::Headers->new;
74 1ce583f0 2024-04-25 thomas.ad my ($status_code, $status) = (200, "OK");
75 1ce583f0 2024-04-25 thomas.ad while (<$gitout>) {
76 1ce583f0 2024-04-25 thomas.ad local $/ = "\r\n";
77 1ce583f0 2024-04-25 thomas.ad chomp;
78 1ce583f0 2024-04-25 thomas.ad last if m/^$/;
79 1ce583f0 2024-04-25 thomas.ad
80 1ce583f0 2024-04-25 thomas.ad if (m/^Status: ([0-9]+)(.*)$/) {
81 1ce583f0 2024-04-25 thomas.ad ($status_code, $status) = ($1, $2);
82 1ce583f0 2024-04-25 thomas.ad chomp $status;
83 1ce583f0 2024-04-25 thomas.ad next;
84 1ce583f0 2024-04-25 thomas.ad }
85 1ce583f0 2024-04-25 thomas.ad
86 1ce583f0 2024-04-25 thomas.ad # XXX we don't support 'folded' headers
87 1ce583f0 2024-04-25 thomas.ad my ($name, $value) = split(':', $_);
88 1ce583f0 2024-04-25 thomas.ad $headers->header($name => $value);
89 1ce583f0 2024-04-25 thomas.ad }
90 1ce583f0 2024-04-25 thomas.ad
91 1ce583f0 2024-04-25 thomas.ad my $resp = HTTP::Response->new($status_code, $status, $headers,
92 1ce583f0 2024-04-25 thomas.ad sub {
93 1ce583f0 2024-04-25 thomas.ad my $r = read($gitout, my $buf, 1024);
94 1ce583f0 2024-04-25 thomas.ad warn "error reading git output: $!" unless defined $r;
95 1ce583f0 2024-04-25 thomas.ad return undef if not defined($r) or $r == 0;
96 1ce583f0 2024-04-25 thomas.ad return $buf;
97 1ce583f0 2024-04-25 thomas.ad });
98 1ce583f0 2024-04-25 thomas.ad
99 1ce583f0 2024-04-25 thomas.ad $client->send_response($resp);
100 1ce583f0 2024-04-25 thomas.ad
101 1ce583f0 2024-04-25 thomas.ad close($gitout);
102 1ce583f0 2024-04-25 thomas.ad waitpid($gitpid, 0);
103 1ce583f0 2024-04-25 thomas.ad
104 1ce583f0 2024-04-25 thomas.ad printf "GET %s: 200 OK\n", $req->uri->path;
105 1ce583f0 2024-04-25 thomas.ad }
106 1ce583f0 2024-04-25 thomas.ad
107 1ce583f0 2024-04-25 thomas.ad sub handle_post {
108 1ce583f0 2024-04-25 thomas.ad my ($req, $client) = @_;
109 1ce583f0 2024-04-25 thomas.ad my $done = 0;
110 1ce583f0 2024-04-25 thomas.ad
111 1ce583f0 2024-04-25 thomas.ad my $path = $req->uri->path;
112 1ce583f0 2024-04-25 thomas.ad $ENV{PATH_TRANSLATED} = "/$repo_root/$path";
113 1ce583f0 2024-04-25 thomas.ad $ENV{REQUEST_METHOD} = 'POST';
114 1ce583f0 2024-04-25 thomas.ad $ENV{QUERY_STRING} = "";
115 1ce583f0 2024-04-25 thomas.ad $ENV{CONTENT_TYPE} = $req->header('Content-Type');
116 1ce583f0 2024-04-25 thomas.ad
117 1ce583f0 2024-04-25 thomas.ad my $gitpid = open2(my $gitout, my $gitin, 'git', 'http-backend');
118 1ce583f0 2024-04-25 thomas.ad
119 1ce583f0 2024-04-25 thomas.ad my $content = $req->content();
120 1ce583f0 2024-04-25 thomas.ad my $len = length($content);
121 1ce583f0 2024-04-25 thomas.ad while ($len > 0) {
122 1ce583f0 2024-04-25 thomas.ad my $w = syswrite($gitin, $content, $len);
123 1ce583f0 2024-04-25 thomas.ad last if $w <= 0;
124 1ce583f0 2024-04-25 thomas.ad $len -= $w;
125 1ce583f0 2024-04-25 thomas.ad $content = substr($content, $w);
126 1ce583f0 2024-04-25 thomas.ad }
127 1ce583f0 2024-04-25 thomas.ad
128 1ce583f0 2024-04-25 thomas.ad die "failed to upload payload" if ($len != 0);
129 1ce583f0 2024-04-25 thomas.ad
130 1ce583f0 2024-04-25 thomas.ad close($gitin);
131 1ce583f0 2024-04-25 thomas.ad
132 1ce583f0 2024-04-25 thomas.ad my $headers = HTTP::Headers->new;
133 1ce583f0 2024-04-25 thomas.ad my ($status_code, $status) = (200, "OK");
134 1ce583f0 2024-04-25 thomas.ad while (<$gitout>) {
135 1ce583f0 2024-04-25 thomas.ad local $/ = "\r\n";
136 1ce583f0 2024-04-25 thomas.ad chomp;
137 1ce583f0 2024-04-25 thomas.ad last if m/^$/;
138 1ce583f0 2024-04-25 thomas.ad
139 1ce583f0 2024-04-25 thomas.ad if (m/^Status: ([0-9]+)(.*)$/) {
140 1ce583f0 2024-04-25 thomas.ad ($status_code, $status) = ($1, $2);
141 1ce583f0 2024-04-25 thomas.ad chomp $status;
142 1ce583f0 2024-04-25 thomas.ad next;
143 1ce583f0 2024-04-25 thomas.ad }
144 1ce583f0 2024-04-25 thomas.ad
145 1ce583f0 2024-04-25 thomas.ad # XXX we don't support 'folded' headers
146 1ce583f0 2024-04-25 thomas.ad my ($name, $value) = split(':', $_);
147 1ce583f0 2024-04-25 thomas.ad $headers->header($name => $value);
148 1ce583f0 2024-04-25 thomas.ad }
149 1ce583f0 2024-04-25 thomas.ad
150 1ce583f0 2024-04-25 thomas.ad my $resp = HTTP::Response->new($status_code, $status, $headers,
151 1ce583f0 2024-04-25 thomas.ad sub {
152 1ce583f0 2024-04-25 thomas.ad my $r = read($gitout, my $buf, 1024);
153 1ce583f0 2024-04-25 thomas.ad if (not defined($r) or $r == 0) {
154 1ce583f0 2024-04-25 thomas.ad warn "read error: $!" unless defined $r;
155 1ce583f0 2024-04-25 thomas.ad return undef;
156 1ce583f0 2024-04-25 thomas.ad }
157 1ce583f0 2024-04-25 thomas.ad return $buf;
158 1ce583f0 2024-04-25 thomas.ad });
159 1ce583f0 2024-04-25 thomas.ad
160 1ce583f0 2024-04-25 thomas.ad $client->send_response($resp);
161 1ce583f0 2024-04-25 thomas.ad
162 1ce583f0 2024-04-25 thomas.ad close($gitout);
163 1ce583f0 2024-04-25 thomas.ad waitpid($gitpid, 0);
164 1ce583f0 2024-04-25 thomas.ad
165 1ce583f0 2024-04-25 thomas.ad printf "POST %s: 200 OK\n", $req->uri->path;
166 1ce583f0 2024-04-25 thomas.ad }
167 1ce583f0 2024-04-25 thomas.ad
168 1ce583f0 2024-04-25 thomas.ad while (1) {
169 1ce583f0 2024-04-25 thomas.ad my $client = $server->accept();
170 1ce583f0 2024-04-25 thomas.ad
171 1ce583f0 2024-04-25 thomas.ad while (my $req = $client->get_request) {
172 1ce583f0 2024-04-25 thomas.ad if ($req->method eq "GET") {
173 1ce583f0 2024-04-25 thomas.ad handle_get($req, $client);
174 1ce583f0 2024-04-25 thomas.ad } elsif ($req->method eq "POST") {
175 1ce583f0 2024-04-25 thomas.ad handle_post($req, $client);
176 8c61ad04 2024-04-25 thomas.ad } else {
177 8c61ad04 2024-04-25 thomas.ad warn "unknown method ". $req->method . "\n";
178 8c61ad04 2024-04-25 thomas.ad my $res = HTTP::Response->new(405,
179 8c61ad04 2024-04-25 thomas.ad "Method not Allowed");
180 8c61ad04 2024-04-25 thomas.ad
181 8c61ad04 2024-04-25 thomas.ad last;
182 1ce583f0 2024-04-25 thomas.ad }
183 1ce583f0 2024-04-25 thomas.ad }
184 1ce583f0 2024-04-25 thomas.ad
185 1ce583f0 2024-04-25 thomas.ad $client->close();
186 1ce583f0 2024-04-25 thomas.ad }