const std = @import("std"); const os = std.os; pub fn main() !void { try run_server(4096, .{ 127, 0, 0, 1 }, 8888); } const ServerError = os.SocketError || os.BindError || os.ListenError || os.AcceptError || os.RecvFromError || SendHttpResponseError; fn run_server( comptime BUFFER_SIZE: usize, host: [4]u8, port: u16, ) ServerError!void { const SOCK_DOMAIN = os.AF.INET; const SOCK_TYPE = os.SOCK.STREAM; // Make socket descriptor. var sockfd = try os.socket(SOCK_DOMAIN, SOCK_TYPE, 0); defer { os.closeSocket(sockfd); std.debug.print("closed socket {d}\n", .{sockfd}); } std.debug.print("opened socket {d}\n", .{sockfd}); // Bind socket to address. const sockaddr = @bitCast(os.sockaddr, os.sockaddr.in{ .family = SOCK_DOMAIN, .port = std.mem.nativeToBig(u16, port), .addr = @bitCast(u32, host), // .zeros = ... }); try os.bind(sockfd, &sockaddr, @sizeOf(@TypeOf(sockaddr))); try os.listen(sockfd, 1); std.debug.print( "listening at @{d}.{d}.{d}.{d}:{d}\n", .{ host[0], host[1], host[2], host[3], port }, ); // Accept an incoming connection. while (true) { const sockfd_accept = try os.accept(sockfd, null, null, 0); defer { os.closeSocket(sockfd_accept); std.debug.print("connection to socket {d} closed\n", .{sockfd_accept}); } std.debug.print("accepted connection to socket {d}\n", .{sockfd_accept}); var buffer: [BUFFER_SIZE]u8 = undefined; // Receive incoming messages. const msg_len = try std.os.recv(sockfd_accept, &buffer, 0); if (msg_len == 0) break; const socket_msg = buffer[0..msg_len]; std.debug.print(" Received message:\n{s}\n", .{socket_msg}); // Send response. _ = try sendHttpResponse(BUFFER_SIZE, sockfd_accept, socket_msg); } } const SendHttpResponseError = os.SendError || HttpHeadersIterator.NextError || error{ Overflow, NoStartLine, BadStartLine, }; fn sendHttpResponse( comptime BUFFER_SIZE: usize, sockfd: os.socket_t, request: []const u8, ) SendHttpResponseError!void { const start_line = request[0 .. std.mem.indexOf(u8, request, "\r\n") orelse request.len]; if (request.len == 0) return SendHttpResponseError.NoStartLine; // TODO don't assume start line is OK! // Make the writer object. var send_writer = OsSendWriter{ .sockfd = sockfd }; var buf_send_writer = std.io.BufferedWriter( BUFFER_SIZE, OsSendWriter.Writer, ){ .unbuffered_writer = send_writer.writer() }; var writer = buf_send_writer.writer(); var json_writer = std.json.writeStream(writer, 3); _ = try writer.write("HTTP/1.1 200 OK\r\n\r\n"); var headers_iter = HttpHeadersIterator.init(request[start_line.len + 2 ..]); // Build the JSON blob. try json_writer.beginObject(); while (try headers_iter.next()) |header| { try json_writer.objectField(header[0]); try json_writer.emitString(header[1]); } try json_writer.endObject(); // Flush the buffer. try buf_send_writer.flush(); } const OsSendWriter = struct { sockfd: os.socket_t, const Self = @This(); const WriteError = os.SendError; pub fn write(self: *Self, str: []const u8) WriteError!usize { return os.send(self.sockfd, str, 0); } pub const Writer = std.io.Writer(*Self, WriteError, write); pub fn writer(self: *Self) Writer { return .{ .context = self }; } }; const HttpHeadersIterator = struct { lines_iter: std.mem.SplitIterator(u8), const Self = @This(); pub fn init(request: []const u8) Self { return .{ .lines_iter = .{ .buffer = request, .delimiter = "\r\n", .index = 0 } }; } const NextError = error{NoDivider}; pub fn next(self: *Self) !?struct { []const u8, []const u8 } { const DIVIDER: []const u8 = ": "; const line = self.lines_iter.next() orelse return null; if (line.len == 0) return null; const div_pos = std.mem.indexOf(u8, line, DIVIDER) orelse return NextError.NoDivider; const result = .{ line[0..div_pos], line[div_pos + DIVIDER.len ..], }; return result; } };