Function respond [src]

Send an entire HTTP response to the client, including headers and body. Automatically handles HEAD requests by omitting the body. Unless transfer_encoding is specified, uses the "content-length" header. If the request contains a body and the connection is to be reused, discards the request body, leaving the Server in the ready state. If this discarding fails, the connection is marked as not to be reused and no error is surfaced. Asserts status is not continue. Asserts there are at most 25 extra_headers. Asserts that "\r\n" does not occur in any header name or value.

Prototype

pub fn respond( request: *Request, content: []const u8, options: RespondOptions, ) Response.WriteError!void

Parameters

request: *Requestcontent: []const u8options: RespondOptions

Possible Errors

AccessDenied WriteError

File descriptor does not hold the required rights to write to it.

BrokenPipe WriteError
ConnectionResetByPeer WriteError

Connection reset by peer.

DeviceBusy WriteError
DiskQuota WriteError
FileTooBig WriteError
InputOutput WriteError
InvalidArgument WriteError
LockViolation WriteError

The process cannot access the file because another process has locked a portion of the file. Windows-only.

MessageTooBig WriteError

The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible. The message is not transmitted.

NoDevice WriteError

This error occurs when a device gets disconnected before or mid-flush while it's being written to - errno(6): No such device or address.

NoSpaceLeft WriteError
NotOpenForWriting WriteError
OperationAborted WriteError
PermissionDenied WriteError
ProcessNotFound WriteError

This error occurs in Linux if the process being written to no longer exists.

SystemResources WriteError
Unexpected UnexpectedError

The Operating System returned an undocumented error code.

This error is in theory not possible, but it would be better to handle this error than to invoke undefined behavior.

When this error code is observed, it usually means the Zig Standard Library needs a small patch to add the error code to the error set for the respective function.

WouldBlock WriteError

This error occurs when no global event loop is configured, and reading from the file descriptor would block.

Source

pub fn respond( request: *Request, content: []const u8, options: RespondOptions, ) Response.WriteError!void { const max_extra_headers = 25; assert(options.status != .@"continue"); assert(options.extra_headers.len <= max_extra_headers); if (std.debug.runtime_safety) { for (options.extra_headers) |header| { assert(header.name.len != 0); assert(std.mem.indexOfScalar(u8, header.name, ':') == null); assert(std.mem.indexOfPosLinear(u8, header.name, 0, "\r\n") == null); assert(std.mem.indexOfPosLinear(u8, header.value, 0, "\r\n") == null); } } const transfer_encoding_none = (options.transfer_encoding orelse .chunked) == .none; const server_keep_alive = !transfer_encoding_none and options.keep_alive; const keep_alive = request.discardBody(server_keep_alive); const phrase = options.reason orelse options.status.phrase() orelse ""; var first_buffer: [500]u8 = undefined; var h = std.ArrayListUnmanaged(u8).initBuffer(&first_buffer); if (request.head.expect != null) { // reader() and hence discardBody() above sets expect to null if it // is handled. So the fact that it is not null here means unhandled. h.appendSliceAssumeCapacity("HTTP/1.1 417 Expectation Failed\r\n"); if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n"); h.appendSliceAssumeCapacity("content-length: 0\r\n\r\n"); try request.server.connection.stream.writeAll(h.items); return; } h.fixedWriter().print("{s} {d} {s}\r\n", .{ @tagName(options.version), @intFromEnum(options.status), phrase, }) catch unreachable; switch (options.version) { .@"HTTP/1.0" => if (keep_alive) h.appendSliceAssumeCapacity("connection: keep-alive\r\n"), .@"HTTP/1.1" => if (!keep_alive) h.appendSliceAssumeCapacity("connection: close\r\n"), } if (options.transfer_encoding) |transfer_encoding| switch (transfer_encoding) { .none => {}, .chunked => h.appendSliceAssumeCapacity("transfer-encoding: chunked\r\n"), } else { h.fixedWriter().print("content-length: {d}\r\n", .{content.len}) catch unreachable; } var chunk_header_buffer: [18]u8 = undefined; var iovecs: [max_extra_headers * 4 + 3]std.posix.iovec_const = undefined; var iovecs_len: usize = 0; iovecs[iovecs_len] = .{ .base = h.items.ptr, .len = h.items.len, }; iovecs_len += 1; for (options.extra_headers) |header| { iovecs[iovecs_len] = .{ .base = header.name.ptr, .len = header.name.len, }; iovecs_len += 1; iovecs[iovecs_len] = .{ .base = ": ", .len = 2, }; iovecs_len += 1; if (header.value.len != 0) { iovecs[iovecs_len] = .{ .base = header.value.ptr, .len = header.value.len, }; iovecs_len += 1; } iovecs[iovecs_len] = .{ .base = "\r\n", .len = 2, }; iovecs_len += 1; } iovecs[iovecs_len] = .{ .base = "\r\n", .len = 2, }; iovecs_len += 1; if (request.head.method != .HEAD) { const is_chunked = (options.transfer_encoding orelse .none) == .chunked; if (is_chunked) { if (content.len > 0) { const chunk_header = std.fmt.bufPrint( &chunk_header_buffer, "{x}\r\n", .{content.len}, ) catch unreachable; iovecs[iovecs_len] = .{ .base = chunk_header.ptr, .len = chunk_header.len, }; iovecs_len += 1; iovecs[iovecs_len] = .{ .base = content.ptr, .len = content.len, }; iovecs_len += 1; iovecs[iovecs_len] = .{ .base = "\r\n", .len = 2, }; iovecs_len += 1; } iovecs[iovecs_len] = .{ .base = "0\r\n\r\n", .len = 5, }; iovecs_len += 1; } else if (content.len > 0) { iovecs[iovecs_len] = .{ .base = content.ptr, .len = content.len, }; iovecs_len += 1; } } try request.server.connection.stream.writevAll(iovecs[0..iovecs_len]); }