╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ web.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
136 ) !void { 137 var send_buffer = [_]u8{0} ** 4096; 138 var recv_buffer = [_]u8{0} ** 4096; 139 var conn_br = stream.reader(io, &recv_buffer); 140 var conn_bw = stream.writer(io, &send_buffer); 141 var http_server = std.http.Server.init(&conn_br.interface, &conn_bw.interface); 142 143 while (http_server.reader.state == .ready) { 144 var request = http_server.receiveHead() catch |receive_err| switch (receive_err) { 145 error.HttpConnectionClosing => break, 146 error.ReadFailed => break, 147 else => |e| return e, 148 }; 149 150 handleRequest(io, &http_server, &request, allocator, admin_repo_path) catch |request_err| { 151 try err.print("web ui request failed: {s}\n", .{@errorName(request_err)}); 152 try err.flush(); 153 if (http_server.reader.state == .received_head) { 154 http_server.reader.state = .ready; 155 } 156 try writeSimpleResponse(&http_server, 500, "Internal Server Error", "text/plain", @errorName(request_err)); 157 }; 158 try http_server.out.flush(); 159 break; 160 } 161 } 162 163 fn handleRequest( 164 io: std.Io, 165 http_server: *std.http.Server, 166 request: *std.http.Server.Request, 167 allocator: std.mem.Allocator, 168 admin_repo_path: []const u8, 169 ) !void { 170 const method = if (request.head.method == .HEAD) .GET else request.head.method; 171 if (method != .GET) { 172 if (http_server.reader.state == .received_head) { 173 http_server.reader.state = .ready; 174 } 175 try writeSimpleResponse(http_server, 405, "Method Not Allowed", "text/plain", "method not allowed"); 176 return; 177 } 178 179 const uri = try std.Uri.parseAfterScheme("", request.head.target); 180 const path = uri.path.percent_encoded; 181 182 if (std.mem.eql(u8, path, "/favicon.ico")) { 183 if (http_server.reader.state == .received_head) { 184 http_server.reader.state = .ready; 185 } 186 try writeStaticResponse(http_server, 204, "No Content", "image/x-icon", "", true); 187 return; 188 } 189 190 const asset = findAsset(path) orelse { 191 if (http_server.reader.state == .received_head) { 192 http_server.reader.state = .ready; 193 } 194 try writeSimpleResponse(http_server, 404, "Not Found", "text/plain", "not found"); 195 return; 196 }; 197 198 if (http_server.reader.state == .received_head) { 199 http_server.reader.state = .ready; 200 } 201 202 if (std.mem.eql(u8, asset.path, "index.html")) { 203 const index_html = try renderIndexHtml(io, allocator, admin_repo_path); 204 defer allocator.free(index_html); 205 try writeStaticResponse(http_server, 200, "OK", asset.content_type, index_html, request.head.method == .HEAD); 206 } else { 207 try writeStaticResponse(http_server, 200, "OK", asset.content_type, asset.body, request.head.method == .HEAD); 208 } 209 } 210 211 fn findAsset(request_path: []const u8) ?Asset { 212 const path = if (std.mem.eql(u8, request_path, "/")) 213 "index.html" 214 else if (request_path.len > 1 and request_path[0] == '/') 215 request_path[1..] 216 else 217 return null; 218 219 for (assets) |asset| { 220 if (std.mem.eql(u8, path, asset.path)) return asset; 221 } 222 return null; 223 } 224 225 fn writeSimpleResponse( 226 http_server: *std.http.Server, 227 code: u16, 228 message: []const u8, 229 content_type: []const u8, 230 body: []const u8, 231 ) !void { 232 try http_server.out.print( 233 "HTTP/1.1 {d} {s}\r\nContent-Type: {s}\r\nContent-Length: {d}\r\n\r\n{s}", 234 .{ code, message, content_type, body.len, body }, 235 ); 236 } 237 238 fn writeStaticResponse( 239 http_server: *std.http.Server, 240 code: u16, 241 message: []const u8, 242 content_type: []const u8, 243 body: []const u8, 244 head_only: bool, 245 ) !void { 246 try http_server.out.print( 247 "HTTP/1.1 {d} {s}\r\nContent-Type: {s}\r\nContent-Length: {d}\r\n\r\n", 248 .{ code, message, content_type, body.len }, 249 ); 250 if (!head_only) { 251 try http_server.out.writeAll(body); 252 } 253 } 254 255 fn logError(err: *std.Io.Writer, comptime fmt: []const u8, args: anytype) void { 256 err.print(fmt, args) catch return; 257 err.flush() catch {}; 258 } 259