2022-06-23 07:25:50 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const util = @import("util");
|
|
|
|
const http = @import("./lib.zig");
|
|
|
|
|
2022-07-09 22:43:35 +00:00
|
|
|
const response = @import("./server/response.zig");
|
2022-11-05 10:09:59 +00:00
|
|
|
const request = @import("./request.zig");
|
2022-07-09 22:43:35 +00:00
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
pub const Response = struct {
|
|
|
|
alloc: std.mem.Allocator,
|
|
|
|
stream: std.net.Stream,
|
|
|
|
should_close: bool = false,
|
|
|
|
pub const Stream = response.ResponseStream(std.net.Stream.Writer);
|
2022-11-05 07:26:53 +00:00
|
|
|
pub fn open(self: *Response, status: http.Status, headers: *const http.Fields) !Stream {
|
2022-10-13 09:23:57 +00:00
|
|
|
if (headers.get("Connection")) |hdr| {
|
|
|
|
if (std.ascii.indexOfIgnoreCase(hdr, "close")) |_| self.should_close = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.open(self.alloc, self.stream.writer(), headers, status);
|
|
|
|
}
|
2022-10-16 12:48:12 +00:00
|
|
|
|
2022-11-05 07:26:53 +00:00
|
|
|
pub fn upgrade(self: *Response, status: http.Status, headers: *const http.Fields) !std.net.Stream {
|
2022-10-16 12:48:12 +00:00
|
|
|
try response.writeRequestHeader(self.stream.writer(), headers, status);
|
|
|
|
return self.stream;
|
|
|
|
}
|
2022-10-13 09:23:57 +00:00
|
|
|
};
|
2022-06-23 07:25:50 +00:00
|
|
|
|
2022-07-09 22:43:35 +00:00
|
|
|
const Request = http.Request;
|
2022-06-23 07:25:50 +00:00
|
|
|
const request_buf_size = 1 << 16;
|
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
pub fn serveConn(conn: std.net.StreamServer.Connection, ctx: anytype, handler: anytype, alloc: std.mem.Allocator) !void {
|
|
|
|
// TODO: Timeouts
|
|
|
|
while (true) {
|
|
|
|
std.log.debug("waiting for request", .{});
|
|
|
|
var arena = std.heap.ArenaAllocator.init(alloc);
|
|
|
|
defer arena.deinit();
|
2022-07-09 21:18:43 +00:00
|
|
|
|
2022-11-07 07:38:21 +00:00
|
|
|
var req = request.parse(arena.allocator(), conn.stream.reader()) catch |err| {
|
2022-10-13 09:23:57 +00:00
|
|
|
return handleError(conn.stream.writer(), err) catch {};
|
2022-07-09 21:18:43 +00:00
|
|
|
};
|
2022-10-13 09:23:57 +00:00
|
|
|
std.log.debug("done parsing", .{});
|
2022-07-09 21:18:43 +00:00
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
var res = Response{
|
|
|
|
.alloc = arena.allocator(),
|
|
|
|
.stream = conn.stream,
|
|
|
|
};
|
2022-07-09 21:18:43 +00:00
|
|
|
|
2022-11-17 03:55:38 +00:00
|
|
|
std.log.debug("{any}", .{req});
|
|
|
|
std.log.debug("Opening handler", .{});
|
2022-11-07 07:38:21 +00:00
|
|
|
handler(ctx, &req, &res);
|
2022-10-13 09:23:57 +00:00
|
|
|
std.log.debug("done handling", .{});
|
2022-07-09 21:18:43 +00:00
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
if (req.headers.get("Connection")) |hdr| {
|
|
|
|
if (std.ascii.indexOfIgnoreCase(hdr, "close")) |_| return;
|
|
|
|
} else if (req.headers.get("Keep-Alive")) |hdr| {
|
|
|
|
std.log.debug("keep-alive: {s}", .{hdr});
|
|
|
|
} else if (req.protocol == .http_1_0) return;
|
2022-07-09 21:18:43 +00:00
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
if (res.should_close) return;
|
2022-07-09 21:18:43 +00:00
|
|
|
}
|
2022-10-13 09:23:57 +00:00
|
|
|
}
|
2022-06-23 07:25:50 +00:00
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
/// Writes an error response message and requests closure of the connection
|
2022-06-26 06:35:31 +00:00
|
|
|
fn handleError(writer: anytype, err: anyerror) !void {
|
|
|
|
const status: http.Status = switch (err) {
|
2022-10-13 09:23:57 +00:00
|
|
|
error.EndOfStream => return, // Do nothing, the client closed the connection
|
2022-06-26 06:35:31 +00:00
|
|
|
error.BadRequest => .bad_request,
|
|
|
|
error.UnsupportedMediaType => .unsupported_media_type,
|
|
|
|
error.HttpVersionNotSupported => .http_version_not_supported,
|
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
else => .internal_server_error,
|
2022-06-26 06:35:31 +00:00
|
|
|
};
|
|
|
|
|
2022-10-13 09:23:57 +00:00
|
|
|
try writer.print("HTTP/1.1 {} {?s}\r\nConnection: close\r\n\r\n", .{ @enumToInt(status), status.phrase() });
|
2022-06-26 06:35:31 +00:00
|
|
|
}
|