Skip to content

Latest commit

 

History

History
281 lines (237 loc) · 17.2 KB

File metadata and controls

281 lines (237 loc) · 17.2 KB

Surf HTTP Adapter v1

Audience: independent Go consumers and Framework composers. Status: implemented API with local protocol/ownership tests; release, licensing and deployment qualification remain separate. Issue119 owns this provider and its necessary native corrections.

Import github.com/frost-leo/fathomry/adapters/httpclient/surf/v1. Requests and headers are github.com/enetx/http, not a converted net/http DTO. No owning SDK client, Builder, transport, Multipart, raw connection or filesystem path escapes.

Selected source and delivery

Selected SDK: Surf v1.0.206, origin 7da0502899af06f8318f95e632797cb2ac0c6c20; HTTP2 v1.0.26 and HTTP3 v1.0.9. The local Surf, H2 and H3 corrections have revisions v3, v2 and v4 respectively, separate from upstream versions. See Surf provenance, H2 and H3. Original source notices and transport-fork redistribution uncertainties are retained; tests do not grant source permissions or release clearance.

Root replace directives are not inherited and nested modules are absent from root ZIPs. Independent applications use the generator's actual immutable versioned replacement policy; development mode explicitly selects a checkout. Build reports the executing module/replacement graph, not source attestation. Do not remove the selected corrections merely because another graph compiles.

Prepare and own

Prepare(Settings, NativeOptions) freezes the final selection offline, without sockets, SDK client construction or arbitrary profile/factory calls. Validate(Settings) checks strict data only. Recommend(Settings) covers the data-only native-standard-TLS selection; native-aware callers use Prepared.Policy.

Prepared.Policy and Compose come from Internal's authoritative metadata plus public bridge/source-record costs. Compose covers exactly the listed overlapping owners, including retired generations. Policies reject unrepresentable totals, insufficient Runtime/Inbox capacity and oversized replacement roots before native dispatch; they never silently enlarge caller mechanisms.

Create adapters.Runtime and adapters.Inbox[Result] from the policy, then call Prepared.Open. Passing a second Native selection is rejected. Open returns an Owner; retain and close any non-nil owner even alongside an error. Owner alone has shutdown authority. Client/Handle are non-owning. Open's ctx owns the entire source lifetime, not just construction waiting. Source construction is local-only and is not endpoint readiness evidence.

Close seals work, cancels retained operations, joins native writers/producers and closes actual resources. Close waiting can time out while cleanup continues; ShutdownComplete and repeatable Release/Close report actual local completion. Borrowed code/readers must cooperate; an uncooperative callback can retain ownership.

Settings and native borrowing

Version zero selects format1. Pointer nil means unset; explicit zero/false/empty mode is preserved. Durations are integer nanoseconds. Settings serialization may contain proxy credentials; normal fmt/slog is redacted.

Setting Default / applicability
Name, Version Named source; version0/1
Mode negotiated; http1, http2, prefer-http3, h2c
ProxyURL, RoutingLocked Native configured route; unlocked by default
DisableCompression false; retains native framing/decoding distinctions
MaxActive, QueuedCalls 8,0; zero queue disables queueing
MaxRoutes, MaxTCPConnections, MaxUDPSockets 16,32,16; source-wide ownership
MaxHTTP3Clients 32; cached, pending and retiring client entries, not UDP sockets
MaxHTTP3QPACKTableBytes 64KiB declared per-client table ceiling; explicit0..64MiB
MaxHTTP3QPACKBlockedStreams 128 declared blocked-stream ceiling; explicit0..1024
MaxProfileBytes 1MiB; 1KiB..1MiB declared lazy/profile envelope
MaxHTTP2StreamBytes 8MiB; 4MiB..1GiB lazy receive-stream ceiling
MaxRequestBytes, MaxResponseBytes 8MiB each; positive up to1GiB
MaxHeaderBytes, MaxNativeHeaderBytes 64KiB,10MiB; distinct retained/native parser limits
MaxRoundTrips, MaxReplays 10,32; maxima128,1024
NativeRetries, RetryCodes, RetryDelay 0, native default codes when enabled,0; no added business retry
AdmissionTimeout, Timeout, IdleConnTimeout 30s,30s,90s; positive1ms..24h

AdmissionTimeout is Internal admission; public queue waiting is controlled by the method context. Timeout bounds admitted root lifetime, including automatic retained stream cleanup. Public policy includes source slots as well as operation slots. IdleConnTimeout governs idle TCP H1/H2/JA/h2c connections, not active streams or H3's separate native QUIC idle/keepalive policy. DisableCompression controls Surf's automatic response decoding: true preserves encoded bytes, while default/false decodes and validates them. It does not rewrite a caller's Accept-Encoding choice. MaxNativeHeaderBytes uses each selected parser's accounting, not a universal wire byte unit: H1 counts its received header block; H2/H3 include per-field accounting, and ordinary enetx/http H2 adds its native 320-byte allowance. JA-H2/h2c and H3 use the supplied field-list limit directly. MaxHeaderBytes separately bounds retained metadata; duplicate wire fields can exceed a native limit while retained data fits.

