hd-lang

#Http

Status: standard library specification draft.

This chapter defines std.http, which lib/std writes in ordinary hd over the language tier:

  • the capability trait Http, and the free helpers get! and post!;
  • the struct values Request and Response, and the enums Method and HttpError;
  • status checking, retained error responses, and the JSON calls;
  • which request URLs are valid;
  • ScriptedHttp, the deterministic provider of Http;
  • what the playground binds for Http, and how a block_on call waits there.

Which provider a command binds, and which hosts its grant covers, is CLI tier (Host Capabilities).

#Read-Once Bodies

A Body is a value whose copies share private consumption state.

  1. std-http.body.declarationstd.http declares Body with private shared state.
  2. std-http.body.from-bytesBody::from_bytes(bytes: List[u8]) -> Body creates a fresh unread body holding those bytes.
  3. std-http.body.shared-stateCopying a Body shares its bytes and read state with the original.
  4. std-http.body.bytesbytes(&self) -> Result[List[u8], BodyError] claims the body and returns its bytes.
  5. std-http.body.usedAny later read through any copy returns BodyError.Used.
  6. std-http.body.claim-before-decodeEvery reader claims the body before decoding; a decoding failure leaves it used.
  7. std-http.body.empty-claimReading an empty body also claims it.
  8. std-http.body.texttext(&self) -> Result[string, BodyError] reads UTF-8, replacing malformed sequences with U+FFFD.
  9. std-http.body.jsonjson[T < Deserialize](&self) -> Result[T, BodyError] decodes the body's text through std.json.
  10. std-http.body.json-errorA JSON decoding failure returns BodyError.Json containing the decoder's error.
  11. std-http.body.formform[T < Deserialize](&self) -> Result[T, BodyError] decodes a URL-encoded form using Typed Query And Form Values.
  12. std-http.body.form-errorA form decoding failure returns BodyError.Form containing its FormError.
  13. std-http.body.value-equalityBody implements Eq by comparing payload and read state, independently of allocation identity.
  14. std-http.body.debug-privateBody implements Debug, showing its length and read state without its bytes.

#Typed Query And Form Values

Query pairs and URL-encoded bodies share one serde codec.

  1. std-http.form-codec.query-readerUrl.query_as[T < Deserialize](&self) -> Result[T, FormError] decodes its query pairs into T.
  2. std-http.form-codec.parameter-recordThe root value is a struct whose fields are parameters named by their serde wire names.
  3. std-http.form-codec.scalarScalar values use the existing serde textual conversions.
  4. std-http.form-codec.deep-objectA struct-valued parameter uses one bracket level, parameter[property], whose properties are scalars or optional scalars.
  5. std-http.form-codec.repeated-scalarsA list or tuple of supported scalars uses repeated occurrences of its parameter name, in occurrence order.
  6. std-http.form-codec.tuple-countA tuple with the wrong number of values returns Invalid(field, text).
  7. std-http.form-codec.duplicate-scalarRepeated scalar parameters return Invalid(field, text) with the first occurrence's text, even when the texts are equal.
  8. std-http.form-codec.missing-listAn absent list parameter decodes to an empty list.
  9. std-http.form-codec.optionalAn absent optional value decodes to None; an explicit empty string remains present.
  10. std-http.form-codec.defaultsAn absent field with a declared serde default takes that default.
  11. std-http.form-codec.unknownUnknown keys are ignored after percent-encoding and UTF-8 validation.
  12. std-http.form-codec.missing-scalarA missing required scalar returns Missing(field) with its declared wire path.
  13. std-http.form-codec.invalid-scalarA failed scalar conversion returns Invalid(field, text) with its declared wire path and original text.
  14. std-http.form-codec.unit-enumA selected unit enum variant is a scalar whose text is its variant name.
  15. std-http.form-codec.payload-enumA visited enum variant with payload fields returns Unsupported(field).
  16. std-http.form-codec.unsupported-containersA visited dynamic map, deeper record or nested list returns Unsupported(field).
  17. std-http.form-codec.unsupported-visitsUnsupported values are rejected when visited, without schema preflight.
  18. std-http.form-codec.empty-unvisitedEmpty record lists and absent optional records encode and decode as empty or absent without error.
  19. std-http.form-codec.bracketed-renameA member wire name containing [ or ] returns Unsupported(field).
  20. std-http.form-codec.body-pairsForm decoding splits nonempty parts at & and each part's first =, retaining their order.
  21. std-http.form-codec.body-empty-valueA form part without = has an empty value.
  22. std-http.form-codec.body-decodeForm names and values percent-decode UTF-8 and interpret + as space.
  23. std-http.form-codec.strict-inputInvalid percent-encoding or invalid UTF-8 returns FormError, including in unknown pairs.
  24. std-http.form-codec.query-writerUrl.set_query_as[T < Serialize](&mut self, value: T) -> void replaces the query using the same serde codec.
  25. std-http.form-codec.query-write-orderWriting retains member declaration order and list or tuple item order as decoded query pairs.
  26. std-http.form-codec.query-write-absenceWriting an absent optional value emits no pair.
  27. std-http.form-codec.query-write-emptyEvery query write clears the explicit empty-query presence bit.
  28. std-http.form-codec.query-write-errorA failed query write retains a private FormError, which later query_as returns directly.
  29. std-http.form-codec.query-replacementA successful set_query_as replaces the query and clears its pending preparation error.
  30. std-http.form-codec.query-append-erroradd_param does not clear a pending query preparation error.
  31. std-http.form-codec.query-resolve-inheritResolution retains a pending query error exactly when RFC 3986 inherits the base query, including empty and fragment-only references.
  32. std-http.form-codec.query-resolve-replaceA reference replacing the query clears its pending error, including an explicit query, new path, authority or scheme.

