tiny.http.Connection
Defined in tiny.http.
API (48)
Actions
Public operations.
beginTurnbufferInputbufferedInputcloseconsumeBufferedInputcurrentStatedeinitdriveProtocolhasBufferedInputinitinputCapacityinputStatusinstallProtocolinterruptlastActivitymarkClosingmarkWebSocketreadrequestScratchresponseScratchretainInputsetReadTimeoutsetWriteTimeoutshouldConsumeBufferedInputshouldWaitForReadtakeSockettransportClosedwaitForInputwaitForReadwrite
Types and contracts
Public types and contracts.
Fields and members
Public fields and members.
boot_clockidinput_capacity_rejectionsinput_lengthinput_storagelast_activity_nsprotocolrequest_scratchresponse_scratchsocketstatetransportturn_dispositionwrite_timeout_ms
Source
Source: lib/http/src/connection.zig:181
zig
pub const Connection = struct { id: usize, socket: sys.Socket, state: std.atomic.Value(State), transport: std.atomic.Value(TransportState), boot_clock: time.BootClock, last_activity_ns: std.atomic.Value(u64), input_storage: []u8, input_length: usize, request_scratch: message.RequestScratch, response_scratch: response.Scratch, input_capacity_rejections: std.atomic.Value(u64), write_timeout_ms: ?u32, protocol: ?Protocol, turn_disposition: TurnDisposition, pub const State = enum(u8) { http, websocket, closing, closed, }; const TurnDisposition = enum { close, wait_for_read, consume_buffered_input, }; const TransportState = enum(u8) { open, interrupting, interrupted, closed, }; const Protocol = struct { context: *anyopaque, drive: *const fn (*anyopaque) anyerror!void, deinit: *const fn (*anyopaque) void, }; pub const InputLimits: type = StorageLimits; pub const InputCapacity: type = StorageCapacity; pub const InputStorage: type = ConnectionInputStorage; pub fn init( id: usize, socket: sys.Socket, input_storage: []u8, request_scratch: message.RequestScratch, response_scratch: response.Scratch, boot_clock: time.BootClock, started_at: time.BootInstant, ) Connection { return .{ .id = id, .socket = socket, .state = std.atomic.Value(State).init(.http), .transport = std.atomic.Value(TransportState).init(.open), .boot_clock = boot_clock, .last_activity_ns = std.atomic.Value(u64).init(started_at.asNanoseconds()), .input_storage = input_storage, .input_length = 0, .request_scratch = request_scratch, .response_scratch = response_scratch, .input_capacity_rejections = std.atomic.Value(u64).init(0), .write_timeout_ms = null, .protocol = null, .turn_disposition = .close, }; } pub fn deinit(self: *Connection) void { self.close(); if (self.protocol) |protocol| protocol.deinit(protocol.context); self.protocol = null; self.input_length = 0; self.input_storage = &.{}; self.request_scratch = .{ .headers = &.{}, .body = &.{}, .header_line_bytes = 0, }; self.response_scratch = .{ .headers = &.{}, .head = &.{} }; } pub fn close(self: *Connection) void { while (true) { const current = self.transport.load(.acquire); switch (current) { .closed => return, .interrupting => { std.atomic.spinLoopHint(); continue; }, .open, .interrupted => {}, } if (self.transport.cmpxchgWeak(current, .closed, .acq_rel, .acquire) == null) break; } sys.close(self.socket); self.state.store(.closed, .release); } pub fn takeSocket(self: *Connection) sys.Socket { std.debug.assert(self.protocol == null); std.debug.assert(self.transport.cmpxchgStrong( .open, .closed, .acq_rel, .acquire, ) == null); self.state.store(.closed, .release); return self.socket; } pub fn interrupt(self: *Connection) void { if (self.transport.cmpxchgStrong(.open, .interrupting, .acq_rel, .acquire) != null) return; self.markClosing(); sys.shutdownReadWrite(self.socket); self.transport.store(.interrupted, .release); } pub fn transportClosed(self: *Connection) void { self.transport.store(.closed, .release); self.state.store(.closed, .release); } pub fn currentState(self: *const Connection) State { return self.state.load(.acquire); } pub fn markClosing(self: *Connection) void { if (self.transport.load(.acquire) != .closed) self.state.store(.closing, .release); } pub fn markWebSocket(self: *Connection) void { std.debug.assert(self.currentState() == .http); self.state.store(.websocket, .release); self.waitForInput(); } pub fn beginTurn(self: *Connection) void { self.turn_disposition = .close; } pub fn waitForRead(self: *Connection) void { self.turn_disposition = .wait_for_read; } pub fn waitForInput(self: *Connection) void { self.turn_disposition = .consume_buffered_input; } pub fn shouldWaitForRead(self: *const Connection) bool { return self.turn_disposition != .close; } pub fn shouldConsumeBufferedInput(self: *const Connection) bool { return self.turn_disposition == .consume_buffered_input; } pub fn lastActivity(self: *const Connection) time.BootInstant { return .fromNanoseconds(self.last_activity_ns.load(.acquire)); } pub fn hasBufferedInput(self: *const Connection) bool { return self.input_length != 0; } pub fn bufferedInput(self: *const Connection) []const u8 { return self.input_storage[0..self.input_length]; } pub fn inputCapacity(self: *const Connection) usize { return self.input_storage.len; } pub fn requestScratch(self: *Connection) message.RequestScratch { return self.request_scratch; } pub fn responseScratch(self: *Connection) response.Scratch { return self.response_scratch; } pub fn inputStatus(self: *const Connection) InputStatus { return .{ .capacity_rejections = self.input_capacity_rejections.load(.acquire), }; } pub fn installProtocol( self: *Connection, context: anytype, comptime drive: anytype, comptime deinit_protocol: anytype, ) void { const Context = @TypeOf(context); const info = @typeInfo(Context); if (info != .pointer or info.pointer.size != .one) { @compileError("connection protocol context must be a single-item pointer"); } const Callbacks = struct { fn run(erased: *anyopaque) anyerror!void { const typed: Context = @ptrCast(@alignCast(erased)); return drive(typed); } fn destroy(erased: *anyopaque) void { const typed: Context = @ptrCast(@alignCast(erased)); deinit_protocol(typed); } }; std.debug.assert(self.protocol == null); self.protocol = .{ .context = @ptrCast(@constCast(context)), .drive = Callbacks.run, .deinit = Callbacks.destroy, }; self.state.store(.websocket, .release); self.waitForInput(); } pub fn driveProtocol(self: *Connection) !void { const protocol = self.protocol orelse return error.ProtocolNotInstalled; try protocol.drive(protocol.context); } pub fn read(self: *Connection, buf: []u8) !usize { if (self.transport.load(.acquire) == .closed) { return error.ConnectionClosed; } if (buf.len == 0) return 0; if (self.input_length > 0) { const n = @min(buf.len, self.input_length); @memcpy(buf[0..n], self.input_storage[0..n]); self.consumeBufferedInput(n); try self.noteActivity(); return n; } return self.readSocket(buf); } pub fn bufferInput(self: *Connection) !usize { if (self.input_length == self.input_storage.len) { self.recordInputCapacityRejection(); return error.InputCapacityExceeded; } const n = try self.readSocket(self.input_storage[self.input_length..]); self.input_length += n; return n; } pub fn retainInput(self: *Connection, data: []const u8) InputExhaustion!void { if (data.len > self.input_storage.len - self.input_length) { self.recordInputCapacityRejection(); return error.InputCapacityExceeded; } @memcpy(self.input_storage[self.input_length..][0..data.len], data); self.input_length += data.len; } pub fn consumeBufferedInput(self: *Connection, count: usize) void { std.debug.assert(count <= self.input_length); const remaining = self.input_length - count; std.mem.copyForwards( u8, self.input_storage[0..remaining], self.input_storage[count..self.input_length], ); self.input_length = remaining; } fn recordInputCapacityRejection(self: *Connection) void { var current = self.input_capacity_rejections.load(.acquire); while (current != std.math.maxInt(u64)) { if (self.input_capacity_rejections.cmpxchgWeak( current, current + 1, .acq_rel, .acquire, )) |observed| { current = observed; } else { return; } } } fn readSocket(self: *Connection, buf: []u8) !usize { std.debug.assert(buf.len != 0); if (self.transport.load(.acquire) == .closed) { return error.ConnectionClosed; } const n = sys.recv(self.socket, buf, 0) catch |err| switch (err) { error.BadFileDescriptor => { self.markClosing(); return error.ConnectionClosed; }, error.ConnectionResetByPeer, error.ConnectionTimedOut, => { self.markClosing(); return error.ConnectionClosed; }, error.WouldBlock => return error.WouldBlock, else => return err, }; if (n == 0) { self.markClosing(); return error.ConnectionClosed; } try self.noteActivity(); return n; } pub fn write(self: *Connection, data: []const u8) !void { if (self.transport.load(.acquire) == .closed) { return error.ConnectionClosed; } var sent: usize = 0; while (sent < data.len) { const n = sys.sendNoSignal(self.socket, data[sent..]) catch |err| switch (err) { error.ConnectionResetByPeer, error.BrokenPipe, => { self.markClosing(); return error.ConnectionClosed; }, error.WouldBlock => { try self.waitWritable(); continue; }, else => return err, }; if (n == 0) { self.markClosing(); return error.ConnectionClosed; } sent += n; try self.noteActivity(); } } fn waitWritable(self: *Connection) !void { const ready = try sys.pollWritable(self.socket, self.writePollTimeoutMs()); if (!ready) { self.markClosing(); return error.ConnectionTimedOut; } } fn noteActivity(self: *Connection) time.ClockError!void { const now = try self.boot_clock.now(); self.last_activity_ns.store(now.asNanoseconds(), .release); } fn writePollTimeoutMs(self: *const Connection) i32 { const timeout = self.write_timeout_ms orelse return -1; const max_timeout: u32 = @intCast(std.math.maxInt(i32)); return @intCast(@min(timeout, max_timeout)); } pub fn setReadTimeout(self: *Connection, timeout_ms: u32) !void { try sys.setReadTimeout(self.socket, timeout_ms); } pub fn setWriteTimeout(self: *Connection, timeout_ms: u32) !void { try sys.setWriteTimeout(self.socket, timeout_ms); self.write_timeout_ms = if (timeout_ms == 0) null else timeout_ms; }};Source: lib/http/src/root.zig:30
zig
pub const Connection = connection.Connection;Complete caller list for Connection.markClosing
8 direct callers.
tiny.http.Connection.interrupt[method] atlib/http/src/connection.zig:297lib.http.src.connection.Connection.readSocket[method] — private source atlib/http/src/connection.zig:471in nearest public ownerlib.http.src.connectionlib.http.src.connection.Connection.waitWritable[method] — private source atlib/http/src/connection.zig:526in nearest public ownerlib.http.src.connectiontiny.http.Connection.write[method] atlib/http/src/connection.zig:498lib.http.src.websocket.WebSocket.processControlFrame[method] — private source atlib/http/src/websocket.zig:726in nearest public ownerlib.http.src.websocketlib.http.src.websocket.WebSocket.readFrameBytes[method] — private source atlib/http/src/websocket.zig:812in nearest public ownerlib.http.src.websocketlib.http.src.websocket.WebSocket.rejectMessageTooBig[method] — private source atlib/http/src/websocket.zig:887in nearest public ownerlib.http.src.websocketlib.http.src.websocket.WebSocket.rejectProtocol[method] — private source atlib/http/src/websocket.zig:879in nearest public ownerlib.http.src.websocket
Audit
| Definitions | 35 |
|---|---|
| Public names | 35 |
| Members | 18 |
| Version | 26.7.0 |
| Revision | daab053ee433 |