net

package
v0.30.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package net — the listen address value.

Package net — the completed-call observation record.

Package net — the outbound client configuration.

Package net — range 0.2.11.* (ADR 0029 core/net block).

Package net — the accepted stream connection port.

Package net — the shutdown signal a long-lived handler observes.

Package net — the configuration-friendly duration value.

Package net — declares the sentinel *errs.Error outcomes of the network domain. Each var's name equals its errs.Define Reason in SCREAMING_SNAKE form. Service implementations and the pkg/v1 facades wrap these sentinels; they declare no codes of their own (the ADR 0016 proc precedent).

Package net — compile-time interface assertions, kept out of the production source per KTN-IFACE-ASSERT-PLACEMENT.

Package net — the opaque, redacting TLS identity value.

Package net — the on-disk TLS material description.

Package net — the TLS identity construction parameters.

Package net — the per-group resource ceilings.

Package net — the loop engine's contract: the connection reference, the posted operation, the option kinds a loop group judges, and the ports a loop and its handler implement (ADR 0186 §D4).

Package net — a loop group's reported state: the backend it runs on, how it got there, and its rings' counters.

Package net — the loop engine's configuration value and its backends.

Package net — TLS material parsing helpers shared by NewIdentity.

Package net — the generic handler decorator.

Package net — the received datagram port.

Package net — the server lifecycle phase.

Package net — the outbound authorisation port.

Package net — compile-time interface assertions, kept out of the production source per KTN-IFACE-ASSERT-PLACEMENT.

Package net — the completed outbound response value.

Package net — the Server-Sent Events frame: the value a stream sends and the rules a frame must satisfy to be carried at all. Writing it — the encoder that splits Data into data lines, and the comment frame — is internal/service/net/sse's (ADR 0160 §4).

Package net — the server's reported state.

Package net — the per-phase deadlines.

Package net — the WebSocket protocol's vocabulary (RFC 6455): the opening handshake's constants and the control-frame ceiling. Reading and writing the protocol — the handshake's key check and digest, the frame codec, the close payload, the UTF-8 check — is internal/service/net/websocket's (ADR 0160 §4).

Package net — the WebSocket close code (RFC 6455 §7.4) and the predicates that govern it in both directions. The close payload's wire form is internal/service/net/websocket's (ADR 0160 §4).

Package net — the WebSocket application message (RFC 6455 §5.6). Its UTF-8 rule (§8.1) is checked by the engine on the bytes, as they come off and go onto the wire: internal/service/net/websocket.ValidateText (ADR 0160 §4).

Package net — the WebSocket frame opcode (RFC 6455 §5.2).

Package net — shared sentinel-wrapping helper.

Index

Constants

View Source
const (
	// WSKeyHeader carries the client's 16 random bytes, base64-encoded.
	WSKeyHeader string = "Sec-WebSocket-Key"
	// WSAcceptHeader carries the server's proof it understood the handshake.
	WSAcceptHeader string = "Sec-WebSocket-Accept"
	// WSVersionHeader carries the client's version, and the server's
	// counter-offer when it refuses one.
	WSVersionHeader string = "Sec-WebSocket-Version"
	// WSProtocolHeader carries the subprotocol list, then the single choice.
	WSProtocolHeader string = "Sec-WebSocket-Protocol"
	// WSExtensionsHeader carries the extension offer. This domain negotiates
	// none, so it is never echoed — see the package CLAUDE.md.
	WSExtensionsHeader string = "Sec-WebSocket-Extensions"
	// WSUpgradeToken is the token both Upgrade headers must carry.
	WSUpgradeToken string = "websocket"
)

The handshake headers, spelled once so no caller mistypes one into a silent miss.

View Source
const CodeAddressInUse errs.Code = 0x00_02_0B_60 // 0.2.11.96

