authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-05-02 21:44:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:28-07:00
loge4126aa213e30d4fe93981a27160acc56e3db4f8
treeeabc86ac0072c9e6ef775918fd51e1fff3efc432
parent4716c0036678cef93388a25401f32f03fb16dbc1

std.http.Server: remove the 25 header limit

no longer makes sense when there is an output buffer

1 files changed, 18 insertions(+), 59 deletions(-)

lib/std/http/Server.zig+18-59
......@@ -298,16 +298,13 @@ pub const Request = struct {
298298 /// no error is surfaced.
299299 ///
300300 /// Asserts status is not `continue`.
301 /// Asserts there are at most 25 extra_headers.
302301 /// Asserts that "\r\n" does not occur in any header name or value.
303302 pub fn respond(
304303 request: *Request,
305304 content: []const u8,
306305 options: RespondOptions,
307306 ) std.io.Writer.Error!void {
308 const max_extra_headers = 25;
309307 assert(options.status != .@"continue");
310 assert(options.extra_headers.len <= max_extra_headers);
311308 if (std.debug.runtime_safety) {
312309 for (options.extra_headers) |header| {
313310 assert(header.name.len != 0);
......@@ -323,88 +320,50 @@ pub const Request = struct {
323320
324321 const phrase = options.reason orelse options.status.phrase() orelse "";
325322
326 var first_buffer: [500]u8 = undefined;
327 var h = std.ArrayListUnmanaged(u8).initBuffer(&first_buffer);
323 const out = request.server.out;
328324 if (request.head.expect != null) {
329325 // reader() and hence discardBody() above sets expect to null if it
330326 // is handled. So the fact that it is not null here means unhandled.
331 h.appendSliceAssumeCapacity("HTTP/1.1 417 Expectation Failed\r\n");
332 if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n");
333 h.appendSliceAssumeCapacity("content-length: 0\r\n\r\n");
334 try request.server.out.writeAll(h.items);
327 var vecs: [3][]const u8 = .{
328 "HTTP/1.1 417 Expectation Failed\r\n",
329 if (keep_alive) "" else "connection: close\r\n",
330 "content-length: 0\r\n\r\n",
331 };
332 try out.writeVecAll(&vecs);
335333 return;
336334 }
337 h.printAssumeCapacity("{s} {d} {s}\r\n", .{
335 try out.print("{s} {d} {s}\r\n", .{
338336 @tagName(options.version), @intFromEnum(options.status), phrase,
339337 });
340338
341339 switch (options.version) {
342 .@"HTTP/1.0" => if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"),
343 .@"HTTP/1.1" => if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n"),
340 .@"HTTP/1.0" => if (keep_alive) try out.writeAll("connection: keep-alive\r\n"),
341 .@"HTTP/1.1" => if (!keep_alive) try out.writeAll("connection: close\r\n"),
344342 }
345343
346344 if (options.transfer_encoding) |transfer_encoding| switch (transfer_encoding) {
347345 .none => {},
348 .chunked => h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"),
346 .chunked => try out.writeAll("transfer-encoding: chunked\r\n"),
349347 } else {
350 h.printAssumeCapacity("content-length: {d}\r\n", .{content.len});
348 try out.print("content-length: {d}\r\n", .{content.len});
351349 }
352350
353 var chunk_header_buffer: [18]u8 = undefined;
354 var iovecs: [max_extra_headers * 4 + 3][]const u8 = undefined;
355 var iovecs_len: usize = 0;
356
357 iovecs[iovecs_len] = h.items;
358 iovecs_len += 1;
359
360351 for (options.extra_headers) |header| {
361 iovecs[iovecs_len] = header.name;
362 iovecs_len += 1;
363
364 iovecs[iovecs_len] = ": ";
365 iovecs_len += 1;
366
367 if (header.value.len != 0) {
368 iovecs[iovecs_len] = header.value;
369 iovecs_len += 1;
370 }
371
372 iovecs[iovecs_len] = "\r\n";
373 iovecs_len += 1;
352 var vecs: [4][]const u8 = .{ header.name, ": ", header.value, "\r\n" };
353 try out.writeVecAll(&vecs);
374354 }
375355
376 iovecs[iovecs_len] = "\r\n";
377 iovecs_len += 1;
356 try out.writeAll("\r\n");
378357
379358 if (request.head.method != .HEAD) {
380359 const is_chunked = (options.transfer_encoding orelse .none) == .chunked;
381360 if (is_chunked) {
382 if (content.len > 0) {
383 const chunk_header = std.fmt.bufPrint(
384 &chunk_header_buffer,
385 "{x}\r\n",
386 .{content.len},
387 ) catch unreachable;
388
389 iovecs[iovecs_len] = chunk_header;
390 iovecs_len += 1;
391
392 iovecs[iovecs_len] = content;
393 iovecs_len += 1;
394
395 iovecs[iovecs_len] = "\r\n";
396 iovecs_len += 1;
397 }
398
399 iovecs[iovecs_len] = "0\r\n\r\n";
400 iovecs_len += 1;
361 if (content.len > 0) try out.print("{x}\r\n{s}\r\n", .{ content.len, content });
362 try out.writeAll("0\r\n\r\n");
401363 } else if (content.len > 0) {
402 iovecs[iovecs_len] = content;
403 iovecs_len += 1;
364 try out.writeAll(content);
404365 }
405366 }
406
407 try request.server.out.writeVecAll(iovecs[0..iovecs_len]);
408367 }
409368
410369 pub const RespondStreamingOptions = struct {