Skip to documentation
SLOP

tiny.http.Connection

Reference tiny.http Connection

Defined in tiny.http.

API (48)

Actions

Public operations.

Types and contracts

Public types and contracts.

Fields and members

Public fields and members.

No direct callersNo direct callstiny.httpConnection
Static calls · unresolved targets: unknown · external targets: unknown.

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;
Called byCallstest sourcelib.http.src.connectiontest: Connection input storage seals ...private sourcelib.http.src.connection.ConnectionreadSocketprivate sourcelib.http.src.connection.ConnectionrecordInputCapacityRejectionConnectionbufferInput
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.http.src.connectiontest: Connection input storage seals ...ConnectionbufferedInput
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsConnectiondeinitConnectionclose
Static calls · unresolved targets: 1 · external targets: 3.
Called byCallsNo direct callsConnectionreadtest sourcelib.http.src.connectiontest: Connection input storage seals ...ConnectionconsumeBufferedInput
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsConnectionmarkWebSocketConnectioncurrentState
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate sourcelib.http.src.connection.TestConnectiondeinittest sourcelib.http.src.connectiontest: Connection input storage seals ...ConnectioncloseConnectiondeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callsprivate sourcelib.http.src.connection.TestConnectioninittest sourcelib.http.src.connectiontest: Connection input storage seals ...Connectioninit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callstest sourcelib.http.src.connectiontest: Connection input storage seals ...ConnectioninputStatus
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersConnectionwaitForInputConnectioninstallProtocol
Static calls · unresolved targets: 2 · external targets: 1.
Called byCallsNo direct callersConnectionmarkClosingConnectioninterrupt
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsNo direct callsConnectioninterruptprivate sourcelib.http.src.connection.ConnectionreadSocketprivate sourcelib.http.src.connection.ConnectionwaitWritableConnectionwriteprivate sourcelib.http.src.websocket.WebSocketprocessControlFrame+3 moreConnectionmarkClosing
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersConnectioncurrentStateConnectionwaitForInputConnectionmarkWebSocket
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.http.src.connectiontest: Connection input storage seals ...ConnectionconsumeBufferedInputprivate sourcelib.http.src.connection.ConnectionnoteActivityprivate sourcelib.http.src.connection.ConnectionreadSocketConnectionread
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.http.src.connectiontest: Connection input storage seals ...private sourcelib.http.src.connection.ConnectionrecordInputCapacityRejectionConnectionretainInput
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsConnectioninstallProtocolConnectionmarkWebSocketConnectionwaitForInput
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersConnectionmarkClosingprivate sourcelib.http.src.connection.ConnectionnoteActivityprivate sourcelib.http.src.connection.ConnectionwaitWritableConnectionwrite
Static calls · unresolved targets: 0 · external targets: 2.

Complete caller list for Connection.markClosing

8 direct callers.

Audit

Definitions35
Public names35
Members18
Version26.7.0
Revisiondaab053ee433