# Calling

> UCall for Rust

A `Client` submits calls without driving the network, and polls to drive them and collect what finished.
It holds up to `connections` connections, reusing them across calls, and carries up to `pipelined_calls` calls on each.

```rust
use std::time::Duration;
use ucall::*;

let mut client = Client::new(ClientOptions { connections: 4, pipelined_calls: 16, ..Default::default() })?;
for index in 0..64u64 {
    client.submit(ClientCall::post("http://127.0.0.1:8545/infer", vec![index as u8]), index)?;
}
let mut completed: [Completed; 16] = Default::default();
let mut remaining = 64;
while remaining != 0 {
    for done in client.take_completed(&mut completed, Duration::from_secs(1))? {
        match &done.response {
            Ok(response) => println!("call {} answered {}", done.context, response.status_code().0),
            Err(error) => println!("call {} failed: {error}", done.context),
        }
        remaining -= 1;
    }
}

let response = client.get("http://127.0.0.1:8545/health")?;
let keep = response.header("cache-control").map(<[u8]>::to_vec);
drop(client);
println!("{:?} {:?}", response.body(), keep);
```

`submit` takes a `ClientCall`, which owns its URL, header lines, body and security until the call completes, and tags it with a `u64` context.
A call admitted once completes exactly once, and a client already holding `calls` unretrieved calls refuses more with `Status::Contended`.
`take_completed` drives every admitted call and fills a caller-provided buffer with finished ones, waiting up to its timeout for the first, and a zero timeout performs one nonblocking round.
`get`, `post`, `put`, `delete`, `patch` and `call` submit one call and poll until it finishes, keeping the completions of other calls they retrieve meanwhile for the next `take_completed`, which hands those out first.
`ClientCall::options` carries the call's own `ClientCallOptions`: a timeout in place of the client's, retries, redirect hops, security, and the client and server keys.

`ClientOptions::max_response_bytes` caps what a call buffers, failing it with `Status::Overflow` past the cap, and `ClientOptions::log` reports dials that fail every address, refused handshakes and refused redirects.
Host names resolve through the library's own stub resolver, driven by the client's polling, and connections race the answered addresses as Happy Eyeballs does.

Every received response is a `Response`, whatever its HTTP status, and only a failed exchange is an `Error`; `Response::fields` iterates its header fields as a request's does.
A `Response` owns its received bytes and is `Send`, so it stays readable after its `Client` is dropped and may move to another thread.
Dropping the client cancels the calls still in flight and discards completions not yet retrieved.

`use ucall::Client;`

`use ucall::ClientCall;`

`use ucall::ClientCallOptions;`

`use ucall::ClientOptions;`

`use ucall::Completed;`

`use ucall::Response;`

## Client

An HTTP client, created, submitted to, polled, and dropped on one thread.

```rust
pub struct Client
```