#Named Status Codes

  1. std-http.status.declarationstd.http declares HttpStatus with the variants and numeric codes below, and Other(code: u16).
Variant Code
Ok 200
Created 201
Accepted 202
NoContent 204
PartialContent 206
MovedPermanently 301
Found 302
SeeOther 303
NotModified 304
TemporaryRedirect 307
PermanentRedirect 308
BadRequest 400
Unauthorized 401
Forbidden 403
NotFound 404
MethodNotAllowed 405
RequestTimeout 408
Conflict 409
Gone 410
PreconditionFailed 412
ContentTooLarge 413
UnsupportedMediaType 415
UnprocessableContent 422
TooManyRequests 429
InternalServerError 500
NotImplemented 501
BadGateway 502
ServiceUnavailable 503
GatewayTimeout 504
  1. std-http.status.codeHttpStatus.code(&self) -> u16 returns the variant's numeric code.
  2. std-http.status.from-codeHttpStatus::from_code(code: u16) -> HttpStatus returns the named variant for a listed code, and Other(code) otherwise.
  3. std-http.status.is-successHttpStatus.is_success(&self) -> bool is true exactly for codes from 200 through 299.
  4. std-http.status.numeric-equalityTwo HttpStatus values are equal exactly when their numeric codes are equal, including named variants and Other.
  5. std-http.status.numeric-hashHttpStatus implements Hash by hashing its u16 code.
  6. std-http.status.debugHttpStatus implements structural Debug.

#Header Names

  1. std-http.header-name.declarationstd.http declares HeaderName with the variants and wire names below, and Other(name: string).
Variant Wire name
Accept accept
AcceptEncoding accept-encoding
AcceptLanguage accept-language
ContentDisposition content-disposition
ContentEncoding content-encoding
ContentLength content-length
ContentRange content-range
ContentType content-type
Range range
CacheControl cache-control
ETag etag
Expires expires
IfMatch if-match
IfModifiedSince if-modified-since
IfNoneMatch if-none-match
LastModified last-modified
Vary vary
Authorization authorization
Cookie cookie
RetryAfter retry-after
SetCookie set-cookie
WwwAuthenticate www-authenticate
AccessControlAllowCredentials access-control-allow-credentials
AccessControlAllowHeaders access-control-allow-headers
AccessControlAllowMethods access-control-allow-methods
AccessControlAllowOrigin access-control-allow-origin
AccessControlMaxAge access-control-max-age
AccessControlRequestHeaders access-control-request-headers
AccessControlRequestMethod access-control-request-method
Origin origin
ContentSecurityPolicy content-security-policy
StrictTransportSecurity strict-transport-security
XContentTypeOptions x-content-type-options
XFrameOptions x-frame-options
Allow allow
Forwarded forwarded
Host host
Location location
Referer referer
XForwardedFor x-forwarded-for
XForwardedHost x-forwarded-host
XForwardedProto x-forwarded-proto
Date date
Server server
UserAgent user-agent
XRequestId x-request-id
  1. std-http.header-name.wireHeaderName.wire_name(&self) -> string returns the listed wire name, or the Other name with ASCII uppercase letters lowercased.
  2. std-http.header-name.parseHeaderName implements Parse[Error = HeaderError].
  3. std-http.header-name.parse-knownParsing a listed wire name, ignoring ASCII case, returns its named variant.
  4. std-http.header-name.parse-otherParsing another valid field name returns Other with its ASCII letters lowercased.
  5. std-http.header-name.parse-tokenA valid field name is a nonempty sequence of ASCII letters, digits, or a punctuation character from !#$%&'*+-.^_`|~, as RFC 9110 §5.6.2 defines.
  6. std-http.header-name.parse-invalidParsing any other text returns HeaderError.InvalidName holding the original text.
  7. std-http.header-name.wire-equalityTwo HeaderName values are equal exactly when their wire names are equal.
  8. std-http.header-name.wire-hashHeaderName implements Hash by hashing its wire name as a string.
  9. std-http.header-name.debugHeaderName implements structural Debug.
  10. std-http.header-error.declarationHeaderError is an error enum with InvalidName(name: string) and InvalidValue(name: string).
  11. std-http.header-error.displayThose variants display invalid header name NAME and invalid value for header NAME, respectively.
  12. std-http.header-error.traitsHeaderError implements Eq and Debug.

#Header Collections

  1. std-http.header-collection.declarationHeaders is an ordered collection of (HeaderName, string) pairs with private storage.
  2. std-http.header-collection.newHeaders::new() -> Headers returns an empty collection.
  3. std-http.header-collection.from-pairsFrom[List[(string, string)]] retains every pair in input order, parsing valid names as HeaderName and retaining invalid names as Other for wire validation.
  4. std-http.header-collection.from-mapFrom[Map[string, string]] converts each map entry to one header pair.
  5. std-http.header-collection.getget(&self, name: HeaderName) -> string? returns matching values joined by ", " in their stored order, or None when absent, except for SetCookie.
  6. std-http.header-collection.set-cookie-firstget(SetCookie) returns the first raw Set-Cookie value without joining lines; response_cookies() reads every line.
  7. std-http.header-collection.setset(&mut self, name: HeaderName, value: string) -> void replaces every matching pair with one pair holding value.
  8. std-http.header-collection.removeremove(&mut self, name: HeaderName) -> void removes every matching pair.
  9. std-http.header-collection.entriesentries(&self) -> List[(HeaderName, string)] returns all stored pairs in order.
  10. std-http.header-collection.traitsHeaders implements structural Eq and Debug.
  11. std-http.request-cookies.readrequest_cookies(&self) -> List[(string, string)] returns name/value pairs from Cookie fields in received order, retaining duplicate names.
  12. std-http.request-cookies.firstrequest_cookie(&self, name: string) -> string? returns the first value for that case-sensitive cookie name, or None when absent.
  13. std-http.request-cookies.setset_request_cookie(&mut self, name: string, value: string) -> void replaces that name's cookies while retaining other cookies in one Cookie header.

