Documentation
¶
Overview ¶
Package engine holds the stages of a call that the root package's public types are built on. They live here, not in the root package, so that a test outside the root package can measure them. It imports neither the root package nor anything that imports it, and no file of it imports unsafe.
Index ¶
- Constants
- Variables
- func AppendSafeText(dst []byte, s string, limit int, double bool) []byte
- func AppendState[R ~[]byte, C content[R]](buf *[]byte, state any) error
- func DecodeSystemOneInto(ctx context.Context, logger *slog.Logger, body []byte, q *wire.Prepared, ...) error
- func FalsyJSON(raw []byte) bool
- func IsSecretHeader(name string) bool
- func KeyNeedle(key string) bool
- func QuotedName(name string) string
- func ReadBody(r io.Reader, declared, limit int64) ([]byte, error)
- func SafeMessage(s string) string
- func SafeName(name string) string
- type BodyMember
- type Client
- type Config
- type ConfigRef
- type Credentials
- type Failure
- type FailureKind
- type HeaderRedactor
- type Prepared
- type ProxyCreds
- type RedactedHeaders
- type Request
- type Response
- type SystemOneAlloc
- type Transport
- func (t *Transport) Close() error
- func (t *Transport) Credentials(req *http.Request) Credentials
- func (t *Transport) ErrorText(req *http.Request, err error) string
- func (t *Transport) ResponseRedactor(r HeaderRedactor, resp *http.Response) HeaderRedactor
- func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)
- func (t *Transport) Stats() h2gate.Stats
Constants ¶
const ( SystemOnePath = "/v1/systemone" ModelsPath = "/v1/models" )
The API endpoints, appended to the base URL (py:_core/constants.py:5-6).
const ( MsgSkippedAnswer = "Ignoring answer with unrecognized type" MsgSkippedAnswers = "Ignoring more answers with unrecognized types" )
The WARN lines for answers of unknown types.
const ( MaxNameChars = 128 MaxMessageChars = 200 )
The caps on text the SDK did not write, in characters counted after escaping: a name, such as an extra body member's, and a sentence another layer wrote, such as the encoder's.
const HeaderRetryCount = "X-TypeSafe-Retry-Count"
HeaderRetryCount is the header only retries carry, with the number of attempts before them (py:_core/transport.py:66-68).
const ( // InitialUndeclared is the first buffer of a body that declares no // length (a chunked body); it doubles as the body grows. InitialUndeclared = 4 << 10 )
The first buffer of a response body read.
const MaxInlineAnswers = 4
MaxInlineAnswers is the largest question set whose answer entries are allocated with the call's response.
const MaxPathChars = 320
MaxPathChars caps a whole field path in characters, counted after escaping: it holds the deepest path of the response schema, answers.<name>.probabilities.<key>, with both server-chosen names at MaxNameChars (the Rust port's MAX_PATH_CHARS).
const MaxProxyUserinfos = 16
MaxProxyUserinfos bounds the proxy userinfos a ProxyCreds remembers.
const MinKeyNeedleBytes = 8
MinKeyNeedleBytes is the shortest API key the SDK looks for inside other text: a header value it redacts, and a WithHeader name it refuses. A shorter key, such as a test's "test" or "k", occurs in ordinary names and values, and looking for it would hide or refuse them; real keys are far longer. Redaction by header name does not depend on the key and always applies.
const Redacted = "***"
Redacted is what a credential is printed as.
const RepeatScanLimit = 32
RepeatScanLimit is the number of strings up to which a repeat is found by comparing each with the ones before it; past it a map is used. At 32 strings the scan makes at most 496 comparisons, cheaper than the map's allocations, and question sets and option lists are rarely longer.
Variables ¶
var CanonicalRetryCount = http.CanonicalHeaderKey(HeaderRetryCount)
CanonicalRetryCount is the canonical form of X-TypeSafe-Retry-Count, the key a header map holds it under.
var ErrTooLarge = errors.New("typesafe: response body over the size limit")
ErrTooLarge ends a body read that passed the size limit.
Functions ¶
func AppendSafeText ¶
AppendSafeText appends s, text the SDK did not write, to dst escaped and cut at limit characters, as the Rust port's src/text.rs renders such text. Printable characters, non-ASCII and U+FFFD included, are written as they are; a control character (C0, DEL, C1), a byte that is not UTF-8 and a format character that reorders or hides the text around it (hidesText) are written as Go escapes: \n, \r, \t, \x1b, \xff, \u2028. A backslash is doubled when double is set, for a name, so that a name spelling \x1b cannot read like one holding the byte; a sentence keeps its backslashes, which its own layer wrote. A cut never splits a character or an escape and is marked with U+2026.
func AppendState ¶
AppendState appends a request state: text, a JSON object or an array. R and C are the root package's RawJSON and Content (EncodeBody).
func DecodeSystemOneInto ¶
func DecodeSystemOneInto(ctx context.Context, logger *slog.Logger, body []byte, q *wire.Prepared, model string, dst *wire.SystemOneResult, spare []wire.AnswerEntry) error
DecodeSystemOneInto decodes body, the body of a successful System One response, into *dst, with spare as the room for the answers (codec.DecodeSystemOneInto). q is the question set the request asked and model the model it named: the answers' strings are theirs where equal, so the result never aliases the body. A body the decoder refuses returns the decoder's error, which the root package wraps into a *ResponseValidationError naming the first failure the Python SDK would report.
Every answer of a type this version does not model is dropped and logged at WARN through logger, as the Python SDK logs "Ignoring answer %r with unrecognized type %r": at most codec.MaxSkipped lines per response, each with the answer's name and type escaped and cut at 128 characters, then one line counting the rest. The lines are logged even when the decode then fails, for the answers the Python SDK would have logged before failing. A nil logger logs nothing.
func FalsyJSON ¶
FalsyJSON reports whether the JSON value raw is one Python finds false: null, false, "", [], {} or a number equal to zero. Invalid JSON is not falsy; writing it reports the problem.
func IsSecretHeader ¶
IsSecretHeader reports whether the value of the header name is a credential by its name, compared without regard to case: one of secretHeaderNames, or a name that contains "token" or "secret" (py:_core/logging.py:32-34). The rule is strings.ToLower's: an ASCII name, as every name on the wire is (net/http refuses others), is compared with its letters folded in place, which allocates nothing, where lower-casing a mixed-case name such as "Content-Type" copies it; any other name is lower-cased with strings.ToLower.
func KeyNeedle ¶
KeyNeedle reports whether key is long enough to be looked for inside other text (MinKeyNeedleBytes).
func QuotedName ¶
QuotedName returns name between double quotes, escaped with its backslashes doubled, and cut at MaxNameChars.
func ReadBody ¶
ReadBody reads a response body of at most limit bytes. declared is its Content-Length, or -1 when it declares none.
- A declared length above limit is refused before anything is read.
- The first buffer holds min(declared, initialDeclared) bytes for a declared body and InitialUndeclared for an undeclared one.
- A full buffer grows by doubling, up to the end the body may reach: its declared length while it is within it, else the limit. Growth that would pass half that end goes to the end at once.
- The buffer that can reach the end has one byte of room beyond it, so the read that finds the end of the body, or the byte after the limit, needs no other buffer; the buffers before it are exact powers of two, so none costs the allocator a page for one byte.
- The read stops at limit + 1 bytes: that byte makes the body too large.
It returns ErrTooLarge for a body over the limit, and a read's own error other than io.EOF as it is.
func SafeMessage ¶
SafeMessage returns s, a sentence the SDK did not write, escaped with its backslashes kept and cut at MaxMessageChars.
func SafeName ¶
SafeName returns name, which the server chose, escaped with its backslashes doubled and cut at MaxNameChars, without quotes.
Types ¶
type BodyMember ¶
BodyMember is one member a call adds to the top level of its request body, as the Python SDK's extra_body does: the member's name and its value.
type Client ¶
type Client[R any] struct { // contains filtered or unexported fields }
Client is the state behind the root package's Client: its configuration, how errors name its endpoints, and its counters. R is the root package's RetryPolicy (Config).
func NewClient ¶
func NewClient[R any](cfg *Config[R], redact func() HeaderRedactor, systemOneEndpoint, modelsEndpoint string) *Client[R]
NewClient returns the state of a client configured by cfg, whose errors name its endpoints systemOneEndpoint and modelsEndpoint. redact is cfg's Redactor method value, which the client's responses keep (Config.RedactorFunc); the caller takes it where cfg's type is concrete, since a method value taken here, in a generic function, also holds the instantiation's dictionary: 24 bytes against 16.
func (*Client[R]) Built ¶
Built reports whether c was built by NewClient: false for a nil c and for the zero Client.
func (*Client[R]) ModelsEndpoint ¶
ModelsEndpoint returns how errors name the list-models endpoint.
func (*Client[R]) SystemOneEndpoint ¶
SystemOneEndpoint returns how errors name the System One endpoint.
type Config ¶
type Config[R any] struct { // APIKey is the key after trimming, which the redaction of logged // headers looks for in their values. APIKey string // SystemOneURL and ModelsURL are the two endpoints, shared read-only by // every request. SystemOneURL *url.URL ModelsURL *url.URL // SystemOneLog and ModelsLog are how log records name the endpoints: // the URL, or the API path alone under WithLogEndpointHost(false). SystemOneLog string ModelsLog string // Model is the model a request names when the call names none. Model string // Timeout is the deadline of each attempt; zero means none // (WithNoTimeout), since a zero WithTimeout is refused. Timeout time.Duration // ConnectTimeout is the deadline for opening a connection. ConnectTimeout time.Duration // MaxResponseBytes is the largest response body a request reads. MaxResponseBytes int64 // Logger receives the client's records; it is never nil. Logger *slog.Logger // SystemOneHeader and ModelsHeader are the headers of every POST // /v1/systemone and GET /v1/models request, built once. A request sends // its template and never writes to it. SystemOneHeader http.Header ModelsHeader http.Header // Transport carries every request of the client. Transport *Transport // Retry is the retry policy of a call that passes none. Retry R // Random is the jitter source of the retry backoff; nil means // math/rand/v2's Float64. No option sets it: tests write it, before the // client's first call, to pin the jitter of a whole call. Random func() float64 // contains filtered or unexported fields }
Config is a client's settings once every source has been consulted and every value checked. Nothing changes it after the root package's options.resolve returns, so requests read it without locking. R is the root package's RetryPolicy, which this package cannot name; the root package instantiates every type here that holds a Config with it, so a call reads the client's policy as a field, with no conversion.
func (*Config[R]) Redactor ¶
func (c *Config[R]) Redactor() HeaderRedactor
Redactor returns the redactor of the response headers the error types keep, for the client c configures (NewHeaderRedactor): its API key and no proxy's credentials, which Transport.ResponseRedactor adds for the response that may hold them.
func (*Config[R]) RedactorFunc ¶
func (c *Config[R]) RedactorFunc() func() HeaderRedactor
RedactorFunc returns the client's Redactor method value, which a response of the client keeps (Response.SetRedactor); nil before NewClient.
type ConfigRef ¶
ConfigRef holds a client's *Config, so that the key is two pointers away from the Client: fmt prints a pointer field as an address, except under a verb a pointer does not take, such as %s or %q, where it prints the value the field points to, which is this struct, whose one field is again a pointer, printed as an address. The configuration's fields are promoted through it.
type Credentials ¶
type Credentials []string
Credentials are the values a transport error's text must not show: the credentials of the request that failed, each in the forms an error text may quote it in, longest first. Build them with RequestCredentials.
A transport's error text is written by code the SDK does not control (a caller's RoundTripper or dialer, a proxy, net/http), which may repeat a header value, and it reaches the SDK error's message, its log record and the chain errors.Unwrap walks.
func RequestCredentials ¶
func RequestCredentials(h http.Header) Credentials
RequestCredentials returns the credentials of a request with header h.
func (Credentials) Cause ¶
func (c Credentials) Cause(err error) error
Cause returns the error an SDK error made from the transport's error err unwraps to: err itself, unless a printed form of err or of an error its chain wraps (errors.Unwrap, both forms) holds a credential ([Credentials.printed]), in which case it returns a [scrubbedError] standing in for err, so that no printed form of the SDK error or of anything it unwraps to shows the credential. The stand-in keeps err's text and the rendering of its chain ([Credentials.detail]), each with its credentials replaced.
An error whose fields point to the request, such as a caller's error type that keeps the *http.Request, is kept: fmt prints a pointer inside a value as an address, so no printed form shows the credential, and errors.As reaches the error with the request the caller's own RoundTripper, dialer or body gave it.
type Failure ¶
type Failure struct {
Kind FailureKind
// Key is the member that could not be encoded: "state", or the key of
// the extra body member when Extra is set.
Key string
Extra bool
// Err is the encoder's error.
Err error
}
Failure is why EncodeBody built no body. It is returned by value, so a call that builds its body allocates nothing for it; the root package turns it into its public error: a *ConfigError for FailNoQuestions and FailModel, an *InvalidRequestError naming the member for FailEncode.
func EncodeBody ¶
func EncodeBody[R ~[]byte, C content[R]](state any, model string, q *wire.Prepared, extra []BodyMember) (codec.Body, Failure)
EncodeBody encodes the request body of one System One call into a pooled scratch buffer, for the root package's encodeBody. It returns the body holding the call's reference, which the caller drops with Release when the call returns.
The body is the JSON object {"state":…,"model":…,"questions":…} followed by the members of extra, laid out as the Python SDK's {**body, **extra_body}: an extra member named "state", "model" or "questions" replaces that member's value where it stands, and the value it replaces is not encoded at all; any other name is appended in the order extra first names it. When extra names a member more than once, the last value is written.
R and C are the root package's RawJSON and Content, which this package cannot name. The state, and an extra "state" too, must be text, a JSON object or an array: sonic writes it (codec.EncodeState); an R state, or the R a non-nil *R points to, is written as it is after codec.AppendRawState's check; a C is written as a question writes it. model is written as a JSON string and q's bytes as they are. Any other value may be any JSON value: sonic writes it (codec.EncodeValue), an R value as it is after codec.AppendRawValue's check, a C as a question writes it and an unset C as null. The root package's encodeBody documents the float spelling, map order and json.Marshaler rules, which are this function's.
It fails, with a Failure the root package turns into its error, when q is nil or holds no question, when model is not valid UTF-8, or when a member cannot be encoded. The configuration is checked first.
type FailureKind ¶
type FailureKind uint8
FailureKind is what kept EncodeBody from building a body.
const ( // FailNone means the body was built. FailNone FailureKind = iota // FailNoQuestions means the question set is nil or holds no question. FailNoQuestions // FailModel means the model is not valid UTF-8. FailModel // FailEncode means a member could not be encoded. FailEncode )
type HeaderRedactor ¶
type HeaderRedactor struct {
// contains filtered or unexported fields
}
HeaderRedactor redacts the response header that the error types keep and the log records print: a header is a credential by its name always, by holding the client's API key when the key is at least MinKeyNeedleBytes long ([isCredential]), and, in the response to a plain-HTTP request through a proxy, by holding a whole credential of the proxies the SDK's transport chose, from MinKeyNeedleBytes too ([ProxyCreds.inHeader]), which such a proxy may repeat in the header of its own answer. Its zero value redacts by name alone, for an error built without a client's key, such as UnmarshalJSON's. Build a client's with NewHeaderRedactor, and a response's with HeaderRedactor.WithProxies.
func NewHeaderRedactor ¶
func NewHeaderRedactor(apiKey string) HeaderRedactor
NewHeaderRedactor returns the redactor for a client whose API key is apiKey: by name and by apiKey when apiKey is at least MinKeyNeedleBytes long, by name alone otherwise.
func (HeaderRedactor) Header ¶
func (r HeaderRedactor) Header(h http.Header) http.Header
Header returns h's headers in a new map in which every value of each header that is a credential is replaced by "***", one "***" per value, and every other header shares its value slice with h; nil stays nil. The error types that keep a response's header store this map, so no rendering of them, and no caller that dumps their Header, shows a credential the server sent.
func (HeaderRedactor) RequestID ¶
func (r HeaderRedactor) RequestID(h http.Header) (string, bool)
RequestID returns the request id in h as the error types show it from the header r redacted (HeaderRedactor.Header): the x-typesafe-request-id values joined by ", ", each "***" when one of them holds the client's API key or a credential of r's proxies, and whether the header was present. The INFO "response" record reads the id through it, so the record shows "***" where Error does. The header's name is not a credential's (IsSecretHeader), so only the key and the proxies' credentials are looked for, and the name is not tested. h is not copied: one value costs no allocation, and several cost the one string they are joined into.
func (HeaderRedactor) ResponseFunc ¶
func (r HeaderRedactor) ResponseFunc(keyOnly func() HeaderRedactor) func() HeaderRedactor
ResponseFunc returns the func a Response keeps to redact its header when the call has returned (Response.SetRedactor), for r, the redactor its call used (Transport.ResponseRedactor). When r looks for no proxy's credentials, which is every response but one to a plain-HTTP request through a proxy, it returns keyOnly, the client's own method value (Config.RedactorFunc), which costs nothing. Otherwise it returns a func that gives r with its proxies' credentials as they are now (ProxyCreds.Snapshot): the client's set holds the most recent MaxProxyUserinfos and forgets the oldest, so a response kept while the client goes through that many other proxies would otherwise lose its own proxy's credential. That path costs two allocations, the snapshot and the func.
func (HeaderRedactor) WithProxies ¶
func (r HeaderRedactor) WithProxies(proxies *ProxyCreds) HeaderRedactor
WithProxies returns r looking also for the whole credentials of proxies, for the header of a response that a proxy may have written itself: Transport.ResponseRedactor gives it to a response to a plain-HTTP request through a proxy alone.
type Prepared ¶
type Prepared struct {
// contains filtered or unexported fields
}
Prepared is the state behind the root package's Prepared: the bytes a request sends as "questions" and the tables the decoder matches answers against.
type ProxyCreds ¶
type ProxyCreds struct {
// contains filtered or unexported fields
}
ProxyCreds is the credentials of the proxies the proxy func of the SDK's own transport chose: the userinfo of each proxy URL the func returned, recorded where net/http asks it (h2gate.Config.OnProxy), never by asking it again, so that a func with side effects or one that rotates proxies is asked only as net/http asks it. It holds the most recent MaxProxyUserinfos distinct userinfos and forgets the oldest; a proxy chosen again becomes the most recent.
The set belongs to the transport, not to a request: a request that waited at the gate for another's dial shares that dial's error without asking the func (the gate gives a leader's dial error to its waiters), and its error still holds the credential of the proxy the dial chose. Every transport error of the client is scrubbed of every credential in the set, whatever its length, each word of a password among them (Transport.Credentials). The header of a response to a plain-HTTP request that went through a proxy, which the proxy may have written itself, a 407 among them, is scanned for the whole credentials alone, the Basic token and the password as it is and as the URL escapes it, those of 8 bytes or more, as for the API key (Transport.ResponseRedactor, HeaderRedactor.WithProxies, [ProxyCreds.inHeader]): a word of a password, or a short one, would match ordinary header values, a Retry-After among them.
A nil *ProxyCreds, a caller's transport's (WithHTTPTransport, WithRoundTripper), holds none. The set is written only when the func returns a proxy with userinfo, and read on the error path, for every response to check that it holds any (a lock-free load that allocates nothing), and for a header scan of a response through a proxy.
func (*ProxyCreds) Credentials ¶
func (p *ProxyCreds) Credentials() Credentials
Credentials returns every credential of every userinfo in p, the text needles, longest first, or nil; the slice is shared and must not be modified.
func (*ProxyCreds) Record ¶
func (p *ProxyCreds) Record(proxy *url.URL)
Record adds the userinfo of proxy, if it has one, as the most recent, forgetting the oldest when the set is full. It is h2gate's OnProxy.
func (*ProxyCreds) Snapshot ¶
func (p *ProxyCreds) Snapshot() *ProxyCreds
Snapshot returns a set that holds the credentials p, which must not be nil, holds now and never changes: what a response keeps to redact its header after its call has returned, when p may since have forgotten the proxy the response went through (HeaderRedactor.ResponseFunc). It costs one allocation; the credentials themselves are shared, since p replaces rather than changes them.
type RedactedHeaders ¶
type RedactedHeaders struct {
// contains filtered or unexported fields
}
RedactedHeaders is a header map as a log record shows it: a group with one attribute per header, in name order, whose value is the header's values joined by ", ", or "***" when they are a credential ([HeaderRedactor.credential]). Build it with NewRedactedHeaders.
It is a slog.LogValuer, so the map is walked and its values joined only when a handler keeps the record. Boxing the value into an attribute still allocates, so a request path that must not allocate with debug records off checks slog.Logger.Enabled before it builds the attribute.
No rendering prints the map or a credential. Every fmt verb prints the redacted form (RedactedHeaders.Format), and so do an unresolved slog.Value and slog.Attr, whose String methods print a LogValuer through fmt. The map and the redactor sit behind the one pointer field, so the two renderings fmt makes without calling Format, the %p verb and a RedactedHeaders held in an unexported field of another value, print an address.
func NewRedactedHeaders ¶
func NewRedactedHeaders(header http.Header, r HeaderRedactor) RedactedHeaders
NewRedactedHeaders returns header as a log record shows it, each header r counts as a credential redacted ([HeaderRedactor.credential]): by its name, by a value that holds the client's API key, and, in the response to a plain-HTTP request through a proxy, by a value that holds the proxy's credential.
func (RedactedHeaders) Format ¶
func (r RedactedHeaders) Format(f fmt.State, _ rune)
Format writes the redacted form, LogValue().String(), such as "[Accept=application/json Authorization=***]", whatever the verb and its flags. A String method alone would not do: %d and %x bypass it and print the fields.
func (RedactedHeaders) LogValue ¶
func (r RedactedHeaders) LogValue() slog.Value
LogValue returns the redacted headers as a group value; the zero RedactedHeaders is an empty group.
type Request ¶
type Request struct {
Method string
URL *url.URL
// FirstURL is where the first attempt copies URL: storage allocated
// with the call's response, so that the copy costs no allocation of
// its own. A retry copies URL to a fresh URL.
FirstURL *url.URL
LogURL string // how log records name the endpoint
Endpoint string // how errors name the endpoint
// Header is the call's header template, which every attempt's header
// starts from, and Timeout the deadline of each attempt (zero: none).
Header http.Header
Timeout time.Duration
// Body and GetBody are the encoded body and its GetBody, set for a
// request with a body; GetBody is made once per call.
Body codec.Body
GetBody func() (io.ReadCloser, error)
}
Request is what every attempt of one call sends.
func (*Request) AttemptHeader ¶
AttemptHeader returns the header map of attempt. The first attempt sends the call's template itself: the client's, built once, or the call's own when it sets headers. That map is shared by every call and never written, and net/http's RoundTripper contract forbids a transport to modify a request, so no copy is made. A retry sends a fresh map over the template, whose value slices it shares (each has len == cap, so an append reallocates), with X-TypeSafe-Retry-Count, which only retries carry (py:_core/transport.py:66-68).
type Response ¶
type Response struct {
// contains filtered or unexported fields
}
Response is the state behind the root package's SystemOneResponse: the decoded result and the HTTP metadata of the response it came in.
func (*Response) Meta ¶
func (r *Response) Meta() *wire.ResponseMeta
Meta returns the HTTP metadata: the status, the header and the body.
func (*Response) Redactor ¶
func (r *Response) Redactor() HeaderRedactor
Redactor returns the redactor of the response's client, or the zero HeaderRedactor, which redacts by name alone, for a response no request made.
func (*Response) Result ¶
func (r *Response) Result() *wire.SystemOneResult
Result returns the decoded result.
func (*Response) SetRedactor ¶
func (r *Response) SetRedactor(red func() HeaderRedactor)
SetRedactor sets the func that returns the redactor of the response's client (Config.RedactorFunc); nil drops it.
type SystemOneAlloc ¶
SystemOneAlloc is what a SystemOne call keeps on the heap, in one piece: the response it returns and its first attempt's copy of the endpoint URL (Request.FirstURL), which that attempt's request points to. The copy lives as long as the response, 144 B that a held response keeps alive, and nothing reads it but the transport.
func NewSystemOneAlloc ¶
func NewSystemOneAlloc(n int) (*SystemOneAlloc, []wire.AnswerEntry)
NewSystemOneAlloc allocates a call's SystemOneAlloc and, for a question set of n questions, 1 to MaxInlineAnswers, an array of n answer entries in the same allocation, which it returns as an empty slice for the decode (codec.DecodeSystemOneInto): a response that answers the questions asked, the usual case, then costs no allocation for its entries. Each size is its own type, so the array is exactly n entries. A larger set gets its entries from the decode, in an allocation of their own, as does a response with more answers than questions.
Lifetime: the entries share one block with the response and the first attempt's URL copy, so an Answers taken from the response keeps the whole block reachable (704 B for three questions): the response, the entries and the 144 B URL copy; an answer value copied out of it holds no pointer into the block. No entry points into the body: the decode copies or interns every string it stores, whichever array holds the entries (TestDecodeDoesNotAliasBody, TestAnswersOutliveTheirResponse).
type Transport ¶
type Transport struct {
// RT is what every request goes through: Gate, or the caller's
// WithRoundTripper.
RT http.RoundTripper
// Gate is the SDK's transport (the default one or WithHTTPTransport's
// clone), nil under WithRoundTripper.
Gate *h2gate.Transport
// Trace is WithClientTrace's hooks, shielded per request.
Trace *httptrace.ClientTrace
Logger *slog.Logger
// Proxies are the credentials of the proxies the SDK's own transport
// chose (WithProxy's func, or http.ProxyFromEnvironment), recorded where
// net/http asks the func (h2gate.Config.OnProxy); nil for a caller's
// transport (WithHTTPTransport, WithRoundTripper), whose proxy the SDK
// does not choose.
Proxies *ProxyCreds
// contains filtered or unexported fields
}
Transport is a client's HTTP transport, built once by the root package when the client is built and shared by every request. The root package sets its fields before the first request and never after.
func (*Transport) Close ¶
Close releases the transport, once: the SDK's transport and a caller's WithHTTPTransport clone close their idle connections; a WithRoundTripper closes its idle connections when it has CloseIdleConnections, as an *http.Transport has, and is then closed when it is an io.Closer. Later calls return the first call's result.
func (*Transport) Credentials ¶
func (t *Transport) Credentials(req *http.Request) Credentials
Credentials returns the credentials an error of the transport for req may repeat ([callCredentials]): those of req's header and, on the SDK's own transport, those of every proxy it chose (ProxyCreds), never asking the proxy func again. A proxy's answer that net/http cannot parse becomes an error net/http builds itself, quoting the answer, before refusedConnect's scrub (internal/h2gate) could see it; a caller's transport keeps its own proxy, which the SDK does not ask.
func (*Transport) ErrorText ¶
ErrorText renders err, an error of the transport for the request req, for its DEBUG records "h2: gate error" and "h2: redial error" (h2gate.Config.ErrorText): every credential of the call (Transport.Credentials) and every URL userinfo replaced by "***" (Credentials.Redact), then escaped and cut at 200 characters (SafeMessage), as the text of a *ConnectionError is. The transport calls it only for a record the logger keeps.
func (*Transport) ResponseRedactor ¶
func (t *Transport) ResponseRedactor(r HeaderRedactor, resp *http.Response) HeaderRedactor
ResponseRedactor returns r, the client's header redactor, for the header of resp, which the transport returned: with the transport's proxy credentials (ProxyCreds) when resp answers a plain-HTTP request for which the proxy func returned a proxy (h2gate.Proxied), the one case in which a proxy writes the response itself and so may repeat in its headers what it was sent. Over HTTPS a proxy only tunnels the API's bytes, and a request without a proxy never reaches one, so there the scan could only redact what is not a credential. The header paths look for the whole credentials alone, from 8 bytes, as for the API key (HeaderRedactor.WithProxies).
func (*Transport) RoundTrip ¶
RoundTrip sends req through the transport and returns the transport's error as it is: the root package maps a failure of the SDK's transport before a connection was had, and an API host that did not speak HTTP/2 under HTTP2Only, to its error types. A caller's trace hooks run shielded: a panic of one is raised again on the caller once the round trip returns.