| ... | ... | @@ -3,6 +3,7 @@ const builtin = @import("builtin"); |
| 3 | 3 | const net = @import("net"); |
| 4 | 4 | const url = @import("url"); |
| 5 | 5 | const nio = @import("nio"); |
| 6 | const extras = @import("extras"); |
| 6 | 7 | |
| 7 | 8 | pub const Method = enum { |
| 8 | 9 | GET, |
| ... | ... | @@ -214,7 +215,7 @@ pub fn open(allocator: std.mem.Allocator, method: Method, input: []const u8) !Cl |
| 214 | 215 | .writer = bufw, |
| 215 | 216 | .reader = .init(conn), |
| 216 | 217 | .status = @enumFromInt(0), |
| 217 | | .headers_raw = "", |
| 218 | .headers = .init(allocator), |
| 218 | 219 | }; |
| 219 | 220 | } |
| 220 | 221 | |
| ... | ... | @@ -224,11 +225,11 @@ pub const ClientRequest = struct { |
| 224 | 225 | writer: nio.BufferedWriter(4096, net.Stream), |
| 225 | 226 | reader: nio.BufferedReader(4096, net.Stream), |
| 226 | 227 | status: Status, |
| 227 | | headers_raw: []const u8, |
| 228 | headers: HeadersMap, |
| 228 | 229 | |
| 229 | | pub fn close(req: *const ClientRequest, allocator: std.mem.Allocator) void { |
| 230 | pub fn close(req: *ClientRequest) void { |
| 230 | 231 | req.stream.close(); |
| 231 | | allocator.free(req.headers_raw); |
| 232 | req.headers.deinit(); |
| 232 | 233 | } |
| 233 | 234 | |
| 234 | 235 | pub fn writeHeader(req: *ClientRequest, name: []const u8, value: []const u8) !void { |
| ... | ... | @@ -258,13 +259,19 @@ pub const ClientRequest = struct { |
| 258 | 259 | _ = try req.readUntilDelimitersBuf(&phrase_buf, "\r\n"); |
| 259 | 260 | req.status = status; |
| 260 | 261 | |
| 261 | | var headers_list = std.ArrayList(u8).init(req.allocator); |
| 262 | | errdefer headers_list.deinit(); |
| 262 | var headers_list = req.headers.data.list.toManaged(req.allocator); |
| 263 | defer req.headers.data.list = headers_list.moveToUnmanaged(); |
| 263 | 264 | while (true) { |
| 264 | | const header_len = try req.readUntilDelimitersArrayList(&headers_list, "\r\n", 1024); |
| 265 | | if (header_len == 0) break; |
| 265 | const header_line = try req.readUntilDelimitersArrayList(&headers_list, "\r\n", 1024); |
| 266 | if (header_line.len == 0) break; |
| 267 | const colon_pos = std.mem.indexOfScalar(u8, header_line, ':') orelse return error.Bad; |
| 268 | const name = header_line[0..colon_pos]; |
| 269 | if (!extras.matchesAll(u8, name, std.ascii.isAscii)) return error.Bad; |
| 270 | for (name) |*c| c.* = std.ascii.toLower(c.*); |
| 271 | if (header_line.len == colon_pos or header_line[colon_pos + 1] != ' ') return error.Bad; |
| 272 | const value = header_line[colon_pos + 2 ..]; |
| 273 | try req.headers.data.lengths.appendSlice(req.allocator, &.{ name.len, 2, value.len, 2 }); |
| 266 | 274 | } |
| 267 | | req.headers_raw = try headers_list.toOwnedSlice(); |
| 268 | 275 | } |
| 269 | 276 | |
| 270 | 277 | pub const ReadError = net.Stream.ReadError; |
| ... | ... | @@ -285,3 +292,29 @@ pub const ClientRequest = struct { |
| 285 | 292 | }; |
| 286 | 293 | } |
| 287 | 294 | }; |
| 295 | |
| 296 | pub const HeadersMap = struct { |
| 297 | data: extras.ManyArrayList(u8), |
| 298 | |
| 299 | pub fn init(allocator: std.mem.Allocator) HeadersMap { |
| 300 | return .{ |
| 301 | .data = .init(allocator), |
| 302 | }; |
| 303 | } |
| 304 | |
| 305 | pub fn deinit(map: *HeadersMap) void { |
| 306 | map.data.deinit(); |
| 307 | } |
| 308 | |
| 309 | pub fn count(map: *const HeadersMap) usize { |
| 310 | return map.data.lengths.items.len / 4; |
| 311 | } |
| 312 | |
| 313 | pub fn name(map: *const HeadersMap, idx: usize) []const u8 { |
| 314 | return map.data.items(idx * 4 + 0); |
| 315 | } |
| 316 | |
| 317 | pub fn value(map: *const HeadersMap, idx: usize) []const u8 { |
| 318 | return map.data.items(idx * 4 + 2); |
| 319 | } |
| 320 | }; |