NativeOptions retains Profile/OS, fresh context-bearing HelloSpecFactory, standard TLSConfig, separate JAConfig and ProxyTLSConfig, native Headers/Jar, DialContext/ListenPacket/Resolver, header-only RequestMiddleware, metadata-only ResponseMiddleware and CheckRedirect. Profile is optional; omission means standard TLS, never automatic browser selection. H3 uses standard TLS, not JA/uTLS.

Client-used data containers are copied: profile/spec data, headers, TLS/JA vectors, certificate byte containers and named certificates, ECH client data and JA ALPS ApplicationSettings. The checked aggregate copied-container declaration is at most 1GiB before cloning. Certificate-pool indexes are cloned; their opaque lazy backing remains immutable borrowed data. Keys, caches, writers and functions are concurrent, cooperative borrows through source release. Server-only TLS fields (including server ECH keys) are not client behavior; their opaque data is not promised as a new client capability or deeply frozen resource.

Lazy ConfigureH2/H3, BuildHeaders, boundary and Hello callbacks do not run during preparation. Outputs are checked where owned. Dynamic native SNI/session/padding and foreign callback memory remain cooperative declarations, not a hard allocation sandbox. Budgets cover selected copied containers, native frame/codec buffers, stream receive ceilings, peer TLS state and actual H3 client cardinality. UDP socket count alone cannot bound QUIC connections. Declared bytes are not heap/RSS. Custom Resolver.Dial callbacks and their returned DNS connections remain owned until actual Close. DNS network sockets use the same TCP/UDP source ceilings; PacketConn framing is preserved. Native lookup-group cancellation initiates Close without canceling a healthy shared lookup waiter. A resolver panic disables its route and retains the original cause through source Close, rather than losing it to Go's DNSError stringification. Ordinary DNS errors retain native retry semantics.

h2c and native profiles

H2C is explicit prior-knowledge HTTP/2, not HTTP/1 Upgrade or HTTPS rewriting. It uses the already managed effective dial path, including supported proxies, socket admission, cancellation and writer ownership. HTTPS origins (including redirects) and actual origin TLS/JA/Hello/shuffle inputs reject before dispatch. ProxyTLSConfig remains meaningful for a TLS proxy and is not confused with origin TLS. To use only HTTP facets of a Variant, explicitly clear HelloSpec, HelloID and ShuffleExtensions while retaining its headers, H2 settings and boundary functions.

Emitted H2 SETTINGS govern receive credit: omitted custom INITIAL_WINDOW_SIZE means65535, explicit zero remains zero, and an empty list keeps native generated defaults. MAX_FRAME_SIZE governs incoming frames, not peer-advertised outgoing limits. Normal larger-window/frame controls and rejecting overflow peers are maintained alongside original-source ablations. Native fluent profile setters keep their own omission rules: a setter that omits zero does not become an explicit zero wire setting. The receive-credit guarantees above concern the SETTINGS actually emitted, not a universal options DTO.

H3 cached/retiring clients are bounded separately. A failed stream may retire its client without killing a retained sibling response. Bodies and real request writers retain their client. Failed/canceled pending dials release reservations once finished. A safe rejected-request replay joins only its previous writer before acquiring the replacement; a genuinely retained sibling may still cause capacity refusal. An explicit remote H3_VERSION_FALLBACK signal uses a separate owned HTTP/1.1-only fallback transport with independent ALPN configuration and the same TCP quota. It does not mutate the shared H2-capable fallback. One-shot and other non-fallback errors still stop before resending; replayable multipart acquires fresh bodies.

Dynamic QPACK ownership

Since revision v3, local H3 supplements the selected upstream static-only decoder with connection-local dynamic response decoding. Relative/post-base indices, literals, insert/duplicate/capacity updates, eviction and Required Insert Count wrapping are supported for informational/final headers and trailers. Static request encoding remains conformant and unchanged; no other provider or shared QPACK module changes.

The two QPACK Settings fields declare technical ceilings, not wire overrides. Absent values select64KiB/128; explicit zero remains zero. Native advertised SETTINGS1/7 keep their actual values and RFC zero defaults, and lazy nonzero values outside a zero/insufficient declaration reject before packet/dial effects. Profile callbacks are not evaluated offline. The existing MaxNativeHeaderBytes bounds each encoded/decoded section; a local section-size refusal does not poison a retained sibling. Malformed codec instructions/references remain connection protocol errors.