#Response Cookies

  1. std-http.cookie.same-siteSameSite has the variants Strict, Lax and None, and implements Eq and Debug.
  2. std-http.cookie.declarationCookie has public fields name: string, value: string, path: string?, domain: string?, max_age: Duration?, expires: Timestamp?, secure: bool, http_only: bool and same_site: SameSite.
  3. std-http.cookie.defaultsA constructed cookie defaults to path = Some("/"), absent domain, max-age and expires, secure = true, http_only = true and same_site = Lax.
  4. std-http.cookie.traitsCookie implements structural Eq and Debug.
  5. std-http.cookie.expiredCookie::expired(name: string) -> Cookie returns that name with an empty value, max_age = Some(Duration::seconds(0)) and the other outgoing defaults.
  6. std-http.response-cookies.readHeaders.response_cookies(&self) -> List[Cookie] parses every separate Set-Cookie line in stored order.
  7. std-http.response-cookies.incoming-defaultsAbsent Path, Domain, Max-Age and Expires parse as None, absent Secure and HttpOnly as false, and absent or unknown SameSite as Lax.
  8. std-http.response-cookies.crlfA Set-Cookie line containing CR or LF contributes no parsed cookie.
  9. std-http.response-cookies.unknown-attributesParsing ignores unrecognized cookie attributes.
  10. std-http.response-cookies.attributesRecognized cookie attributes are parsed according to RFC 6265 §5.2, ignoring ASCII case in attribute names.
  11. std-http.response-cookies.expiresExpires uses the cookie-date algorithm in RFC 6265 §5.1.1, with invalid dates ignored.
  12. std-http.response-cookies.same-siteSameSite parses Strict, Lax and None ignoring ASCII case.
  13. std-http.response-cookies.contextual-pathAn empty or non-slash Path parses as None, clearing any earlier Path; CookieJar later applies the request's default path.

#Writing Response Cookies

  1. std-http.response-cookies.setHeaders.set_response_cookie(&mut self, cookie: Cookie) -> void writes one Set-Cookie line, replacing earlier cookies with that case-sensitive name.
  2. std-http.response-cookies.set-unrelatedSetting a response cookie retains unrelated headers and cookies in their order.
  3. std-http.response-cookies.wire-pairA written cookie begins with its name and value joined by =, without encoding them.
  4. std-http.response-cookies.wire-optionalPresent Path, Domain, Max-Age and Expires fields become corresponding attributes; absent ones are omitted.
  5. std-http.response-cookies.max-age-ceilingMax-Age is the duration's whole seconds rounded up; a positive fraction yields at least 1, and only zero yields 0.
  6. std-http.response-cookies.expires-textExpires uses an RFC 6265 HTTP date in UTC, with English weekday and month names and whole-second precision.
  7. std-http.response-cookies.wire-flagsSecure and HttpOnly attributes are included exactly when their corresponding flags are true.
  8. std-http.response-cookies.wire-same-siteEvery written cookie includes SameSite with its Strict, Lax or None spelling.
  9. std-http.request-cookies.set-componentsRequest cookie setters retain the supplied name and value without interpreting delimiters inside either component.

#Message Conversion Errors

  1. std-http.body-error.declarationBodyError is an error enum with Used, Json(error: JsonError) and Form(error: FormError).
  2. std-http.body-error.display-usedBodyError.Used displays the body was already read.
  3. std-http.body-error.display-jsonBodyError.Json(error) displays invalid JSON body: ERROR, with the JSON error's Display text.
  4. std-http.body-error.display-formBodyError.Form(error) displays invalid form body: ERROR, with the form error's Display text.
  5. std-http.body-error.traitsBodyError implements structural Eq and Debug.
  6. std-http.form-error.declarationFormError is an error enum with Unsupported(field: string), Missing(field: string) and Invalid(field: string, text: string).
  7. std-http.form-error.display-unsupportedFormError.Unsupported(field) displays field FIELD has no form encoding.
  8. std-http.form-error.display-missingFormError.Missing(field) displays missing field FIELD.
  9. std-http.form-error.display-invalidFormError.Invalid(field, text) displays field FIELD: cannot read TEXT.
  10. std-http.form-error.traitsFormError implements structural Eq and Debug.
  11. std-http.url-error.declarationUrlError is an error enum with Invalid(text: string) and RelativeWithoutBase(text: string).
  12. std-http.url-error.display-invalid-textUrlError.Invalid(text) normally displays invalid URL: TEXT.
  13. std-http.url-error.display-relativeUrlError.RelativeWithoutBase(text) displays relative URL without a base: TEXT.
  14. std-http.url-error.traitsUrlError implements structural Eq and Debug.
  15. std-http.url-error.into-httpFrom[UrlError] for HttpError maps either variant to HttpError.InvalidUrl, retaining its input text.
  16. std-http.url-error.display-user-infoFor user-info input, the Invalid error omits credentials from Display and recommends using the Authorization header.

#Parsed URLs

  1. std-http.parsed-url.declarationstd.http.Url is a value with these public fields and private encoding metadata.
Field Type Meaning
scheme string HTTP or HTTPS scheme
host string ASCII authority host, including IP-literal brackets
port u16? Explicit port when present
path string Percent-decoded path
query List[(string, string)] Decoded query pairs in order
fragment string? Decoded fragment when present
  1. std-http.parsed-url.parseUrl::parse(text: string) -> Result[Url, UrlError] parses an absolute HTTP or HTTPS URL satisfying the ASCII-host rule in URLs.
  2. std-http.parsed-url.relativeInput without a scheme returns UrlError.RelativeWithoutBase with the original text.
  3. std-http.parsed-url.invalidMalformed absolute input, unsupported schemes, non-ASCII hosts or invalid ports return UrlError.Invalid with the original text.
  4. std-http.parsed-url.user-infoAn authority containing user-info is rejected as UrlError.Invalid.
  5. std-http.parsed-url.controlsA literal ASCII control character, space or DEL in the input makes the URL invalid.
  6. std-http.parsed-url.scheme-host-caseParsing lowercases ASCII letters in the scheme and host.
  7. std-http.parsed-url.portA nonempty explicit port must be decimal text within the u16 range; an absent or empty port becomes None.
  8. std-http.parsed-url.pathParsing percent-decodes the path as UTF-8 without interpreting encoded / or ? as component separators.
  9. std-http.parsed-url.queryParsing splits nonempty query parts at & and their first =, retaining pair order and repeated names.
  10. std-http.parsed-url.query-empty-valueA query part without = has an empty value.
  11. std-http.parsed-url.query-decodeQuery names and values percent-decode UTF-8 and interpret + as space.
  12. std-http.parsed-url.fragmentParsing percent-decodes the fragment as UTF-8, retaining an explicit empty fragment as Some("").
  13. std-http.parsed-url.invalid-escapeInvalid percent escapes or invalid decoded UTF-8 in the path, query or fragment make the input invalid.
  14. std-http.parsed-url.raw-pathDisplay retains the private escaped path while decoding it gives the current public path.
  15. std-http.parsed-url.changed-pathOtherwise Display percent-encodes the public path as UTF-8, preserving RFC 3986 path characters and escaping literal % and ?.
  16. std-http.parsed-url.raw-path-unicodeLiteral non-ASCII path bytes are percent-encoded without changing existing valid escapes.
  17. std-http.parsed-url.display-queryDisplay writes nonempty query pairs as encoded name=value parts joined by &, encoding each component's UTF-8 bytes separately.
  18. std-http.parsed-url.display-fragmentDisplay writes a present fragment after #, percent-encoding its UTF-8 bytes while retaining permitted fragment characters.
  19. std-http.parsed-url.empty-queryParsing an explicit empty query retains a private presence bit, so an empty public list can still display a trailing ?.
  20. std-http.parsed-url.nonempty-queryA nonempty public query list overrides that presence bit in Display.
  21. std-http.parsed-url.paramparam(&self, name: string) -> string? returns the first value whose name equals name, or None when absent.
  22. std-http.parsed-url.add-paramadd_param[T < Display](&mut self, name: string, value: T) -> void appends the name and the value's Display text as decoded data.
  23. std-http.parsed-url.add-param-empty-queryadd_param clears the earlier empty-query presence bit.
  24. std-http.parsed-url.resolveresolve(&self, reference: string) -> Result[Url, UrlError] resolves a reference by RFC 3986 section 5, preserving encoded path separators and query presence.
  25. std-http.parsed-url.traitsUrl implements Display, structural Eq and Debug.

#Requests And Responses

Requests and responses use parsed fields and a shared read-once body:

pub enum Method:
    Get
    Head
    Post
    Put
    Patch
    Delete
    Options
    Other(name: string)

pub struct Request:
    pub method: Method = .Get
    pub url: Url
    pub headers: Headers = Headers::new()
    pub body: Body = Body::empty()

pub struct Response:
    pub status: HttpStatus = .Ok
    pub headers: Headers = Headers::new()
    pub body: Body = Body::empty()
    pub url: Url? = .None
  1. std-http.types.parsed-messagesstd.http declares Method, Request, and Response as above.
  2. std-http.types.traitsMethod, Request, and Response implement Eq and Debug.
  3. std-http.request.message-defaultsA request supplying only url has method Get, empty headers, and a fresh unread empty body.
  4. std-http.request.default-timeoutA provider that sends requests to a host has a default timeout of 30 seconds.
  5. std-http.headers.typed-fieldsRequest and response headers are Headers values.
  6. std-http.headers.caseHeader names compare ignoring ASCII case, so a provider may change the case of a response header's name.
  7. std-http.headers.combinedA provider may combine repeated non-cookie response fields according to RFC 9110 §5.3.
  8. std-http.headers.cookies-separateA provider must deliver each Set-Cookie value as a separate pair, in received order.
  9. std-http.headers.values-orderRepeated values of one response field retain their received order.
  10. std-http.headers.platform-orderResponse headers of different names may follow the platform's exposed order.
  11. std-http.response.header-typedResponse.header(&self, name: HeaderName) -> string? delegates to headers.get(name).
  12. std-http.response.text-read-onceResponse.text(&self) -> Result[string, BodyError] delegates to body.text().
  13. std-http.response.optional-urlA received response has url = Some(final_url), while a response constructed without a URL has None.
  14. std-http.method.eq.variant-payloadTwo Method values compare equal only when their variants and corresponding payloads are equal.
  15. std-http.types.message-copyCopying a request or response copies its fields, sharing only the body's private consumption state.
  16. std-http.response.message-defaultsAn omitted response status is Ok, with empty headers, a fresh unread empty body, and no URL.
  17. std-http.request.read-bodyRequest bytes, text, json[T], and form[T] borrow &self and delegate to the corresponding body reader.
  18. std-http.request.read-queryRequest.query_as[T < Deserialize](&self) -> Result[T, FormError] delegates to its URL's query reader.
  19. std-http.request.read-headerRequest.header(&self, name: HeaderName) -> string? delegates to headers.get(name).
  20. std-http.request.read-cookieRequest.cookie(&self, name: string) -> string? delegates to headers.request_cookie(name).
  21. std-http.response.read-bytesResponse.bytes(&self) -> Result[List[u8], BodyError] delegates to body.bytes().
  22. std-http.response.read-cookiesResponse.cookies(&self) -> List[Cookie] delegates to headers.response_cookies().

#Request Builder

Message writers return changed values for chaining; readers borrow locally.

  1. std-http.request.new-valueRequest::new(method: Method, url: Url) -> Request returns a value with those fields and the other defaults.
  2. std-http.request.get-valueRequest::get(url: Url) -> Request returns Request::new(.Get, url).
  3. std-http.request.post-valueRequest::post(url: Url) -> Request returns Request::new(.Post, url).
  4. std-http.message.writer-valueEvery set_* message writer takes mut self and returns the changed message value.
  5. std-http.message.writer-detachReplacing a message's body creates a fresh unread body without changing copies of the earlier message.
  6. std-http.message.set-jsonRequest and response set_json[T < Serialize] replace the body with UTF-8 bytes from std.json.encode(value).
  7. std-http.message.json-onceset_json serializes its value once.
  8. std-http.message.json-content-typeset_json replaces Content-Type with application/json.
  9. std-http.message.set-textRequest and response set_text(text: string) replace the body with the text's UTF-8 bytes.
  10. std-http.message.text-content-typeset_text replaces Content-Type with text/plain; charset=utf-8.
  11. std-http.message.set-bytesRequest and response set_bytes(bytes: List[u8]) replace the body without changing headers.
  12. std-http.message.set-headerRequest and response set_header(name: HeaderName, value: string) delegate to headers.set.
  13. std-http.request.set-formRequest.set_form[T < Serialize] prepares the body using Typed Query And Form Values.
  14. std-http.request.form-escapingForm names and values encode UTF-8 bytes, keeping only ASCII letters, digits, *, -, ., and _ unchanged.
  15. std-http.request.form-spaceForm encoding writes a space as + and each remaining escaped byte as % followed by two uppercase hexadecimal digits.
  16. std-http.request.form-pairsForm encoding joins each name and value with = and each pair with &.
  17. std-http.request.form-content-type-replaceset_form replaces Content-Type with application/x-www-form-urlencoded.
  18. std-http.request.form-preparation-errorA failed set_form retains its FormError in the new body instead of panicking.
  19. std-http.request.form-error-readReading or sending that body returns BodyError.Form(error) through the corresponding result type.
  20. std-http.request.set-queryRequest.set_query_as[T < Serialize] delegates to url.set_query_as.
  21. std-http.request.set-cookieRequest.set_cookie(name: string, value: string) delegates to headers.set_request_cookie.
  22. std-http.response.status-constructorResponse::status(code: HttpStatus) -> Response supplies that status and the other defaults.
  23. std-http.response.set-cookieResponse.set_cookie(cookie: Cookie) delegates to headers.set_response_cookie.
  24. std-http.request.send-borrowedRequest.send!(&self, redirects: usize = 10) -> Result[Response, HttpError] $ Http validates the request, then calls its covering provider.
  25. std-http.request.send-query-errorA pending query encoding failure returns HttpError.Body(BodyError.Form(error)) before calling the provider.
  26. std-http.request.send-body-claimSending claims the body once, so another send or reader through any message copy returns BodyError.Used.
  27. std-http.request.send-validation-firstHeader and URL validation failures leave the body unread.
  28. std-http.send.zero-redirects-supportedWith redirects zero, Node and Wasmtime providers return the first response without following or validating its Location.

Why. Local borrows permit reading messages without copying fields. Copies share consumption, while a value writer detaches its replacement body. Typed query and form values use one codec and one error vocabulary.

The provider's default timeout still applies; std.task.apply! with std.task.timeout can impose a shorter bound around a send.

#Client

A client owns a serializable cookie jar and follows redirects through its covering provider.

  1. std-http.client.jar-valuestd.http exports the value struct Client.
  2. std-http.client.jar-fieldClient has the public field jar: CookieJar, defaulting to CookieJar::new().
  3. std-http.client.new-valueClient::new() -> Client returns a value with an empty jar.
  4. std-http.client.with-jarClient::with_jar(jar: CookieJar) -> Client returns a value containing that jar.
  5. std-http.client.send-borrowed-clockClient.send!(&mut self, request: &Request, redirects: usize = 10) -> Result[Response, HttpError] $ Http + Clock sends through Http.
  6. std-http.client.clock-onceA send passing validation reads Clock.now() once, using that timestamp for every cookie lookup and response store across redirects.
  7. std-http.client.body-claimSending claims the supplied request's body once and prepares an independent body for each provider request.
  8. std-http.client.fields-retainedSending leaves request fields unchanged, while its shared body becomes used.
  9. std-http.client.unchecked-final-responseSending returns the final response without checking its status, or propagates a provider error unchanged.
  10. std-http.client.policy-timeoutClient has no timeout setter; a caller applies std.task.timeout around its Result-returning send through std.task.apply!.
  11. std-http.client.jar-fillBefore each request, sending fills an absent Cookie header with eligible jar pairs joined by ; in jar order.
  12. std-http.client.jar-storeEvery response updates the jar before following another redirect, including responses returned without following.
  13. std-http.client.cookies.explicitAn explicit Cookie header takes precedence over the jar for that request without replacing stored cookies.
  14. std-http.client.redirects.zero-providerThe client passes redirects = 0 to each provider call and follows redirects itself.
  15. std-http.client.redirects.statusResponses with status 301, 302, 303, 307 or 308 and a Location header are redirects.
  16. std-http.client.redirects.zero-argumentA zero redirects argument returns the first response without following its Location.
  17. std-http.client.redirects.methodA 303 changes the method to Get; a 301 or 302 changes Post to Get and preserves other methods.
  18. std-http.client.redirects.bodyChanging a method to Get removes its body and its Content-Type, Content-Length and Transfer-Encoding headers.
  19. std-http.client.redirects.resolveA redirect's Location resolves against the current request URL as an RFC 3986 reference.
  20. std-http.client.redirects.cross-hostA redirect to another hostname removes the previous request's Authorization and Cookie headers before applying the target host's jar.
  21. std-http.client.redirects.argument-limitExceeding a positive redirect argument returns HttpError.TooManyRedirects(url), naming the next target without sending to it.
  22. std-http.client.redirects.no-locationA response without Location is returned without following another request.
  23. std-http.client.redirects.parsed-response-urlThe returned response's url is Some(final_request_url).
  24. std-http.client.no-default-buildersClient has no base-URL, default-header, or cookie-lookup builder methods; callers use Url, message headers, and jar directly.

Why. Reading every response retains cookies set during redirects. Plain Request.send! needs only Http; a cookie-aware client also declares clock authority.

CookieJar is plain data; each time-dependent operation takes its timestamp explicitly, following RFC 6265 sections 5.1–5.4.

  1. std-http.jar.declarationCookieJar has private storage and implements Debug, Eq, Serialize, and Deserialize.
  2. std-http.jar.newCookieJar::new() -> CookieJar returns an empty jar.
  3. std-http.jar.explicit-timeA jar retains no clock and reads no implicit time.
  4. std-http.jar.cookies-forcookies_for(&self, url: &Url, now: Timestamp) -> List[(string, string)] returns the name/value pairs eligible at that time.
  5. std-http.jar.storestore(&mut self, url: &Url, response: &Response, now: Timestamp) -> void processes response cookies in header order.
  6. std-http.jar.clear-expiredclear_expired(&mut self, now: Timestamp) -> void removes cookies whose expiry is at or before now.
  7. std-http.jar.host-onlyWithout a Domain attribute, a cookie belongs only to the response request's ASCII-lowercase hostname, independently of its port.
  8. std-http.jar.domain-matchA Domain attribute must match the response hostname exactly or as a suffix preceded by a dot.
  9. std-http.jar.domain-ipAn IP address matches a Domain attribute only exactly.
  10. std-http.jar.domain-rejectA cookie whose Domain does not match the response request's hostname is ignored.
  11. std-http.jar.domain-sendA domain cookie is eligible for its domain and matching subdomains; a host-only cookie requires an exact hostname.
  12. std-http.jar.default-pathAn absent or invalid Path uses the escaped request URI's directory, or / when no directory exists.
  13. std-http.jar.path-matchA request path matches exactly, or extends a cookie path ending with / or followed by /.
  14. std-http.jar.escaped-pathPath matching uses escaped URI paths, so an encoded slash does not create a directory boundary.
  15. std-http.jar.secureA Secure cookie is eligible only for https requests.
  16. std-http.jar.http-onlyHttpOnly does not exclude cookies from these HTTP request operations.
  17. std-http.jar.expiry-max-ageA valid Max-Age sets expiry relative to now, taking precedence over Expires.
  18. std-http.jar.expiry-expiresWithout Max-Age, a valid Expires sets absolute expiry; without either, the cookie has no expiry.
  19. std-http.jar.expired-ineligibleA cookie expiring at or before now is ineligible and removes an existing cookie with the same name, domain, and path.
  20. std-http.jar.identityA cookie replaces the stored cookie with the same name, domain, and path, preserving its position among equally long paths.
  21. std-http.jar.orderEligible cookies are ordered by decreasing path length, then original creation order.
  22. std-http.jar.duplicate-namesDifferent scopes may supply repeated cookie names in the outgoing pairs.
  23. std-http.jar.serde-stateSerde retains cookie values, scopes, Secure and HttpOnly flags, and absolute expiry times without retaining a clock.
  24. std-http.jar.serde-expiry-millisecondsPrivate expiry storage serializes absolute Unix milliseconds as an optional i64, without a calendar-year cap.
  25. std-http.jar.expiry-beyond-rangeA positive Max-Age whose deadline exceeds every representable timestamp cannot expire at a representable timestamp.
  26. std-http.jar.empty-request-pathAn empty request URI path is treated as / for cookie matching.

#URLs

A request URL is an RFC 3986 URL, so its host is plain ASCII:

use std.http.{Http, HttpError, Response, get}

fn shop!() -> Result[Response, HttpError] $ Http:
    _ := get!("https://bücher.example/")         # .Err(HttpError.InvalidUrl(url))
    get!("https://xn--bcher-kva.example/")       # the same host in punycode
  1. std-http.url.validA request URL is valid when it is an absolute http or https URL by RFC 3986, with an ASCII host.
  2. std-http.url.non-asciiA URL whose host holds a non-ASCII character is invalid, so send! returns .Err(HttpError.InvalidUrl(url)) for it.
  3. std-http.url.grantA grant matches a request by its URL host, in lowercase ASCII.
  4. std-http.url.no-normalizeA provider maps the host no other way: no IDNA mapping and no lookalike normalization.

Why. An ASCII-only host leaves one spelling for each host, so a grant and the request it covers always compare the same text.

#Http Errors

Every failure of a request is one error enum:

pub enum HttpError:
    NotGranted(host: string)
    InvalidUrl(url: string)
    Dns(host: string)
    Connect(message: string)
    Tls(message: string)
    Timeout
    TooManyRedirects(url: string)
    Other(message: string)
    Status(response: Response)
    InvalidHeader(name: string)
    Body(error: BodyError)
  1. std-http.error.decl-message-errorsstd.http declares HttpError as above. Code imports it, as in use std.http.HttpError.
  2. std-http.error.traitsHttpError implements Eq, Debug, Display, and std.error.Error.
Rule Variant Means
std-http.error.not-grantedNot granted NotGranted(host) the program's capability grant does not cover the request's host, by Partial Deny
std-http.error.invalid-url-targetInvalid URL InvalidUrl(url) Invalid HTTP target, including a URL rejected by Url::parse
std-http.error.dnsDNS Dns(host) the host name did not resolve
std-http.error.connectConnect Connect(message) no connection was made, or it failed before the whole response arrived
std-http.error.tlsTLS Tls(message) the TLS handshake or a certificate check failed
std-http.error.timeoutTimeout Timeout the request took longer than its timeout
std-http.error.redirect-argument-limitRedirects TooManyRedirects(url) the response exceeded a positive send! redirect limit; url is the last target
std-http.error.otherOther Other(message) any other failure, with the provider's message
std-http.error.status-response-valueStatus Status(response) Response.ok found a status from 400 to 599, retaining the whole response
  1. std-http.error.not-granted.plainThe Display text of NotGranted(host) is http access to HOST is not granted, with the host for HOST.
  2. std-http.error.status.safe-response-textStatus(response) displays HTTP status STATUS for URL, with the response status and its URL before any query or fragment.
  3. std-http.error.status.no-response-secretsThe status error's Display text contains no response body or header values.
  4. std-http.error.transient-borrowedHttpError.is_transient(&self) -> bool returns true for Timeout and Connect errors.
  5. std-http.error.transient-status-codesA Status error is transient for 408, 429, 500, 502, 503, and 504.
  6. std-http.error.permanent-kindsis_transient returns false for every other error.
  7. std-http.error.invalid-url-displayInvalidUrl(url) displays invalid URL: URL unless the input contains user-info.
  8. std-http.error.invalid-url-user-infoUser-info errors instead omit credentials from Display and recommend the Authorization header, including errors converted from UrlError.
  9. std-http.error.invalid-header-displayInvalidHeader(name) displays invalid header NAME, using its header name.
  10. std-http.error.body-displayBody(error) displays the body error's Display text.
  11. std-http.error.from-bodyFrom[BodyError] for HttpError wraps its input in HttpError.Body.

#Sending

Http sends one request and completes with the whole response, with status and body available separately:

use std.http.{Http, HttpError, get}

fn latest_release!(repo: string) -> Result[string, HttpError] $ Http:
    response := get!("https://api.github.com/repos/${repo}/releases/latest")?
    if response.status != .Ok:
        return .Err(.Other("status ${response.status.code()}"))
    .Ok(response.text()?)
  1. std-http.trait.decl-borrowed-requeststd.http declares Http with fn send!(&mut self, request: &Request, redirects: usize) -> Result[Response, HttpError]. Code imports it, as in use std.http.Http.

  2. std-http.send.borrowed-requestsend!(request, redirects) sends the borrowed request and completes with the whole response.

  3. std-http.send.statusEvery status, 404 and 500 included, is an .Ok response, and the caller reads it from status.

  4. std-http.send.redirect-argumentsend! follows at most its redirects argument redirects.

  5. std-http.send.provider-timeoutA host request exceeding the provider's default timeout completes with .Err(HttpError.Timeout).

  6. std-http.send.timeout.wholeA timeout covers the whole request, every redirect included.

  7. std-http.send.cancelCancelling the suspension of send! aborts the request, by req.cancel.external-abort.

  8. std-http.send.cancel.closeAborting a request closes its connection.

  9. std-http.send.mut-ref.reference-valuessend! takes &mut self, so Http is a mutable requirement trait, $.use(Http) is a &mut dyn Http by req.ref.use.reference-values, and a provider may record what it sends.

  10. std-http.bodies.read-once-bufferA body buffers a whole byte sequence behind read-once state; std.http has no streaming transport body.

  11. std-http.helper.get.parsed-responseget!(url: string) -> Result[Response, HttpError] $ Http parses the URL, sends a GET, and checks ok().

  12. std-http.helper.get-postThe free request helpers are get! and post!; free send!, get_text!, and post_json! are not exported.

  13. std-http.helper.post-json-checkedpost![T < Serialize](url: string, value: T) -> Result[Response, HttpError] $ Http parses the URL, sends JSON, and checks ok().

  14. std-http.send.header-validationSending rejects invalid header names and CR or LF in header values as HttpError.InvalidHeader(name) before transport.

  15. std-http.send.cookie-validationSending rejects invalid typed cookie components as HttpError.InvalidHeader(name) before transport.

  16. std-http.send.mutated-urlSending revalidates a URL's public fields, rejecting invalid HTTP targets as HttpError.InvalidUrl(url).

Why. A status is data about a request that worked, as with fetch and Go, and as a non-zero exit status is for Process. Header pairs keep a repeated header, which a map would lose.

#Status And JSON

Response.ok() checks 400–599 while preserving the whole response in the error; the free get! calls it automatically:

use std.error.Error
use std.http.{Http, HttpError, Request, Url, get}
use std.serde.{Deserialize, Serialize}

@derive(Serialize)
struct NewUser:
    name: string

@derive(Deserialize)
struct Created:
    id: i64

fn notes!() -> Result[string, HttpError] $ Http:
    .Ok(get!("https://example.com/notes.txt")?.text()?)   # a 404 is .Err(HttpError.Status(response))

fn create!(name: string) -> Result[Created, *dyn Error] $ Http:
    request := Request::post(Url::parse("https://example.com/users")?).set_json(NewUser { name: name })
    .Ok(request.send!()?.ok()?.json::[Created]()?)              # a bad body is a BodyError.Json
  1. std-http.response.ok.borrowed-retainedResponse.ok(&self) -> Result[Response, HttpError] copies the whole response into Err(Status(response)) for codes 400–599, or Ok(response) otherwise.
  2. std-http.response.json-read-onceResponse.json[T < Deserialize](&self) -> Result[T, BodyError] delegates to body.json[T] without checking status.
  3. std-http.response.body-errors-propagateA function that sends and decodes propagates both errors with ? into an error type covering both, such as *dyn Error (expr.try.convert).

