const std = @import("std"); const main = @import("main.zig"); threadlocal var read_buffer: [2 * 1024 * 1024]u8 = undefined; threadlocal var write_buffer: [2 * 1024 * 1024]u8 = undefined; const log = std.log.scoped(.http); const status = struct { pub const ok = "HTTP/1.1 200 OK\r\n"; pub const created = "HTTP/1.1 201 Created\r\n"; pub const accepted = "HTTP/1.1 202 Accepted\r\n"; pub const non_authoritative_information = "HTTP/1.1 203 Non-Authoritative Information\r\n"; pub const no_content = "HTTP/1.1 204 No Content\r\n"; pub const reset_content = "HTTP/1.1 205 Reset Content\r\n"; pub const partial_content = "HTTP/1.1 206 Partial Content\r\n"; pub const multi_status = "HTTP/1.1 207 Multi-Status\r\n"; pub const already_reported = "HTTP/1.1 208 Already Reported\r\n"; pub const multiple_choices = "HTTP/1.1 300 Multiple Choices\r\n"; pub const moved_permanently = "HTTP/1.1 301 Moved Permanently\r\n"; pub const found = "HTTP/1.1 302 Found\r\n"; pub const see_other = "HTTP/1.1 303 See Other\r\n"; pub const not_modified = "HTTP/1.1 304 Not Modified\r\n"; pub const temporary_redirect = "HTTP/1.1 307 Temporary Redirect\r\n"; pub const permanent_redirect = "HTTP/1.1 308 Permanent Redirect\r\n"; pub const bad_request = "HTTP/1.1 400 Bad Request\r\n"; pub const unauthorized = "HTTP/1.1 401 Unauthorized\r\n"; pub const payment_required = "HTTP/1.1 402 Payment Required\r\n"; pub const forbidden = "HTTP/1.1 403 Forbidden\r\n"; pub const not_found = "HTTP/1.1 404 Not Found\r\n"; pub const method_not_allowed = "HTTP/1.1 405 Method Not Allowed\r\n"; pub const not_acceptable = "HTTP/1.1 406 Not Acceptable\r\n"; pub const proxy_authentication_required = "HTTP/1.1 407 Proxy Authentication Required\r\n"; pub const request_timeout = "HTTP/1.1 408 Request Timeout\r\n"; pub const conflict = "HTTP/1.1 409 Conflict\r\n"; pub const gone = "HTTP/1.1 410 Gone\r\n"; pub const length_required = "HTTP/1.1 411 Length Required\r\n"; pub const precondition_failed = "HTTP/1.1 412 Precondition Failed\r\n"; pub const content_too_large = "HTTP/1.1 413 Content Too Large\r\n"; pub const uri_too_long = "HTTP/1.1 414 URI Too Long\r\n"; pub const unsupported_media_type = "HTTP/1.1 415 Unsupported Media Type\r\n"; pub const range_not_satisfiable = "HTTP/1.1 416 Range Not Satisfiable\r\n"; pub const expectation_failed = "HTTP/1.1 417 Expectation Failed\r\n"; pub const im_a_teapot = "HTTP/1.1 418 I'm a teapot\r\n"; pub const misdirected_request = "HTTP/1.1 421 Misdirected Request\r\n"; pub const unprocessable_content = "HTTP/1.1 422 Unprocessable Content\r\n"; pub const locked = "HTTP/1.1 423 Locked\r\n"; pub const failed_dependency = "HTTP/1.1 424 Failed Dependency\r\n"; pub const upgrade_required = "HTTP/1.1 426 Upgrade Required\r\n"; pub const precondition_required = "HTTP/1.1 428 Precondition Required\r\n"; pub const too_many_requests = "HTTP/1.1 429 Too Many Requests\r\n"; pub const request_header_fields_too_large = "HTTP/1.1 431 Request Header Fields Too Large\r\n"; pub const unavailable_for_legal_reasons = "HTTP/1.1 451 Unavailable For Legal Reasons\r\n"; pub const internal_server_error = "HTTP/1.1 500 Internal Server Error\r\n"; pub const not_implemented = "HTTP/1.1 501 Not Implemented\r\n"; pub const bad_gateway = "HTTP/1.1 502 Bad Gateway\r\n"; pub const service_unavailable = "HTTP/1.1 503 Service Unavailable\r\n"; pub const gateway_timeout = "HTTP/1.1 504 Gateway Timeout\r\n"; pub const http_version_not_supported = "HTTP/1.1 505 HTTP Version Not Supported\r\n"; pub const variant_also_negotiates = "HTTP/1.1 506 Variant Also Negotiates\r\n"; pub const insufficient_storage = "HTTP/1.1 507 Insufficient Storage\r\n"; pub const loop_detected = "HTTP/1.1 508 Loop Detected\r\n"; pub const not_extended = "HTTP/1.1 510 Not Extended\r\n"; pub const network_authentication_required = "HTTP/1.1 511 Network Authentication Required\r\n"; }; const ResponseEmptyOptions = struct { status_text: []const u8 = status.ok, }; const ResponseOptions = struct { status_text: []const u8 = status.ok, media_type: []const u8 = "text/plain; charset=utf-8", response_body: []const u8, }; fn makeResponseEmpty(options: ResponseEmptyOptions) ![]const u8 { var fbs = std.io.fixedBufferStream(&write_buffer); const writer = fbs.writer(); try writer.print("{s}", .{options.status_text}); try writer.print("\r\n", .{}); return fbs.getWritten(); } fn makeResponse(options: ResponseOptions) ![]const u8 { var fbs = std.io.fixedBufferStream(&write_buffer); const writer = fbs.writer(); try writer.print("{s}", .{options.status_text}); try writer.print("Content-Type: {s}\r\n", .{options.media_type}); try writer.print("Content-Length: {d}\r\n", .{options.response_body.len}); try writer.print("\r\n", .{}); try writer.print("{s}", .{options.response_body}); return fbs.getWritten(); } pub fn process(conn: std.net.Server.Connection) !void { defer conn.stream.close(); var arena = std.heap.ArenaAllocator.init(main.allocator); defer arena.deinit(); const allocator = arena.allocator(); _ = allocator; var running = true; while (running) { var header_end: ?usize = null; var request_len: usize = 0; const request_head = blk: while (conn.stream.read(read_buffer[request_len..])) |len| { if (len == 0) { running = false; return; } header_end = std.mem.indexOfPos(u8, &read_buffer, request_len, "\r\n\r\n"); request_len += len; if (header_end) |end| { break :blk read_buffer[0..end]; } } else |err| { return err; }; const response = try makeResponse(.{ .response_body = "PONG\n", }); try conn.stream.writeAll(response); if (request_len > request_head.len + 4) { @memmove(&read_buffer, read_buffer[request_head.len + 4 .. request_len]); } } }