CodeAddressInUse identifies an address another socket already holds, refused at Start rather than shared (#1330).

View Source
const CodeAlreadyStarted errs.Code = 0x00_02_0B_05 // 0.2.11.5

CodeAlreadyStarted identifies a Start call on a server that is already serving.

View Source
const CodeCORSMisconfigured errs.Code = 0x00_02_0B_50 // 0.2.11.80

CodeCORSMisconfigured identifies a CORS policy the SDK refuses to build: no origin, a wildcard with credentials, or an origin, method, header or age it cannot honour.

View Source
const CodeCallFailed errs.Code = 0x00_02_0B_13 // 0.2.11.19

CodeCallFailed identifies an outbound call that did not produce a response.

View Source
const CodeCompressInvalid errs.Code = 0x00_02_0B_49 // 0.2.11.73

CodeCompressInvalid identifies a compression configuration outside its range.

View Source
const CodeConnLimitReached errs.Code = 0x00_02_0B_09 // 0.2.11.9

CodeConnLimitReached identifies a connection rejected because the group's concurrency ceiling was saturated.

View Source
const CodeDrainTimeout errs.Code = 0x00_02_0B_0A // 0.2.11.10

CodeDrainTimeout identifies a shutdown whose drain budget expired with connections still in flight; the remainder is closed hard.

View Source
const CodeEngineUnsupported errs.Code = 0x00_02_0B_38 // 0.2.11.56

CodeEngineUnsupported identifies an Engine a group cannot run on: one its handler's type has no such engine for, one another option contradicts, or a value outside the engine constants (ADR 0187).

View Source
const CodeGroupDuplicate errs.Code = 0x00_02_0B_0C // 0.2.11.12

CodeGroupDuplicate identifies a second declaration of an already-declared listener group name.

View Source
const CodeGroupNameConflict errs.Code = 0x00_02_0B_3A // 0.2.11.58

CodeGroupNameConflict identifies a Name option that contradicts the name the group was declared with, or names nothing (ADR 0187).

View Source
const CodeGroupUnknown errs.Code = 0x00_02_0B_0B // 0.2.11.11

CodeGroupUnknown identifies a lookup for a listener group that was never declared.

View Source
const CodeHTTP1BodyTooLarge errs.Code = 0x00_02_0B_34 // 0.2.11.52

CodeHTTP1BodyTooLarge identifies a Content-Length past the profile's body bound.

View Source
const CodeHTTP1ContentLengthInvalid errs.Code = 0x00_02_0B_2F // 0.2.11.47

CodeHTTP1ContentLengthInvalid identifies a Content-Length the profile cannot frame by: empty, not decimal digits, too long, or present twice, equal or not.

View Source
const CodeHTTP1ExpectRefused errs.Code = 0x00_02_0B_31 // 0.2.11.49

CodeHTTP1ExpectRefused identifies a request carrying an Expect field: the profile sends no interim response, so 100-continue cannot be met.

View Source
const CodeHTTP1FieldDuplicated errs.Code = 0x00_02_0B_30 // 0.2.11.48

CodeHTTP1FieldDuplicated identifies a second Authorization or Host field in one request.

View Source
const CodeHTTP1HeadTooLarge errs.Code = 0x00_02_0B_32 // 0.2.11.50

CodeHTTP1HeadTooLarge identifies a request head past the profile's head bound or its field-line count.

View Source
const CodeHTTP1MethodUnsupported errs.Code = 0x00_02_0B_35 // 0.2.11.53

CodeHTTP1MethodUnsupported identifies a CONNECT request: the profile opens no tunnel.

View Source
const CodeHTTP1Misconfigured errs.Code = 0x00_02_0B_37 // 0.2.11.55

CodeHTTP1Misconfigured identifies profile limits the codec refuses to interpret: a negative bound, or one past its maximum.

View Source
const CodeHTTP1RequestMalformed errs.Code = 0x00_02_0B_2D // 0.2.11.45

CodeHTTP1RequestMalformed identifies a request the restricted HTTP/1.1 profile refuses for its syntax: a bare CR or LF, a NUL or other control byte in a value, obs-fold, whitespace before a colon, a name outside tchar, or a request line that is not method, target and HTTP/1.1 (ADR 0186 §D6).

View Source
const CodeHTTP1ResponseInvalid errs.Code = 0x00_02_0B_36 // 0.2.11.54

CodeHTTP1ResponseInvalid identifies a response the profile's writer refuses to put on the wire: a status outside 200 to 999, a body on a 204 or a 304, a field name that is not a token or that the codec writes itself, or a field value with a control byte.

View Source
const CodeHTTP1TargetTooLong errs.Code = 0x00_02_0B_33 // 0.2.11.51

CodeHTTP1TargetTooLong identifies a request-target past the profile's target bound.

View Source
const CodeHTTP1TransferEncoding errs.Code = 0x00_02_0B_2E // 0.2.11.46

CodeHTTP1TransferEncoding identifies a request carrying a Transfer-Encoding field, chunked or not: the profile frames by Content-Length alone, so it carries no chunked body and no trailer.

View Source
const CodeHandlerAlreadySet errs.Code = 0x00_02_0B_2C // 0.2.11.44

CodeHandlerAlreadySet identifies a second handler declaration on one stream group — Handle, HandleFunc, HandleHTTP or HandleLoop, in any order — which does not replace the first (ADR 0186 §D1).

View Source
const CodeHandlerAmbiguous errs.Code = 0x00_02_0B_40 // 0.2.11.64

CodeHandlerAmbiguous identifies a handler the simple API cannot assign to one engine: it implements both interfaces of its entry and no Engine chooses.

View Source
const CodeHandlerMissing errs.Code = 0x00_02_0B_07 // 0.2.11.7

CodeHandlerMissing identifies a listener group that was bound without a handler.

View Source
const CodeHandlerPanic errs.Code = 0x00_02_0B_08 // 0.2.11.8

CodeHandlerPanic identifies a panic recovered inside a handler; the connection is closed but the process survives.

View Source
const CodeHandlerUnsupported errs.Code = 0x00_02_0B_41 // 0.2.11.65

CodeHandlerUnsupported identifies a handler no engine of the simple API's entry serves.

View Source
const CodeInvalidAddress errs.Code = 0x00_02_0B_02 // 0.2.11.2

CodeInvalidAddress identifies a listen or dial address that is syntactically unusable (empty host and port, malformed socket path, negative port).

View Source
const CodeInvalidDuration errs.Code = 0x00_02_0B_15 // 0.2.11.21

CodeInvalidDuration identifies a configuration duration that is neither a Go duration literal nor an integer nanosecond count.

View Source
const CodeListenFailed errs.Code = 0x00_02_0B_01 // 0.2.11.1

CodeListenFailed identifies a listener that could not be bound to its address.

View Source
const CodeLoopBackendUnavailable errs.Code = 0x00_02_0B_25 // 0.2.11.37

CodeLoopBackendUnavailable identifies an explicit loop backend the running kernel's probe refused, on a platform that has the mechanic (ADR 0186 §D5).

View Source
const CodeLoopCapabilityRefused errs.Code = 0x00_02_0B_3C // 0.2.11.60

CodeLoopCapabilityRefused identifies a LoopValue.Capabilities entry the loop cannot opt into: an engine, a class B capability or an unknown token (ADR 0189).

View Source
const CodeLoopFileRefused errs.Code = 0x00_02_0B_51 // 0.2.11.81

CodeLoopFileRefused identifies a file body the loop's backend cannot send now: no descriptor or pipe left, or a kernel without the operation.

View Source
const CodeLoopFileTruncated errs.Code = 0x00_02_0B_52 // 0.2.11.82

CodeLoopFileTruncated identifies a file body that ended before the length its head declared: the file shrank during the send.

View Source
const CodeLoopGroupStalled errs.Code = 0x00_02_0B_3B // 0.2.11.59

CodeLoopGroupStalled identifies a loop group every ring of which the watchdog drained while the server was serving (ADR 0188).

View Source
const CodeLoopMiddlewareInvalid errs.Code = 0x00_02_0B_3D // 0.2.11.61

CodeLoopMiddlewareInvalid identifies a loop middleware whose layer does not forward every callback to the handler it wraps.

View Source
const CodeLoopNoDescriptors errs.Code = 0x00_02_0B_26 // 0.2.11.38

CodeLoopNoDescriptors identifies a loop group refused at Start because the effective RLIMIT_NOFILE is zero or unreadable, which leaves no descriptor for a ring's table or a connection (ADR 0186 §D5).

View Source
const CodeLoopOptionUnsupported errs.Code = 0x00_02_0B_24 // 0.2.11.36

CodeLoopOptionUnsupported identifies an option a group cannot honour on its engine: Loop on a group not served by a loop handler, BatchReplies on a stream group (ADR 0192), or, on a loop group, Use or an option neither the engine nor the handler honours (ADR 0186 §D1).

View Source
const CodeLoopOutboundFull errs.Code = 0x00_02_0B_3F // 0.2.11.63

CodeLoopOutboundFull identifies a job refused because the lane's queue to its worker is full.

View Source
const CodeLoopOutputFull errs.Code = 0x00_02_0B_28 // 0.2.11.40

CodeLoopOutputFull identifies a write refused because the connection's output, or the loop's global output budget, is at its ceiling.

View Source
const CodeLoopPostQueueFull errs.Code = 0x00_02_0B_2A // 0.2.11.42

CodeLoopPostQueueFull identifies a Post refused because the loop's preallocated queue of posted operations is full.

View Source
const CodeLoopSpillFull errs.Code = 0x00_02_0B_29 // 0.2.11.41

CodeLoopSpillFull identifies an unconsumed tail that does not fit the connection's spill, or the loop's global spill budget.

View Source
const CodeLoopStaleRef errs.Code = 0x00_02_0B_27 // 0.2.11.39

CodeLoopStaleRef identifies an operation on a connection reference whose generation has ended: the connection closed, and its slot may already serve another one.

View Source
const CodeLoopTLSUnsupported errs.Code = 0x00_02_0B_23 // 0.2.11.35

CodeLoopTLSUnsupported identifies a loop group given TLS it cannot take: a raw loop handler, the loop engine speaking plaintext only (ADR 0186 §D2), or a contradiction of the http1 codec's TLS path (ADR 0198).

View Source
const CodeLoopValueInvalid errs.Code = 0x00_02_0B_58 // 0.2.11.88

CodeLoopValueInvalid identifies a LoopValue field a loop group cannot take: an Affinity that does not name one CPU per loop, a negative NotsentLowat (#857), or a busy-poll or ZCRX field out of its range (#979, #980).

View Source
const CodeLoopWrongThread errs.Code = 0x00_02_0B_2B // 0.2.11.43

CodeLoopWrongThread identifies a loop operation called from a thread other than the loop's own, detected under the loopdebug build tag.

View Source
const CodeMetricsRefused errs.Code = 0x00_02_0B_45 // 0.2.11.69

CodeMetricsRefused identifies a RegisterMetrics the meter cannot take, a nil one or one the server is already registered on.

View Source
const CodeNotStarted errs.Code = 0x00_02_0B_06 // 0.2.11.6

CodeNotStarted identifies an operation that requires a running server on one that has not been started.

View Source
const CodeOptionValueInvalid errs.Code = 0x00_02_0B_44 // 0.2.11.68

CodeOptionValueInvalid identifies a group option given a value outside its range.

View Source
const CodePacketOptionUnsupported errs.Code = 0x00_02_0B_43 // 0.2.11.67

CodePacketOptionUnsupported identifies an option a datagram group cannot honour.

View Source
const CodePacketTooLarge errs.Code = 0x00_02_0B_0E // 0.2.11.14

CodePacketTooLarge identifies a datagram longer than the group's accepted size.

View Source
const CodePlaintextRequired errs.Code = 0x00_02_0B_42 // 0.2.11.66

CodePlaintextRequired identifies an HTTP group given neither TLS nor Plaintext.

View Source
const CodePlaintextWithTLS errs.Code = 0x00_02_0B_39 // 0.2.11.57

CodePlaintextWithTLS identifies a group given both Plaintext and TLS: the group cannot both refuse and serve TLS (ADR 0187).

View Source
const CodeProtocolsRefused errs.Code = 0x00_02_0B_3E // 0.2.11.62

CodeProtocolsRefused identifies a Protocols option, or a TLS identity's NextProtos, that an HTTP group cannot serve.

View Source
const CodeProxyHeaderInvalid errs.Code = 0x00_02_0B_48 // 0.2.11.72

CodeProxyHeaderInvalid identifies a connection whose PROXY protocol header was absent, malformed, or sent by a peer the group does not trust.

View Source
const CodeRequestDenied errs.Code = 0x00_02_0B_11 // 0.2.11.17

CodeRequestDenied identifies an outbound request refused by the transport policy before it left the process.

View Source
const CodeResponseTooLarge errs.Code = 0x00_02_0B_12 // 0.2.11.18

CodeResponseTooLarge identifies a response body that exceeded the configured read ceiling.

View Source
const CodeSSEFieldInvalid errs.Code = 0x00_02_0B_17 // 0.2.11.23

CodeSSEFieldInvalid identifies a Server-Sent Events frame the wire format cannot carry: a line terminator inside an id, an event name or a comment (the format has no escape — a newline SPLITS a value, so an id carrying one would silently become a different id plus a stray field), a negative retry, a retry under one millisecond (the wire field is an integer millisecond count, and truncating to zero would say "reconnect immediately"), or a frame with no field set at all.

View Source
const CodeSSEFlushUnsupported errs.Code = 0x00_02_0B_18 // 0.2.11.24

CodeSSEFlushUnsupported identifies a ResponseWriter that cannot be flushed. Server-Sent Events is a streaming format: without a flush every event sits in the transport buffer until the handler returns, which for an endless stream means the client receives nothing, ever.

View Source
const CodeSSEStreamClosed errs.Code = 0x00_02_0B_19 // 0.2.11.25

CodeSSEStreamClosed identifies a send on a stream that has already ended — the client disconnected, the server began draining, or the handler closed it.

View Source
const CodeSSEStreamMisconfigured errs.Code = 0x00_02_0B_1A // 0.2.11.26

CodeSSEStreamMisconfigured identifies a stream option the domain refuses to interpret rather than guess at: a negative keep-alive interval or a negative per-write budget (ADR 0031).

View Source
const CodeServerClosed errs.Code = 0x00_02_0B_04 // 0.2.11.4

CodeServerClosed identifies work refused because the server already stopped accepting; it is the expected terminal outcome of a graceful shutdown.

View Source
const CodeSocketAdoptFailed errs.Code = 0x00_02_0B_0D // 0.2.11.13

CodeSocketAdoptFailed identifies an inherited socket that could not be adopted from the supervisor.

View Source
const CodeStaticMisconfigured errs.Code = 0x00_02_0B_22 // 0.2.11.34

CodeStaticMisconfigured identifies a static file handler refused at construction: no file tree to serve, a security header value the handler will not send, or a Referrer-Policy token no browser recognises (ADR 0130).

View Source
const CodeTLSHandshakeFailed errs.Code = 0x00_02_0B_10 // 0.2.11.16

CodeTLSHandshakeFailed identifies a TLS or mTLS handshake that did not complete.

View Source
const CodeTLSMaterialInvalid errs.Code = 0x00_02_0B_0F // 0.2.11.15

CodeTLSMaterialInvalid identifies TLS material that is absent, malformed, or yields no usable certificate; it is never downgraded to an empty trust store.

View Source
const CodeTooManyRedirects errs.Code = 0x00_02_0B_14 // 0.2.11.20

CodeTooManyRedirects identifies an outbound call that exceeded its redirect budget.

View Source
const CodeUnsafePath errs.Code = 0x00_02_0B_16 // 0.2.11.22

CodeUnsafePath identifies an outbound path carrying a dot segment, literal or percent-encoded. Such a path is refused before any allowlist pattern is tried, because an anchored pattern like ^/v1/supi/[^/]+$ happily matches "/v1/supi/.." which the upstream then normalises to a different resource.

View Source
const CodeUnsupportedNetwork errs.Code = 0x00_02_0B_03 // 0.2.11.3

CodeUnsupportedNetwork identifies a network name the domain does not serve (anything outside tcp, tcp4, tcp6, udp, udp4, udp6, unix, unixgram, unixpacket).

View Source
const CodeWSConnClosed errs.Code = 0x00_02_0B_20 // 0.2.11.32

CodeWSConnClosed identifies an operation on a connection that has ended — the peer sent Close, the server began draining, the handler closed it, or the socket died. It is the terminal outcome a receive loop runs until.

View Source
const CodeWSConnMisconfigured errs.Code = 0x00_02_0B_21 // 0.2.11.33

CodeWSConnMisconfigured identifies a connection option the domain refuses to interpret rather than guess at: a negative ping interval or write budget, or a non-positive size ceiling (ADR 0031).

View Source
const CodeWSHandshakeFailed errs.Code = 0x00_02_0B_1B // 0.2.11.27

CodeWSHandshakeFailed identifies an HTTP request that is not a valid RFC 6455 opening handshake: wrong method, absent or non-"websocket" Upgrade token, a Connection header without "Upgrade", a Sec-WebSocket-Key that does not decode to sixteen bytes, a Sec-WebSocket-Version other than 13, or an Origin the server's policy refuses. The response is written before this is returned, so the handler has nothing left to answer.

View Source
const CodeWSInvalidPayload errs.Code = 0x00_02_0B_1F // 0.2.11.31

CodeWSInvalidPayload identifies a payload the protocol cannot carry: a text message or close reason that is not valid UTF-8 (§8.1), a close code that must never appear on the wire (1004, 1005, 1006, 1015 and the unallocated ranges — §7.4.2), or a close reason past the control-frame ceiling.

View Source
const CodeWSMessageTooLarge errs.Code = 0x00_02_0B_1E // 0.2.11.30

CodeWSMessageTooLarge identifies a frame or an accumulated message beyond the connection's configured ceiling. The frame's announced length is checked BEFORE any buffer is sized from it, because a 64-bit length field a peer chooses is an out-of-memory condition one allocation away.

View Source
const CodeWSProtocolViolation errs.Code = 0x00_02_0B_1D // 0.2.11.29

CodeWSProtocolViolation identifies a frame RFC 6455 forbids: an unmasked client frame (§5.1), a set reserved bit or reserved opcode (§5.2), a fragmented or oversized control frame (§5.5), a non-minimal length encoding (§5.2), a continuation with no message in progress or a new data frame interrupting one (§5.4), or a malformed close payload (§5.5.1). Each fails the connection.

View Source
const CodeWSUpgradeUnsupported errs.Code = 0x00_02_0B_1C // 0.2.11.28

CodeWSUpgradeUnsupported identifies a ResponseWriter whose connection cannot be taken over — an HTTP/2 request, or a middleware wrapper that hides http.Hijacker. WebSocket is not a response format: it replaces HTTP on the socket, so without the hijack there is nothing to upgrade.

View Source
const EnvDisable string = "KITSUNIUM_NET_DISABLE"

EnvDisable is the variable that switches engines off, a comma-separated list of capabilities the operator may switch off.

View Source
const EnvEngine string = "KITSUNIUM_NET_ENGINE"

EnvEngine is the variable that caps every group's engine at one capability of the lattice: min(code, environment), never above the code's choice.

View Source
const HTTP1DefaultBody int64 = 1 << 20

HTTP1DefaultBody is the Content-Length bound a zero HTTP1LimitsValue.Body selects: one mebibyte.

View Source
const HTTP1DefaultHead int = 8192

HTTP1DefaultHead is the head bound a zero HTTP1LimitsValue.Head selects: the request line, every field line and the blank line that ends them, in bytes.

View Source
const HTTP1DefaultTarget int = 2048

HTTP1DefaultTarget is the request-target bound a zero HTTP1LimitsValue.Target selects, in bytes.

View Source
const HTTP1MaxAccessFields int = 8

HTTP1MaxAccessFields is how many request fields an access-log link may allow: it sizes HTTP1AccessValue.Fields, so a record is a value and never a slice that grows.

View Source
const HTTP1MaxBody int64 = 1 << 30

HTTP1MaxBody is the largest Content-Length bound HTTP1LimitsValue.Body may ask for: one gibibyte, which ten decimal digits always hold.

View Source
const HTTP1MaxContentLengthDigits int = 10

HTTP1MaxContentLengthDigits is how many digits a Content-Length may carry, leading zeros included. Ten digits hold HTTP1MaxBody and stay far from the int64 limit, so the value is accumulated with no overflow to check: a longer field is refused before it is read.

View Source
const HTTP1MaxFields int = 64

HTTP1MaxFields is how many field lines one request of the restricted HTTP/1.1 profile may carry. It sizes HTTP1RequestValue.Fields, so a request is framed into a fixed array and never into a slice that grows: one more line is refused (431), never stored elsewhere.

View Source
const HTTP1MaxHead int = 65536

HTTP1MaxHead is the largest head bound HTTP1LimitsValue.Head may ask for.

View Source
const HTTP1MaxRequestID int = 64

HTTP1MaxRequestID is the longest request identifier an HTTP1RequestIDValue holds, in bytes. It sizes the value's array, so an identifier is copied by value and never aliases the bytes it was read from; a longer one is treated as absent by the chain's RequestID link.

View Source
const HTTP1MaxSubject int = 128

HTTP1MaxSubject is the longest subject an HTTP1AuthValue holds, in bytes. It sizes the value's array, so a subject is copied into the request and never aliases the bytes it was read from; a longer one is refused.

View Source
const HTTP1TraceLineLen int = 70

HTTP1TraceLineLen sizes HTTP1ResponseValue.TraceLine: the field name, a version-00 traceparent and the line's end. Its array keeps a deferred answer's trace field, so it never aliases the request.

View Source
const LoopsAuto int = -1

LoopsAuto, set as LoopValue.Loops, runs one loop per GOMAXPROCS but one, at least one, read once at Start: a snapshot that a later change of GOMAXPROCS does not move. The P left over runs the rest of the process (ADR 0199). It is a sentinel, never the zero value, which is one loop (ADR 0187 §D9); State reports the count and "auto" as its source.

View Source
const SSEContentType string = "text/event-stream"

SSEContentType is the media type an event stream must be served under. A client that receives anything else does not start an EventSource at all.

View Source
const SSELastEventIDHeader string = "Last-Event-ID"

SSELastEventIDHeader is the request header a reconnecting client sends, carrying the id of the last event it processed.

View Source
const SSEMinRetry time.Duration = time.Millisecond

SSEMinRetry is the smallest reconnection hint the wire can carry. The retry field is an integer MILLISECOND count, so anything shorter rounds to zero — and zero does not mean "a very short delay", it means "reconnect at once".

View Source
const WSGUID string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

WSGUID is the fixed string RFC 6455 §1.3 concatenates to Sec-WebSocket-Key before hashing. It is a constant of the protocol, not a secret and not a parameter: its only job is to make the accept value impossible to produce by echoing the request, so a cache or a naive proxy cannot fake an upgrade.

View Source
const WSKeyLen int = 16

WSKeyLen is how many bytes Sec-WebSocket-Key decodes to (RFC 6455 §4.1). A key of any other length is not a WebSocket handshake, whatever it decodes.

View Source
const WSMaxControlPayload int = 125

WSMaxControlPayload is the ceiling RFC 6455 §5.5 puts on a control frame's payload. It exists so an endpoint can answer a Ping without buffering: a control frame is answered inline, between the fragments of a message, so it must fit in a fixed buffer that is always available.

View Source
const WSVersion string = "13"

WSVersion is the only protocol version this domain speaks (RFC 6455 §4.1). A request naming any other version is refused WITH this value advertised back, which is what lets a client renegotiate instead of guessing.

Variables

View Source
var (
	// ListenFailed is returned when a listener could not be bound.
	ListenFailed = errs.Static(CodeListenFailed, "LISTEN_FAILED",
		"The server could not bind its listening address",
		"core/net: binding a listener failed; the wrap cause is the OS error",
		0,
		69)

	// AddressInUse is returned by Start for an address another socket already
	// holds (#1330): a bind refused with EADDRINUSE, or, before the shards of an
	// address are opened with SO_REUSEPORT, the plain bind that probes it. The
	// "address" field names it. A group given ShareAddress skips the probe and
	// joins a holder that shares too.
	AddressInUse = errs.Static(CodeAddressInUse, "ADDRESS_IN_USE",
		"The listening address is already in use",
		"core/net: the listen address is held by another socket; the wrap cause is the OS error",
		0,
		69)

	// InvalidAddress is returned for a syntactically unusable address.
	InvalidAddress = errs.Static(CodeInvalidAddress, "INVALID_ADDRESS",
		"The network address is not valid",
		"core/net: the supplied host:port or socket path could not be parsed",
		0,
		64)

	// UnsupportedNetwork is returned for a network name outside the served set.
	UnsupportedNetwork = errs.Static(CodeUnsupportedNetwork, "UNSUPPORTED_NETWORK",
		"The network type is not supported",
		"core/net: network outside {tcp,tcp4,tcp6,udp,udp4,udp6,unix,unixgram,unixpacket}",
		0,
		64)

	// ServerClosed is returned once the server has stopped accepting work.
	ServerClosed = errs.Static(CodeServerClosed, "SERVER_CLOSED",
		"The server is closed",
		"core/net: the server stopped accepting; this is the terminal state of a graceful shutdown",
		0,
		69)

	// AlreadyStarted is returned by Start on a server that is already serving.
	AlreadyStarted = errs.Static(CodeAlreadyStarted, "ALREADY_STARTED",
		"The server is already started",
		"core/net: Start called on a server whose phase is already Serving",
		0,
		70)

	// NotStarted is returned when an operation requires a running server.
	NotStarted = errs.Static(CodeNotStarted, "NOT_STARTED",
		"The server is not started",
		"core/net: the operation requires a running server",
		0,
		70)

	// HandlerMissing is returned when a group was bound without a handler.
	HandlerMissing = errs.Static(CodeHandlerMissing, "HANDLER_MISSING",
		"The listener group has no handler",
		"core/net: a group reached Start with a nil handler",
		0,
		64)

	// HandlerPanic wraps a panic recovered inside a handler.
	HandlerPanic = errs.Static(CodeHandlerPanic, "HANDLER_PANIC",
		"The connection handler failed unexpectedly",
		"core/net: a handler panicked; the connection was closed and the process kept running",
		0,
		70)

	// ConnLimitReached is returned when the group's connection ceiling is full.
	ConnLimitReached = errs.Static(CodeConnLimitReached, "CONN_LIMIT_REACHED",
		"The server is at its connection limit",
		"core/net: the group's concurrency ceiling rejected the connection",
		0,
		75)

	// DrainTimeout is returned when the shutdown drain budget expired.
	DrainTimeout = errs.Static(CodeDrainTimeout, "DRAIN_TIMEOUT",
		"The server did not drain before its deadline",
		"core/net: connections were still in flight when the drain budget expired",
		0,
		75)

	// GroupUnknown is returned for a lookup of an undeclared group.
	GroupUnknown = errs.Static(CodeGroupUnknown, "GROUP_UNKNOWN",
		"The listener group is not declared",
		"core/net: no group is registered under that name",
		0,
		64)

	// GroupDuplicate is returned for a second declaration of one group name.
	GroupDuplicate = errs.Static(CodeGroupDuplicate, "GROUP_DUPLICATE",
		"The listener group is already declared",
		"core/net: a group with that name already exists on this server",
		0,
		64)

	// SocketAdoptFailed is returned when an inherited socket could not be adopted.
	SocketAdoptFailed = errs.Static(CodeSocketAdoptFailed, "SOCKET_ADOPT_FAILED",
		"An inherited socket could not be adopted",
		"core/net: a supervisor-passed descriptor could not be turned into a listener",
		0,
		71)

	// PacketTooLarge names the reason a datagram beyond the group's accepted
	// size is dropped. It is deliberately not returned to anyone: a read loop
	// has no caller, so the drop is reported through
	// StateValue.OversizedPackets. The sentinel stays because it is what gives
	// that count a documented name in the registry.
	PacketTooLarge = errs.Static(CodePacketTooLarge, "PACKET_TOO_LARGE",
		"The datagram is larger than the accepted size",
		"core/net: the received datagram exceeded the group's maximum packet size",
		0,
		65)

	// TLSMaterialInvalid is returned for absent, malformed, or empty TLS material.
	// It is deliberately loud: an unusable bundle must never degrade into a trust
	// store that silently verifies nothing.
	TLSMaterialInvalid = errs.Static(CodeTLSMaterialInvalid, "TLS_MATERIAL_INVALID",
		"The TLS material is missing or invalid",
		"core/net: the certificate, key, or CA bundle yielded no usable material",
		0,
		78)

	// TLSHandshakeFailed is returned when a TLS or mTLS handshake did not complete.
	TLSHandshakeFailed = errs.Static(CodeTLSHandshakeFailed, "TLS_HANDSHAKE_FAILED",
		"The TLS handshake failed",
		"core/net: the TLS or mTLS handshake did not complete",
		0,
		69)

	// RequestDenied is returned when the transport policy refused an outbound call.
	// The public message never names the refused path, so a denial cannot leak the
	// shape of the private API surface through a log line.
	RequestDenied = errs.Static(CodeRequestDenied, "REQUEST_DENIED",
		"The request was refused by policy",
		"core/net: the transport policy refused the outbound request",
		0,
		77)

	// ResponseTooLarge is returned when a response body exceeded the read ceiling.
	ResponseTooLarge = errs.Static(CodeResponseTooLarge, "RESPONSE_TOO_LARGE",
		"The response is larger than the accepted size",
		"core/net: the response body exceeded the configured maximum size",
		0,
		65)

	// CallFailed is returned when an outbound call produced no response.
	CallFailed = errs.Static(CodeCallFailed, "CALL_FAILED",
		"The outbound call failed",
		"core/net: the outbound call did not produce a response",
		0,
		69)

	// TooManyRedirects is returned when a call exceeded its redirect budget.
	TooManyRedirects = errs.Static(CodeTooManyRedirects, "TOO_MANY_REDIRECTS",
		"The outbound call exceeded its redirect budget",
		"core/net: the redirect chain was longer than the configured maximum",
		0,
		69)

	// InvalidDuration is returned for a configuration duration that parses as
	// neither a Go duration literal nor an integer nanosecond count.
	InvalidDuration = errs.Static(CodeInvalidDuration, "INVALID_DURATION",
		"The duration value is not valid",
		"core/net: the value is neither a Go duration literal nor a nanosecond count",
		0,
		78)

	// UnsafePath is returned for a path carrying a dot segment in any form. It is
	// evaluated before the allowlist, because Go normalises dot segments neither
	// in url.URL nor in the transport, so an anchored pattern alone is not enough.
	UnsafePath = errs.Static(CodeUnsafePath, "UNSAFE_PATH",
		"The request path is not in normalised form",
		"core/net: the path carries a literal or percent-encoded dot segment",
		0,
		77)

	// SSEFieldInvalid is returned for a Server-Sent Events frame the wire format
	// cannot carry. The format has no escape mechanism: a newline inside a value
	// SPLITS it rather than being quoted, so an id or an event name carrying one
	// would arrive as a different value plus a stray field. It is refused, never
	// truncated — a silently shortened id is a resume token that points at the
	// wrong place.
	SSEFieldInvalid = errs.Static(CodeSSEFieldInvalid, "SSE_FIELD_INVALID",
		"The event cannot be represented in the event-stream format",
		"core/net: an SSE frame carried a line terminator in a single-line field, an unrepresentable retry, or no field at all",
		0,
		64)

	// SSEFlushUnsupported is returned when the ResponseWriter cannot be flushed.
	// Without a flush the events accumulate in the transport buffer and reach the
	// client only when the handler returns, so an endless stream delivers nothing
	// at all — the failure is total and silent, which is why it is refused at
	// construction rather than discovered in production.
	SSEFlushUnsupported = errs.Static(CodeSSEFlushUnsupported, "SSE_FLUSH_UNSUPPORTED",
		"The response cannot be streamed",
		"core/net: the http.ResponseWriter implements neither Flush nor http.Flusher",
		0,
		70)

	// SSEStreamClosed is returned by a send on a stream that has already ended:
	// the client disconnected, the server began draining, or the handler closed
	// it. It is the terminal outcome a streaming handler loops until.
	SSEStreamClosed = errs.Static(CodeSSEStreamClosed, "SSE_STREAM_CLOSED",
		"The event stream is closed",
		"core/net: the SSE stream ended — peer gone, server draining, or closed by the handler",
		0,
		69)

	// SSEStreamMisconfigured is returned for a stream option the domain refuses
	// to interpret rather than guess at (ADR 0031). A zero keep-alive interval is
	// CLAMPED to the domain default, because a working keep-alive needs no
	// explanation; a negative one is REFUSED, because no value the SDK invented
	// for it would be defensible and "never" already has an explicit spelling.
	SSEStreamMisconfigured = errs.Static(CodeSSEStreamMisconfigured, "SSE_STREAM_MISCONFIGURED",
		"The event stream options are not valid",
		"core/net: a negative keep-alive interval or write budget was supplied",
		0,
		78)

	// WSHandshakeFailed is returned when an HTTP request is not a valid RFC 6455
	// opening handshake. The upgrade is refused with an HTTP status rather than
	// a Close frame, because at that point there is no WebSocket connection to
	// close — the response is still an ordinary HTTP one.
	WSHandshakeFailed = errs.Static(CodeWSHandshakeFailed, "WS_HANDSHAKE_FAILED",
		"The WebSocket handshake is not valid",
		"core/net: the request does not satisfy RFC 6455 §4.2.1, or its origin was refused",
		0,
		65)

	// WSUpgradeUnsupported is returned when the response's connection cannot be
	// hijacked. It is deliberately distinct from a failed handshake: the request
	// was fine and the peer did nothing wrong — this stack cannot take over the
	// socket, which is a deployment fact (HTTP/2, or a middleware that wraps the
	// ResponseWriter without forwarding Unwrap), not a client error.
	WSUpgradeUnsupported = errs.Static(CodeWSUpgradeUnsupported, "WS_UPGRADE_UNSUPPORTED",
		"The connection cannot be upgraded",
		"core/net: the http.ResponseWriter exposes no http.Hijacker, so the socket cannot be taken over",
		0,
		70)

	// WSProtocolViolation is returned for a frame RFC 6455 forbids. Every case it
	// covers FAILS THE CONNECTION rather than being skipped: a peer that sent a
	// frame this endpoint ignored believes it said something, and the two would
	// be desynchronised from that point on.
	WSProtocolViolation = errs.Static(CodeWSProtocolViolation, "WS_PROTOCOL_VIOLATION",
		"The WebSocket frame is not valid",
		"core/net: the frame violates RFC 6455 framing — masking, reserved bits/opcodes, control-frame shape, minimal length, or fragmentation order",
		0,
		65)

	// WSMessageTooLarge is returned when a frame or a reassembled message exceeds
	// the connection's ceiling. The check runs against the ANNOUNCED length,
	// before any buffer is sized from it: allocating from a 64-bit integer the
	// peer chose is a denial of service in one line.
	WSMessageTooLarge = errs.Static(CodeWSMessageTooLarge, "WS_MESSAGE_TOO_LARGE",
		"The WebSocket message is larger than the accepted size",
		"core/net: the frame or the accumulated message exceeded the configured ceiling",
		0,
		65)

	// WSInvalidPayload is returned for a payload the protocol cannot carry: text
	// that is not UTF-8, a close code that must never be sent, or a close reason
	// past the control-frame ceiling. It is one sentinel for both directions on
	// purpose — the rule a peer broke is the rule this endpoint must not break.
	WSInvalidPayload = errs.Static(CodeWSInvalidPayload, "WS_INVALID_PAYLOAD",
		"The WebSocket payload is not valid",
		"core/net: invalid UTF-8, an unsendable close code, or an oversized close reason",
		0,
		65)

	// WSConnClosed is returned by any operation on a connection that has ended.
	// It is the terminal outcome a receive loop runs until, and it carries the
	// close code the peer sent when there was one.
	WSConnClosed = errs.Static(CodeWSConnClosed, "WS_CONN_CLOSED",
		"The WebSocket connection is closed",
		"core/net: the connection ended — peer close, server draining, closed by the handler, or a dead socket",
		0,
		69)

	// WSConnMisconfigured is returned for an option the domain refuses to
	// interpret rather than guess at (ADR 0031). A zero ping interval is CLAMPED
	// to the domain default, because a working heartbeat needs no explanation; a
	// negative one is REFUSED, and "never" has its own spelling.
	WSConnMisconfigured = errs.Static(CodeWSConnMisconfigured, "WS_CONN_MISCONFIGURED",
		"The WebSocket options are not valid",
		"core/net: a negative interval or budget, or a non-positive size ceiling, was supplied",
		0,
		78)

	// StaticMisconfigured is returned when a static file handler is built with
	// nothing it can serve safely: no file tree, a header value carrying a
	// control character, or a Referrer-Policy token no browser knows — which a
	// browser ignores, silently falling back to a weaker default. The "option"
	// field names which (ADR 0130).
	StaticMisconfigured = errs.Static(CodeStaticMisconfigured, "STATIC_MISCONFIGURED",
		"The static file handler configuration is not valid",
		"core/net: a nil file tree, a header value with a control character, or an unknown Referrer-Policy token",
		0,
		78)

	// LoopTLSUnsupported is returned by Start for a loop group given TLS that
	// cannot take it: a raw loop handler — the loop engine is plaintext only (ADR
	// 0186 §D2) — or, for the http1 codec, which takes TLS on the portable backend
	// (ADR 0198), a zero identity, an explicit native backend or a loop
	// middleware; the "option" field names it.
	LoopTLSUnsupported = errs.Static(CodeLoopTLSUnsupported, "LOOP_TLS_UNSUPPORTED",
		"The loop engine does not serve TLS",
		"core/net: a loop group was given a TLS identity; the loop engine is plaintext only",
		0,
		78)

	// LoopOptionUnsupported is returned by Start for an option a group cannot honour
	// on its engine: Loop on a group served by Handle, HandleFunc or HandleHTTP, or
	// on a datagram group; Use on a loop group; or an option neither the loop
	// engine nor its handler honours. A group with no handler fails with
	// HandlerMissing instead. The "option" field names it. It is judged at
	// Start, once the handler is known, whatever order the declarations came in.
	LoopOptionUnsupported = errs.Static(CodeLoopOptionUnsupported, "LOOP_OPTION_UNSUPPORTED",
		"The group option is not supported by its engine",
		"core/net: an option the group's engine or its loop handler cannot honour",
		0,
		78)

	// LoopBackendUnavailable is returned by Start for an explicit loop backend the
	// running kernel's probe refused. The "stage" and "reason" fields name the probe
	// step that failed. A GOOS that has no such mechanic at all returns the shared
	// UnsupportedPlatform instead (ADR 0018 §(a)).
	LoopBackendUnavailable = errs.Static(CodeLoopBackendUnavailable, "LOOP_BACKEND_UNAVAILABLE",
		"The requested loop backend is not available on this kernel",
		"core/net: the kernel probe refused the requested loop backend",
		0,
		69)

	// LoopNoDescriptors is returned by Start for a loop group when the effective
	// RLIMIT_NOFILE soft limit is zero or cannot be read: no ring table and no
	// connection could hold a descriptor. It is checked before the ring count is
	// derived, so it is never a division by zero.
	LoopNoDescriptors = errs.Static(CodeLoopNoDescriptors, "LOOP_NO_DESCRIPTORS",
		"The process has no file descriptor available for the loop",
		"core/net: RLIMIT_NOFILE is zero or unreadable; the loop has no descriptor to use",
		0,
		71)

	// LoopStaleRef is returned by a loop operation on a reference whose connection
	// has closed. The operation does nothing: the slot may already name another
	// connection, and the generation is what tells the two apart.
	LoopStaleRef = errs.Static(CodeLoopStaleRef, "LOOP_STALE_REF",
		"The connection is closed",
		"core/net: the connection reference's generation has ended",
		0,
		70)

	// LoopOutputFull is returned by Write when the bytes would pass the connection's
	// output ceiling or the loop's global output budget. Nothing is copied; the
	// caller waits for OnWritable or closes.
	LoopOutputFull = errs.Static(CodeLoopOutputFull, "LOOP_OUTPUT_FULL",
		"The connection cannot take more output now",
		"core/net: the connection's output or the loop's output budget is at its ceiling",
		0,
		75)

	// LoopSpillFull is the error OnClose receives for a connection whose unconsumed
	// input passed its spill ceiling or the loop's global spill budget. The loop
	// closes it rather than read further.
	LoopSpillFull = errs.Static(CodeLoopSpillFull, "LOOP_SPILL_FULL",
		"The connection sent more than it may hold unread",
		"core/net: the unconsumed input passed the spill ceiling",
		0,
		75)

	// LoopPostQueueFull is returned by Post when the loop's preallocated queue is
	// full. The operation is not queued and will not run.
	LoopPostQueueFull = errs.Static(CodeLoopPostQueueFull, "LOOP_POST_QUEUE_FULL",
		"The loop cannot take more posted work now",
		"core/net: the loop's post queue is full",
		0,
		75)

	// LoopOutboundFull is returned by LoopOutbound.Send when the lane's
	// preallocated queue to its worker is full (R3). The job is not queued; the
	// refusal is counted in LoopStateValue.OutboundRefused.
	LoopOutboundFull = errs.Static(CodeLoopOutboundFull, "LOOP_OUTBOUND_FULL",
		"The server cannot take more offloaded work now",
		"core/net: the lane's queue to its worker is full",
		0,
		75)

	// LoopWrongThread is returned under the loopdebug build tag by Write, Close or
	// SetDeadline called from a thread other than the loop's own. Without the tag
	// the check is not compiled; the call is a contract violation either way.
	LoopWrongThread = errs.Static(CodeLoopWrongThread, "LOOP_WRONG_THREAD",
		"The operation was called outside its loop",
		"core/net: a loop-thread operation was called from another thread",
		0,
		70)

	// HandlerAlreadySet is returned by Start for a stream group whose handler was
	// declared twice — by Handle, HandleFunc, HandleHTTP or HandleLoop, in any
	// order. The second declaration does not replace the first; the "group" field
	// names the group.
	HandlerAlreadySet = errs.Static(CodeHandlerAlreadySet, "HANDLER_ALREADY_SET",
		"The listener group already has a handler",
		"core/net: a second handler declaration on one stream group",
		0,
		64)

	// HTTP1RequestMalformed is the refusal of a request whose syntax the profile
	// does not accept. It is answered 400 with Connection: close, and the
	// connection is closed: no handler sees the request.
	HTTP1RequestMalformed = errs.Static(CodeHTTP1RequestMalformed, "HTTP1_REQUEST_MALFORMED",
		"The request is malformed",
		"core/net: a bare CR or LF, a NUL or control byte, obs-fold, whitespace before a colon, a non-token name, or a bad request line",
		400,
		65)

	// HTTP1TransferEncoding is the refusal of any Transfer-Encoding. A
	// request that carries one alongside a Content-Length is the classic smuggling
	// vector (RFC 9112 §11.2), so the profile never chooses between them.
	HTTP1TransferEncoding = errs.Static(CodeHTTP1TransferEncoding, "HTTP1_TRANSFER_ENCODING",
		"Transfer-Encoding is not supported",
		"core/net: a Transfer-Encoding field, chunked or not; the profile frames by Content-Length alone",
		400,
		65)

	// HTTP1ContentLengthInvalid is the refusal of a Content-Length the profile
	// cannot frame by. A second one is refused even when it repeats the first:
	// two parsers disagreeing on which one counts is how a body becomes a request.
	HTTP1ContentLengthInvalid = errs.Static(CodeHTTP1ContentLengthInvalid, "HTTP1_CONTENT_LENGTH_INVALID",
		"The Content-Length is not valid",
		"core/net: an empty, non-digit, over-long or repeated Content-Length",
		400,
		65)

	// HTTP1FieldDuplicated is the refusal of a second Authorization or Host. The
	// profile hands a handler one of each, and picking one of two would let a
	// front end and this server judge different credentials or origins.
	HTTP1FieldDuplicated = errs.Static(CodeHTTP1FieldDuplicated, "HTTP1_FIELD_DUPLICATED",
		"A request field is repeated",
		"core/net: a second Authorization or Host field",
		400,
		65)

	// HTTP1ExpectRefused is the refusal of an Expect field, answered 417
	// (RFC 9110 §10.1.1).
	HTTP1ExpectRefused = errs.Static(CodeHTTP1ExpectRefused, "HTTP1_EXPECT_REFUSED",
		"The expectation cannot be met",
		"core/net: an Expect field; the profile sends no 100 Continue",
		417,
		65)

	// HTTP1HeadTooLarge is the refusal of a head past the profile's bound, in
	// bytes or in field lines, answered 431.
	HTTP1HeadTooLarge = errs.Static(CodeHTTP1HeadTooLarge, "HTTP1_HEAD_TOO_LARGE",
		"The request header is too large",
		"core/net: the head did not end within its byte bound, or carried more than HTTP1MaxFields field lines",
		431,
		65)

	// HTTP1TargetTooLong is the refusal of a request-target past the profile's
	// bound, answered 414.
	HTTP1TargetTooLong = errs.Static(CodeHTTP1TargetTooLong, "HTTP1_TARGET_TOO_LONG",
		"The request target is too long",
		"core/net: the request-target is longer than the target bound",
		414,
		65)

	// HTTP1BodyTooLarge is the refusal of a Content-Length past the profile's
	// body bound, answered 413 before a byte of the body is read.
	HTTP1BodyTooLarge = errs.Static(CodeHTTP1BodyTooLarge, "HTTP1_BODY_TOO_LARGE",
		"The request content is too large",
		"core/net: the declared Content-Length is past the body bound",
		413,
		65)

	// HTTP1MethodUnsupported is the refusal of CONNECT, answered 501: a tunnel is
	// outside the profile (ADR 0186 §D6).
	HTTP1MethodUnsupported = errs.Static(CodeHTTP1MethodUnsupported, "HTTP1_METHOD_UNSUPPORTED",
		"The request method is not supported",
		"core/net: a CONNECT request",
		501,
		65)

	// HTTP1ResponseInvalid is returned by the profile's writer for a response it
	// will not put on the wire. Nothing is appended: a half-written response on a
	// kept-alive connection would frame every response after it wrongly.
	HTTP1ResponseInvalid = errs.Static(CodeHTTP1ResponseInvalid, "HTTP1_RESPONSE_INVALID",
		"The response is not valid",
		"core/net: a status outside 200-999, a body on 204 or 304, a non-token or codec-owned field name, or a control byte in a field value",
		0,
		64)

	// HTTP1Misconfigured is returned for profile limits the codec refuses to
	// interpret rather than guess at (ADR 0031): a zero bound is the default, a
	// negative one, or one past its maximum, is refused. The "option" field names
	// which.
	HTTP1Misconfigured = errs.Static(CodeHTTP1Misconfigured, "HTTP1_MISCONFIGURED",
		"The HTTP/1.1 profile limits are not valid",
		"core/net: a negative bound, a head past HTTP1MaxHead, a target past the head, or a body past HTTP1MaxBody",
		0,
		78)

	// EngineUnsupported is returned by Start for a group whose Engine cannot serve
	// it: EngineGoroutine on a group served by HandleLoop or given Loop, EngineLoop
	// on a group served by Handle, HandleFunc or HandleHTTP or on a datagram group,
	// or a value outside the engine constants. The "engine" field names the value.
	// It is refused, never replaced by another engine (ADR 0187).
	EngineUnsupported = errs.Static(CodeEngineUnsupported, "ENGINE_UNSUPPORTED",
		"The group's engine cannot serve its handler",
		"core/net: the Engine option names an engine the group's handler or options cannot run on",
		0,
		78)

	// PlaintextWithTLS is returned by Start for a group given both Plaintext and
	// TLS, in either order. The group is never served in plaintext nor over TLS by
	// default: the contradiction is the caller's to settle (ADR 0187).
	PlaintextWithTLS = errs.Static(CodePlaintextWithTLS, "PLAINTEXT_WITH_TLS",
		"The group cannot be both plaintext and TLS",
		"core/net: a group was given both Plaintext and TLS",
		0,
		78)

	// HandlerAmbiguous is returned by Start for a simple-API group whose handler
	// implements both interfaces of its entry — http.Handler and HTTP1Handler for
	// HTTP, ConnHandler and LoopHandler for TCP — with no Engine to choose
	// (ADR 0197). The "entry" and "type" fields name them.
	HandlerAmbiguous = errs.Static(CodeHandlerAmbiguous, "HANDLER_AMBIGUOUS",
		"The handler could run on two engines; choose one with Engine",
		"core/net: the handler implements both interfaces of its entry and no Engine chooses",
		0,
		64)

	// HandlerUnsupported is returned by Start for a simple-API group whose handler
	// no engine of its entry serves: a PacketHandler given to HTTP, a LoopHandler
	// given to UDP, an HTTP handler given to TCP (its "serve" field names HTTP),
	// any other type (ADR 0197). The "entry" and "type" fields name them, on
	// every one of these refusals.
	HandlerUnsupported = errs.Static(CodeHandlerUnsupported, "HANDLER_UNSUPPORTED",
		"The handler cannot be served by this entry point",
		"core/net: no engine of the entry serves the handler's type",
		0,
		64)

	// PlaintextRequired is returned by Start for a group that serves HTTP —
	// net/http's or the http1 codec — given neither TLS nor Plaintext: a group
	// without TLS says so (ADR 0187 §D7, ADR 0197). The "group" field names it.
	PlaintextRequired = errs.Static(CodePlaintextRequired, "PLAINTEXT_REQUIRED",
		"The HTTP group needs TLS or Plaintext",
		"core/net: an HTTP group without TLS must say Plaintext",
		0,
		78)

	// PacketOptionUnsupported is returned by Start for a datagram group given an
	// option the datagram engine does not apply — anything but MaxPacketSize,
	// BatchSize, BatchReplies, Adopt, Name, Engine and Plaintext — refused rather
	// than dropped
	// (ADR 0187 §D4, ADR 0197). The "option" field names it.
	PacketOptionUnsupported = errs.Static(CodePacketOptionUnsupported, "PACKET_OPTION_UNSUPPORTED",
		"The datagram group cannot honour one of its options",
		"core/net: the datagram group was given an option it cannot honour",
		0,
		78)

	// OptionValueInvalid is returned by Start for a group option whose value is
	// outside its range — MaxBodyBytes below -1, a buffer size or a backlog below
	// zero — refused rather than read as a default (ADR 0031). The "group" and
	// "option" fields name them.
	OptionValueInvalid = errs.Static(CodeOptionValueInvalid, "OPTION_VALUE_INVALID",
		"A group option's value is out of range",
		"core/net: a group option was given a value outside its range",
		0,
		78)

	// ProxyHeaderInvalid is what a connection of a ProxyProtocol group is refused
	// with: a peer outside Trusted, a header that is malformed or too long, or one
	// not finished within Timeout. The connection is closed before any byte of it
	// is served, and the refusal counted in ListenerStateValue.ProxyRefused.
	ProxyHeaderInvalid = errs.Static(CodeProxyHeaderInvalid, "PROXY_HEADER_INVALID",
		"The connection's PROXY protocol header is invalid",
		"core/net: a PROXY protocol header was refused",
		0,
		65)

	// CompressInvalid is what Compress returns for a level outside gzip's range or a
	// negative minimum. The "option" field names which.
	CompressInvalid = errs.Static(CodeCompressInvalid, "COMPRESS_INVALID",
		"A response compression option is out of range",
		"core/net: a Compress configuration is out of range",
		0,
		78)

	// MetricsRefused is returned by RegisterMetrics for a nil meter ("why" is
	// "no meter") or for a meter the server is already registered on ("why" is
	// "already registered"): a second registration would run every callback twice
	// per collection.
	MetricsRefused = errs.Static(CodeMetricsRefused, "METRICS_REFUSED",
		"The server's metrics cannot be registered on this meter",
		"core/net: the server's metrics cannot be registered on this meter",
		0,
		78)

	// GroupNameConflict is returned by Start for a group given a Name that is empty
	// or differs from the non-empty name it was declared with. The "group" and
	// "name" fields carry both.
	GroupNameConflict = errs.Static(CodeGroupNameConflict, "GROUP_NAME_CONFLICT",
		"The group's name is not consistent",
		"core/net: the Name option contradicts the group's declared name, or is empty",
		0,
		64)

	// LoopGroupStalled is returned by Serve once every ring of a loop group has been
	// drained by its watchdog while the server was serving, and by the server's
	// liveness check once that has lasted the stall grace (ADR 0188). The "group"
	// field names it. A process restarted with the same environment stalls the
	// same way: the runbook sets KITSUNIUM_NET_DISABLE.
	LoopGroupStalled = errs.Static(CodeLoopGroupStalled, "LOOP_GROUP_STALLED",
		"A loop group stopped accepting connections",
		"core/net: every ring of the loop group was drained by its watchdog; set KITSUNIUM_NET_DISABLE before restarting",
		0,
		69)

	// LoopMiddlewareInvalid is returned by Start when a loop middleware's layer
	// does not forward every callback to the handler it wraps: it does not
	// implement LoopLayered, its Next is not the handler it was given, or one of
	// the eight callbacks sent through a probe chain did not reach the handler.
	// The "group" and "callback" fields name it.
	LoopMiddlewareInvalid = errs.Static(CodeLoopMiddlewareInvalid, "LOOP_MIDDLEWARE_INVALID",
		"A loop middleware does not forward every callback",
		"core/net: a loop middleware's layer does not forward every callback to the next handler",
		0,
		78)

	// LoopFileRefused is returned by LoopFileWriter.WriteFile when the backend
	// cannot send this file now: no descriptor or pipe left, or a kernel without
	// the splice operation. Nothing was queued, so the caller may answer another
	// way.
	LoopFileRefused = errs.Static(CodeLoopFileRefused, "LOOP_FILE_REFUSED",
		"The file body could not be queued",
		"core/net: a loop backend refused a file body: no descriptor left or the operation is missing",
		0,
		69)

	// LoopValueInvalid is returned by Start for a loop group whose LoopValue
	// names an Affinity that is not one CPU, from 0 to 8191, per loop, a
	// negative NotsentLowat or SlabBudget, a BusyPoll or BusyPollBudget out of
	// its range, or a negative ZCRX field; the field travels as a field of the
	// error.
	LoopValueInvalid = errs.Static(CodeLoopValueInvalid, "LOOP_VALUE_INVALID",
		"The loop group's configuration is invalid",
		"core/net: a LoopValue field is out of its range: Affinity or NotsentLowat",
		0,
		78)

	// LoopFileTruncated is the error OnClose reports when a file body ended before
	// the length its head declared: the file shrank during the send, and the
	// connection is closed so the client sees the short body.
	LoopFileTruncated = errs.Static(CodeLoopFileTruncated, "LOOP_FILE_TRUNCATED",
		"The file ended before its declared length",
		"core/net: a file body ended before its declared length",
		0,
		65)

	// CORSMisconfigured is returned when a CORS policy is built from a value the
	// SDK refuses: no origin, "*" with Credentials or among other origins, an
	// origin that is not one exact scheme://host[:port], a method or a field
	// name that is not a token, a negative MaxAge. The "option" field names it.
	CORSMisconfigured = errs.Static(CodeCORSMisconfigured, "CORS_MISCONFIGURED",
		"The CORS policy is not valid",
		"core/net: a CORS policy names no origin, a wildcard with credentials, or an origin, method, field name or age it cannot honour",
		0,
		78)

	// ProtocolsRefused is returned by Start for an HTTP group whose Protocols
	// option is empty, holds an empty identifier, repeats or names an unknown
	// one, asks for h2
	// without TLS, or contradicts its identity's NextProtos — naming an HTTP
	// version the group does not serve, or none it does — and for a group that
	// serves no http.Handler given Protocols. The "reason" field names it
	// (ADR 0195).
	ProtocolsRefused = errs.Static(CodeProtocolsRefused, "PROTOCOLS_REFUSED",
		"The group's HTTP protocols are misconfigured",
		"core/net: the group's HTTP protocols cannot be served as configured",
		0,
		78)

	// LoopCapabilityRefused is returned by Start for a loop group whose
	// LoopValue.Capabilities names something it cannot opt into: an engine, a
	// class B capability, which stays off until BENCH.md shows its threshold, or a
	// token outside the registry. The "capability" field names it. It is refused,
	// never ignored (ADR 0189).
	LoopCapabilityRefused = errs.Static(CodeLoopCapabilityRefused, "LOOP_CAPABILITY_REFUSED",
		"The loop group asks for a capability it cannot opt into",
		"core/net: LoopValue.Capabilities names an engine, a class B capability or an unknown token",
		0,
		78)
)

Functions

func AcceptsEncoding added in v0.30.0

func AcceptsEncoding(value, coding string) bool

AcceptsEncoding reports whether the Accept-Encoding field value admits coding (RFC 9110 §12.5.3): named with a non-zero quality, or covered by "*" with one. A value past 1 KiB or 32 elements is not parsed, and admits nothing — the field is the peer's, and identity is a safe answer.

func Chain

func Chain[H any](h H, middlewares ...Middleware[H]) H

Chain applies middlewares to a handler, outermost first.

Chain(h, a, b, c) yields a(b(c(h))): the first middleware listed is the first to see a connection. That ordering matches how the list reads on the page, which is the only ordering a reader will guess correctly.

func DrainSignal

func DrainSignal(ctx context.Context) <-chan struct{}

DrainSignal returns the drain channel carried by ctx, or nil when the server serving this request publishes none.

A nil channel is the right absence: receiving from it blocks forever, so a select that watches it alongside other cases simply never fires that case. A caller therefore needs no nil check.

func WithDrainSignal

func WithDrainSignal(ctx context.Context, draining <-chan struct{}) context.Context

WithDrainSignal returns ctx carrying the channel a server closes when it begins draining.

A request context is deliberately NOT what carries this. Cancelling it would tell every in-flight handler to abandon the response it is halfway through, which is the opposite of a graceful drain — the drain exists so those responses finish. This signal is additive: a handler that ignores it behaves exactly as before, and a handler that holds a connection open indefinitely — an event stream, a long poll — gets the one piece of information it cannot otherwise have, namely that finishing now is the cooperative thing to do.

The channel is closed, never sent on, so every observer sees it and a late observer sees it immediately.

Types

type AddressValue

type AddressValue struct {
	// Network is the socket family: tcp, tcp4, tcp6, udp, udp4, udp6, unix,
	// unixgram or unixpacket.
	Network string `json:"network"`
	// Addr is the bind target: "host:port" for the IP families, a filesystem
	// path for the unix families.
	Addr string `json:"addr"`
}

AddressValue names one socket to bind. It is a value type so a group's listen set is comparable and copyable, and so a configuration file can express it directly.

func (AddressValue) String

func (a AddressValue) String() string

String renders the address as "network://addr" for logs and error fields.

type CORSAnswerValue added in v0.30.0

type CORSAnswerValue struct {
	// Lines are the field lines, each "Name: value\r\n", for the http1 codec.
	Lines []byte
	// Fields are the fields by name, for net/http's Header.
	Fields []CORSFieldValue
}

CORSAnswerValue is what a CORSPolicy gives one response: the same fields twice, built once and shared read only.

type CORSFieldValue added in v0.30.0

type CORSFieldValue struct {
	// Name is the field name.
	Name string
	// Value is the field value.
	Value string
}

CORSFieldValue is one response field of a CORS answer, for an engine that sets its fields by name.

type CORSPolicy added in v0.30.0

type CORSPolicy struct {
	// contains filtered or unexported fields
}

CORSPolicy is a CORSValue checked and built: what each origin is answered, ready before the first request. It is safe for concurrent use and allocates nothing per answer.

func NewCORSPolicy added in v0.30.0

func NewCORSPolicy(cfg CORSValue) (*CORSPolicy, error)

NewCORSPolicy checks cfg and builds what each origin is answered, refusing with CORSMisconfigured, the "option" field named, whatever it cannot honour.

func (*CORSPolicy) Actual added in v0.30.0

func (p *CORSPolicy) Actual(origin []byte) CORSAnswerValue

Actual is the answer to a request that is not a preflight, from origin: Vary: Origin always, and the CORS fields when origin is listed. An empty origin is a request carrying none.

func (*CORSPolicy) Preflight added in v0.30.0

func (p *CORSPolicy) Preflight(origin, method []byte) CORSAnswerValue

Preflight is the answer to a preflight from origin asking for method: the CORS fields when origin and method are both allowed, Vary: Origin alone otherwise, so the browser refuses it. It never answers a 403.

type CORSValue added in v0.30.0

type CORSValue struct {
	// Origins are the exact origins allowed, as a browser serializes them:
	// scheme://host[:port], in lower case, with no path. "*" allows any origin,
	// alone in the list and without Credentials. "null" is refused: it is what a
	// sandboxed page and a local file both send.
	Origins []string
	// Methods are the methods a preflight may ask for; empty selects the
	// CORS-safelisted GET, HEAD and POST, never every method.
	Methods []string
	// Headers are the request fields a preflight may ask for; empty allows only
	// the CORS-safelisted fields.
	Headers []string
	// ExposeHeaders are the response fields a page may read.
	ExposeHeaders []string
	// Credentials lets a page send cookies and read the answer. It is refused
	// with "*": that combination publishes every user's answers to every site.
	Credentials bool
	// MaxAge is how long a browser may keep a preflight's answer, clamped to 24
	// hours; zero writes no Access-Control-Max-Age, negative is refused.
	MaxAge DurationValue
}

CORSValue is a cross-origin resource sharing policy: which origins a browser may read answers from, and with what. Its zero value is refused: naming no origin would let nothing through while looking configured (ADR 0031). An origin is compared byte for byte and never echoed unless listed; an origin not listed receives no CORS field and never a 403, since the browser is what enforces the answer.

type CallHook

type CallHook func(call CallValue)

CallHook receives one CallValue per completed outbound call. It is a function port rather than an interface because it is a single-method contract with no state, and a func keeps the domain free of a dependency on the logger or the metrics package: the consumer wires it to whichever it uses.

A CallHook MUST be safe for concurrent use and MUST NOT block — it runs on the calling goroutine, so a slow hook slows every request.

type CallValue

type CallValue struct {
	// Method is the uppercase HTTP method.
	Method string
	// Host is the authority the call was sent to.
	Host string
	// Path is the request path.
	Path string
	// Status is the HTTP status code, or zero when the call produced no response.
	Status int
	// Bytes is the number of response body bytes read.
	Bytes int64
	// Duration is the wall-clock time from request start to response headers.
	// It deliberately excludes the body: the record is emitted once the body is
	// done with, but this field measures the upstream's time to first answer,
	// which is the one a slow upstream and a large payload do not share.
	Duration time.Duration
	// Err is what ended the call: a policy refusal, a transport failure, the
	// response ceiling, or a failed close. Nil when the body was read in full.
	Err error
}

CallValue records one completed outbound call for observation, so a caller can log, meter or trace every egress without wrapping each call site by hand.

It is handed to a CallHook once the response body is FINISHED WITH — read to the end, failed, or closed — not when the headers arrive. A body's size is only known after it has been read, and an over-sized body or a failed close happens later still, so a record emitted at the headers could carry neither: it reported every call as a clean success of zero bytes. A refused request and an unreachable upstream are still reported immediately, because in neither case is there a body to wait for.

type Capability added in v0.26.20

type Capability string

Capability names one engine of the lattice the operator's switches restrict (ADR 0188), iouring > epoll = kqueue > portable > goroutine, or one optional kernel capability a loop group probes (ADR 0189). Its values are the tokens KITSUNIUM_NET_ENGINE and KITSUNIUM_NET_DISABLE take; these blocks are the registry they are parsed against, and any other token is unknown. KITSUNIUM_NET_ENGINE takes an engine only.

const (
	// CapabilityIOURing is io_uring, the top of the lattice. Switched off, or capped
	// below, no loop group calls io_uring_setup.
	CapabilityIOURing Capability = "iouring"
	// CapabilityEpoll is epoll, below io_uring. Switched off, a loop group that would
	// have fallen back to it runs on the portable backend.
	CapabilityEpoll Capability = "epoll"
	// CapabilityKqueue is kqueue, at epoll's rank on darwin and the BSDs (ADR 0222).
	// Switched off, or capped below, a loop group there runs on the portable backend
	// and no kqueue is probed.
	CapabilityKqueue Capability = "kqueue"
)

The engines an operator may switch off with KITSUNIUM_NET_DISABLE (ADR 0188).

const (
	// CapabilityPortable is the portable loop backend, the loop family's floor on
	// every platform, and what an unknown token caps the engine at.
	CapabilityPortable Capability = "portable"
	// CapabilityGoroutine is the goroutine engine, the bottom of the lattice. A loop
	// handler capped at it runs on the portable backend, the lowest engine that can
	// serve one.
	CapabilityGoroutine Capability = "goroutine"
)

The floors of the lattice, which KITSUNIUM_NET_ENGINE may name and nothing switches off (ADR 0188).

const (
	// CapabilityURingMultishotRecv is io_uring's multishot receive. Switched off,
	// every receive is single-shot and re-armed after each completion.
	CapabilityURingMultishotRecv Capability = "uring-multishot-recv"
	// CapabilityURingPbuf is io_uring's provided-buffer ring. The io_uring backend
	// receives into nothing else: switched off, io_uring is never set up and the group
	// runs on epoll.
	CapabilityURingPbuf Capability = "uring-pbuf"
	// CapabilityURingPbufInc is the provided-buffer ring consumed incrementally
	// (IOU_PBUF_RING_INC, 6.12): a buffer serves recv after recv, each from where the
	// last stopped (ADR 0204). Switched off, or refused by the kernel, each recv takes
	// a whole buffer.
	CapabilityURingPbufInc Capability = "uring-pbuf-inc"
	// CapabilityURingDirect is io_uring's direct descriptors, chosen when the release
	// check passes (a kernel from 6.13). Switched off, connections live in the process
	// table and the release check does not run.
	CapabilityURingDirect Capability = "uring-direct"
	// CapabilityEpollExclusive is EPOLLEXCLUSIVE on every loop's listener
	// registration. Switched off, every loop's epoll is woken by each connection.
	CapabilityEpollExclusive Capability = "epoll-exclusive"
	// CapabilityTCPNoDelay is TCP_NODELAY on the listeners a loop group accepts on,
	// inherited by every accepted socket. Switched off, it is not set.
	CapabilityTCPNoDelay Capability = "tcp-nodelay"
	// CapabilityUDPRxqOvfl is SO_RXQ_OVFL on a batched UDP socket (Linux 2.6.33):
	// the kernel attaches its count of the datagrams it dropped for want of
	// receive-buffer room to each message read, which State reports as
	// DatagramStateValue.KernelDrops. Switched off, the option is not set and the
	// count stays zero.
	CapabilityUDPRxqOvfl Capability = "udp-rxq-ovfl"
	// CapabilityUDPPktinfo is IP_PKTINFO and IPV6_RECVPKTINFO on a UDP socket
	// bound to a wildcard address (#972): each datagram names its own destination,
	// read back by Packet.To, and Reply leaves from that address. Off Linux the row
	// reads unprobed. Switched off, the option is not set, Packet.To is the bound
	// address and the kernel chooses a reply's source.
	CapabilityUDPPktinfo Capability = "udp-pktinfo"
	// CapabilityURingRecvmsg is io_uring's multishot RECVMSG (6.0) on a
	// datagram group served by PacketGroup.HandleLoop (#852): each datagram, its
	// sender's address and UDP GRO's segment size land in a provided buffer, and
	// the replies of a batch leave as SENDMSG, UDP_SEGMENT runs included. Absent —
	// a probe stage refused — or switched off, the group's sockets are read by the
	// batched reader of ADR 0192 on the runtime's netpoller, the epoll lane.
	CapabilityURingRecvmsg Capability = "uring-multishot-recvmsg"
	// CapabilityTCPKeepAlive is SO_KEEPALIVE with TCP_KEEPIDLE, TCP_KEEPINTVL and
	// TCP_KEEPCNT on the listeners a loop group accepts on, inherited by every
	// accepted socket: 15 s, 15 s and 9 probes, the standard library's own, so a peer
	// that vanished frees its slot on both engines. Switched off, or set negative,
	// it is not set.
	CapabilityTCPKeepAlive Capability = "tcp-keepalive"
	// CapabilityTCPUserTimeout is TCP_USER_TIMEOUT on the listeners a loop group
	// accepts on, inherited by every accepted socket: the time output may stay
	// unacknowledged before the kernel closes the connection with an error. Switched
	// off, or with no timeout asked for, it is not set.
	CapabilityTCPUserTimeout Capability = "tcp-user-timeout"
	// CapabilityURingSockGetsockopt is io_uring's socket getsockopt command
	// (IORING_OP_URING_CMD with SOCKET_URING_OP_GETSOCKOPT, Linux 6.7), with which
	// the loop reads a Unix connection's SO_PEERCRED on its direct descriptor at
	// accept, so a group with a Unix listener keeps direct descriptors and LoopPeer
	// still answers. Switched off, or absent, a group with a Unix listener runs on
	// process descriptors, where LoopPeer asks the socket.
	CapabilityURingSockGetsockopt Capability = "uring-sock-getsockopt"
)

The class A-kernel capabilities, on once probed, which KITSUNIUM_NET_DISABLE may switch off (ADR 0189).

const (
	// CapabilityURingDeferTaskrun is IORING_SETUP_DEFER_TASKRUN with SINGLE_ISSUER on
	// the ring a loop's thread waits on; the other rings keep COOP_TASKRUN.
	CapabilityURingDeferTaskrun Capability = "uring-defer-taskrun"
	// CapabilityURingNoSQArray is IORING_SETUP_NO_SQARRAY: the SQ indexes the SQEs
	// directly, with no indirection array.
	CapabilityURingNoSQArray Capability = "uring-no-sqarray"
	// CapabilityURingRingFDs registers each ring's descriptor with its thread
	// (IORING_REGISTER_RING_FDS), at most 16 a thread, and unregisters it before the
	// ring closes.
	CapabilityURingRingFDs Capability = "uring-ring-fds"
	// CapabilityURingSQRewind is IORING_SETUP_SQ_REWIND with NO_SQARRAY: every
	// submission is filled from SQE 0. It needs the kernel 7.0 lane before it may
	// leave class A-code.
	CapabilityURingSQRewind Capability = "uring-sq-rewind"
	// CapabilityURingRecvBundle is io_uring's receive bundle (IORING_RECVSEND_BUNDLE,
	// 6.10): one completion fills as many provided buffers as the bytes waiting need
	// (ADR 0205). Opted into, on only where a probe recv fills two buffers — not with
	// incremental consumption before 6.15; otherwise each completion fills one buffer.
	CapabilityURingRecvBundle Capability = "uring-recv-bundle"
	// CapabilityTCPDeferAccept is TCP_DEFER_ACCEPT on the listeners a loop group
	// accepts on: a connection is handed to the group once its peer has sent, or the
	// group's read bound has run out, rounded up to whole seconds, so a peer that
	// connects and says nothing takes no slot. Opted into, it is set only where the
	// group has a read bound.
	CapabilityTCPDeferAccept Capability = "tcp-defer-accept"
	// CapabilityReuseportLoops is one SO_REUSEPORT listener per loop of a loop group
	// (#856): opted into with Shards(0), each loop accepts on its own queue, the
	// kernel's four-tuple hash spreading the connections, and the drain takes each
	// loop's queue. Switched off, the group serves one shared listener.
	CapabilityReuseportLoops Capability = "reuseport-loops"
	// CapabilityLoopAffinity pins each loop's thread to the CPU LoopValue.Affinity
	// names for it (#857): a refused CPU leaves the loop unpinned and the row
	// missing, a fault.
	CapabilityLoopAffinity Capability = "loop-affinity"
	// CapabilitySOIncomingCPU is SO_INCOMING_CPU on each per-loop listener of
	// Shards(0) whose loop LoopValue.Affinity pins (#857): the kernel prefers the
	// listener whose CPU received the connection. Opted into, it needs both.
	CapabilitySOIncomingCPU Capability = "so-incoming-cpu"
	// CapabilityTCPNotsentLowat is TCP_NOTSENT_LOWAT on a loop group's TCP
	// listeners, LoopValue.NotsentLowat bytes, 128 KiB when only opted into (#857).
	CapabilityTCPNotsentLowat Capability = "tcp-notsent-lowat"
	// CapabilityHTTP2Loop is the HTTP/2 adapter of a loop group (ADR 0221). Switched
	// off, the group serves HTTP/1.1 alone: the client preface is refused 400 and
	// closed, and State's row reads disabled. It leaves class A-code once its merge
	// gate holds (ADR 0189 §D1).
	CapabilityHTTP2Loop Capability = "http2-loop"
	// CapabilityBusyPoll is busy polling of the network device's receive queues while
	// a loop waits (#979): IORING_REGISTER_NAPI on each io_uring loop's ring 0 and
	// EPIOCSPARAMS on each epoll loop's instance, both Linux 6.9, for
	// LoopValue.BusyPoll with prefer_busy_poll set. It spends CPU to spare the
	// wake-up an interrupt costs; a socket the kernel gives no NAPI id — loopback, a
	// veth without GRO — is not polled. Before 6.9 the row reads absent, and
	// unprobed on the portable backend. It leaves class A-code only through the
	// paired gate of ADR 0189's amendment of 2026-11-11.
	CapabilityBusyPoll Capability = "busy-poll"
)

The class A-code capabilities, opt-in, which KITSUNIUM_NET_DISABLE may switch off (ADR 0189).

const (
	// CapabilityURingSendZC is io_uring's zero-copy send (IORING_OP_SEND_ZC, Linux
	// 6.0), class A-code since #969 although it sits in this block: a group opts in
	// through LoopValue.Capabilities, every flight of at least LoopValue.SendZCMin
	// bytes then leaves zero-copy, released at the kernel's notification. The
	// probe asks REGISTER_PROBE for the opcode, then sends one datagram zero-copy
	// on loopback and waits for its notification. Such a group's listeners are
	// bound with plain TCP, Go's default MPTCP switched off for that group alone
	// (ListenerStateValue.MultipathTCP reads false): a socket accepted from an
	// MPTCP listener answers EOPNOTSUPP, after which the loop sends plainly. A
	// socket that refuses anyway — a Unix one, an adopted MPTCP listener's —
	// turns the loop's later flights plain, and one past RLIMIT_MEMLOCK that
	// flight alone.
	CapabilityURingSendZC Capability = "uring-send-zc"
	// CapabilityURingBundle is io_uring's send bundles; the receive bundle is
	// CapabilityURingRecvBundle's (ADR 0205).
	CapabilityURingBundle Capability = "uring-bundle"
	// CapabilityURingZCRX is io_uring's zero-copy receive (#980): IORING_OP_RECV_ZC
	// (58) into an area registered with IORING_REGISTER_ZCRX_IFQ (32), Linux 6.15, on
	// a ring set up with DEFER_TASKRUN and CQE32, from a NIC receive queue with
	// header and data split that the operator steered to the group's port, the
	// queue registered under CAP_NET_ADMIN. The row names the stage that stops it —
	// the kernel without the opcode, no LoopValue.ZCRX interface, or the register's
	// answer on a throwaway ring — and stays class B, receiving nothing, until its
	// BENCH.md row and threshold exist (ADR 0189, amended 2026-11-11).
	CapabilityURingZCRX Capability = "uring-zcrx"
	// CapabilityReuseportCBPF is a classic BPF program steering a datagram group's
	// SO_REUSEPORT sockets (#977): opted into with Steer, it hands each datagram to
	// the socket of index cpu mod n, the softirq's CPU. Refused — EPERM under a
	// seccomp filter, EINVAL before Linux 4.5 — the row reads absent and the hash
	// spread serves. The stream side, #857, is the loop's to do.
	CapabilityReuseportCBPF Capability = "reuseport-cbpf"
	// CapabilityURingMinTimeout is io_uring's minimum wait (min_wait_usec), only where
	// the kernel advertises IORING_FEAT_MIN_TIMEOUT.
	CapabilityURingMinTimeout Capability = "uring-min-timeout"
	// CapabilityTCPFastOpen is TCP_FASTOPEN on a loop group's listeners (#857): one
	// round trip saved to a repeat client where net.ipv4.tcp_fastopen allows it.
	CapabilityTCPFastOpen Capability = "tcp-fastopen"
)

The class B capabilities, declared, not built, which KITSUNIUM_NET_DISABLE may name (ADR 0189).

type CapabilityValue added in v0.26.28

type CapabilityValue struct {
	// Name is the capability.
	Name Capability `json:"name"`
	// Class is "a-kernel", "a-code" or "b".
	Class string `json:"class"`
	// Status is "on" (probed and in use), "available" (probed, not asked for),
	// "absent" (probed, the kernel lacks it), "missing" (a class A-code
	// capability the code opted into and the kernel lacks: a fault),
	// "disabled" (switched off by KITSUNIUM_NET_DISABLE or an unknown token,
	// never probed) or "unprobed" (the backend the group runs on has no use for
	// it).
	Status string `json:"status"`
	// Reason names the probe stage and the kernel's answer, or the switch.
	Reason string `json:"reason,omitempty"`
}

CapabilityValue reports one optional kernel capability of a loop group: what its probe found and whether the group runs with it (ADR 0189). A capability the operator switched off is never probed.

type ClientConfig

type ClientConfig struct {
	// BaseURL is the origin every relative path resolves against.
	BaseURL string `json:"base_url"`
	// DialTimeout bounds establishing the TCP connection.
	DialTimeout DurationValue `json:"dial_timeout"`
	// HandshakeTimeout bounds the TLS handshake.
	HandshakeTimeout DurationValue `json:"handshake_timeout"`
	// ResponseTimeout bounds the wait for response headers after the request
	// has been written. It is separate from TotalTimeout so a slow upstream is
	// distinguishable from a large body.
	ResponseTimeout DurationValue `json:"response_timeout"`
	// TotalTimeout bounds the whole call, body included.
	TotalTimeout DurationValue `json:"total_timeout"`
	// MaxResponseSize caps the response body in bytes. A body of exactly this
	// size is admitted; only one past it fails the call, and the body is never
	// truncated.
	MaxResponseSize int64 `json:"max_response_size"`
	// MaxRedirects caps the redirect chain. Every hop is re-authorised by the
	// policy, and a hop to another origin is refused unless RedirectOrigins
	// names it, so a redirect cannot walk out of the allowed surface.
	MaxRedirects int `json:"max_redirects"`
	// RedirectOrigins names the origins, each written scheme://host[:port], a
	// redirect may lead to besides the origin of the request that started the
	// chain. Empty — the default — refuses with RequestDenied every redirect
	// that changes the scheme, the host or the port, https to http included:
	// the built-in policies judge the method and the path, so a hop that keeps
	// the path and swaps the origin would otherwise pass them. A listed origin
	// is followed, and receives none of the DefaultHeaders. An entry that is
	// not a bare http or https origin fails New with InvalidAddress.
	RedirectOrigins []string `json:"redirect_origins"`
	// DefaultHeaders are added to every request that does not already set them
	// and is sent to the origin of BaseURL — or, with no BaseURL, to whatever
	// origin the request names. They are never sent to another origin: a
	// request built for one does not receive them, and a redirect that leaves
	// the origin of the request that started the chain is sent without them.
	DefaultHeaders map[string]string `json:"default_headers"`
}

ClientConfig describes an outbound client. Durations use DurationValue so a configuration file can say "5s" rather than a nanosecond count.

It sits in core beside LimitsValue and TimeoutsValue, which carry the same kind of knobs for the inbound half. Keeping both faces' configuration in one layer is what lets a consumer decode a whole network stanza against the contract package alone, without importing an implementation to name the type of a field it is setting.

type CompressValue added in v0.30.0

type CompressValue struct {
	// Types lists the Content-Types, without parameters, that may be compressed.
	// Empty selects text/plain, text/css, text/javascript, text/csv, text/xml,
	// application/json, application/javascript, application/xml, image/svg+xml
	// and application/wasm. text/html is not among them: a page that reflects a
	// request's input beside a secret is what BREACH reads, and a caller who
	// compresses HTML names it here, knowing it does not do that.
	Types []string `json:"types"`
	// Level is gzip's level, 1 to 9 or -1 for its default; zero selects -1.
	Level int `json:"level"`
	// MinBytes is the smallest body compressed: a smaller one is sent as it is,
	// as its gzip container would cost more than it saves. Zero selects 1024; a
	// negative value is refused.
	MinBytes int `json:"min_bytes"`
}

CompressValue configures the response compression of Compress: the types it may compress, which are an allowlist, the smallest body worth it, and gzip's level. Every field's zero value is the default, never no bound (ADR 0031).

type Conn

type Conn interface {
	stdnet.Conn
	// ID returns the monotonic identifier assigned at accept time, for
	// correlating log lines belonging to one connection.
	ID() uint64
	// Group returns the name of the listener group that accepted this
	// connection, so one handler serving several groups can tell them apart.
	Group() string
	// Buffer returns a connection-scoped scratch buffer sized by the group's
	// read-buffer limit. It is recycled with the connection, which is what lets
	// a handler read without allocating per connection.
	Buffer() []byte
}

Conn is one accepted stream connection.

It embeds stdlib net.Conn, so a handler reads and writes it exactly as it would any socket and every io helper keeps working — io.Copy, bufio, encoding wrappers, crypto/tls. Everything the domain adds is additive, which is what keeps "plug a handler in" a four-line exercise.

A Conn is pooled and recycled once ServeConn returns. A handler MUST NOT retain it, nor the slice returned by Buffer, past that point — the same contract the logger's builder already carries.

The connection a server hands out also implements io.ReaderFrom and io.WriterTo, so io.Copy in either direction reaches the socket's own copy — sendfile and splice on a plaintext TCP socket — and ConnUnwrapper. A direction the group bounds keeps a bound per operation. Into the connection (WriteTimeout), a source the socket sends without user space — a file or a socket, any syscall.Conn, alone or under an io.LimitedReader — still goes by the socket's own copy, one mebibyte under each write deadline; any other source goes by the generic copy, one bounded write at a time. Out of it (ReadTimeout or IdleTimeout), the generic copy, one bounded read at a time.

IFACE-PLUGIN: the concrete type stays unexported in internal/service/net/server.

type ConnHandler

type ConnHandler interface {
	ServeConn(ctx context.Context, c Conn) error
}

ConnHandler serves one accepted stream connection.

Returning an error does not kill the server: the connection is closed and the error is logged and metered. That is deliberate — one malformed peer must never be able to take down the process.

Implementations MUST be safe for concurrent use: the server runs one goroutine per connection and they all share the handler.

type ConnHandlerFunc

type ConnHandlerFunc func(ctx context.Context, c Conn) error

ConnHandlerFunc adapts a plain function to ConnHandler.

func (ConnHandlerFunc) ServeConn

func (f ConnHandlerFunc) ServeConn(ctx context.Context, c Conn) error

ServeConn implements ConnHandler by calling f.

type ConnUnwrapper added in v0.30.0

type ConnUnwrapper interface {
	// Unwrap returns the socket beneath the connection.
	Unwrap() stdnet.Conn
}

ConnUnwrapper is the optional capability of a Conn that hands out the socket under it, for what the standard library asserts on the concrete type: writev through net.Buffers.WriteTo on a plaintext TCP socket. A TLS group's answer is its *tls.Conn, where no zero-copy path exists.

The socket is the connection's and is borrowed for the handler's call: the caller MUST NOT close it, and what it reads or writes through it is under none of the group's bounds, so it sets its own deadlines. A connection answers false to the assertion when it has no such socket; the port itself is a sibling of Conn, which stays frozen (ADR 0039).

type DatagramStateValue added in v0.26.31

type DatagramStateValue struct {
	// TruncatedPackets is the number of messages dropped because the kernel cut
	// them: a coalesced message past its buffer (MSG_TRUNC under UDP GRO), or a
	// message whose control data did not fit (MSG_CTRUNC). Neither can be split
	// into whole datagrams, so none of what survives is delivered.
	TruncatedPackets uint64 `json:"truncated_packets"`
	// GSOOversized is the number of GSO sends the kernel refused with EMSGSIZE:
	// a sizing bug, counted, which leaves GSO on.
	GSOOversized uint64 `json:"gso_oversized"`
	// ReplyFailures is the number of replies a BatchReplies group queued during
	// a batch that did not leave: Reply had already reported them sent.
	ReplyFailures uint64 `json:"reply_failures"`
	// KernelDrops is the number of datagrams the kernel dropped before a read
	// could see them, for want of room in a socket's receive buffer: the last
	// SO_RXQ_OVFL count of each batched UDP socket, summed (#973). A gauge of the
	// kernel's own counter, read from the messages that arrive, so a drop after
	// the last message is seen with the next one. The kernel counts what it
	// refused to queue, so under UDP GRO a coalesced message of N datagrams is
	// one drop: the count then stands below the datagrams lost. Zero where the
	// socket is not batched, and under KITSUNIUM_NET_DISABLE=udp-rxq-ovfl.
	KernelDrops uint64 `json:"kernel_drops"`
	// Capabilities reports the optional kernel capabilities of the datagram
	// sockets — udp-rxq-ovfl and udp-pktinfo, and uring-multishot-recvmsg for the
	// groups PacketGroup.HandleLoop serves — as a loop group's do: on, absent, disabled by the
	// operator, or unprobed while no batched socket exists.
	Capabilities []CapabilityValue `json:"capabilities,omitempty"`
}

DatagramStateValue counts what the batched datagram paths drop (ADR 0192): the messages a read cut short, and the replies a batch queued that did not leave. Each is a drop nobody above a read loop could be told of, so the counter is the report.

type DurationValue

type DurationValue time.Duration

DurationValue is a time.Duration that round-trips through JSON as either a Go duration string ("30s", "1m30s") or a raw nanosecond count. The SDK's config loader (ADR 0028) decodes through a JSON round-trip, and encoding/json renders a bare time.Duration as nanoseconds — which would force an operator to write 30000000000 in a YAML file to mean thirty seconds. This type accepts both and always emits the readable form.

The mixed receivers are required by encoding/json and are not a style slip: MarshalJSON must take a value so a non-pointer struct field marshals, and UnmarshalJSON must take a pointer so it can assign. The standard library's time.Time carries the same asymmetry for the same reason.

PROMOTION CANDIDATE: this type is domain-neutral and stdlib-only, so it belongs in internal/kernel or internal/core/app/config the moment a second domain needs it. It is declared here because the repo's bar for a shared primitive is two real consumers arising from an actual duplication, and today there is one.

func (DurationValue) Duration

func (d DurationValue) Duration() time.Duration

Duration returns the value as a plain time.Duration.

func (DurationValue) MarshalJSON

func (d DurationValue) MarshalJSON() (encoded []byte, err error)

MarshalJSON emits the readable duration string so a written-back configuration file stays legible.

func (DurationValue) String

func (d DurationValue) String() string

String renders the canonical Go duration form, e.g. "1m30s".

func (*DurationValue) UnmarshalJSON

func (d *DurationValue) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts a quoted Go duration string ("30s") or a bare number of nanoseconds, so both a hand-written config file and a machine-generated one decode.

type EngineValue added in v0.26.18

type EngineValue uint8

EngineValue names the engine family a group runs on, passed by Engine (ADR 0187): the goroutine engine or the loop engine. The zero value, EngineAuto, lets the handler's type choose. The backend inside the loop family is LoopValue.Backend's.

const (
	// EngineAuto lets the group's handler type choose its engine (ADR 0187): a loop
	// handler runs on the loop engine, every other handler on the goroutine engine.
	EngineAuto EngineValue = iota
	// EngineGoroutine is the default engine: one goroutine per connection on the
	// runtime's netpoller. It serves Handle, HandleFunc and HandleHTTP.
	EngineGoroutine
	// EngineLoop is the loop engine of ADR 0186. It serves HandleLoop; Loop picks its
	// backend.
	EngineLoop
)

The engine families a group may name with Engine. The zero value is EngineAuto.

func (EngineValue) String added in v0.26.18

func (e EngineValue) String() string

String renders the engine as an error field spells it.

type FallbacksValue added in v0.26.20

type FallbacksValue struct {
	// Count is the number of fallbacks since the server was built.
	Count uint64 `json:"count"`
	// LastGroup names the group of the last fallback.
	LastGroup string `json:"last_group,omitempty"`
	// LastReason is the last fallback's reason.
	LastReason string `json:"last_reason,omitempty"`
	// LastAt is when the last fallback happened; zero when none did.
	LastAt time.Time `json:"last_at"`
}

FallbacksValue counts the times a group started on a lower engine than the one its code asked for, because the better one failed, and keeps the last one (ADR 0188). An override by the environment is not a fallback.

type FaultValue added in v0.26.20

type FaultValue struct {
	// Source says what reported it: "listener", "loop", "ring", "stall",
	// "capability", "gso" or "environment".
	Source string `json:"source"`
	// Group names the group concerned; empty for the environment.
	Group string `json:"group,omitempty"`
	// Reason names the fault and, where one exists, the runbook's switch.
	Reason string `json:"reason"`
}

FaultValue reports one thing the server runs without, or cannot do, now: a degraded listener or loop group, a drained ring, a stalled loop group, or an environment token the registry does not know (ADR 0188).

type HTTP1AccessLogger added in v0.26.30

type HTTP1AccessLogger interface {
	// LogHTTP1 receives one request's record.
	LogHTTP1(rec HTTP1AccessValue)
}

HTTP1AccessLogger receives the http1 chain's access-log records. Every slice of rec aliases the request or the response and is valid only while LogHTTP1 runs: a logger that keeps a byte copies it. It runs on the lane's thread, once per request, and must not block it: a logger that writes to a file or a socket queues the record.

IFACE-PLUGIN: what a consumer writes; the link that calls it is internal/service/net/http1's.

type HTTP1AccessStage added in v0.30.0

type HTTP1AccessStage uint8

HTTP1AccessStage says which moment of an answer's life an access record describes.

const (
	// HTTP1AccessAnswered is an answer written when the handler returned.
	HTTP1AccessAnswered HTTP1AccessStage = iota
	// HTTP1AccessDeferred is the first record of an answer deferred to the
	// batch's end; its Status is zero.
	HTTP1AccessDeferred
	// HTTP1AccessPending is an answer handed to the lane's worker: the record's
	// only one, its Status zero.
	HTTP1AccessPending
	// HTTP1AccessCompleted is a deferred answer's second record: Status and
	// BodyLen are the answer's, Elapsed the time since it was deferred.
	HTTP1AccessCompleted
)

type HTTP1AccessValue added in v0.26.30

type HTTP1AccessValue struct {
	// Lane is the request's lane.
	Lane int
	// Route is the route a router link matched, zero for none.
	Route int
	// Method is the request method.
	Method []byte
	// Path is the request's canonical path, without its query.
	Path []byte
	// Status is the answer's status code, 200 when the handler left it zero;
	// zero for an answer still deferred.
	Status int
	// BodyLen is the length of the answer's body.
	BodyLen int
	// Authenticated reports whether an authenticating link verified the request.
	Authenticated bool
	// TraceID is the trace the request joined, read from its traceparent by a
	// Trace link in the same chain; the zero value without one.
	TraceID trace.TraceID
	// Stage says what the record is: an answer written, one deferred or pending
	// and so with no status yet, or a deferred answer's completion.
	Stage HTTP1AccessStage
	// ID is the request's number (HTTP1RequestValue.ID): what a sampling decision
	// or a slow-request record is keyed on, the same on both records of a deferred
	// answer.
	ID uint64
	// Elapsed is the time from the batch's start to this record, the wait behind
	// the batch's earlier requests included; on a deferred answer's second record
	// the time since it was deferred.
	Elapsed DurationValue
	// RequestID is the request's correlation identifier, the one its answer
	// carries in X-Request-Id; empty without a RequestID link in the chain.
	RequestID HTTP1RequestIDValue
	// Proto is the request's HTTP major version, 1 or 2.
	Proto uint8
	// Stream is the HTTP/2 stream the request arrived on, zero on HTTP/1.1.
	Stream uint32
	// NFields is how many entries of Fields the record filled.
	NFields int
	// Fields holds the allowed request fields the request carried, in the order
	// the link was given their names.
	Fields [HTTP1MaxAccessFields]HTTP1FieldValue
}

HTTP1AccessValue is one request's access-log record, as the http1 chain's logging link hands it to an HTTP1AccessLogger: what the request asked, how it was routed and answered, and the request fields the link allows, by name. It never holds the Authorization field, the query or the body: the link refuses to allow a credential field, and the record has no slot for the rest. Every slice aliases the request or the response.

type HTTP1AuthValue added in v0.26.30

type HTTP1AuthValue struct {
	// contains filtered or unexported fields
}

HTTP1AuthValue is what an authenticating link of the http1 chain established about a request: a subject of at most HTTP1MaxSubject bytes, held in an array, and 64 bits of claims the application gives meaning to. It is a value, so copying a request copies it, and it never aliases the bytes the credentials were read from. The zero value is no subject and no claim.

func (*HTTP1AuthValue) Claims added in v0.26.30

func (a *HTTP1AuthValue) Claims() uint64

Claims returns the claims.

func (*HTTP1AuthValue) Reset added in v0.26.30

func (a *HTTP1AuthValue) Reset()

Reset clears the subject and the claims.

func (*HTTP1AuthValue) SetClaims added in v0.26.30

func (a *HTTP1AuthValue) SetClaims(c uint64)

SetClaims sets the claims.

func (*HTTP1AuthValue) SetSubject added in v0.26.30

func (a *HTTP1AuthValue) SetSubject(b []byte) bool

SetSubject copies b into the value and reports true, or leaves the value as it is and reports false when b is longer than HTTP1MaxSubject.

func (*HTTP1AuthValue) Subject added in v0.26.30

func (a *HTTP1AuthValue) Subject() []byte

Subject returns the subject, aliasing the value: valid while the value is, and empty when none was set.

type HTTP1Batcher added in v0.26.17

type HTTP1Batcher interface {
	// EndBatch ends the batch of the lane whose index is lane: the Lane every
	// request of the batch carried.
	EndBatch(lane int)
}

HTTP1Batcher is the optional capability of an HTTP1Handler served on a loop group: the end of each batch of the loop's requests.

The loop adapter calls EndBatch once per loop iteration that served at least one request, on the loop's thread, after every request of the batch went through ServeHTTP1 and before any answer of the batch is written. A handler runs the work it batched there — one transaction for every request, say — and completes each response it deferred (HTTP1ResponseValue.Defer). Every byte the batch's requests aliased is still valid while EndBatch runs, and none is once it returns. Like every callback of the loop, it must not block the loop's thread longer than the batch's own work.

IFACE-PLUGIN: what a consumer writes beside HTTP1Handler; the adapter that calls it is internal/service/net/http1's.

type HTTP1BearerVerifier added in v0.26.30

type HTTP1BearerVerifier interface {
	// VerifyBearer reports whether token, the credentials after "Bearer ", is
	// valid, and fills auth when it is.
	VerifyBearer(token []byte, auth *HTTP1AuthValue) bool
}

HTTP1BearerVerifier verifies a Bearer token for the http1 chain's authenticating link. VerifyBearer reports whether token is valid and, when it is, sets the subject and the claims on auth, which belongs to the request: the link copies nothing back, and nothing the verifier keeps may alias token, whose bytes the connection reuses once the batch ends. It runs on the lane's thread, once per request the link judges, and must not block it.

IFACE-PLUGIN: what a consumer writes; the link that calls it is internal/service/net/http1's.

type HTTP1BodyAborter added in v0.30.0

type HTTP1BodyAborter interface {
	// AbortBody tells the handler a request's body will not arrive whole.
	AbortBody(req *HTTP1RequestValue)
}

HTTP1BodyAborter is the optional capability of an HTTP1BodyReader that keeps something between chunks: the adapter calls AbortBody when a connection closes with a body still arriving, so the request's state can be freed. req is the value OnBody saw.

IFACE-PLUGIN: what a consumer writes beside HTTP1BodyReader; the adapter that calls it is internal/service/net/http1's.

type HTTP1BodyReader added in v0.30.0

type HTTP1BodyReader interface {
	// OnBody receives the body's next bytes, chunk valid for this call only.
	// last is true exactly once, on the bytes that complete the Content-Length.
	OnBody(req *HTTP1RequestValue, chunk []byte, last bool)
}

HTTP1BodyReader is the optional capability of an HTTP1Handler that takes a request's body as it arrives instead of whole in Request.Body (#844): OnBody is called with each chunk of the bytes that have arrived, on the lane's thread, and the request is served, by ServeHTTP1, once the last chunk was given, with Body nil. req's head stays valid across the calls and until ServeHTTP1 returns; each chunk is valid for its call only, so the spill holds a head and never a body, and Limits.Body bounds the Content-Length rather than LoopValue.SpillCap. A request with no body makes no call. A connection that closes before the last chunk ends the stream with no further call but HTTP1BodyAborter.AbortBody, when the handler has it. The adapter finds it on the handler given to HandleLoop, so a handler behind chain links does not stream.

IFACE-PLUGIN: what a consumer writes beside HTTP1Handler; the adapter that calls it is internal/service/net/http1's.

type HTTP1FieldValue added in v0.26.14

type HTTP1FieldValue struct {
	// Name is the field name, validated as an RFC 9110 token.
	Name []byte
	// Value is the field value, without its leading and trailing spaces and
	// tabs; it holds no NUL, CR, LF or other control byte but the tab.
	Value []byte
}

HTTP1FieldValue is one field line of a request, as it arrived: its name, in the case the client wrote it, and its value with the surrounding whitespace removed. Both slices alias the bytes the request was framed from.

type HTTP1FileValue added in v0.30.0

type HTTP1FileValue struct {
	// R reads the file: *os.File where a descriptor is to be sent, or any other
	// io.ReaderAt (an embed.FS file) through the copy path. Nil means no file.
	R io.ReaderAt
	// Off is the offset of the first byte to send.
	Off int64
	// Len is how many bytes to send; what Content-Length says.
	Len int64
}

HTTP1FileValue is the body of an HTTP1ResponseValue when it is a file: the reader the file comes from and the byte range to send. Only a pending answer's worker fills it (HTTP1Worker); the loop adapter sends it behind the head, with no copy when R is a file the connection's backend can take (a descriptor and LoopFileWriter), and otherwise through a bounded copy, read on the lane's worker and never on its thread. The codec owns R from the moment the answer comes back and closes it, when it is an io.Closer, on every path, the client's early close and the drain included. R nil means no file.

type HTTP1Handler added in v0.26.14

type HTTP1Handler interface {
	ServeHTTP1(req *HTTP1RequestValue, res *HTTP1ResponseValue)
}

HTTP1Handler answers one request of the restricted HTTP/1.1 profile.

It sees only requests the codec framed and accepted: a refusal is answered and the connection closed before any handler runs, and no handler can turn one into an answer of its own. It fills res — the status, extra field lines and the body — and the codec writes the framing: the status line, Content-Length and Connection: close.

req is valid until ServeHTTP1 returns, and so is every byte it aliases unless the caller says longer: on the loop engine those bytes stay valid until the loop's batch ends, HTTP1Batcher.EndBatch included. A handler that keeps one past that copies it. On the loop engine a handler runs on the loop's thread and must not block it.

type HTTP1HandlerFunc added in v0.26.14

type HTTP1HandlerFunc func(req *HTTP1RequestValue, res *HTTP1ResponseValue)

HTTP1HandlerFunc adapts a plain function to HTTP1Handler.

func (HTTP1HandlerFunc) ServeHTTP1 added in v0.26.14

func (f HTTP1HandlerFunc) ServeHTTP1(req *HTTP1RequestValue, res *HTTP1ResponseValue)

ServeHTTP1 implements HTTP1Handler by calling f.

type HTTP1Limiter added in v0.30.0

type HTTP1Limiter interface {
	// Allow reports whether one more request under key may be served, and when
	// it may not, after how many seconds a client should try again. now is a
	// monotonic count of nanoseconds: an instant that moves backwards refills
	// nothing. key is valid for this call only.
	Allow(key []byte, now int64) (ok bool, retryAfter int)
}

HTTP1Limiter decides whether one more request under a key may be served: a rate-limit port the chain's RateLimit link calls, per request and on the lane's thread, so an implementation must be safe for every lane at once and allocate nothing. NewKeyedBuckets is the SDK's; resilience's limiter adapts to it.

IFACE-PLUGIN: what a consumer may implement; the call is internal/service/net/http1's.

type HTTP1LimitsValue added in v0.26.14

type HTTP1LimitsValue struct {
	// Head bounds the request line, the field lines and the blank line that
	// ends them, in bytes: zero selects HTTP1DefaultHead, at most HTTP1MaxHead.
	Head int `json:"head"`
	// Target bounds the request-target, in bytes: zero selects
	// HTTP1DefaultTarget, and it may not exceed Head.
	Target int `json:"target"`
	// Body bounds the Content-Length a request may declare: zero selects
	// HTTP1DefaultBody, at most HTTP1MaxBody.
	Body int64 `json:"body"`
}

HTTP1LimitsValue bounds what one request of the restricted HTTP/1.1 profile may carry. Zero means the profile's default, never unbounded; a negative field, or one past its maximum, is refused when the codec is built.

type HTTP1RequestIDValue added in v0.30.0

type HTTP1RequestIDValue struct {
	// contains filtered or unexported fields
}

HTTP1RequestIDValue is a request's correlation identifier: at most HTTP1MaxRequestID bytes of visible ASCII (0x21 to 0x7E), held in an array. It is a value, so copying a request or a response copies it and it never aliases the bytes it was read from. The zero value is no identifier.

func (*HTTP1RequestIDValue) Bytes added in v0.30.0

func (v *HTTP1RequestIDValue) Bytes() []byte

Bytes returns the identifier, aliasing the value: valid while the value is, and empty when none was set.

func (*HTTP1RequestIDValue) Reset added in v0.30.0

func (v *HTTP1RequestIDValue) Reset()

Reset clears the identifier.

func (*HTTP1RequestIDValue) Set added in v0.30.0

func (v *HTTP1RequestIDValue) Set(b []byte) bool

Set copies b into the value and reports true, or leaves the value as it is and reports false when b is empty, longer than HTTP1MaxRequestID or holds a byte outside 0x21 to 0x7E: the codec writes the value into a response head, so what the wire cannot carry never enters it.

type HTTP1RequestValue added in v0.26.14

type HTTP1RequestValue struct {
	// Method is the request method, a token.
	Method []byte
	// Target is the request-target exactly as sent, still percent-encoded.
	Target []byte
	// Host is the Host field's value; HasHost says whether it was present.
	Host []byte
	// Authorization is the Authorization field's value; HasAuthorization says
	// whether it was present.
	Authorization []byte
	// Body is the request content, Content-Length bytes after the head. It is
	// empty until the whole body has arrived.
	Body []byte
	// ContentLength is the declared body length, zero when the field is absent.
	ContentLength int64
	// HeadLen is the length of the head: the request line, the field lines and
	// the blank line. It is zero until the head is complete.
	HeadLen int
	// Consumed is HeadLen plus ContentLength: the bytes this request takes from
	// the stream, whatever its method or route. The next pipelined request
	// starts there. It is set as soon as the head is complete, before the body
	// arrives, so an early answer knows how much it has to drain.
	Consumed int
	// NFields is how many entries of Fields the request filled.
	NFields int
	// Fields holds the request's field lines in the order they arrived,
	// Host, Authorization and Content-Length included.
	Fields [HTTP1MaxFields]HTTP1FieldValue
	// HasHost reports whether the request carried a Host field.
	HasHost bool
	// HasAuthorization reports whether the request carried an Authorization
	// field.
	HasAuthorization bool
	// Close reports whether the client asked to close the connection after
	// this request: a Connection field holding the close token.
	Close bool
	// Lane is the index of the lane that framed the request, from 0 to the
	// group's lane count less one — its LoopIO.Loop — set by the loop adapter
	// before the handler runs. One lane is one thread or one goroutine, so a
	// handler finds its per-lane state in a slice sized at LoopStarter.OnStart,
	// with no lock. Parse leaves it as it is; outside the loop adapter it is
	// what the caller set.
	Lane int
	// Path is the request's canonical path, the one target every link of a
	// chain routes and judges on, computed once by the codec: the target's
	// path up to its query, an absolute-form target reduced to its path once
	// its authority matched Host. It is still percent-encoded, and holds no
	// dot-segment, no empty segment, no backslash and no encoded slash, dot,
	// backslash or NUL: the codec refuses a target that would (400). An
	// asterisk-form target, OPTIONS only, is "*".
	Path []byte
	// Query is the target's query, after its "?" and without it; empty when
	// the target has none.
	Query []byte
	// Route is the identifier of the route a router link matched, zero for
	// none. Parse resets it to zero; only a link sets it.
	Route int
	// Auth is what an authenticating link established: the subject and the
	// claims, copied by value into the request, so nothing of them aliases
	// bytes the connection reuses. Parse resets it; Authenticated says
	// whether a link set it.
	Auth HTTP1AuthValue
	// Authenticated reports whether an authenticating link verified the
	// request's credentials and filled Auth. Parse resets it to false.
	Authenticated bool
	// Trace is the request's W3C trace context, as the http1 chain's Trace link
	// read it from the traceparent field: invalid (IsValid false) when no such
	// link ran, when the field is absent, or when it is malformed, a request
	// being served either way (§4.3). tracestate is not read: a request carries
	// the 25 bytes of the context and no list. Parse resets it.
	Trace trace.SpanContextValue
	// Scheme is the :scheme pseudo-field of an HTTP/2 request, empty on HTTP/1.1.
	// An HTTP/2 codec maps a request before Canonical (ADR 0221): Method from
	//:method; Target from :path, origin-form or "*" (an absolute-form :path is
	// RefusalTargetForm); Host from :authority, else the Host field, both present
	// and different RefusalTargetHost, HasHost set; Fields the regular fields in
	// arrival order with HPACK's lowercase names; Authorization, ContentLength and
	// Body as on HTTP/1.1; Close, HeadLen and Consumed zero.
	Scheme []byte
	// Proto is the request's HTTP major version, 1 or 2.
	Proto uint8
	// Stream is the HTTP/2 stream the request arrived on, zero on HTTP/1.1.
	Stream uint32
	// ID identifies the request on its lane — the lane in the top 16 bits, a
	// counter from 1 in the rest — set by the loop adapter before the handler
	// runs, so two lanes never hold the same one. It is a number the client
	// never sees, what a sampling decision or a slow-request record is keyed on.
	// Parse leaves it as it is; outside the loop adapter it is what the caller
	// set.
	ID uint64
	// Conn names the connection the request arrived on, set by the loop adapter
	// before the handler runs: what a handler keeps to find, at
	// HTTP1Streamer.OnStreamOpen, the stream this request opened. Parse leaves it
	// as it is.
	Conn LoopConnRef
	// RoutePrefix is how many leading bytes of Path the Prefix route a router link
	// matched covers, zero for an exact route or none: what a file tree strips to
	// find the name under its route. Parse resets it; only a link sets it.
	RoutePrefix int
	// RequestID is the request's correlation identifier, as the http1 chain's
	// RequestID link accepted it from X-Request-Id or minted it: copied by
	// value, so a handler that needs it past the batch copies this value into
	// its own job table. Parse resets it; it is empty when no such link ran.
	RequestID HTTP1RequestIDValue
}

HTTP1RequestValue is one request of the restricted HTTP/1.1 profile, framed by the codec (internal/service/net/http1).

It is not named RequestValue: that name is the outbound request the Policy port judges, and the two never meet. The profile's prefix is the domain's way of naming a protocol's value, as SSEEventValue and WSMessageValue do.

Every slice aliases the bytes the codec read: the value is valid while those bytes are, which on the loop engine is until OnBatchEnd returns. A handler that keeps a byte past that copies it. Fields is an array, so a value framed into a reused HTTP1RequestValue allocates nothing. Auth is a value, not a slice: what a link established survives the bytes it was read from.

A link of a chain may read every field and set Route, Auth and Authenticated; writing Consumed, HeadLen or Body changes nothing, since the adapter frames with what Parse returned, never with what a link left.

func (*HTTP1RequestValue) Field added in v0.26.14

func (r *HTTP1RequestValue) Field(name string) ([]byte, bool)

Field returns the value of the first field named name and true, or nil and false when the request carries none. Names compare case-insensitively on A–Z only, so no locale or Unicode folding can make two names equal.

type HTTP1ResponseValue added in v0.26.14

type HTTP1ResponseValue struct {
	// Status is the status code, 200 to 999; zero means 200. The profile
	// sends no interim response.
	Status int
	// Header holds the response's extra field lines, each written as
	// "Name: value\r\n" by the codec's writer. It never holds Content-Length or
	// Connection, which the codec writes.
	Header []byte
	// Body is the response content.
	Body []byte
	// Close asks the codec to close the connection after this response.
	Close bool
	// Defer, set by the handler, holds the answer back until the end of the
	// loop's batch, for work it batches — one database transaction for every
	// request of the batch, say. The response value then stays valid, at the
	// same address, until HTTP1Batcher.EndBatch returns, and the handler
	// completes it there: it fills it and clears Defer. An answer still
	// deferred when EndBatch returns is answered 500 and its connection
	// closed. Only the loop adapter reads it; AppendResponse does not.
	Defer bool
	// Pending, set by the handler, hands the answer to the lane's worker
	// (HTTP1Worker, R3): the loop adapter sends Job to it and writes the
	// answer the worker fills, from the loop's thread, once it comes back.
	// Answers after it on the same connection wait for it, in order. A queue
	// to the worker that is full answers 503 with Retry-After at once, the
	// connection kept; a connection that closes first drops the answer. Pending
	// with Defer, or with no worker, is answered 500 and the connection closed.
	// Only the loop adapter reads it.
	Pending bool
	// Job is the integer a pending answer's worker is given: the handler's own
	// index of what the work needs, which it copied out of the request, since
	// the request's bytes do not outlive the batch.
	Job uint64
	// TraceLine holds the traceparent field line the http1 chain's Trace link
	// writes on this answer, Header pointing at it; a value, so a deferred
	// answer keeps it past the request. Only the link sets it.
	TraceLine [HTTP1TraceLineLen]byte
	// RequestID is the identifier the codec writes as an X-Request-Id field after
	// Header, when set: a value, so a deferred or a pending answer keeps it past
	// the request, and a field written per request needs no slice to outlive the
	// batch. The chain's RequestID link sets it.
	RequestID HTTP1RequestIDValue
	// Stream answers with a response delimited by the connection's close (RFC 9112
	// §6.3 item 8): the codec writes the status line and the fields with
	// Connection: close and no Content-Length, refusing HEAD, 204 and 304 with
	// HTTP1ResponseInvalid, and the handler keeps writing the body through
	// HTTP1Streamer. Requests pipelined behind it are dropped with the connection.
	// A Stream with Defer or Pending, behind a held answer, or with no
	// HTTP1Streamer in the handler is answered 500 and the connection closed. It
	// does not make a chunked answer: ADR 0186 §D6 still refuses every coding.
	Stream bool
	// Upgrade answers 101 Switching Protocols: the codec writes the status line,
	// Connection: Upgrade and Header — which holds the protocol's own fields —
	// and no length, then every later byte of the connection goes to
	// HTTP1Upgrader. It carries no body; with one, or with no HTTP1Upgrader in the
	// handler, the answer is 500 and the connection closed.
	Upgrade bool
	// RetryAfter, when above zero, is written by the codec as a Retry-After field
	// in seconds after Header: a value, so a refusal needs no slice of its own.
	// The chain's RateLimit link sets it.
	RetryAfter int
	// CORS holds the field lines the chain's CORS link gives this answer, written
	// by the codec after Header: its own slot, so the link costs nothing beside a
	// Headers link, and its bytes are the policy's, shared and read only.
	CORS []byte
	// File, set by a pending answer's worker, makes the answer's body a file: the
	// codec writes the head with Content-Length File.Len and no body bytes, and
	// sends the file behind it. A HEAD answer closes File.R unsent. With Body, on a
	// 204 or a 304, or outside a worker's answer it is answered 500 and the
	// connection closed.
	File HTTP1FileValue
}

HTTP1ResponseValue is the answer an HTTP1Handler gives to one request.

The handler sets the status, appends any field lines to Header with the codec's writer and the content to Body. The codec writes the status line, the Content-Length computed from Body and Connection: close when it is needed: the handler never spells the response's framing, so it cannot get it wrong.

type HTTP1StreamValue added in v0.30.0

type HTTP1StreamValue struct {
	// IO is the loop the connection lives on.
	IO LoopIO
	// Ref is the connection.
	Ref LoopConnRef
}

HTTP1StreamValue is a stream response opened on a connection, handed to HTTP1Streamer's callbacks. It is a value a handler keeps for as long as the connection lives, and every method runs on the lane's thread: a callback, or a LoopIO.Post.

func (HTTP1StreamValue) Deadline added in v0.30.0

func (s HTTP1StreamValue) Deadline(d time.Duration) error

Deadline arms the connection's timer, HTTP1Streamer.OnStreamTimeout firing after d.

func (HTTP1StreamValue) End added in v0.30.0

func (s HTTP1StreamValue) End() error

End closes the connection once the output already written has left: the only way a response delimited by close ends.

func (HTTP1StreamValue) Send added in v0.30.0

func (s HTTP1StreamValue) Send(p []byte) error

Send writes p as the next bytes of the body, copied before it returns. It is refused as LoopIO.Write is: LoopOutputFull when the connection's output is past its ceiling, LoopStaleRef when it closed.

type HTTP1Streamer added in v0.30.0

type HTTP1Streamer interface {
	// OnStreamOpen is called once the stream's head has been queued, from the
	// batch's end: s is valid for as long as the connection lives.
	OnStreamOpen(s HTTP1StreamValue)
	// OnStreamTimeout is called when the deadline s.Deadline armed expires.
	OnStreamTimeout(s HTTP1StreamValue)
	// OnStreamDrain is called once when the group starts draining: the last call
	// before the drain closes the connection, so a handler says goodbye and ends
	// the stream itself.
	OnStreamDrain(s HTTP1StreamValue)
	// OnStreamClose is called once the connection has closed; err says why, nil for an End or a clean close.
	OnStreamClose(s HTTP1StreamValue, err error)
}

HTTP1Streamer is the optional capability of an HTTP1Handler that answers with Stream: the loop adapter tells it when the stream's head has been written, and when it closes, on the lane's thread. Found on the handler given to HandleLoop or on the one a chain of links ends in; with none, a Stream is answered 500.

IFACE-PLUGIN: what a consumer writes beside HTTP1Handler; the adapter that calls it is internal/service/net/http1's.

type HTTP1Upgrader added in v0.30.0

type HTTP1Upgrader interface {
	// OnUpgradedData is handed bytes after the head of an upgraded connection and
	// returns how many it took: the rest waits in the spill for its next bytes,
	// and buf is valid for this call only.
	OnUpgradedData(s HTTP1StreamValue, buf []byte) int
}

HTTP1Upgrader is the optional capability of an HTTP1Streamer that takes the connection's bytes after a 101: found alongside it, called on the lane's thread.

IFACE-PLUGIN: what a consumer writes beside HTTP1Streamer; the adapter that calls it is internal/service/net/http1's.

type HTTP1Worker added in v0.26.33

type HTTP1Worker interface {
	// WorkHTTP1 fills res, the answer to the pending request of lane whose Job
	// was job.
	WorkHTTP1(lane int, job uint64, res *HTTP1ResponseValue)
}

HTTP1Worker is the optional capability of an HTTP1Handler served on a loop group that answers off the loop's thread (R3): a response the handler marks Pending is handed to the lane's worker goroutine, which calls WorkHTTP1 with the response's Job and a response value of its own to fill; the loop adapter writes it, from the loop's thread, once WorkHTTP1 returns. WorkHTTP1 may block. It reads nothing of the request, whose bytes do not outlive the batch: the handler copied what the work needs and named it by job.

IFACE-PLUGIN: what a consumer writes beside HTTP1Handler; the adapter that calls it is internal/service/net/http1's.

type HTTPCountsValue added in v0.30.0

type HTTPCountsValue struct {
	// HTTP1Requests counts the HTTP/1.1 requests served.
	HTTP1Requests uint64
	// HTTP2Requests counts the HTTP/2 requests served.
	HTTP2Requests uint64
	// HTTP2Errors counts the HTTP/2 errors by type, a row for each counted.
	HTTP2Errors []corehttp2.ErrorCountValue
}

HTTPCountsValue is what a LoopHTTPCounter reports: the requests a loop adapter served per version and the HTTP/2 errors it counted, the numbers ListenerStateValue carries under the same names.

type IdentityFileParams

type IdentityFileParams struct {
	// CertFile is the PEM leaf certificate followed by any intermediates.
	// Supplying it without KeyFile is always a configuration slip, never an
	// intention, and is refused: an mTLS client that silently degrades to plain
	// TLS is a security hole that reports nothing.
	CertFile string `json:"cert_file"`
	// KeyFile is the PEM private key matching CertFile.
	KeyFile string `json:"key_file"`
	// RootsFile is the PEM CA bundle used to verify the peer. Leaving it empty
	// selects the platform trust store, which is right for a public endpoint and
	// wrong for a private PKI.
	RootsFile string `json:"roots_file"`
	// ClientCAFile is the PEM CA bundle a server accepts client certificates
	// from. Required whenever RequireClientCert is set.
	ClientCAFile string `json:"client_ca_file"`
	// ServerName overrides the name verified against the peer certificate.
	//
	// This is far more often required than its "optional" typing suggests. Any
	// deployment reached through a port forward or a tunnel connects to
	// 127.0.0.1 while the peer certificate names the real service, and the
	// handshake then fails every single time until ServerName is set. Treat it
	// as mandatory whenever the dial address is not the certificate's name.
	ServerName string `json:"server_name"`
	// MinVersion is the lowest accepted TLS version. Zero selects TLS 1.3;
	// anything below TLS 1.2 is refused.
	MinVersion uint16 `json:"min_version"`
	// NextProtos is the ALPN protocol list, e.g. {"h2", "http/1.1"}.
	NextProtos []string `json:"next_protos"`
	// RequireClientCert turns a server identity into mutual TLS.
	RequireClientCert bool `json:"require_client_cert"`
}

IdentityFileParams names TLS material to read from the filesystem. Every path is optional on its own; the combination is validated once the bytes are in hand, by the same core constructor that validates in-memory material.

It is the disk-sourced twin of IdentityParams and lives beside it on purpose: the two describe one domain concept — the material an identity is built from — and splitting them across layers would let the rules that govern them drift apart, which is exactly what this domain refuses for TLS.

type IdentityParams

type IdentityParams struct {
	// CertPEM is the PEM-encoded leaf certificate followed by any intermediates.
	// Paired with KeyPEM; supplying one without the other is an error.
	CertPEM []byte
	// KeyPEM is the PEM-encoded private key matching CertPEM.
	KeyPEM []byte
	// RootsPEM is the PEM-encoded CA bundle used to verify the peer. When empty
	// the platform trust store is used, which is the right default for a public
	// endpoint and the wrong one for a private PKI.
	RootsPEM []byte
	// ClientCAPEM is the PEM-encoded CA bundle a server accepts client
	// certificates from. It is meaningless on a client identity.
	ClientCAPEM []byte
	// ServerName overrides the name verified against the peer certificate. It is
	// required when connecting to a host by IP whose certificate names a DNS name.
	ServerName string
	// MinVersion is the lowest accepted TLS version. Zero selects TLS 1.3;
	// anything below TLS 1.2 is refused outright.
	MinVersion uint16
	// NextProtos is the ALPN protocol list, e.g. {"h2", "http/1.1"}.
	NextProtos []string
	// RequireClientCert turns a server identity into mutual TLS: the peer must
	// present a certificate that chains to ClientCAPEM.
	RequireClientCert bool
}

IdentityParams carries in-memory TLS material. Every field is optional on its own; the combination is what is validated by NewIdentity. Sourcing material from memory rather than from disk is what lets a consumer pull certificates out of a secret manager without ever writing them to a filesystem.

type IdentityValue

type IdentityValue struct {
	// contains filtered or unexported fields
}

IdentityValue is an opaque TLS identity: a certificate chain, the trust anchors used to verify the peer, and the negotiated-version policy. Its String and GoString output is always "<redacted>" so an accidental %v, %s or %#v can never spill key material into a log line. The only way out is ClientConfig or ServerConfig, which mint a fresh *tls.Config per call.

The zero value is valid and means "no identity" — IsZero reports it.

func NewIdentityValue

func NewIdentityValue(p IdentityParams) (id IdentityValue, err error)

NewIdentityValue validates in-memory TLS material and returns the opaque identity. It fails loudly where the standard library is silent: a CA bundle that yields no usable certificate is TLSMaterialInvalid, never an empty pool that would verify nothing.

func (IdentityValue) ClientConfig

func (i IdentityValue) ClientConfig() *tls.Config

ClientConfig returns a fresh *tls.Config for dialling a peer. Each call yields a new value so a caller mutating the result cannot affect any other user of the same identity.

"Fresh" covers the slices and the trust pool, not only the outer struct. A new *tls.Config whose Certificates and RootCAs still point at the identity's own would leave cfg.Certificates[0] = other and cfg.RootCAs.AddCert(evil) reaching every other configuration the identity has ever minted, including ones already in use — which is the opposite of what an opaque, immutable identity is for. Who pays that cost, and how often, is the CALLER's choice and not a property of this method: net/client calls it once inside New and net/server once per listener, but service/app/mail calls it per connection — which for SMTP is per message — deliberately, because StartTLS takes the config and a transport that reused one would hand every send a *tls.Config another send could still be mutating. Three slice clones are nothing beside an SMTP round trip; the sentence that used to sit here said "never per request", which was true of the two net callers and false of the third.

func (IdentityValue) GoString

func (i IdentityValue) GoString() string

GoString implements fmt.GoStringer so %#v stays redacted too — fmt bypasses String for Go-syntax formatting and would otherwise dump the unexported fields.

func (IdentityValue) IsZero

func (i IdentityValue) IsZero() bool

IsZero reports whether the identity carries no material at all, which is the zero value a caller gets when no TLS was configured.

func (IdentityValue) ServerConfig

func (i IdentityValue) ServerConfig() *tls.Config

ServerConfig returns a fresh *tls.Config for accepting connections, switching to mutual TLS when the identity requires a client certificate.

func (IdentityValue) String

func (i IdentityValue) String() string

String implements fmt.Stringer and always redacts so key material never reaches a log line through %v or %s.

type LimitsValue

type LimitsValue struct {
	// MaxConns caps concurrent connections in the group. Beyond it, a new
	// connection is accepted and immediately closed with ConnLimitReached, which
	// is kinder than letting the accept queue silently overflow.
	MaxConns int `json:"max_conns"`
	// ReadBufferSize sizes the per-connection scratch buffer returned by
	// Conn.Buffer.
	ReadBufferSize int `json:"read_buffer_size"`
	// MaxPacketSize is the largest datagram accepted. A larger one is DROPPED
	// and counted in StateValue.OversizedPackets, never delivered truncated:
	// the kernel discards the tail, so the prefix that survives reads exactly
	// like a complete message and the handler cannot tell the difference.
	MaxPacketSize int `json:"max_packet_size"`
	// Backlog is the depth the kernel queues established connections at, before the
	// accept loop takes them (#978). Zero keeps the standard library's, the host's
	// somaxconn; the kernel clamps a larger value to it, and State says so. A
	// stream group sets it with the Backlog option, on both engines; Windows
	// keeps the platform's own.
	Backlog int `json:"backlog"`
	// ReceiveBuffer is the receive buffer, in bytes, the kernel is asked for on
	// the group's sockets, before bind (SO_RCVBUF): a datagram group's, a stream
	// group's listeners and so the sockets they accept (#973). Zero keeps the
	// kernel's default. The kernel doubles the value and clamps it to rmem_max;
	// State reports what it holds, in ListenerStateValue.ReceiveBuffer, and a
	// clamp is no fault.
	ReceiveBuffer int `json:"receive_buffer"`
	// SendBuffer is the send buffer, in bytes, asked as ReceiveBuffer is
	// (SO_SNDBUF, clamped to wmem_max).
	SendBuffer int `json:"send_buffer"`
	// Shards is the number of independent listeners to open on one address with
	// SO_REUSEPORT, each with its own accept loop, to remove accept contention.
	// Zero selects the platform default.
	Shards int `json:"shards"`
	// BatchSize is how many datagrams to read per syscall. Zero selects the
	// default; one disables batching.
	BatchSize int `json:"batch_size"`
}

LimitsValue bounds what one listener group may consume. Every field is optional; zero means "use the domain default", never "unbounded" — an unset ceiling must not be the dangerous one.

type ListenerStateValue

type ListenerStateValue struct {
	// Group is the listener group's name.
	Group string `json:"group"`
	// Address is the address actually bound, which for a port of zero is the
	// one the kernel chose rather than the one requested.
	Address string `json:"address"`
	// Network is the socket family.
	Network string `json:"network"`
	// Shards is how many listeners were actually opened on this address.
	Shards int `json:"shards"`
	// Degraded reports that a requested optimisation was unavailable and the
	// server fell back. It is surfaced rather than logged once at startup,
	// because a silent fallback is indistinguishable from a working one and
	// nobody re-reads yesterday's logs to find out.
	Degraded bool `json:"degraded"`
	// DegradedReason names the fallback in plain words when Degraded is set.
	DegradedReason string `json:"degraded_reason,omitempty"`
	// Adopted reports a listener inherited from a supervisor rather than bound
	// by this process.
	Adopted bool `json:"adopted"`
	// Exclusive reports that no other socket can bind the listener's address
	// while it lives: SO_EXCLUSIVEADDRUSE on Windows, set on a unicast TCP or
	// UDP socket this process binds and read back, never set, on an adopted
	// one. It is false on every other platform, which has no such option.
	Exclusive bool `json:"exclusive"`
	// Shared reports a listener its group bound with ShareAddress: SO_REUSEPORT
	// set on every socket of the address and no probe made, so another process
	// that shares too may bind the same address and take part of its
	// connections (#1330). It is false on a listener bound without the option,
	// whose sharded sockets were opened only once a plain bind found the
	// address free, and on an adopted one.
	Shared bool `json:"shared,omitempty"`
	// BindWait is how long the bind waited for its address to come free: on
	// Windows, a restarted server's address stays taken until the previous
	// process's connections end, and the bind is retried, bounded. Zero when
	// the first attempt bound.
	BindWait DurationValue `json:"bind_wait"`
	// H2C reports an HTTP group serving HTTP/2 in cleartext, by prior knowledge alone.
	H2C bool `json:"h2c,omitempty"`
	// HTTP2 is the HTTP/2 bounds the group serves with, resolved: a group given
	// none reads ADR 0195's numbers. Nil for a listener that serves no HTTP.
	HTTP2 *corehttp2.LimitsValue `json:"http2,omitempty"`
	// MaxBodyBytes is the request-body bound an HTTP group served by net/http
	// holds on this listener: the default's 1 MiB, the option's value, or -1
	// for none. Zero on every other listener, which has no such bound.
	MaxBodyBytes int64 `json:"max_body_bytes"`
	// Backlog is the depth of the listener's accept queue as applied: the
	// Backlog option's value, or the host's somaxconn for the default, and the
	// clamp when the host's somaxconn is below what was asked (Degraded, with
	// its reason). Zero for an adopted socket, whose queue the supervisor sized,
	// and for a datagram socket.
	Backlog int `json:"backlog"`
	// ReceiveBuffer is the socket's receive buffer as the kernel holds it,
	// read back after bind, or after the group's size was applied to an
	// adopted socket (SO_RCVBUF): on Linux twice what was asked, clamped to
	// rmem_max, the kernel's default when nothing was asked. Zero where the
	// platform cannot say. A clamp is reported here and is no fault.
	ReceiveBuffer int `json:"receive_buffer"`
	// SendBuffer is the socket's send buffer as the kernel holds it, read back
	// as ReceiveBuffer is (SO_SNDBUF, wmem_max).
	SendBuffer int `json:"send_buffer"`
	// MultipathTCP reports a stream listener whose socket is Multipath TCP,
	// read back from the socket (SO_PROTOCOL is IPPROTO_MPTCP) after bind or
	// adoption: on Linux, what Go's net.Listen opens by default since Go 1.24
	// where the kernel has MPTCP (net.mptcp.enabled), unless GODEBUG's
	// multipathtcp says otherwise. A loop group that opts into uring-send-zc
	// on io_uring binds plain TCP instead, and its rows read false, because a
	// socket accepted from an MPTCP listener refuses IORING_OP_SEND_ZC; the
	// operator switching uring-send-zc off, or capping the group at epoll or
	// the portable backend, gives it Go's default back (#969, ADR 0189).
	// False on every other platform, for a Unix socket and for a datagram one.
	MultipathTCP bool `json:"multipath_tcp"`
	// ProxyRefused counts the connections a ProxyProtocol group refused — peers
	// outside Trusted, and headers malformed or not finished within Timeout. It
	// is the group's count, on each of its listeners' rows.
	ProxyRefused uint64 `json:"proxy_refused,omitempty"`
	// HTTP1Requests counts the HTTP/1.1 requests the group served since Start
	// (#966): one count per request on net/http's engine, one per batch on the
	// loop engine, whose adapter counts them. It is the group's count, on each
	// of its listeners' rows; zero on a group that serves no HTTP.
	HTTP1Requests uint64 `json:"http1_requests,omitempty"`
	// HTTP2Requests counts the HTTP/2 requests the group served, as HTTP1Requests does.
	HTTP2Requests uint64 `json:"http2_requests,omitempty"`
	// HTTP2Errors counts the group's HTTP/2 protocol errors by type, a row for
	// each type counted at least once, in the vocabulary's order: on net/http's
	// engine what its CountError reports, an unanswered PING included; on the
	// loop engine each RST_STREAM and GOAWAY its codec answers with an error
	// and each header list past its bound. net/http counts neither a header
	// list past MaxHeaderBytes, which it answers 431 itself, nor a frame past
	// its frame bound.
	HTTP2Errors []corehttp2.ErrorCountValue `json:"http2_errors,omitempty"`
	// ALPN is the protocol list a TLS HTTP group's listeners offer, in the
	// order offered (#964): the group's versions intersected with its
	// identity's NextProtos, on net/http's engine and on a TLS loop codec
	// group alike. Nil for a group that negotiates nothing.
	ALPN []string `json:"alpn,omitempty"`
}

ListenerStateValue reports one bound listener.

It carries the address actually bound rather than the one requested, and it reports any fallback explicitly, so a caller never has to infer either.

type LoopBackend added in v0.26.13

type LoopBackend uint8

LoopBackend names the mechanic a loop group runs on (ADR 0186 §D5). The zero value is LoopBackendAuto.

const (
	// LoopBackendAuto takes the best backend the platform's probe reaches:
	// io_uring, then epoll on Linux, kqueue on darwin, the portable backend
	// elsewhere — a native backend the probe refuses falls back to it. On
	// FreeBSD, OpenBSD, NetBSD and DragonFly it stays portable while no
	// paired row was measured there: kqueue runs there when named,
	// LoopBackendKqueue (ADR 0222).
	LoopBackendAuto LoopBackend = iota
	// LoopBackendIOURing is io_uring, on linux/amd64, arm64, riscv64 and loong64,
	// after the kernel's staged probe.
	LoopBackendIOURing
	// LoopBackendEpoll is epoll, on every Linux architecture.
	LoopBackendEpoll
	// LoopBackendPortable is the default engine's accept path and goroutines,
	// serialised onto one handler goroutine, on every platform.
	LoopBackendPortable
	// LoopBackendKqueue is kqueue, on darwin, FreeBSD, OpenBSD, NetBSD and
	// DragonFly, after a staged probe (ADR 0222). LoopBackendAuto takes it on
	// darwin, where its paired gate held on macos-15; on the BSDs it runs
	// only when named.
	LoopBackendKqueue
)

The loop backends. The zero value is LoopBackendAuto.

func (LoopBackend) String added in v0.26.13

func (b LoopBackend) String() string

String renders the backend as State and the start line spell it.

type LoopConnRef added in v0.26.8

type LoopConnRef struct {
	// Ring is the ring, inside its loop, that holds the connection.
	Ring uint32
	// Slot is the connection's index in the ring's table.
	Slot uint32
	// Gen is the slot's generation. It is never zero for a live connection, so
	// the zero LoopConnRef is stale on every loop.
	Gen uint32
}

LoopConnRef names one connection of a loop: the ring it lives on, its slot in that ring's table and the generation of that slot. It is a comparable value with no behaviour, so a handler can key a map with it and hold it without holding anything of the engine.

A slot is reused only once every operation of its previous generation has completed, and the generation then moves on, so an operation on a reference whose connection closed does nothing and returns LoopStaleRef — it can never reach the connection that took the slot since. The zero value names no connection.

type LoopDrainer added in v0.30.0

type LoopDrainer interface {
	// OnDrain is called once for ref, when the drain reached a connection it may
	// close: a place to say goodbye — an SSE retry field, a WebSocket 1001 — and
	// to close it.
	OnDrain(io LoopIO, ref LoopConnRef)
}

LoopDrainer is the optional capability of a LoopHandler that wants a last word on a connection before the group's drain closes it: OnDrain is called once per connection, on its loop's thread, when the drain would close it now. Whatever it wrote, the connection closes at the next batch unless it already asked to.

type LoopFileSpanValue added in v0.30.0

type LoopFileSpanValue struct {
	// FD is the open descriptor, read and never closed by the engine.
	FD int
	// Off is the offset of the first byte to send.
	Off int64
	// Len is how many bytes to send.
	Len int64
}

LoopFileSpanValue is the file a LoopFileWriter sends: a descriptor and the byte range, what a kernel call names.

type LoopFileWriter added in v0.30.0

type LoopFileWriter interface {
	// WriteFile queues head, then the file's Len bytes from Off, behind the
	// connection's pending output: head may be empty, and is counted against the
	// output ceiling as Write's bytes are. The descriptor is duplicated before
	// WriteFile returns, so the caller may close its own at once. No byte of the
	// file is ever read by the process.
	//
	// OnWritable fires when the file's last byte has left. While a file is in
	// flight the connection's Write and WriteFile are refused with LoopOutputFull,
	// and a close asked waits for the file. It returns LoopFileRefused when the
	// backend cannot take this file (nothing was queued), LoopOutputFull when head
	// would pass the output ceiling, LoopStaleRef when the connection closed.
	WriteFile(ref LoopConnRef, head []byte, file LoopFileSpanValue) error
}

LoopFileWriter is the optional capability of a LoopIO that sends a file's bytes from the kernel to the connection, with no copy through the process: sendfile on epoll, splice through a pipe of the connection's own on io_uring. It is found by a type assertion on the LoopIO a callback receives, never required of it (ADR 0039). WriteFile is called on the loop's thread only.

type LoopHTTPCodec added in v0.30.0

type LoopHTTPCodec interface {
	// SetHTTP is called once, before any other callback, with the group's HTTP settings.
	SetHTTP(v LoopHTTPValue)
	// Draining announces the group's drain. It is called once, from any
	// goroutine, before the engine starts closing connections for the drain.
	Draining()
	// InFlight reports whether ref's connection, of lane, holds requests the
	// drain must leave open: true keeps it past the drain's first sweep, and
	// eviction passes it by.
	InFlight(lane int, ref LoopConnRef) bool
}

LoopHTTPCodec is the optional capability of a LoopHandler that speaks more than one HTTP version on a group (ADR 0221): SetHTTP tells it, once at Start, before any other callback, what the group serves; Draining tells it, from the goroutine that drains the group, that the drain has begun, before any connection is closed for it; InFlight reports, on the lane's thread, whether ref still has work a drain must let finish.

type LoopHTTPCounter added in v0.30.0

type LoopHTTPCounter interface {
	// HTTPCounts returns the adapter's counts, read from any goroutine.
	HTTPCounts() HTTPCountsValue
}

LoopHTTPCounter is the optional capability of a LoopHandler that serves HTTP (#966): HTTPCounts reports, from any goroutine, the requests it served per version and the HTTP/2 errors it counted since Start, which State folds into the group's listener rows.

type LoopHTTPValue added in v0.30.0

type LoopHTTPValue struct {
	// Protocols is the group's Protocols list, nil when the group gave none.
	Protocols []string
	// HTTP2Off is set when KITSUNIUM_NET_DISABLE switched http2-loop off.
	HTTP2Off bool
	// HTTP2 is the HTTP2 option's bounds, the zero value when the group gave none.
	HTTP2 corehttp2.LimitsValue
}

LoopHTTPValue is what a group tells a LoopHTTPCodec at Start: the versions its Protocols option names, nil when it set none, and whether the operator switched http2-loop off.

type LoopHandler added in v0.26.8

type LoopHandler interface {
	// OnOpen is called once per accepted connection, before any OnData for it.
	// Pressure(ref) already answers for this connection.
	OnOpen(io LoopIO, ref LoopConnRef)
	// OnData hands the handler the bytes received on ref and returns how many of
	// them it consumed, from 0 to len(buf). buf stays valid until the next
	// OnBatchEnd returns, never later, and the handler must not write to it.
	// The unconsumed tail is kept in the connection's spill and handed back
	// first, followed by the next bytes, on the next OnData; a tail past the
	// spill ceiling closes the connection with LoopSpillFull. A value outside
	// 0 to len(buf) is treated as len(buf).
	OnData(io LoopIO, ref LoopConnRef, buf []byte) int
	// OnTimeout is called when the deadline set by SetDeadline on ref expires.
	// The connection stays open: closing it is the handler's choice.
	OnTimeout(io LoopIO, ref LoopConnRef)
	// OnWritable is called when ref's pending output falls below the loop's
	// low-water mark after a Write was refused with LoopOutputFull or left output
	// pending past the mark, so a producer that paused can resume.
	OnWritable(io LoopIO, ref LoopConnRef)
	// OnBatchEnd is called once per loop iteration, after the OnData calls of
	// every ring of the loop, and before any buffer handed to them returns to the
	// kernel. It is where a handler flushes work it batched — one database
	// commit for every request of the batch, say — before writing the answers.
	OnBatchEnd(io LoopIO)
	// OnClose is called once per connection, last. err is nil when the peer
	// closed cleanly or the handler called Close, and otherwise names why the
	// loop closed it — LoopSpillFull, an idle timeout, the drain, a socket
	// error. ref is stale once OnClose returns.
	OnClose(io LoopIO, ref LoopConnRef, err error)
}

LoopHandler is the port a consumer implements to be served by a loop group (ADR 0186 §D4).

One thread calls it: every callback of one loop runs on that loop's thread, one at a time, so a handler needs no lock for state it keeps per loop. It MUST NOT block: a blocking callback stalls every connection of its loop. A callback receives the LoopIO of the loop that calls it and the reference of the connection concerned; neither is valid past the loop's life, and a reference past its connection's OnClose is stale.

IFACE-PLUGIN: the engines live in internal/service/net/loop and internal/service/net/server; this port is what a consumer writes.

type LoopIO added in v0.26.8

type LoopIO interface {
	// Write copies p into ref's output and returns. Writes on one reference leave
	// in the order they were made, and p may be reused once Write returns. It is
	// refused with LoopOutputFull, copying nothing, when p would pass the
	// connection's output ceiling or the loop's global output budget, and with
	// LoopStaleRef when ref's connection has closed.
	Write(ref LoopConnRef, p []byte) error
	// Close closes ref's connection once the output already written has left,
	// and calls OnClose. A second Close, or a Close on a stale reference, returns
	// LoopStaleRef and does nothing.
	Close(ref LoopConnRef) error
	// SetDeadline arms ref's timer to fire OnTimeout after d, on the loop's
	// coarse timer wheel: it fires no earlier than d and at most one wheel tick
	// late. A d of zero or less disarms it. Arming again replaces the previous
	// deadline.
	SetDeadline(ref LoopConnRef, d time.Duration) error
	// Pressure reports whether ref's connection was accepted, or has since come,
	// under the loop's soft pressure: the group serves more connections than its
	// soft bound. It is a primitive; what a protocol makes of it — HTTP/1.1
	// answers Connection: close — is the protocol adapter's. A stale reference
	// reports false.
	Pressure(ref LoopConnRef) bool
	// Post queues op to run once on the loop's thread with arg, and wakes the
	// loop. It is the one operation any goroutine may call. The queue is
	// preallocated and holds no closure: op is a plain function and arg an
	// integer the caller gives meaning to. Each posted operation runs exactly
	// once, in the order posted; a full queue refuses with LoopPostQueueFull, and
	// a loop that has stopped with ServerClosed.
	Post(op PostFunc, arg uintptr) error
	// Loop returns the index of the loop this LoopIO belongs to, from 0 to the
	// group's lane count less one, fixed for the loop's life: its lane. A
	// handler keeps its per-lane state — a scratch, a batch, a buffer — in a
	// slice indexed by it and sized at LoopStarter.OnStart, so a callback finds
	// its own with no lock and no map. The portable backend runs LoopValue.Loops
	// lanes too, one handler goroutine each. Any goroutine may call it.
	Loop() int
}

LoopIO is what a loop offers its handler: the operations on its connections (ADR 0186 §D4). Each loop implements it with a pointer of its own, handed to every callback.

Write, Close and SetDeadline belong to the loop's thread: they are called from a callback, never from another goroutine, which is what lets them take no lock. Under the loopdebug build tag a call from another thread is refused with LoopWrongThread. Post is the one operation another goroutine may call. Every error is a preallocated sentinel; none of the calls allocates when p belongs to the caller's heap or mapping.

IFACE-PLUGIN: the concrete loops stay unexported in internal/service/net/loop and internal/service/net/server.

type LoopJobValue added in v0.26.33

type LoopJobValue struct {
	// Ref is the connection the job answers.
	Ref LoopConnRef
	// Slot is the sender's index of the job's state.
	Slot uint32
	// Gen is the generation of that slot when the job was sent.
	Gen uint32
	// Arg is the sender's own argument.
	Arg uint64
}

LoopJobValue is one job a lane sends its worker through LoopOutbound (R3): the connection it answers, by its full reference, the slot the sender keeps it in with that slot's own generation, and an integer the sender gives meaning to. It is a value with no pointer, so the queue that carries it holds nothing the garbage collector must scan, and a completion that comes back after its connection closed finds a stale reference, never another connection.

type LoopLayer added in v0.26.29

type LoopLayer struct {
	// contains filtered or unexported fields
}

LoopLayer is the base of a loop middleware's layer: it forwards the eight callbacks — OnOpen, OnData, OnTimeout, OnWritable, OnBatchEnd, OnClose, Honours and OnStart — to the handler it wraps, and Next returns it. A layer embeds it, built by NewLoopLayer, and overrides the callbacks it acts on; an override forwards the callback in turn, through the embedded LoopLayer, unless the layer answers it itself — a refused connection's bytes, say. Honours answers for the wrapped handler, which a LoopOptionHonourer answers and any other handler does not; OnStart reaches a LoopStarter and stops at any other handler.

func NewLoopLayer added in v0.26.29

func NewLoopLayer(next LoopHandler) LoopLayer

NewLoopLayer returns the base of a loop middleware's layer over next: the LoopLayer a layer embeds, forwarding every callback to next.

func (LoopLayer) Honours added in v0.26.29

func (l LoopLayer) Honours(k OptionKind) bool

Honours reports whether the wrapped handler honours the option of kind k: its own Honours when it is a LoopOptionHonourer, false otherwise.

func (LoopLayer) Next added in v0.26.29

func (l LoopLayer) Next() LoopHandler

Next returns the handler the layer wraps.

IFACE-PLUGIN: the wrapped handler is whatever the next layer, or the consumer, wrote.

func (LoopLayer) OnBatchEnd added in v0.26.29

func (l LoopLayer) OnBatchEnd(io LoopIO)

OnBatchEnd forwards to the wrapped handler.

func (LoopLayer) OnClose added in v0.26.29

func (l LoopLayer) OnClose(io LoopIO, ref LoopConnRef, err error)

OnClose forwards to the wrapped handler.

func (LoopLayer) OnData added in v0.26.29

func (l LoopLayer) OnData(io LoopIO, ref LoopConnRef, buf []byte) int

OnData forwards to the wrapped handler and returns what it consumed.

func (LoopLayer) OnDrain added in v0.30.0

func (l LoopLayer) OnDrain(io LoopIO, ref LoopConnRef)

OnDrain forwards to the wrapped handler when it is a LoopDrainer.

func (LoopLayer) OnOpen added in v0.26.29

func (l LoopLayer) OnOpen(io LoopIO, ref LoopConnRef)

OnOpen forwards to the wrapped handler.

func (LoopLayer) OnStart added in v0.26.29

func (l LoopLayer) OnStart(lanes int)

OnStart forwards to the wrapped handler when it is a LoopStarter.

func (LoopLayer) OnTimeout added in v0.26.29

func (l LoopLayer) OnTimeout(io LoopIO, ref LoopConnRef)

OnTimeout forwards to the wrapped handler.

func (LoopLayer) OnWritable added in v0.26.29

func (l LoopLayer) OnWritable(io LoopIO, ref LoopConnRef)

OnWritable forwards to the wrapped handler.

type LoopLayered added in v0.26.29

type LoopLayered interface {
	// Next returns the handler the layer wraps.
	Next() LoopHandler
}

LoopLayered is what every layer a loop middleware returns implements: Next is the handler the layer wraps. A layer embeds LoopLayer, which forwards the eight callbacks — the six of LoopHandler, Honours and OnStart — to Next, and overrides the ones it acts on, forwarding them in turn. At Start the server walks the chain by Next and sends each callback through a probe chain; a layer that does not implement LoopLayered, or does not forward a callback, fails Start with LoopMiddlewareInvalid. The chain honours an option when one of its layers or the handler does.

type LoopOffloader added in v0.26.33

type LoopOffloader interface {
	// Worker returns the worker the group's lanes offload to; nil offloads
	// nothing.
	Worker() LoopWorker
	// OnOutbound gives the offloader its lanes' outbounds, once.
	OnOutbound(v LoopOutboundValue)
	// OnDone is called on the lane's thread with each job its worker ran.
	OnDone(io LoopIO, job LoopJobValue)
	// Pending reports, on the lane's thread, whether ref, a connection of lane,
	// waits for an answer its worker has not given back: a draining loop keeps
	// it open until it has, and eviction passes it by.
	Pending(lane int, ref LoopConnRef) bool
}

LoopOffloader is the optional capability of a LoopHandler that offloads blocking work (R3). At Start the server walks the chain by Next for the first offloader whose Worker is not nil, starts one worker goroutine per lane, and calls OnOutbound once, after LoopStarter.OnStart and before any other callback. Each job a lane sends comes back through OnDone, on that lane's thread, once Work has returned; a job whose connection closed meanwhile comes back too, so the offloader frees what it kept for it. A draining loop keeps open every connection Pending names. The workers stop after the loop engine has: on Shutdown once the drain is over, queued jobs then dropped.

type LoopOptionHonourer added in v0.26.8

type LoopOptionHonourer interface {
	// Honours reports whether the handler applies the option of kind k itself.
	Honours(k OptionKind) bool
}

LoopOptionHonourer is the optional capability of a LoopHandler that honours group options the loop engine does not: a protocol adapter that applies a header deadline, say. At Start, server refuses with LoopOptionUnsupported every option set on a loop group that neither the engine nor the handler honours (ADR 0186 §D1). A handler without it honours nothing of its own.

type LoopOutbound added in v0.26.33

type LoopOutbound interface {
	// Send queues job for the lane's worker, or refuses with LoopOutboundFull.
	Send(io LoopIO, job LoopJobValue) error
}

LoopOutbound is one lane's queue to its worker (R3), preallocated (LoopValue.Outbound). Send is called on the lane's thread only, with the lane's LoopIO, and never blocks: it queues the job and wakes the worker, or refuses with LoopOutboundFull, queueing nothing.

IFACE-PLUGIN: the queue is internal/service/net/server's.

type LoopOutboundValue added in v0.26.33

type LoopOutboundValue struct {
	// Lanes are the lanes' outbounds, by lane.
	Lanes []LoopOutbound
	// OutputCap is LoopValue.OutputCap, its default applied.
	OutputCap int
}

LoopOutboundValue is what a loop group gives its offloader at Start, once, after LoopStarter.OnStart and before any other callback: one outbound per lane, indexed by LoopIO.Loop, and the output ceiling of one connection (LoopValue.OutputCap, its default applied), which bounds what an offloader holds back for a connection while one of its answers is pending.

type LoopPeer added in v0.30.0

type LoopPeer interface {
	// Peer returns the credentials the kernel recorded for ref's peer when the
	// connection was made (SO_PEERCRED), with Verified set, and true; false for a
	// connection that is not a Unix one, for a stale ref and for one whose
	// descriptor the backend cannot ask.
	Peer(ref LoopConnRef) (LoopPeerValue, bool)
}

LoopPeer is the optional capability of a LoopIO that names who is on the other side of a Unix connection (ADR 0148, #974): found by a type assertion on the LoopIO a callback receives, never required of it (ADR 0039). Peer is called on the loop's thread only, allocates nothing, and says nothing it cannot verify: a TCP connection, a stale ref, or a connection whose descriptor the backend holds as a direct descriptor, which has no process descriptor to ask, answers false.

IFACE-PLUGIN: the loops are internal/service/net/loop's and server's.

type LoopPeerValue added in v0.30.0

type LoopPeerValue struct {
	// UID is the peer's user id.
	UID uint32
	// GID is the peer's group id.
	GID uint32
	// PID is the peer's process id.
	PID uint32
	// Verified is true when the kernel named the peer.
	Verified bool
}

LoopPeerValue is who the kernel says is on the other side of a Unix connection: the user, group and process that connected, as SO_PEERCRED recorded them when the connection was made. Verified is true when the kernel named them; a value the loop could not read is never returned, so Verified is true wherever a LoopPeer answers.

type LoopRingStateValue added in v0.26.13

type LoopRingStateValue struct {
	// Loop is the loop's index in its group.
	Loop int `json:"loop"`
	// Ring is the ring's index in its loop.
	Ring int `json:"ring"`
	// Conns is the number of connections the ring holds now.
	Conns int64 `json:"conns"`
	// Accepted is the number of connections the ring accepted.
	Accepted uint64 `json:"accepted"`
	// Closed is the number of connections the ring closed.
	Closed uint64 `json:"closed"`
	// Batches is the number of loop iterations that reaped this ring.
	Batches uint64 `json:"batches"`
	// Completions is the number of completions or events the ring reaped.
	Completions uint64 `json:"completions"`
	// BufferStarved is the number of receives that found no provided buffer
	// (ENOBUFS) and waited for one to come back.
	BufferStarved uint64 `json:"buffer_starved"`
	// EnterRetries is the number of times submitting to the kernel was retried
	// after it was busy — the completion queue full, typically.
	EnterRetries uint64 `json:"enter_retries"`
	// AcceptPauses is the number of times the ring stopped accepting: the
	// descriptor reserve reached, or the process out of descriptors.
	AcceptPauses uint64 `json:"accept_pauses"`
	// Evicted is the number of idle connections closed to admit others.
	Evicted uint64 `json:"evicted"`
	// WatchdogFires is the number of times the liveness watchdog saw a stall it
	// could observe and acted on it.
	WatchdogFires uint64 `json:"watchdog_fires"`
	// BatchesCut is the number of batches BatchBudget stopped while this ring
	// still had completions to reap (ADR 0206).
	BatchesCut uint64 `json:"batches_cut"`
	// BytesIn is the number of bytes the ring received and delivered to the handler.
	BytesIn uint64 `json:"bytes_in"`
	// BytesOut is the number of bytes the ring sent: the answers the kernel took, a short
	// send counted as it left.
	BytesOut uint64 `json:"bytes_out"`
	// OutputRefused is the number of Writes the ring refused with LoopOutputFull: the
	// connection's output ceiling, or the slab's budget, reached.
	OutputRefused uint64 `json:"output_refused"`
	// SpillClosed is the number of connections the ring closed with LoopSpillFull:
	// unconsumed input past SpillCap, or the slab's budget spent.
	SpillClosed uint64 `json:"spill_closed"`
	// Timeouts is the number of deadlines of SetDeadline that fired (OnTimeout calls).
	Timeouts uint64 `json:"timeouts"`
	// IdleClosed is the number of connections the ring closed for IdleTimeout.
	IdleClosed uint64 `json:"idle_closed"`
	// PendingOutput is the bytes written and not yet sent, a gauge: what the ring's
	// connections still owe their peers at the last batch's end.
	PendingOutput int64 `json:"pending_output"`
	// Draining reports a ring the watchdog judged sick: it accepts nothing new
	// and closes its connections as they fall idle.
	Draining bool `json:"draining"`
}

LoopRingStateValue reports one ring of one loop: counters the loop refreshes at the end of each batch, read without stopping it (ADR 0186 §D8).

type LoopSpillChecker added in v0.30.0

type LoopSpillChecker interface {
	// CheckSpill is called once, before any other callback, with the most
	// unconsumed input a connection may hold: a returned error fails Start.
	CheckSpill(spillCap int) error
}

LoopSpillChecker is the optional capability of a LoopHandler that holds whole requests in the connection's spill, so a request past LoopValue.SpillCap would close with LoopSpillFull and no status: CheckSpill is told the group's effective cap, once, at Start, before any address is bound, and either adapts its bounds to it or refuses the group's start.

type LoopStarter added in v0.26.29

type LoopStarter interface {
	// OnStart is called once, before any other callback, with the number of
	// lanes: every LoopIO.Loop the group's callbacks see is from 0 to lanes
	// less one.
	OnStart(lanes int)
}

LoopStarter is the optional capability of a LoopHandler that keeps state per lane: OnStart tells it how many lanes the group runs, once, before any other callback, so it sizes a slice indexed by LoopIO.Loop and no callback grows one. A lane is one loop thread on a native backend, one handler goroutine on the portable backend; its count is LoopValue.Loops, zero meaning one, on every backend, a fallback from a native backend to the portable one included.

type LoopStateValue added in v0.26.13

type LoopStateValue struct {
	// Group is the loop group's name.
	Group string `json:"group"`
	// Backend is the backend in use.
	Backend LoopBackend `json:"backend"`
	// ProbeStage names the last stage of the kernel probe the backend passed,
	// or the stage that failed when the group fell back.
	ProbeStage string `json:"probe_stage"`
	// Degraded reports that the group runs on a backend other than the best one
	// it asked for: auto fell back from io_uring, say.
	Degraded bool `json:"degraded"`
	// DegradedReason names the fallback and its cause when Degraded is set.
	DegradedReason string `json:"degraded_reason,omitempty"`
	// Loops is the number of loops running.
	Loops int `json:"loops"`
	// LoopsSource says where Loops came from: "explicit", "auto" — LoopsAuto,
	// GOMAXPROCS read at Start — or "single" when Loops was zero.
	LoopsSource string `json:"loops_source"`
	// Rings is the number of rings per loop.
	Rings int `json:"rings"`
	// RingsSource says where Rings came from: "explicit", "derived" from the
	// connection bounds and the descriptor limit, or "single" when no bound was
	// set or the backend runs one ring.
	RingsSource string `json:"rings_source"`
	// RLimitNoFile is the effective RLIMIT_NOFILE soft limit read at Start. The
	// SDK never raises it; the Go runtime already lifts the soft limit to the
	// hard one.
	RLimitNoFile uint64 `json:"rlimit_nofile"`
	// GOMAXPROCS is runtime.GOMAXPROCS(0) read at Start. The SDK never sets it.
	GOMAXPROCS int `json:"gomaxprocs"`
	// BatchP99 is the 99th percentile of one loop iteration's duration, over the
	// loops' recent batches.
	BatchP99 DurationValue `json:"batch_p99"`
	// OutboundRefused counts the jobs a full queue to a worker refused
	// (LoopOutboundFull), every lane's: for the http1 adapter, the 503s it
	// answered for them. Zero when the group offloads nothing.
	OutboundRefused uint64 `json:"outbound_refused"`
	// SlabInUse is the bytes of the spill and output chunks every loop's slab holds at
	// the last batches' end, chunk sizes counted, a gauge.
	SlabInUse int64 `json:"slab_in_use"`
	// SlabMapped is the bytes every loop's slab has mapped outside the Go heap. A size
	// class keeps one wholly free arena and unmaps the others on the loop's tick
	// (#809), so it falls back after a burst.
	SlabMapped int64 `json:"slab_mapped"`
	// SlabBudget is the ceiling of SlabInUse, every loop's summed: past it a Write is
	// refused and a spill closes its connection, by name, never by the kernel's OOM
	// killer.
	SlabBudget int64 `json:"slab_budget"`
	// SlabBudgetSource says where each loop's slab budget came from (#809):
	// "explicit" for LoopValue.SlabBudget, "derived" from the cgroup allowance
	// memlimit read, "default" for 128 MiB a loop; empty on the portable
	// backend, which has no slab.
	SlabBudgetSource string `json:"slab_budget_source"`
	// RingStates reports every ring of every loop, loop by loop.
	RingStates []LoopRingStateValue `json:"ring_states"`
	// Capabilities reports every optional kernel capability the group's backend
	// probed or was told not to, the server's socket probes merged in
	// (ADR 0189).
	Capabilities []CapabilityValue `json:"capabilities,omitempty"`
}

LoopStateValue reports one loop group: the backend it runs on, how it got there, how it is sized, and its rings' counters (ADR 0186 §D8). A fallback is reported here and by Degraded, never silently.

type LoopValue added in v0.26.13

type LoopValue struct {
	// Backend is the mechanic to run on. LoopBackendAuto takes the best one the
	// platform's probe reaches and reports any fallback in State; an explicit
	// backend the platform cannot run is refused at Start.
	Backend LoopBackend `json:"backend"`
	// Loops is the number of loops, each one goroutine locked to its OS thread
	// on a native backend. Zero selects one; LoopsAuto selects GOMAXPROCS less
	// one, at least one, read once at Start. The SDK never sets GOMAXPROCS:
	// sizing the process against its loops is the consumer's policy, and State
	// reports both. A native loop yields to the Go scheduler every 5 ms of
	// serving while GOMAXPROCS exceeds the group's loops (ADR 0199).
	Loops int `json:"loops"`
	// Rings is the number of descriptor shards inside one loop, each with its
	// own table of at most min(RLIMIT_NOFILE, IORING_MAX_FIXED_FILES) entries:
	// what lets io_uring serve more connections than RLIMIT_NOFILE. Zero
	// derives it, ⌈max(MaxConns, SoftConns) / min(RLIMIT_NOFILE,
	// IORING_MAX_FIXED_FILES)⌉, and one when both are zero; that is the only
	// formula, and State says where the value came from. epoll and the portable
	// backend run one ring whatever it says.
	Rings int `json:"rings"`
	// CQBatch bounds the completions or events one loop iteration reaps before
	// it calls OnBatchEnd. Zero selects the domain default.
	CQBatch int `json:"cq_batch"`
	// SpillCap is the most unconsumed input, in bytes, one connection may hold
	// between OnData calls. Past it the connection is closed with
	// LoopSpillFull. Zero selects the domain default. It is the ceiling on a
	// request's head and body together for an http1 handler that takes the
	// body whole (a LoopSpillChecker adapts to it at Start); an
	// HTTP1BodyReader's spill holds only the head.
	SpillCap int `json:"spill_cap"`
	// OutputCap is the most written and unsent output, in bytes, one
	// connection may hold. A Write past it is refused with LoopOutputFull. Zero
	// selects the domain default.
	OutputCap int `json:"output_cap"`
	// SoftConns is the soft connection bound: a connection accepted while the
	// group serves more is flagged under pressure (LoopIO.Pressure), so a
	// protocol can ask its peer to leave. It refuses nothing; MaxConns does.
	// Zero sets no soft bound.
	SoftConns int `json:"soft_conns"`
	// EvictIdle is how long a connection must have been idle before the loop
	// may close it to admit another under hard pressure — the process out of
	// descriptors, or MaxConns reached. A connection with unread bytes is
	// never evicted. Zero evicts nothing.
	EvictIdle DurationValue `json:"evict_idle"`
	// Capabilities opts the group into kernel capabilities of class A-code
	// (ADR 0189). Each is probed and used where the kernel has it, reported
	// "missing" and as a fault where it does not, and never used once
	// KITSUNIUM_NET_DISABLE names it. An engine, a class B capability or an
	// unknown token is refused at Start with LoopCapabilityRefused. Nil asks
	// for none; the class A-kernel capabilities need no opt-in.
	Capabilities []Capability `json:"capabilities,omitempty"`
	// Outbound is how many jobs one lane's queue to its worker holds (R3),
	// when the group's handler offloads (LoopOffloader). A Send past it is
	// refused with LoopOutboundFull and counted in LoopStateValue. Zero
	// selects 256.
	Outbound int `json:"outbound"`
	// BatchWait bounds how long an io_uring loop holds its wait for
	// BatchWaitMin completions while it is loaded: its previous batch reaped
	// at least four, or two receives carrying bytes at least come within the
	// bound at the rate they arrive. A loaded loop so serves larger batches for fewer
	// wakeups (ADR 0199, ADR 0202). It
	// bounds each grouped wait, not a request's latency: a request read over
	// several batches may meet one at each. Zero selects 250 µs,
	// a negative value turns the grouped wait off, and anything past the 10 ms
	// tick is the tick. epoll and the portable backend ignore it.
	BatchWait DurationValue `json:"batch_wait"`
	// BatchWaitMin is how many completions the grouped wait waits for before
	// BatchWait runs out. Zero selects 32; past CQBatch it is CQBatch.
	BatchWaitMin int `json:"batch_wait_min"`
	// BatchBudget bounds how long an io_uring loop reaps completions in one
	// batch: past it, a ring stops reaping, and a ring after it reaps at most one
	// more lot of 16 before it stops too — a bounded overrun, not a hard stop.
	// What is left waits for the next batch, which starts at the ring it stopped
	// at and does not wait. A
	// request is answered once its batch ends, after OnBatchEnd, so a batch of
	// thousands of requests holds every answer for its whole length (ADR 0206).
	// A batch that ran past half of it hands its answers to the kernel at once
	// instead of at the next wait. Zero, or a negative value, bounds nothing:
	// the batch reaps up to four CQBatch a ring, as before. epoll and the
	// portable backend ignore it.
	BatchBudget DurationValue `json:"batch_budget"`
	// SendZCMin is the smallest flight an io_uring loop sends zero-copy, in
	// bytes, once the group opted into uring-send-zc (#969): a flight of at least
	// that many bytes leaves by IORING_OP_SEND_ZC and its slab chunk stays the
	// kernel's until the notification says the pages are free, a shorter one by
	// IORING_OP_SEND as before. Zero selects 16 KiB; a negative value is refused
	// with LoopValueInvalid. epoll and the portable backend ignore it, and so
	// does a group that did not opt in.
	SendZCMin int `json:"send_zc_min,omitempty"`
	// Affinity pins each loop's thread to one CPU, Affinity[i] for loop i, with
	// sched_setaffinity once the thread is locked (loop-affinity, class A-code,
	// #857). Nil pins nothing; otherwise it names one CPU per loop, from 0 to
	// 8191, or Start refuses it with LoopValueInvalid. A CPU the kernel refuses —
	// offline, outside the process's set, or a seccomp filter — leaves that loop
	// unpinned, serving, and the row missing, a fault. With Shards(0) and
	// so-incoming-cpu opted into, each loop's listener asks the kernel for the
	// connections its CPU received. The portable backend ignores it.
	Affinity []int `json:"affinity,omitempty"`
	// NotsentLowat is TCP_NOTSENT_LOWAT, in bytes, on the group's TCP listeners,
	// inherited by each accepted socket (tcp-notsent-lowat, class A-code, Linux
	// 3.12, #857): the kernel takes at most that many unsent bytes, so a write
	// is short and OnWritable fires once the peer drained, not once megabytes
	// of socket buffer emptied. Zero sets nothing unless Capabilities opts into
	// tcp-notsent-lowat, which selects 128 KiB; a positive value opts in;
	// negative is refused with LoopValueInvalid. The portable backend ignores it.
	NotsentLowat int `json:"notsent_lowat"`
	// SlabBudget is the most payload memory, in bytes, one loop maps outside the
	// Go heap for its connections' spill and output together (#809): past it a
	// Write is refused with LoopOutputFull and a spill closes its connection
	// with LoopSpillFull. Zero derives it from the cgroup allowance the server
	// reads with memlimit, a quarter of the allowance shared by the loops and
	// at most 128 MiB, and is 128 MiB where no allowance is found; negative is
	// refused with LoopValueInvalid. A size class's wholly free arenas past the
	// first are unmapped on the loop's tick. The portable backend ignores it.
	SlabBudget int `json:"slab_budget"`
	// BusyPoll is busy-poll's window (class A-code, #979): how long a loop's wait
	// polls the device queues of its sockets before it sleeps, io_uring's
	// busy_poll_to and epoll's busy_poll_usecs, rounded up to whole
	// microseconds. Zero selects 50 µs once Capabilities opts into busy-poll; it
	// opts nothing in by itself. Negative, or past one second, is refused with
	// LoopValueInvalid. The portable backend ignores it.
	BusyPoll DurationValue `json:"busy_poll"`
	// BusyPollBudget is how many packets one poll of busy-poll may take, epoll's
	// busy_poll_budget: zero is the kernel's, and past 64 the kernel asks for
	// CAP_NET_ADMIN, its refusal reading the row missing. Negative, or past
	// 65535, is refused with LoopValueInvalid. io_uring always takes the
	// kernel's; the portable backend ignores it.
	BusyPollBudget int `json:"busy_poll_budget"`
	// ZCRX names the NIC receive queue uring-zcrx would serve (class B, #980). A
	// zero Interface keeps the row off. While the row is class B a named queue
	// is only probed — registered on a throwaway ring that is closed at once,
	// the kernel's answer in the row's reason — and no byte is received through
	// it.
	ZCRX ZCRXValue `json:"zcrx"`
}

LoopValue configures a loop group's engine (ADR 0186 §D5). It is a value of its own, passed by the Loop group option, so LimitsValue and TimeoutsValue keep the meanings they publish for every engine. Every field is optional: zero selects the domain default, never "unbounded".

type LoopWorker added in v0.26.33

type LoopWorker interface {
	// Work runs job, sent by the lane whose index is lane.
	Work(lane int, job LoopJobValue)
}

LoopWorker runs a lane's offloaded jobs (R3): Work is called on the lane's worker goroutine, one per lane, off the loop's thread, one job at a time in the order the lane sent them, and may block. When it returns, the job goes back to the lane, whose offloader's OnDone sees it on the loop's thread.

type Middleware

type Middleware[H any] func(next H) H

Middleware decorates a handler with another of the same type.

It is generic over the handler type so stream and datagram handlers share one shape and one Chain helper, instead of the domain carrying two near-identical middleware types that would inevitably drift.

type OptionKind added in v0.26.8

type OptionKind uint8

OptionKind names a group option, for the one question a loop group asks of its options at Start: is this one honoured (LoopOptionHonourer)? It names options, not their values.

const (
	// OptionNone names no option; it is the zero value.
	OptionNone OptionKind = iota
	// OptionTLS is TLS. A loop group always refuses it, with LoopTLSUnsupported.
	OptionTLS
	// OptionMiddleware is Use. A loop group always refuses it: there is no
	// ConnHandler to decorate.
	OptionMiddleware
	// OptionReadTimeout is ReadTimeout.
	OptionReadTimeout
	// OptionWriteTimeout is WriteTimeout.
	OptionWriteTimeout
	// OptionIdleTimeout is IdleTimeout. The loop engine honours it.
	OptionIdleTimeout
	// OptionReadHeaderTimeout is ReadHeaderTimeout, which only a protocol adapter
	// can honour.
	OptionReadHeaderTimeout
	// OptionMaxHeaderBytes is MaxHeaderBytes, which only a protocol adapter can
	// honour.
	OptionMaxHeaderBytes
	// OptionHandshakeTimeout is HandshakeTimeout.
	OptionHandshakeTimeout
	// OptionReadBufferSize is ReadBufferSize.
	OptionReadBufferSize
	// OptionMaxPacketSize is MaxPacketSize.
	OptionMaxPacketSize
	// OptionBatchSize is BatchSize.
	OptionBatchSize
	// OptionShards is Shards.
	OptionShards
	// OptionMaxConns is MaxConns. The loop engine honours it, refusing with
	// ConnLimitReached.
	OptionMaxConns
	// OptionAdopt is Adopt. The loop engine honours it.
	OptionAdopt
	// OptionPreHandshake is PreHandshake.
	OptionPreHandshake
	// OptionPlaintext is Plaintext. Every engine honours it; with TLS it is refused
	// with PlaintextWithTLS.
	OptionPlaintext
	// OptionName is Name. Every engine honours it.
	OptionName
	// OptionEngine is Engine. Every engine honours it, or refuses the group with
	// EngineUnsupported.
	OptionEngine
	// OptionLoopMiddleware is UseLoop. The loop engine honours it; every group no
	// loop handler serves refuses it with LoopOptionUnsupported.
	OptionLoopMiddleware
	// OptionBatchReplies is BatchReplies. A datagram group honours it; a stream
	// group, goroutine or loop, refuses it with LoopOptionUnsupported (ADR 0192).
	OptionBatchReplies
	// OptionProtocols is Protocols. An HTTP group served by net/http honours
	// it, and so does the http1 codec given TLS when it names "http/1.1"
	// alone; any other list for that codec is refused with ProtocolsRefused.
	// Any other loop group — the http1 codec without TLS included — refuses
	// it with LoopOptionUnsupported unless its handler or a layer honours it
	// (LoopOptionHonourer), and any other group with ProtocolsRefused.
	OptionProtocols
	// OptionMaxBodyBytes is MaxBodyBytes. An HTTP group served by net/http
	// honours it; a loop group refuses it with LoopOptionUnsupported, its body
	// bound being the http1 profile's own, and a datagram group with
	// PacketOptionUnsupported.
	OptionMaxBodyBytes
	// OptionBacklog is Backlog. A stream group honours it on both engines; a
	// datagram group refuses it with PacketOptionUnsupported.
	OptionBacklog
	// OptionReceiveBuffer is ReceiveBuffer. A stream group honours it on both
	// engines and a datagram group on its socket; the accepted sockets of a loop
	// group inherit the listener's.
	OptionReceiveBuffer
	// OptionSendBuffer is SendBuffer, honoured as OptionReceiveBuffer is.
	OptionSendBuffer
	// OptionProxyProtocol is ProxyProtocol. A goroutine-engine TCP group honours it;
	// a loop group refuses it with LoopOptionUnsupported, a datagram group with
	// PacketOptionUnsupported.
	OptionProxyProtocol
	// OptionSteer is Steer. A datagram group honours it; every other group refuses it
	// with LoopOptionUnsupported.
	OptionSteer
	// OptionKeepAlive is KeepAlive. Every stream engine honours it.
	OptionKeepAlive
	// OptionUserTimeout is UserTimeout. Every stream engine honours it.
	OptionUserTimeout
	// OptionHTTP2 is HTTP2. An HTTP group honours it; a loop group refuses it with
	// LoopOptionUnsupported unless its handler honours it (the http2 adapter
	// does), a datagram group with PacketOptionUnsupported.
	OptionHTTP2
	// OptionShareAddress is ShareAddress. Every engine honours it on an IP address
	// where the platform has SO_REUSEPORT; a Unix socket's address is refused with
	// LoopOptionUnsupported, or PacketOptionUnsupported on a datagram group, and a
	// platform without the option with UnsupportedPlatform (#1330).
	OptionShareAddress
)

The group options a loop group judges at Start, by kind.

func (OptionKind) String added in v0.26.8

func (k OptionKind) String() string

String renders the option's name, as its constructor spells it.

type OverrideValue added in v0.26.20

type OverrideValue struct {
	// Source is "environment" when KITSUNIUM_NET_ENGINE or KITSUNIUM_NET_DISABLE
	// is set to a non-empty value, and empty otherwise.
	Source string `json:"source,omitempty"`
	// Raw is what was read, each variable set as NAME=value, separated by a
	// space, in the order EnvEngine, EnvDisable.
	Raw string `json:"raw,omitempty"`
	// Applied is the highest engine the environment lets a group run on, after
	// both variables; empty when nothing is set. A group runs on
	// min(its code's choice, Applied), skipping what Disabled names.
	Applied Capability `json:"applied,omitempty"`
	// Disabled lists the engines KITSUNIUM_NET_DISABLE switched off.
	Disabled []Capability `json:"disabled,omitempty"`
}

OverrideValue reports what the operator's environment did to the engines, read once, before any probe (ADR 0188). The zero value is "no override".

type Packet

type Packet interface {
	// Data returns the datagram payload.
	Data() []byte
	// From returns the sender's address.
	From() stdnet.Addr
	// To returns the local address the datagram arrived on: on a UDP socket
	// bound to a wildcard address the address the sender used, read from the
	// datagram (IP_PKTINFO, IPV6_RECVPKTINFO; Linux), with the socket's port;
	// otherwise the socket's bound address.
	To() stdnet.Addr
	// ID returns the monotonic identifier assigned at read time.
	ID() uint64
	// Group returns the name of the listener group that received it.
	Group() string
	// Reply writes a datagram back to the sender. It exists so the common case
	// needs no reference to the underlying connection. On a socket bound to a
	// wildcard address it leaves from the address To reports, so a multi-homed
	// host answers from the address the client used.
	Reply(b []byte) (n int, err error)
}

Packet is one received datagram.

Data is valid only until ServePacket returns: the buffer is recycled straight back into the read batch, which is what makes batched datagram reading allocation-free. A handler that needs the payload afterwards must copy it.

IFACE-PLUGIN: the concrete type stays unexported in internal/service/net/server.

type PacketHandler

type PacketHandler interface {
	ServePacket(ctx context.Context, p Packet) error
}

PacketHandler serves one received datagram.

Returning an error does not kill the server: it is logged and metered, and reading continues. A datagram service must survive a malformed packet.

Implementations MUST be safe for concurrent use.

type PacketHandlerFunc

type PacketHandlerFunc func(ctx context.Context, p Packet) error

PacketHandlerFunc adapts a plain function to PacketHandler.

func (PacketHandlerFunc) ServePacket

func (f PacketHandlerFunc) ServePacket(ctx context.Context, p Packet) error

ServePacket implements PacketHandler by calling f.

type PacketLoopHandler added in v0.30.0

type PacketLoopHandler interface {
	// OnPacket hands the handler one datagram: buf is its payload and from its
	// sender. A coalesced message (UDP GRO) is one call a datagram, in order; a
	// message the kernel cut, or a datagram past the group's MaxPacketSize, is
	// counted in State and never handed on. buf stays valid until the lane's next
	// OnBatchEnd returns, never later, and the handler must not write to it.
	OnPacket(io PacketLoopIO, from netip.AddrPort, buf []byte)
	// OnBatchEnd is called once a batch, after its OnPacket calls and before any
	// buffer handed to them goes back to the kernel; the batch's replies leave
	// after it returns.
	OnBatchEnd(io PacketLoopIO)
}

PacketLoopHandler serves a datagram group on the loop engine (#852, ADR 0039: a sibling of PacketHandler, never a widening of it): PacketGroup.HandleLoop takes one. Each lane — one socket of the group, a shard or an adopted one — calls it on its own thread, one callback at a time, so a handler keeps its per-lane state in a slice indexed by PacketLoopIO.Loop and sized at LoopStarter.OnStart, which a handler that implements it receives once, with the lane count, before any datagram. A callback must not block: it stalls every datagram of its lane.

IFACE-PLUGIN: what a consumer writes; the lanes are internal/service/net/loop's and internal/service/net/server's.

type PacketLoopIO added in v0.30.0

type PacketLoopIO interface {
	// Reply queues p for to, from the lane's socket, and returns: the batch's
	// replies leave together once OnBatchEnd returns — on io_uring as SENDMSG,
	// a run to one peer of one size as one UDP_SEGMENT send (ADR 0192) — and p
	// may be reused once Reply returns. It is refused with LoopOutputFull,
	// copying nothing, when the lane's reply queue has no room; a queued reply
	// the kernel refuses is counted in Datagrams.ReplyFailures. On a socket bound
	// to a wildcard address that reads each datagram's destination (udp-pktinfo),
	// a reply to a peer that sent a datagram in the batch being handed on leaves
	// from the address that datagram reached, the newest one's, as Packet.Reply
	// does (#972); a source the kernel refuses is sent from its own choice, and a
	// reply to a peer that sent nothing in the batch leaves from the kernel's
	// choice.
	Reply(to netip.AddrPort, p []byte) error
	// Loop returns the lane's index, from 0 to the group's lane count less one,
	// fixed for the lane's life.
	Loop() int
}

PacketLoopIO is what a datagram lane offers its PacketLoopHandler. Reply and Loop belong to the lane's thread: they are called from a callback, never from another goroutine.

IFACE-PLUGIN: the lanes stay unexported in internal/service/net/loop and internal/service/net/server.

type Phase

type Phase uint8

Phase is where a server sits in its lifecycle. It is reported by State so an operator — or a readiness probe — can tell "not yet listening" apart from "listening" and from "draining", which a single boolean cannot express.

const (
	// PhaseNew is a constructed server that has not bound anything yet.
	PhaseNew Phase = iota
	// PhaseStarting is binding listeners; some may already be up.
	PhaseStarting
	// PhaseServing is bound and accepting.
	PhaseServing
	// PhaseDraining has stopped accepting and is waiting for in-flight work.
	PhaseDraining
	// PhaseStopped has released every listener.
	PhaseStopped
)

The lifecycle phases, in the order a server passes through them.

func (Phase) String

func (p Phase) String() string

String renders the phase for logs and status output.

type PlatformValue added in v0.26.20

type PlatformValue struct {
	// GOOS is runtime.GOOS.
	GOOS string `json:"goos"`
	// GOARCH is runtime.GOARCH.
	GOARCH string `json:"goarch"`
	// Kernel is the operating system's release as the platform reports it: the
	// uname release on Linux, kern.osrelease on darwin and the BSDs,
	// major.minor.build as GetVersion reports it on Windows; empty when it cannot
	// be read, and on illumos and Solaris, where the standard library reads none.
	Kernel string `json:"kernel,omitempty"`
	// Go is runtime.Version(), the toolchain the binary was built with.
	Go string `json:"go"`
}

PlatformValue names what the server runs on, so a report says which kernel a probe ran against (ADR 0188).

type Policy

type Policy interface {
	Allow(req RequestValue) error
}

Policy authorises an outbound request before it leaves the process.

The contract that makes this worth a port: implementations are evaluated inside the RoundTripper, underneath every call site. A caller cannot construct a request that skips the check, so "this client is read-only" becomes a property of the code rather than a convention the next contributor must remember. Allow returns nil to permit the request and a typed error to refuse it; the refusal is reported as RequestDenied without echoing the path, so a denial cannot leak the private API surface through a log line.

Implementations MUST be safe for concurrent use.

IFACE-PLUGIN: concrete policies live in internal/service/net/client; their types stay unexported behind their constructors.

type PolicyFunc

type PolicyFunc func(req RequestValue) error

PolicyFunc adapts a plain function to the Policy interface.

func (PolicyFunc) Allow

func (f PolicyFunc) Allow(req RequestValue) error

Allow implements Policy by calling f.

type PostFunc added in v0.26.8

type PostFunc func(io LoopIO, arg uintptr)

PostFunc is an operation posted to a loop with LoopIO.Post. It runs once, on the loop's thread, with the loop's LoopIO and the integer it was posted with. It is a plain function rather than a closure so the queue holds no pointer the poster built: a top-level function and an index into the caller's own table post without allocating.

type ProxyProtocolValue added in v0.30.0

type ProxyProtocolValue struct {
	// Trusted lists the CIDRs of the balancers. Empty, or a range that does not
	// parse, fails Start with OptionValueInvalid.
	Trusted []string `json:"trusted"`
	// Timeout bounds the header's arrival, on the server's clock. Zero selects
	// 3 s; a negative value fails Start with OptionValueInvalid.
	Timeout DurationValue `json:"timeout"`
}

ProxyProtocolValue configures the PROXY protocol on a TCP listener group, behind an L4 balancer that announces the real client (HAProxy's protocol, v1 and v2 both, chosen by the header's first byte).

The header is believed from a peer whose real address falls in one of Trusted's ranges and from nobody else: any other peer is closed before a byte of it is read. A trusted peer must send a header, within Timeout: a connection without one, or with a malformed one, is refused. There is no "accept both", so a listener cannot be half-fooled by a peer that sends a header it was not told to. The address announced is what RemoteAddr reports, Admission's key included; a LOCAL command or an UNKNOWN family keeps the socket's own.

type RequestValue

type RequestValue struct {
	// Method is the uppercase HTTP method, e.g. "GET".
	Method string
	// Scheme is the URL scheme, e.g. "https".
	Scheme string
	// Host is the authority, host[:port]. It is carried because a policy that
	// sees only the path cannot defend against a redirect that keeps the path
	// and swaps the origin.
	Host string
	// EscapedPath is the percent-encoded path exactly as it will be sent. It is
	// never the decoded form — see the type comment.
	EscapedPath string
	// RawQuery is the encoded query string, without the leading "?".
	RawQuery string
}

RequestValue describes an outbound request to the transport policy.

Two field choices are load-bearing and were paid for in a real integration:

  • The path is the ESCAPED form, the one that goes on the wire. url.URL.Path is already percent-DECODED, so a policy that judges it accepts "%2e%2e" — which the upstream then reinterprets as "..". Judging the decoded form makes a correctly written allowlist bypassable, so this type does not carry it.
  • RawQuery is present because a policy often needs to require a parameter, not just permit a path. A read-only policy that cannot see the query cannot, for instance, insist that a listing call passes the filter that makes its result correct.

The value is immutable by construction: a policy receives a copy and cannot mutate the request it is judging.

type ResponseValue

type ResponseValue struct {
	// Status is the HTTP status code. It is set even when the call also returns
	// an error, so a caller can tolerate a specific status deliberately.
	Status int
	// Header is the response header set.
	Header http.Header
	// Body is the fully-read response body, already bounded by the client's
	// size cap. It is nil for a status that carries no body, such as 204.
	Body []byte
}

ResponseValue is a fully-read outbound response.

The client returns this rather than an *http.Response on purpose. A returned *http.Response makes the caller responsible for closing the body and for reading it under a size cap, and a single forgotten Close leaks a connection. It also makes an accurate byte count impossible: the size of a body is only known once it has been read, so an observation hook firing at header time can never report it. Reading and closing inside the client fixes both.

type SSEEventValue

type SSEEventValue struct {
	// ID is the frame's "id" field. A client stores the last non-empty id it
	// saw and sends it back as Last-Event-ID when it reconnects, so this is the
	// resume token — and it is the CALLER's to mint, because only the caller
	// knows what it means. Empty emits no id field, which leaves the client's
	// stored id untouched; that is the format's own behaviour, not an omission.
	// An id carrying U+0000 is refused with SSEFieldInvalid, as one carrying a
	// line terminator is: a client ignores such an id field (WHATWG HTML,
	// "process the field"), so its stored resume token would silently stay the
	// previous one.
	ID string `json:"id"`
	// Name is the frame's "event" field — the event type the client dispatches
	// under. Empty means the default type, "message".
	Name string `json:"event"`
	// Data is the payload. Every line terminator it carries (LF, CR or CRLF)
	// becomes a separate "data:" line, which the client rejoins with "\n". A
	// payload that mixed terminators therefore arrives normalised to LF: the
	// value is preserved, its byte spelling is not.
	Data string `json:"data"`
	// Retry is the reconnection delay hint, emitted as an integer millisecond
	// count. Zero emits no retry field.
	Retry DurationValue `json:"retry"`
}

SSEEventValue is one Server-Sent Events frame.

The format has no escape mechanism. A line terminator inside a value is not quoted, it SPLITS the value: that is exactly what makes a multi-line payload expressible — Data is emitted as one "data:" line per line and the client rejoins them with "\n" — and exactly what makes a line terminator inside ID or Name unrepresentable. Those are refused rather than truncated, because a silently shortened id is a resume token pointing at the wrong place.

func (SSEEventValue) IsZero

func (e SSEEventValue) IsZero() bool

IsZero reports whether the frame carries no field at all.

func (SSEEventValue) Validate

func (e SSEEventValue) Validate() error

Validate reports whether the frame can be carried by the wire format.

It is separate from the encoder (internal/service/net/sse.AppendEvent) so a caller can check a frame before committing to send it, and so the encoder can validate everything BEFORE it appends anything — a half-written frame on a stream is unrecoverable, since the peer has already read it.

type StateValue

type StateValue struct {
	// Phase is where the server sits in its lifecycle.
	Phase Phase `json:"phase"`
	// Listeners describes every bound listener, including any degradation.
	Listeners []ListenerStateValue `json:"listeners"`
	// ActiveConns is the number of connections currently being served.
	ActiveConns int64 `json:"active_conns"`
	// TotalConns is the number accepted since the server started.
	TotalConns uint64 `json:"total_conns"`
	// RejectedConns is the number turned away by the connection ceiling.
	RejectedConns uint64 `json:"rejected_conns"`
	// OversizedPackets is the number of datagrams dropped for passing their
	// group's MaxPacketSize. They are counted rather than delivered because the
	// kernel has already discarded the tail, and the prefix that survives is
	// indistinguishable from a complete message to the handler receiving it. A
	// read loop has nobody to return an error to, so this counter is the report.
	OversizedPackets uint64 `json:"oversized_packets"`
	// AcceptBackoffs is the number of times an accept loop waited after Accept
	// failed with anything but its listener closing — the process out of file
	// descriptors, typically, with connections still queued. Retrying at once
	// would spin a core per listener while the condition lasts; each wait is
	// 5 ms, doubling to 1 s, and the next accepted connection starts the curve
	// over. A count that keeps rising is a server that cannot accept.
	AcceptBackoffs uint64 `json:"accept_backoffs"`
	// SuppressedLogs counts the lines of net/http's own log, and the failed TLS
	// handshakes, the server did not write: over the cap of ten records a second,
	// or with no logger to write them to (WithLogger). A count that rises is a
	// port being probed, or a handler that panics.
	SuppressedLogs uint64 `json:"suppressed_logs"`
	// Datagrams counts what the batched datagram paths drop (ADR 0192).
	Datagrams DatagramStateValue `json:"datagrams"`
	// Loops reports every loop group (ADR 0186 §D8). It is empty while the
	// server serves none.
	Loops []LoopStateValue `json:"loops,omitempty"`
	// Override reports what KITSUNIUM_NET_ENGINE and KITSUNIUM_NET_DISABLE did
	// (ADR 0188).
	Override OverrideValue `json:"override"`
	// Faults lists everything the server runs without now, the degradations of
	// Listeners and Loops included; Degraded is true when it is not empty.
	Faults []FaultValue `json:"faults,omitempty"`
	// Fallbacks counts the engine fallbacks and keeps the last one.
	Fallbacks FallbacksValue `json:"fallbacks"`
	// Platform names the operating system, its release and the toolchain.
	Platform PlatformValue `json:"platform"`
}

StateValue is a snapshot of the server's lifecycle and listeners.

It is a value, so a caller can hold it, log it, or compare it without racing the server that produced it.

func (StateValue) Degraded

func (s StateValue) Degraded() bool

Degraded reports whether the server runs with any fault — a listener or loop group that fell back from a requested optimisation, a drained ring, a stalled loop group, an unknown environment token — so a caller can assert "no silent degradation" in one call. It reads Faults, and still the Degraded flags of Listeners and Loops for a value built by hand (ADR 0188).

type SteerValue added in v0.30.0

type SteerValue uint8

SteerValue names how a Steer group spreads its datagrams over its shards. The zero value, SteerHash, is the kernel's SO_REUSEPORT 4-tuple hash.

const (
	// SteerHash spreads a group's datagrams with the kernel's 4-tuple hash.
	SteerHash SteerValue = iota
	// SteerCPU hands each datagram to shard cpu mod n, the softirq's CPU
	// (SO_ATTACH_REUSEPORT_CBPF, Linux 4.5).
	SteerCPU
)

type TimeoutsValue

type TimeoutsValue struct {
	// Read bounds one read from the peer.
	Read DurationValue `json:"read"`
	// Write bounds one write to the peer.
	Write DurationValue `json:"write"`
	// Idle bounds how long a connection may sit with no traffic at all.
	Idle DurationValue `json:"idle"`
	// Handshake bounds the TLS handshake, which is the phase a hostile peer can
	// most cheaply stall. Unlike the others, zero here does NOT mean unbounded:
	// the negotiation happens before any handler exists to notice it, so it
	// falls back to the domain's default instead.
	Handshake DurationValue `json:"handshake"`
	// KeepAlive is the idle time before the first keepalive probe of an accepted
	// socket, and the interval between probes: zero is 15 s, the standard
	// library's, and negative sets none.
	KeepAlive DurationValue `json:"keep_alive"`
	// UserTimeout is TCP_USER_TIMEOUT, the time sent output may stay
	// unacknowledged before the kernel closes the connection: zero is Write's
	// bound when one is set, otherwise none.
	UserTimeout DurationValue `json:"user_timeout"`
}

TimeoutsValue bounds each phase of a connection separately.

Per-phase deadlines rather than one overall budget: a slow reader, a slow writer and an idle keep-alive connection are three different problems with three different right answers, and a single timeout cannot tell them apart.

Every field is a PER-OPERATION bound, refreshed each time, not a budget for the connection's whole life. The distinction is the whole point: a peer sending one byte per second violates a per-read bound at once and stays inside a lifetime bound until the lifetime runs out.

type WSCloseCode

type WSCloseCode uint16

WSCloseCode is the status code a Close frame carries (RFC 6455 §7.4).

const (
	// WSCloseNormal is a completed purpose, on either side.
	WSCloseNormal WSCloseCode = 1000
	// WSCloseGoingAway is a server shutting down or a browser navigating away.
	// It is what a drain sends.
	WSCloseGoingAway WSCloseCode = 1001
	// WSCloseProtocolError is a frame the RFC forbids.
	WSCloseProtocolError WSCloseCode = 1002
	// WSCloseUnsupportedData is a well-formed frame carrying data this endpoint
	// cannot accept — binary to a text-only peer, for instance.
	WSCloseUnsupportedData WSCloseCode = 1003
	// WSCloseNoStatus means the peer's Close frame carried no code. It is
	// reported to the application and never sent.
	WSCloseNoStatus WSCloseCode = 1005
	// WSCloseAbnormal means the connection died without a Close frame. It is
	// reported to the application and never sent.
	WSCloseAbnormal WSCloseCode = 1006
	// WSCloseInvalidPayload is a text frame that is not valid UTF-8, or a close
	// reason that is not.
	WSCloseInvalidPayload WSCloseCode = 1007
	// WSClosePolicyViolation is the generic refusal when no other code fits.
	WSClosePolicyViolation WSCloseCode = 1008
	// WSCloseTooLarge is a message beyond what this endpoint accepts.
	WSCloseTooLarge WSCloseCode = 1009
	// WSCloseExtensionRequired is a client giving up because the server did not
	// negotiate an extension it needs. A server never sends it.
	WSCloseExtensionRequired WSCloseCode = 1010
	// WSCloseInternalError is an unexpected condition on this side.
	WSCloseInternalError WSCloseCode = 1011
	// WSCloseTLSHandshake means the TLS handshake failed. It is reported to the
	// application and never sent — there is no connection to send it on.
	WSCloseTLSHandshake WSCloseCode = 1015
)

The close codes RFC 6455 §7.4.1 defines. Three of them — WSCloseNoStatus, WSCloseAbnormal and WSCloseTLSHandshake — are values an application may OBSERVE but that MUST NOT appear on the wire: they describe the absence of a close frame, so sending one would be a statement contradicted by its own delivery.

func (WSCloseCode) Echoable

func (c WSCloseCode) Echoable() WSCloseCode

Echoable returns the code to send back when answering this one.

It exists because §5.5.1 says an endpoint SHOULD echo the peer's status code, and the one code a peer can leave us holding — WSCloseNoStatus, for a Close with no payload — is a code §7.4.1 forbids on the wire. Echoing blindly is therefore a protocol error waiting for the first well-behaved peer that closes without saying why.

func (WSCloseCode) Sendable

func (c WSCloseCode) Sendable() bool

Sendable reports whether the code may appear in a Close frame on the wire.

It is deliberately a property of the CODE rather than a check inside the sender, because the same question is asked twice from opposite directions: a peer that sends 1006 is committing a protocol error, and so is this endpoint if it ever does. One predicate, both directions, no chance of the two drifting apart.

type WSMessageValue

type WSMessageValue struct {
	// Binary reports whether the payload is opaque bytes (opcode 0x2) rather
	// than UTF-8 text (opcode 0x1). The zero value is text, which is the
	// opcode a zero-valued frame would carry anyway.
	Binary bool `json:"binary"`
	// Data is the reassembled payload. For a text message it is valid UTF-8,
	// which the engine verifies on both the receiving and the sending side.
	Data []byte `json:"data"`
}

WSMessageValue is one complete WebSocket application message, reassembled from however many frames carried it.

A message is the unit an application reasons about; a frame is the unit the transport reasons about. Handing the application frames would make fragmentation — which is the sender's private choice of chunk size, not a semantic boundary — visible in every consumer.

func (WSMessageValue) OpCode

func (m WSMessageValue) OpCode() WSOpCode

OpCode returns the frame opcode this message is carried under.

type WSOpCode

type WSOpCode byte

WSOpCode is a frame's four-bit type field (RFC 6455 §5.2).

const (
	// WSContinuation continues the message the previous frame began.
	WSContinuation WSOpCode = 0x0
	// WSText carries a UTF-8 payload, validated on both sides.
	WSText WSOpCode = 0x1
	// WSBinary carries opaque bytes.
	WSBinary WSOpCode = 0x2
	// WSClose begins or completes the closing handshake.
	WSClose WSOpCode = 0x8
	// WSPing asks the peer to answer with a Pong carrying the same payload.
	WSPing WSOpCode = 0x9
	// WSPong answers a Ping, or is sent unsolicited as a one-way heartbeat.
	WSPong WSOpCode = 0xA
)

The opcodes RFC 6455 defines. Everything else in the four-bit space is reserved, and a reserved opcode fails the connection rather than being skipped: an endpoint that ignored it would be desynchronised from a peer that believes the frame meant something.

func (WSOpCode) Defined

func (o WSOpCode) Defined() bool

Defined reports whether RFC 6455 assigns this opcode a meaning.

func (WSOpCode) IsControl

func (o WSOpCode) IsControl() bool

IsControl reports whether the opcode names a control frame.

Control frames are the ones that may be injected BETWEEN the fragments of a message, so the distinction decides which fragmentation rules apply.

func (WSOpCode) String

func (o WSOpCode) String() string

String renders the opcode for a log line or an error field.

type ZCRXValue added in v0.30.0

type ZCRXValue struct {
	// Interface is the network interface's name; empty names none.
	Interface string `json:"interface"`
	// Queue is the index of the interface's receive queue; negative is refused
	// with LoopValueInvalid.
	Queue int `json:"queue"`
	// AreaBytes is the size of the area the kernel lands payloads in, rounded up
	// to whole pages: zero selects 4 MiB, negative is refused with
	// LoopValueInvalid.
	AreaBytes int `json:"area_bytes"`
}

ZCRXValue names the network device queue io_uring's zero-copy receive registers (uring-zcrx, class B, ADR 0189 amended 2026-11-11): the interface, the receive queue its operator steered to the group's port, and the size of the area the payloads land in. Its zero value names none.

Directories

Path Synopsis
Package http2 is the contract of the SDK's HTTP/2 codec (ADR 0221): the values both engines read — the frame header, the settings, the bounds of ADR 0195 §D3 as a value — and the sentinels and codes its refusals carry, range 0.3.109.*, declared here at the engine's path (ADR 0160).
Package http2 is the contract of the SDK's HTTP/2 codec (ADR 0221): the values both engines read — the frame header, the settings, the bounds of ADR 0195 §D3 as a value — and the sentinels and codes its refusals carry, range 0.3.109.*, declared here at the engine's path (ADR 0160).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL