# `nupp.io.http.spi`
Shared interfaces for HTTP implementations.
The HTTP module selects during initialization. Provider functions
have no implicit receiver; Client and Response methods do. Request, Options,
Version, and Capabilities come from `nupp.io.http.messages`, URI from
`nupp.io.uri`, and Body is the canonical `nupp.io` Reader interface.
A Client owns its transport and outstanding work. A successful send returns an
affine Response owning the response resources and body reader. Preserve borrowing
of the Request during send and exclusive access during pump. Closing clients
and responses must release resources on success, error, and cancellation paths.
Implementers must not replace these affine contracts with unowned table aliases.
Capabilities describe actual behavior, including streaming, policy control, and
protocol observability. Unsupported options must follow the public HTTP API's
error behavior. The facade selects the implementation at require-time; clients
and resource methods retain direct provider operations.
## Types
### `Body` _interface_
```nupp
interface Body is nupp.Closeable ...
```
A forward-only byte source.
An interface rather than the concrete things that satisfy it. A buffer's
reader, an open file and an HTTP response body are all one of these, so
code written against the contract works over any of them without knowing
which it has.
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`read`](#nupp.io.http.spi.Body.read) | method | Reads up to count bytes. |
| [`readSpan`](#nupp.io.http.spi.Body.readSpan) | method | Reads directly into a checked writable span. |
| [`readInto`](#nupp.io.http.spi.Body.readInto) | method | Reads into a buffer. |
| [`transferTo`](#nupp.io.http.spi.Body.transferTo) | method | Writes everything left to a writer. |
#### `read` _method_
```nupp
read: function(exclusive self: Reader, count: integer): (string?, string?)
```
Reads up to `count` bytes.
An empty answer is the end.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Body` | this reader |
| `count` | `integer` | the most bytes to read |
##### Returns
| Type | Description |
| --- | --- |
| `string?` | the bytes, or nil when the reader is closed |
| `string?` | why it could not read, when unsuccessful |
##### Raises
| Type | Condition |
| --- | --- |
| `string` | when count is not a positive integer |
#### `readSpan` _method_
```nupp
readSpan: function(exclusive self: Reader, exclusive destination: span.Writable): (integer?, string?)
```
Reads directly into a checked writable span. A zero answer is the end.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Body` | this reader |
| `exclusive destination` | `span.Writable\` | the positive-sized range to fill |
##### Returns
| Type | Description |
| --- | --- |
| `integer?` | how many bytes were read, or nil when the reader is closed |
| `string?` | why it could not read, when unsuccessful |
#### `readInto` _method_
```nupp
readInto: function(
exclusive self: Reader,
exclusive destination: Buffer,
offset: integer?,
count: integer?
): (integer?, string?)
```
Reads into a buffer.
A zero answer is the end.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Body` | this reader |
| `exclusive destination` | `Buffer` | the buffer to write into |
| `offset` | `integer?` | where in the destination to start, or the beginning |
| `count` | `integer?` | the most bytes to read |
##### Returns
| Type | Description |
| --- | --- |
| `integer?` | how many bytes were read, or nil when the reader is closed |
| `string?` | why it could not read, when unsuccessful |
#### `transferTo` _method_
```nupp
transferTo: function(exclusive self: Reader, exclusive destination: Writer): (integer?, string?)
```
Writes everything left to a writer.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Body` | this reader |
| `exclusive destination` | `Writer` | the writer to fill |
##### Returns
| Type | Description |
| --- | --- |
| `integer?` | how many bytes moved, or nil on failure |
| `string?` | why it could not, when unsuccessful |
### `Client` _interface_
```nupp
interface Client is nupp.Closeable ...
```
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`pump`](#nupp.io.http.spi.Client.pump) | method | Services outstanding client work, waiting up to the given milliseconds for some of it to move, while holding... |
| [`pending`](#nupp.io.http.spi.Client.pending) | method | Returns the number of outstanding requests or transfers. |
| [`send`](#nupp.io.http.spi.Client.send) | method | Borrows the Request while sending and returns an owned Response or an error. |
#### `pump` _method_
```nupp
pump: function(exclusive self: Client, timeoutMs: integer?): nil
```
`@readonly`
Services outstanding client work, waiting up to the given milliseconds
for some of it to move, while holding exclusive client access.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Client` | |
| `timeoutMs` | `integer?` | |
##### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### `pending` _method_
```nupp
pending: function(self: Client): integer
```
`@readonly`
Returns the number of outstanding requests or transfers.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `Client` | |
##### Returns
| Type | Description |
| --- | --- |
| `integer` | |
#### `send` _method_
```nupp
send: function(self: Client, borrows request: Request): (Response?, Error?)
```
`@readonly`
Borrows the Request while sending and returns an owned Response or an error.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `Client` | |
| `borrows request` | `Request` | |
##### Returns
| Type | Description |
| --- | --- |
| `Response?` | |
| `Error?` | |
### `Provider` _interface_
```nupp
interface Provider ...
```
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`priority`](#nupp.io.http.spi.Provider.priority) | field | |
| [`newClient`](#nupp.io.http.spi.Provider.newClient) | method | Constructs an owned client with the requested transport policy. |
| [`capabilities`](#nupp.io.http.spi.Provider.capabilities) | method | Reports the guarantees this implementation actually provides. |
#### `priority` _field_
```nupp
priority: integer?
```
`@readonly`
#### `newClient` _method_
```nupp
newClient: function(options: Options?): Client
```
`@readonly`
Constructs an owned client with the requested transport policy.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `options` | `Options?` | |
##### Returns
| Type | Description |
| --- | --- |
| `Client` | |
#### `capabilities` _method_
```nupp
capabilities: function(): Capabilities
```
`@readonly`
Reports the guarantees this implementation actually provides.
##### Returns
| Type | Description |
| --- | --- |
| `Capabilities` | |
### `Response` _interface_
```nupp
interface Response is nupp.Closeable ...
```
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`status`](#nupp.io.http.spi.Response.status) | field | HTTP response status code. |
| [`version`](#nupp.io.http.spi.Response.version) | field | Observed protocol version, absent when the host cannot expose it. |
| [`url`](#nupp.io.http.spi.Response.url) | field | Canonical final response URI, including completed redirects. |
| [`body`](#nupp.io.http.spi.Response.body) | field | Owned response reader whose lifetime is governed by the response. |
| [`ok`](#nupp.io.http.spi.Response.ok) | method | Reports whether the status is in the successful 2xx range. |
| [`header`](#nupp.io.http.spi.Response.header) | method | Returns one header value by case-insensitive name, or nil if absent. |
| [`headerValues`](#nupp.io.http.spi.Response.headerValues) | method | Returns all values for a case-insensitive header name. |
| [`headers`](#nupp.io.http.spi.Response.headers) | method | Returns a fresh response header mapping keyed by lowercase name. |
#### `status` _field_
```nupp
status: integer
```
`@readonly`
HTTP response status code.
#### `version` _field_
```nupp
version: Version?
```
`@readonly`
Observed protocol version, absent when the host cannot expose it.
#### `url` _field_
```nupp
url: URI
```
`@readonly`
Canonical final response URI, including completed redirects.
#### `body` _field_
```nupp
body: Body
```
`@readonly`
Owned response reader whose lifetime is governed by the response.
#### `ok` _method_
```nupp
ok: function(self: Response): boolean
```
`@readonly`
Reports whether the status is in the successful 2xx range.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `Response` | |
##### Returns
| Type | Description |
| --- | --- |
| `boolean` | |
#### `header` _method_
```nupp
header: function(self: Response, name: string): string?
```
`@readonly`
Returns one header value by case-insensitive name, or nil if absent.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `Response` | |
| `name` | `string` | |
##### Returns
| Type | Description |
| --- | --- |
| `string?` | |
#### `headerValues` _method_
```nupp
headerValues: function(self: Response, name: string): {string}
```
`@readonly`
Returns all values for a case-insensitive header name.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `Response` | |
| `name` | `string` | |
##### Returns
| Type | Description |
| --- | --- |
| `{string}` | |
#### `headers` _method_
```nupp
headers: function(self: Response): {[string]: string}
```
`@readonly`
Returns a fresh response header mapping keyed by lowercase name.
Repeated values are joined with `, ` except `set-cookie`, whose first value
is returned; use `headerValues` to preserve every value.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `Response` | |
##### Returns
| Type | Description |
| --- | --- |
| `{\[string\]: string}` | |