Allocations happen at construction, sized by the resolved call budget, and in header arrays
that grow to their largest call once; polling allocates nothing. Dropping it cancels the calls
in flight, while retrieved [`Response`](#Response)s stay readable.

```
use std::time::Duration;
use ucall::*;

let mut client = Client::new(ClientOptions { connections: 2, pipelined_calls: 4, ..Default::default() })?;
for index in 0..8u64 {
    client.submit(ClientCall::post(format!("http://{address}/echo"), vec![index as u8]), index)?;
}
let mut completed: [Completed; 8] = Default::default();
let mut answered = 0;
while answered &#x3C; 8 {
    for done in client.take_completed(&#x26;mut completed, Duration::from_secs(1))? {
        assert_eq!(done.response.as_ref().map(Response::body), Ok([done.context as u8].as_slice()));
        answered += 1;
    }
}

let response = client.post(format!("http://{address}/echo"), b"blocking".as_slice())?;
drop(client);
assert_eq!(response.body(), b"blocking");
```

## new

Starts a client; it holds no connection until the first call is polled.

```rust
pub fn new(options: ClientOptions<'_>) -> Result<Client>
```

## submit

Admits `request` without driving the network; its completion carries `context`.

```rust
pub fn submit(&mut self, request: ClientCall, context: u64) -> Result<()>
```

Success guarantees exactly one completion from [`Client::take_completed`](#take_completed); a full client
refuses with [`Status::Contended`](https://www.unum.cloud/docs/ucall/rust/errors.md#Contended) until completions are retrieved.

## take_completed

Drives every admitted call on this thread and fills the front of `completed` with finished ones, waiting up to `timeout` for the first; zero performs one nonblocking round.

```rust
pub fn take_completed<'completed>(
    &mut self,
    completed: &'completed mut [Completed],
    timeout: Duration,
) -> Result<&'completed mut [Completed]>
```

Answers the filled prefix, empty when nothing finished in time. Completions kept by
[`Client::call`](#call), [`Client::get`](#get) or [`Client::post`](#post) come first, without waiting.

## call

Submits `request` and polls until it finishes, keeping other calls' completions for the next [`Client::take_completed`](#take_completed).

```rust
pub fn call(&mut self, request: ClientCall) -> Result<Response>
```

The response arrives whatever its HTTP status; a failed exchange answers its [`Error`](https://www.unum.cloud/docs/ucall/rust/errors.md#Error).

## get

Fetches `url` and polls until the response arrives.

```rust
pub fn get(&mut self, url: impl Into<Cow<'static, str>>) -> Result<Response>
```

## post

Posts [`body`](#body) to `url` and polls until the response arrives.

```rust
pub fn post(
    &mut self,
    url: impl Into<Cow<'static, str>>,
    body: impl Into<Cow<'static, [u8]>>,
) -> Result<Response>
```

## put

Puts [`body`](#body) at `url` and polls until the response arrives.

```rust
pub fn put(
    &mut self,
    url: impl Into<Cow<'static, str>>,
    body: impl Into<Cow<'static, [u8]>>,
) -> Result<Response>
```

## delete

Deletes `url` and polls until the response arrives.

```rust
pub fn delete(&mut self, url: impl Into<Cow<'static, str>>) -> Result<Response>
```

## patch

Patches `url` with [`body`](#body) and polls until the response arrives.

```rust
pub fn patch(
    &mut self,
    url: impl Into<Cow<'static, str>>,
    body: impl Into<Cow<'static, [u8]>>,
) -> Result<Response>
```

## ClientCall

One HTTP call, owning everything the library reads until the call completes.

```rust
pub struct ClientCall
```

- `verb` `Verb`: The HTTP method.
- `url` `Cow<'static, str>`: The full URL, `http://` or `http+noise://`.
- `headers` `Vec<Cow<'static, [u8]>>`: Full header lines like "accept: text/plain"; Host, Content-Length and User-Agent are
generated.
- `body` `Cow<'static, [u8]>`: The request body; empty sends no payload.
- `options` `ClientCallOptions`: The call's own time budget, retries, redirects and security.

Implements: `Clone`, [`Debug`](https://www.unum.cloud/docs/ucall/rust/serving.md#Debug)

## get

A GET of `url` with no headers.

```rust
pub fn get(url: impl Into<Cow<'static, str>>) -> Self
```

## post

A POST of [`body`](#body) to `url` with no headers.

```rust
pub fn post(url: impl Into<Cow<'static, str>>, body: impl Into<Cow<'static, [u8]>>) -> Self
```

## put

A PUT of [`body`](#body) to `url` with no headers.

```rust
pub fn put(url: impl Into<Cow<'static, str>>, body: impl Into<Cow<'static, [u8]>>) -> Self
```

## delete

A DELETE of `url` with no headers.

```rust
pub fn delete(url: impl Into<Cow<'static, str>>) -> Self
```

## patch

A PATCH of [`body`](#body) to `url` with no headers.

```rust
pub fn patch(url: impl Into<Cow<'static, str>>, body: impl Into<Cow<'static, [u8]>>) -> Self
```

## ClientCallOptions

One call's time budget, retries, redirects and security, each zero field taking its default.

```rust
pub struct ClientCallOptions
```

- `timeout` `Duration`: Time budget including queueing and redirects; zero takes the client's `call_timeout`.
- `max_retries` `usize`: Connection attempts made again before sending the request; zero means 3.
- `max_redirects` `usize`: Redirect hops followed before failing; zero means 5.
- `security` `Security`: The transport security; an `http+noise://` URL implies Noise XX.
- `noise_private_key` `Option<NoiseKey>`: The private key this call authenticates with in place of the client's own.
- `noise_peer_key` `Option<NoiseKey>`: The server's static key, which Noise IK and KK need in advance.

Implements: `Clone`, `Copy`, `StructuralPartialEq`, `PartialEq`, `Eq`, [`Debug`](https://www.unum.cloud/docs/ucall/rust/serving.md#Debug), `Default`

## ClientOptions

What a client may hold open at once, how long it waits, and who it is under Noise.

```rust
pub struct ClientOptions<'server>
```

[`connections`](https://www.unum.cloud/docs/ucall/rust/serving.md#connections) is required; every other zero field takes its documented default.

- `connections` `usize`: Connections open at once; zero fails with [`Status::InvalidArgument`](https://www.unum.cloud/docs/ucall/rust/errors.md#InvalidArgument).
- `pipelined_calls` `usize`: Calls one connection may carry at once; zero means one.
- `calls` `usize`: Calls admitted at once, unretrieved completions included; zero means [`connections`](https://www.unum.cloud/docs/ucall/rust/serving.md#connections) times
`pipelined_calls`.
- `connect_timeout` `Duration`: Time budget for establishing a connection; zero means 5 seconds.
- `call_timeout` `Duration`: Time budget for a full exchange, including queueing and redirects; zero means 30 seconds.
- `noise_private_key` `Option<NoiseKey>`: The client identity for Noise handshakes.
- `peer_authorizer` `Option<Arc<PeerAuthorizer>>`: Vets each server's authenticated static key; required for Noise XX calls.
- `backend` `Backend`: The requested I/O backend.
- `cpu_class` `Option<CPUClass>`: The CPU class the client confines itself to; [`None`](https://www.unum.cloud/docs/ucall/rust/noise-keys.md#None) takes what the CPU offers.
- `log` `Option<Log>`: Where log lines go; [`None`](https://www.unum.cloud/docs/ucall/rust/noise-keys.md#None) keeps the client silent.
- `max_response_bytes` `usize`: The largest response a call buffers, past which it fails with [`Status::Overflow`](https://www.unum.cloud/docs/ucall/rust/errors.md#Overflow); zero
takes the largest receive buffer the kernel grants a socket.
- `server` `Option<&'server Server>`: A server whose event loop drives this client through that loop's polling, sharing its
engine; bound before the loop first polls, used only on its polling thread, and its
[`Client::take_completed`](#take_completed) only hands out what the loop finished.
- `event_loop` `usize`: Which of `server`'s event loops; ignored without `server`.

Implements: `Default`

## Completed

One finished call: the context it was submitted with, and its response or failure.

```rust
pub struct Completed
```

- `context` `u64`: The context passed to [`Client::submit`](#submit).
- `response` `Result<Response>`: The received response, whatever its HTTP status, or why none arrived.

Implements: `Default`

## Response

A received HTTP response, freed on drop.

```rust
pub struct Response
```

It owns its received bytes, so it stays readable after its [`Client`](#Client) is dropped and may move
to another thread.

```
use ucall::*;

let mut client = Client::new(ClientOptions { connections: 1, ..Default::default() })?;
let response = client.get(format!("http://{address}/missing"))?;
drop(client);
let reader = std::thread::spawn(move || {
    assert_eq!(response.status_code(), HTTPStatus::NOT_FOUND);
    assert_eq!(response.header("X-Reason"), Some(b"gone".as_slice()));
    assert_eq!(response.body(), b"no such page");
});
reader.join().unwrap();
```

Implements: `Send`, `Drop`

## status_code

The status code the server sent.

```rust
pub fn status_code(&self) -> HTTPStatus
```

## body

The response body, decoded from chunked framing if it arrived that way.

```rust
pub fn body(&self) -> &[u8]
```

## header

The value of header `name`, matched case-insensitively.

```rust
pub fn header(&self, name: &str) -> Option<&[u8]>
```

## fields

Every header field line in arrival order, repeated names apart: each name lowercased as the parser keeps it, and its value trimmed.

```rust
pub fn fields(&self) -> impl Iterator<Item = (&[u8], &[u8])> + '_
```

