Blame


1 924d1172 2023-12-21 op // This is free and unencumbered software released into the public domain.
2 924d1172 2023-12-21 op //
3 924d1172 2023-12-21 op // Anyone is free to copy, modify, publish, use, compile, sell, or
4 924d1172 2023-12-21 op // distribute this software, either in source code form or as a compiled
5 924d1172 2023-12-21 op // binary, for any purpose, commercial or non-commercial, and by any
6 924d1172 2023-12-21 op // means.
7 924d1172 2023-12-21 op //
8 924d1172 2023-12-21 op // In jurisdictions that recognize copyright laws, the author or authors
9 924d1172 2023-12-21 op // of this software dedicate any and all copyright interest in the
10 924d1172 2023-12-21 op // software to the public domain. We make this dedication for the benefit
11 924d1172 2023-12-21 op // of the public at large and to the detriment of our heirs and
12 924d1172 2023-12-21 op // successors. We intend this dedication to be an overt act of
13 924d1172 2023-12-21 op // relinquishment in perpetuity of all present and future rights to this
14 924d1172 2023-12-21 op // software under copyright law.
15 924d1172 2023-12-21 op //
16 924d1172 2023-12-21 op // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 924d1172 2023-12-21 op // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 924d1172 2023-12-21 op // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 924d1172 2023-12-21 op // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 924d1172 2023-12-21 op // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 924d1172 2023-12-21 op // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 924d1172 2023-12-21 op // OTHER DEALINGS IN THE SOFTWARE.
23 924d1172 2023-12-21 op
24 924d1172 2023-12-21 op use bufio;
25 924d1172 2023-12-21 op use encoding::utf8;
26 924d1172 2023-12-21 op use errors;
27 924d1172 2023-12-21 op use fmt;
28 924d1172 2023-12-21 op use fnmatch;
29 924d1172 2023-12-21 op use io;
30 924d1172 2023-12-21 op use log;
31 924d1172 2023-12-21 op use net;
32 924d1172 2023-12-21 op use types;
33 924d1172 2023-12-21 op
34 924d1172 2023-12-21 op use strings;
35 924d1172 2023-12-21 op
36 924d1172 2023-12-21 op use ev;
37 924d1172 2023-12-21 op
38 924d1172 2023-12-21 op export type version = enum {
39 924d1172 2023-12-21 op HTTP_1_0,
40 924d1172 2023-12-21 op HTTP_1_1,
41 924d1172 2023-12-21 op };
42 924d1172 2023-12-21 op
43 924d1172 2023-12-21 op export type method = enum {
44 924d1172 2023-12-21 op GET,
45 ef30005e 2023-12-21 op HEAD,
46 924d1172 2023-12-21 op POST,
47 ef30005e 2023-12-21 op PUT,
48 ef30005e 2023-12-21 op DELETE,
49 ef30005e 2023-12-21 op CONNECT,
50 ef30005e 2023-12-21 op OPTIONS,
51 ef30005e 2023-12-21 op TRACE,
52 ef30005e 2023-12-21 op PATCH,
53 924d1172 2023-12-21 op };
54 924d1172 2023-12-21 op
55 924d1172 2023-12-21 op export type request = struct {
56 924d1172 2023-12-21 op path: str,
57 924d1172 2023-12-21 op method: method,
58 924d1172 2023-12-21 op version: version,
59 924d1172 2023-12-21 op ctype: str,
60 924d1172 2023-12-21 op csize: size,
61 924d1172 2023-12-21 op chunked: bool,
62 924d1172 2023-12-21 op };
63 924d1172 2023-12-21 op
64 924d1172 2023-12-21 op export type routefn = *fn(*client) (void | error);
65 924d1172 2023-12-21 op
66 924d1172 2023-12-21 op export type client = struct {
67 924d1172 2023-12-21 op fp: io::file,
68 924d1172 2023-12-21 op in: bufio::scanner,
69 924d1172 2023-12-21 op first_line: bool,
70 924d1172 2023-12-21 op request: request,
71 924d1172 2023-12-21 op chunked: bool, // whether to chunk the reply
72 924d1172 2023-12-21 op hdrdone: bool,
73 924d1172 2023-12-21 op done: bool,
74 924d1172 2023-12-21 op // routefn: (void | routefn),
75 924d1172 2023-12-21 op mux: *mux,
76 924d1172 2023-12-21 op };
77 924d1172 2023-12-21 op
78 924d1172 2023-12-21 op export type route = struct {
79 924d1172 2023-12-21 op pattern: str,
80 924d1172 2023-12-21 op routefn: routefn,
81 924d1172 2023-12-21 op };
82 924d1172 2023-12-21 op
83 924d1172 2023-12-21 op export type mux = []route;
84 924d1172 2023-12-21 op
85 924d1172 2023-12-21 op let global: mux = [];
86 924d1172 2023-12-21 op
87 924d1172 2023-12-21 op export fn handle(pattern: str, routefn: routefn) void = {
88 924d1172 2023-12-21 op append(global, route {
89 924d1172 2023-12-21 op pattern = pattern,
90 924d1172 2023-12-21 op routefn = routefn,
91 924d1172 2023-12-21 op });
92 924d1172 2023-12-21 op };
93 924d1172 2023-12-21 op
94 924d1172 2023-12-21 op // XXX generic listen would be very handy!
95 924d1172 2023-12-21 op // something like: http::listen("localhost:8080") or maybe even
96 924d1172 2023-12-21 op // http::listen("unix:/run/foo.sock")
97 924d1172 2023-12-21 op
98 924d1172 2023-12-21 op export fn listen_sock(f: io::file) void = {
99 924d1172 2023-12-21 op ev::add(f, ev::READ, &acceptclt, &global);
100 924d1172 2023-12-21 op };
101 924d1172 2023-12-21 op
102 924d1172 2023-12-21 op fn acceptclt(f: io::file, ev: ev::event, data: nullable *opaque) void = {
103 924d1172 2023-12-21 op let sock = match(net::accept(f, net::sockflag::NONBLOCK)) {
104 924d1172 2023-12-21 op case let err: net::error =>
105 924d1172 2023-12-21 op log::printfln("accept: {}", net::strerror(err));
106 924d1172 2023-12-21 op return;
107 924d1172 2023-12-21 op case let sock: net::socket =>
108 924d1172 2023-12-21 op yield sock;
109 924d1172 2023-12-21 op };
110 924d1172 2023-12-21 op
111 924d1172 2023-12-21 op let mux = data: *mux;
112 924d1172 2023-12-21 op let clt = alloc(client {
113 924d1172 2023-12-21 op fp = sock,
114 924d1172 2023-12-21 op mux = mux,
115 924d1172 2023-12-21 op // routefn = void,
116 924d1172 2023-12-21 op in = bufio::newscanner(sock, types::SIZE_MAX),
117 924d1172 2023-12-21 op first_line = true,
118 924d1172 2023-12-21 op ...
119 924d1172 2023-12-21 op });
120 924d1172 2023-12-21 op
121 924d1172 2023-12-21 op ev::add(sock, ev::READ, &handleclt, clt);
122 924d1172 2023-12-21 op };
123 924d1172 2023-12-21 op
124 924d1172 2023-12-21 op fn parse_req(c: *client) (bool | error | encoding::utf8::invalid) = {
125 924d1172 2023-12-21 op for (true) {
126 924d1172 2023-12-21 op let line = match(bufio::scan_string(&c.in, "\r\n")) {
127 924d1172 2023-12-21 op case io::EOF => return badrequest;
128 924d1172 2023-12-21 op case let err: io::error => return err;
129 924d1172 2023-12-21 op case let line: const str => yield line;
130 924d1172 2023-12-21 op };
131 924d1172 2023-12-21 op
132 924d1172 2023-12-21 op if (c.first_line) {
133 924d1172 2023-12-21 op c.first_line = false;
134 924d1172 2023-12-21 op
135 924d1172 2023-12-21 op let (method, rest) = strings::cut(line, " ");
136 924d1172 2023-12-21 op
137 924d1172 2023-12-21 op switch (method) {
138 ef30005e 2023-12-21 op case "GET" => c.request.method = method::GET;
139 ef30005e 2023-12-21 op case "HEAD" => c.request.method = method::HEAD;
140 ef30005e 2023-12-21 op case "POST" => c.request.method = method::POST;
141 ef30005e 2023-12-21 op case "PUT" => c.request.method = method::PUT;
142 ef30005e 2023-12-21 op case "DELETE" => c.request.method = method::DELETE;
143 ef30005e 2023-12-21 op case "CONNECT" => c.request.method = method::CONNECT;
144 ef30005e 2023-12-21 op case "OPTIONS" => c.request.method = method::OPTIONS;
145 ef30005e 2023-12-21 op case "TRACE" => c.request.method = method::TRACE;
146 ef30005e 2023-12-21 op case "PATCH" => c.request.method = method::PATCH;
147 924d1172 2023-12-21 op case => return badrequest;
148 924d1172 2023-12-21 op };
149 924d1172 2023-12-21 op
150 924d1172 2023-12-21 op let (path, version) = strings::cut(rest, " ");
151 924d1172 2023-12-21 op c.request.path = strings::dup(path);
152 924d1172 2023-12-21 op
153 924d1172 2023-12-21 op switch (version) {
154 924d1172 2023-12-21 op case "HTTP/1.0" =>
155 924d1172 2023-12-21 op c.request.version = version::HTTP_1_1;
156 924d1172 2023-12-21 op case "HTTP/1.1" =>
157 924d1172 2023-12-21 op c.request.version = version::HTTP_1_0;
158 924d1172 2023-12-21 op c.chunked = true;
159 924d1172 2023-12-21 op case => return badrequest;
160 924d1172 2023-12-21 op };
161 924d1172 2023-12-21 op
162 924d1172 2023-12-21 op continue;
163 924d1172 2023-12-21 op };
164 924d1172 2023-12-21 op
165 924d1172 2023-12-21 op // ignore everything for the time being.
166 924d1172 2023-12-21 op if (line == "") {
167 924d1172 2023-12-21 op return true;
168 924d1172 2023-12-21 op };
169 924d1172 2023-12-21 op
170 924d1172 2023-12-21 op continue;
171 924d1172 2023-12-21 op };
172 924d1172 2023-12-21 op };
173 924d1172 2023-12-21 op
174 924d1172 2023-12-21 op fn delclient(c: *client) void = {
175 924d1172 2023-12-21 op ev::del(c.fp);
176 924d1172 2023-12-21 op
177 924d1172 2023-12-21 op bufio::finish(&c.in);
178 924d1172 2023-12-21 op match (io::close(c.fp)) {
179 924d1172 2023-12-21 op case => yield;
180 924d1172 2023-12-21 op };
181 924d1172 2023-12-21 op
182 924d1172 2023-12-21 op free(c);
183 924d1172 2023-12-21 op };
184 924d1172 2023-12-21 op
185 924d1172 2023-12-21 op fn handleclt(f: io::file, ev: ev::event, data: nullable *opaque) void = {
186 924d1172 2023-12-21 op let clt = data: *client;
187 924d1172 2023-12-21 op
188 924d1172 2023-12-21 op match (do_request(clt)) {
189 924d1172 2023-12-21 op case let err: error =>
190 924d1172 2023-12-21 op log::printfln("failure: {}", strerror(err));
191 924d1172 2023-12-21 op delclient(clt);
192 924d1172 2023-12-21 op return;
193 924d1172 2023-12-21 op case =>
194 924d1172 2023-12-21 op if (clt.done) {
195 924d1172 2023-12-21 op delclient(clt);
196 924d1172 2023-12-21 op };
197 924d1172 2023-12-21 op };
198 924d1172 2023-12-21 op };
199 924d1172 2023-12-21 op
200 924d1172 2023-12-21 op fn do_request(clt: *client) (void | error) = {
201 924d1172 2023-12-21 op match (parse_req(clt)) {
202 924d1172 2023-12-21 op case let done: bool =>
203 924d1172 2023-12-21 op if (!done)
204 924d1172 2023-12-21 op return; // try again later
205 924d1172 2023-12-21 op yield;
206 924d1172 2023-12-21 op case errors::again => return; // try again later
207 924d1172 2023-12-21 op case badrequest =>
208 924d1172 2023-12-21 op route_badrequest(clt)?;
209 924d1172 2023-12-21 op clt.done = true;
210 924d1172 2023-12-21 op return;
211 924d1172 2023-12-21 op case let err: io::error =>
212 924d1172 2023-12-21 op log::printfln("parse_req: {}", io::strerror(err));
213 924d1172 2023-12-21 op return;
214 924d1172 2023-12-21 op };
215 924d1172 2023-12-21 op
216 924d1172 2023-12-21 op let path = clt.request.path;
217 924d1172 2023-12-21 op log::printfln("< GET {}", path);
218 924d1172 2023-12-21 op
219 924d1172 2023-12-21 op let found = false;
220 924d1172 2023-12-21 op for (let i = 0z; i < len(clt.mux); i += 1) {
221 924d1172 2023-12-21 op if (fnmatch::fnmatch(clt.mux[i].pattern, path)) {
222 924d1172 2023-12-21 op found = true;
223 924d1172 2023-12-21 op clt.mux[i].routefn(clt)?;
224 924d1172 2023-12-21 op break;
225 924d1172 2023-12-21 op };
226 924d1172 2023-12-21 op };
227 924d1172 2023-12-21 op
228 924d1172 2023-12-21 op if (!found) {
229 924d1172 2023-12-21 op route_notfound(clt)?;
230 924d1172 2023-12-21 op };
231 924d1172 2023-12-21 op
232 924d1172 2023-12-21 op write(clt, "")?; // flush
233 924d1172 2023-12-21 op
234 924d1172 2023-12-21 op clt.done = true;
235 924d1172 2023-12-21 op };
236 924d1172 2023-12-21 op
237 924d1172 2023-12-21 op export fn reply(clt: *client, code: int, status: str) (void | error) = {
238 924d1172 2023-12-21 op log::printfln("> {} {}", code, status);
239 924d1172 2023-12-21 op
240 924d1172 2023-12-21 op let version = if (clt.request.version == version::HTTP_1_0)
241 924d1172 2023-12-21 op "HTTP/1.0" else "HTTP/1.1";
242 924d1172 2023-12-21 op
243 924d1172 2023-12-21 op fmt::fprintf(clt.fp, "{} {} {}\r\n", version, code, status)?;
244 924d1172 2023-12-21 op };
245 924d1172 2023-12-21 op
246 924d1172 2023-12-21 op export fn header(clt: *client, header: str, value: str) (void | error) = {
247 924d1172 2023-12-21 op fmt::fprintf(clt.fp, "{}: {}\r\n", header, value)?;
248 924d1172 2023-12-21 op };
249 924d1172 2023-12-21 op
250 924d1172 2023-12-21 op export fn write(clt: *client, body: str) (void | error) = {
251 924d1172 2023-12-21 op if (!clt.hdrdone) {
252 b28be21f 2023-12-21 op if (clt.chunked) {
253 b28be21f 2023-12-21 op header(clt, "Transfer-Encoding", "chunked")?;
254 b28be21f 2023-12-21 op };
255 924d1172 2023-12-21 op header(clt, "Connection", "closed")?;
256 924d1172 2023-12-21 op
257 924d1172 2023-12-21 op clt.hdrdone = true;
258 924d1172 2023-12-21 op fmt::fprint(clt.fp, "\r\n")?;
259 924d1172 2023-12-21 op };
260 924d1172 2023-12-21 op
261 b28be21f 2023-12-21 op if (clt.chunked) {
262 b28be21f 2023-12-21 op fmt::fprintf(clt.fp, "{:x}\r\n{}\r\n", len(body), body)?;
263 b28be21f 2023-12-21 op } else {
264 b28be21f 2023-12-21 op fmt::fprintf(clt.fp, "{}", body)?;
265 b28be21f 2023-12-21 op };
266 924d1172 2023-12-21 op };
267 924d1172 2023-12-21 op
268 924d1172 2023-12-21 op fn route_notfound(clt: *client) (void | error) = {
269 924d1172 2023-12-21 op reply(clt, 404, "Not Found")?;
270 924d1172 2023-12-21 op header(clt, "Content-Type", "text/plain")?;
271 924d1172 2023-12-21 op write(clt, "Page not found.\n")?;
272 924d1172 2023-12-21 op };
273 924d1172 2023-12-21 op
274 924d1172 2023-12-21 op fn route_badrequest(clt: *client) (void | error) = {
275 924d1172 2023-12-21 op reply(clt, 400, "Bad Request")?;
276 924d1172 2023-12-21 op header(clt, "Content-Type", "text/plain")?;
277 924d1172 2023-12-21 op write(clt, "Bad Request")?;
278 924d1172 2023-12-21 op };