Why. Builder and provider send! calls keep every status available (std-http.send.status). Response.ok() checks explicitly, as reqwest's error_for_status does; the free get! checks by default. Retaining the response lets a caller decode an API's error body.

#Scripted Http

ScriptedHttp is the deterministic Http provider. It consumes replies queued for each URL, and records every request:

use std.http.{Http, Response, ScriptedHttp, get}
fn status!(url: string) -> u16 $ Http:
    match get!(url):
        .Ok(response) => response.status.code()
        .Err(_) => 0

tests:
    use std.testing.assert_equal

    it("reads the scripted status"):
        http := ScriptedHttp::new()
        http.reply("https://example.com/", .Ok(Response::status(.NoContent)))
        $.with(Http=http):
            assert_equal(status!("https://example.com/"), 204, reason="scripted")
        assert_equal(http.received().len(), 1, reason="one request")
  1. std-http.scripted.declstd.http declares ScriptedHttp, which implements Http and Debug, with private fields. Code imports it, as in use std.http.ScriptedHttp.
  2. std-http.scripted.new-emptyScriptedHttp::new() -> *mut ScriptedHttp returns a new provider with no replies and no received requests.
  3. std-http.scripted.reply-capturedreply(&mut self, url: string, result: Result[Response, HttpError]) -> void appends a result for that URL.
  4. std-http.scripted.key-url-displayReplies match the complete request URL Display text exactly, whatever the request method.
  5. std-http.scripted.queue-orderSends consume queued results in insertion order, independently for each URL.
  6. std-http.scripted.repeat-lastAfter a URL's queued results have been consumed, further sends repeat its last result.
  7. std-http.scripted.error-valuesA queued error retains its value, except that Status(response) gets a fresh response body for each delivery.
  8. std-http.scripted.raw-statusEvery status of a queued successful response is returned as .Ok, without a status check.
  9. std-http.scripted.missing-replyA URL without a reply returns .Err(HttpError.Connect("ScriptedHttp has no response for URL")), substituting its full URL.
  10. std-http.scripted.received-bodiesreceived(&self) -> List[Request] returns validated requests in order with independent unread body snapshots, including unanswered requests.
  11. std-http.scripted.no-hostA ScriptedHttp opens no connection.
  12. std-http.scripted.reply-body-snapshotRegistering a response copies its body payload into the reply queue without marking the caller's body used.
  13. std-http.scripted.delivery-body-freshEvery scripted response delivery, including a repeated last reply, receives a fresh unread Body.
  14. std-http.scripted.reply-used-panicRegistering a used body panics with ScriptedHttp.reply: the response body was already read. Panic: explicit-panic.
  15. std-http.scripted.status-body-snapshotThose registration and delivery rules also apply to the response held by a queued HttpError.Status.

Why. An unknown URL is an error that names it, not a 404. So a route that a test forgot never hides behind a valid status.

#Playground

The playground runs a program in a browser. Its Http provider calls the browser's HTTP facilities:

use std.http.{Http, HttpError, get}

fn ping!(url: string) -> string $ Http:
    match get!(url):
        .Ok(response) => "status ${response.status.code()}"
        .Err(.NotGranted(host)) => "the playground grants no ${host}"  # another origin
        .Err(.Connect(_)) => "the browser refused the request"         # a network failure or CORS
        .Err(error) => "${error}"
  1. std-http.playground.originThe playground binds Http with a grant that covers only its own origin, as the browser's same-origin rule does.
  2. std-http.playground.other-originA request to another origin returns .Err(HttpError.NotGranted(host)).
  3. std-http.playground.refusedA request that the browser refuses, for a network failure or by CORS, returns .Err(HttpError.Connect(message)).
  4. std-http.playground.unboundThe playground binds no Process, Sys, or Net provider.
  5. std-http.playground.zero-redirects-errorA granted browser request with redirects: 0 returns .Err(HttpError.Other("the browser host cannot stop redirects")) before sending.

Why. A browser can't tell a CORS refusal from a network failure, so neither can the provider. The playground needs no grant table: the browser's own rule decides.

#Waiting In The Playground

A block_on call in the playground may have to wait for the host, as for a response to get!. How it waits depends on the browser:

Browser A block_on that waits on the host
has JavaScript Promise Integration waits for the host operation, as on any other host
lacks it completes an HTTP request as a synchronous same-origin request, and panics for any other host operation
  1. std-http.playground.block-on.jspiIn a browser with JavaScript Promise Integration, a block_on call in the playground waits for each host operation it needs.
  2. std-http.playground.block-on.syncIn a browser without it, an Http request that a block_on call waits on is made as a synchronous same-origin request.
  3. std-http.playground.block-on.otherIn such a browser, a block_on call that must wait on any other host operation panics, with a message that names the operation. Panic: host-contract.
use std.http.{Http, get}
use std.task.block_on

fn status_of(url: string) -> string $ Http:
    match block_on(get(url)):   # without Promise Integration: a synchronous request
        .Completed(.Ok(response)) => "status ${response.status.code()}"
        .Completed(.Err(error)) => "${error}"
        .Cancelled => "cancelled"
        .Abandoned => "cut short"

Why. A browser thread cannot block, so only Promise Integration lets Wasm wait for a Promise. Without it, a synchronous request is the one wait a browser still offers, and only for HTTP.

See also: Capability Grants, Process, Time.

#Timeout Conversion

  1. std-http.error.from-timed-outHttpError implements std.convert.From[std.task.TimedOut], converting every deadline error to .Timeout.