The dynamic table, bounded instruction scratch, blocked descriptors and feedback FIFO belong to each cached/pending/retiring H3 client. Queued ACKs and cancellations remain source-owned after a root ends. Feedback capacity is derived from MaxActive and the blocked-stream ceiling; exhaustion explicitly terminates the unusable connection, without growing a queue or dropping required instructions. Source shutdown joins actual parser/writer workers before releasing the client slot. Native receive-context cancellation stops only that section, not a shared table or decoder stream. EOF, protocol feedback delivery and public evidence Ack are different facts; closing the source can cancel unsent feedback by closing QUIC. Revision v4 detects peer closure of an idle outgoing decoder critical stream without needing another feedback write. The selected QUIC receive API exposes request-stream resets on reads, not through a receive context. A QPACK-blocked section therefore retains its bounded wait until table progress, method timeout/ cancellation or source/connection shutdown; immediate peer-reset wakeup is not a separate guarantee. Neither normal FIN nor completion of request writing cancels a valid response section awaiting encoder instructions.

Requests and incremental multipart

Do(ctx, cleanupCtx, *enetx/http.Request, ...RequestOptions) returns finite bounded response data; Open(ctx, request, ...) returns a response Stream and receipt. The method context replaces Request.Context, rather than merging them. Native header order, method/Host fields, fixed trailers, native retries and response protocol behavior remain provider-specific. Middleware cannot replace contexts, owning bodies or transports.

Outbound snapshots preserve client fields without copying hidden server routing state such as SetPathValue values. Parsed Form/PostForm/MultipartForm, server TLS, RemoteAddr and Pattern are refused before Internal input acquisition; encode an outbound form in Body or use the owned Multipart path instead. Cookie metadata bounds include copied Unparsed slice entries even when their strings are empty.

RequestOptions version0/1 supports nil ProxyURL inheritance, explicit empty direct routing, per-call proxy addresses, bounded CONNECT headers and Multipart. At most one options value is admitted. Routing is copied; invalid/refused routes never silently become direct. Origin and proxy trust are separate. Request metadata, ProxyURL values and request/options containers must remain immutable until Do/Open returns. Their native snapshot is taken after public admission, not before queued waiting. Admitted readers and factory closures stay borrowed through actual cleanup; caller return is not permission to reuse them.

Multipart contains up to128 combined Fields and Parts. Native ordered unique fields come first, then files in supplied order. Duplicate field names replace their value at the original position; this is not arbitrary field/file interleaving. Each Part has Name/FileName/ContentType and exactly one Input or Open. FileName is only MIME metadata, not a filesystem-access request. Request Body/GetBody conflicts reject.

Input is one-shot. Open(ctx) must return a fresh independent closable reader yielding the same logical bytes on each replay; reader-plus-error still transfers cleanup responsibility. Initial factories are lazy; replay preflight acquires fresh readers before any repeated dispatch, without reading/materializing the whole upload. Comparable aliases are refused; opaque independence and byte equivalence remain caller obligations. Static fields alone are replayable; mixed inputs are one-shot.

The native encoder writes incrementally through an owned pipe. One native/profile boundary is chosen per logical upload and reused for replay. Multipart's generated Content-Type overrides initial caller defaults as in the native API; middleware cannot corrupt the selected boundary. Producer, pipe, already acquired and unused one-shot inputs remain owned until cleanup ends. Cancellation independently closes pipe/inputs before waiting for production; no transport-writer/producer join cycle is used. Every generated body is tracked, including GetBody results that redirect policy does not send.

One-shot input plus NativeRetries>0 rejects before borrowing/sending. A one-shot 307/308 keeps the native redirect response instead of manufacturing replayability. Replayable status retries, redirects and supported protocol fallbacks obtain fresh bodies with the same boundary. A failed/aliased replay stops before another send; native behavior may close the old response body, but observed metadata/causes remain. Nothing adds business idempotence, retries, credential refresh or buffering to fake streaming. Incrementality tests require peer receipt before the tail is generated.

Results, cleanup and evidence

Result/Metadata expose immutable copies of response headers, finite bytes, trailers, protocol/status/URL, source identity, actual generation/correlation, body/input-read counts, RoundTrips and nonexact Attempts. Counts are not exact physical attempts or wire traffic. HTTP status remains data. Complete means final response EOF/integrity without primary failure, not business success or proof of full upload/remote effects.

InputErrorsCopy records input/producer/replay notices separately from an early complete response. Primary, Cleanup, waiter timeout, EOF, Close and Ack remain different facts. Stream permits one reader and concurrent repeatable Close; Close is required after EOF and retains native Surf primary+cleanup semantics. Cleanup does not require a new admission/evidence slot.

For Fixed/Follow, bind Settings to Handle and return Owner.Release in the resource Instance. Using borrows caller mechanisms and an exact generation; Native is not accepted there. Old streams/producers retain their actual source through replacement. Failed/obsolete candidates retain cleanup authority. Receiver Retry redelivers evidence, never reissues HTTP. Direct handling does not acknowledge the Inbox.

Network facility0x202/http_surf is additive. Definitions/Resources and en/zh-CN CLI catalogs work offline; errorbridge preserves deliberate errors.Is/As and redacts diagnostics. Runtime values refuse JSON serialization. See capabilities and the executable public-only direct/Framework fixtures.