# Requests

> UCall for Rust

A handler receives each `Request` by value.
Its reads borrow the received bytes: `verb`, `path`, `query_string`, `path_parameter`, `query`, `header` and `body`, with `peer` naming the interface it arrived on, the client's authenticated Noise key and its `PeerAddress`: `Ip` with the port and IPv6 scope, `Unix`, or `Unknown`.
The path and query values stay percent-encoded as sent, which `ucall::decode_percent` decodes, header names match case-insensitively, and the body arrives whole, decoded from chunked framing if it was sent that way.
`fields` iterates every header field in arrival order as `(name, value)`, names lowercased and values trimmed, as a proxy forwards a head.
`path_segments_count` and `path_segment` read the path component by component, and `parts_count` and `part` read a `multipart/form-data` body as `RequestPart`s, each borrowing its name, content type and payload.

```rust
use ucall::*;

let mut server = Server::new(&[Interface::new("127.0.0.1:0")])?;
server.post("/orders/{order}", |request: Request| {
    let Some(token) = request.header("authorization") else {
        let _ = request.reply_error(HTTPStatus::UNAUTHORIZED, "sign in first");
        return;
    };
    let receipt = [request.path_parameter("order").unwrap_or_default(), b" by ", token].concat();
    let headers = ["content-type: text/plain".into(), "cache-control: no-store".into()];
    let _ = request.reply(HTTPStatus::CREATED, &headers, &receipt);
})?;
```

`reply` and `reply_error` consume the request, so a request is answered at most once, and Content-Length, Date and Server are generated.
A reply copies what it sends, and since it consumes the request, a body made of the request's bytes is assembled first, as `receipt` is above.
A request owns its received bytes and is `Send`, so a handler may hand it to a worker thread and return, and the worker replies whenever it is done.
A request dropped without a reply answers 500, as does one held by a handler that panics.
A reply to a closed connection still consumes the request and reports `Status::ConnectionClosed`; any other failed reply keeps it, and dropping it then answers 500.

`use ucall::DetachedRequest;`

`use ucall::Peer;`

`use ucall::PeerAddress;`

`use ucall::ReplyPart;`

`use ucall::Request;`

`use ucall::RequestPart;`

`use ucall::BytesView;`

`use ucall::Deferred;`

`use ucall::HTTPStatus;`

`use ucall::PercentMode;`

`use ucall::Wait;`

`use ucall::*;`

## DetachedRequest

A request taken off its polling thread, and the only owner of the verbs that may finish later: [`DetachedRequest::reply_parts`](#reply_parts), [`DetachedRequest::reply_stream`](#reply_stream) and [`DetachedRequest::body_chunk`](#body_chunk).

```rust
pub struct DetachedRequest(_);
```

It reads like the [`Request`](#Request) it came from, moves to any thread, and answers 500 when dropped
unreplied. Its replies hand back a [`Deferred`](https://www.unum.cloud/docs/ucall/rust/errors.md#Deferred) while a file or path part still sends, and
waiting on one belongs off the polling thread, as that thread is the one sending it.

```
use std::sync::mpsc;
use ucall::*;

let (handed, received) = mpsc::channel::&#x3C;DetachedRequest>();
let mut server = Server::new(&#x26;[Interface::new("127.0.0.1:0")])?;
server.get("/clip", move |request: Request| handed.send(request.detach()).unwrap())?;
let address = server.address(0).unwrap().to_owned();

// The worker outlives the scope, as the sender it waits on lives in the server's handler.
std::thread::spawn(move || {
    for detached in received {
        let parts = [ReplyPart::Bytes(b"frame".into())];
        if let Ok(Some(deferred)) = detached.reply_parts(HTTPStatus::OK, &#x26;[], &#x26;parts) {
            let _ = deferred.wait(); // or: deferred.await
        }
    }
});
let mut client = Client::new(ClientOptions { connections: 1, ..Default::default() })?;
std::thread::scope(|scope| {
    let polling = scope.spawn(|| server.take_calls(0));
    let response = client.get(format!("http://{address}/clip"));
    server.stop();
    polling.join().unwrap()?;
    assert_eq!(response?.body(), b"frame");
    Ok::&#x3C;(), Error>(())
})?;
```

Implements: `Deref`

## reply

Replies with [`status`](#status), full header lines, and [`body`](https://www.unum.cloud/docs/ucall/rust/calling.md#body), final on return, as [`Request::reply`](#reply) does.

```rust
pub fn reply(self, status: HTTPStatus, headers: &[BytesView<'_>], body: &[u8]) -> Result<()>
```

## reply_error

Replies with an error [`status`](#status) and a plain-text `message`, final on return, as [`Request::reply_error`](#reply_error) does.

```rust
pub fn reply_error(self, status: HTTPStatus, message: &str) -> Result<()>
```

## reply_parts

Replies with [`status`](#status), full header lines, and `parts` concatenated into the body, its Content-Length measured; [`None`](https://www.unum.cloud/docs/ucall/rust/noise-keys.md#None) once final, and a [`Deferred`](https://www.unum.cloud/docs/ucall/rust/errors.md#Deferred) while a file or path part still sends.

```rust
pub fn reply_parts<'f>(
    self,
    status: HTTPStatus,
    headers: &[BytesView<'_>],
    parts: &[ReplyPart<'_, 'f>],
) -> Result<Option<Deferred<'f>>>
```

A part failing before the head left replaces the reply with 404 for a missing, unreadable
or irregular file, 416 for a range past its end, or 500, and the [`Deferred`](https://www.unum.cloud/docs/ucall/rust/errors.md#Deferred) reports the
cause. A refusal on return keeps the request, and dropping it then answers 500.

## reply_stream

Sends the status line and header lines, and opens a body appended part by part: chunked, or of the length a Content-Length header declares.

```rust
pub fn reply_stream<'f>(
    self,
    status: HTTPStatus,
    headers: &[BytesView<'_>],
) -> Result<ReplyStream<'f>>
```

A closed connection still consumes the request and reports [`Status::ConnectionClosed`](https://www.unum.cloud/docs/ucall/rust/errors.md#ConnectionClosed);
any other failure keeps it, and dropping it then answers 500.

## body_chunk

Pulls the body bytes decoded since the last pull, waiting for them to arrive; empty once the body ended.

```rust
pub fn body_chunk(&mut self) -> Result<&[u8]>
```

On a [`BodyDelivery::Buffered`](https://www.unum.cloud/docs/ucall/rust/batches.md#Buffered) endpoint the first pull is the whole body.

On the polling thread, where nothing can arrive while it runs, a pull finding nothing
fails with [`Status::WouldBlock`](https://www.unum.cloud/docs/ucall/rust/errors.md#WouldBlock) instead. A body cut short fails with
[`Status::ConnectionClosed`](https://www.unum.cloud/docs/ucall/rust/errors.md#ConnectionClosed), and a malformed one with [`Status::Malformed`](https://www.unum.cloud/docs/ucall/rust/errors.md#Malformed).

## Peer

Which interface a request arrived on, and who sent it.

```rust
pub struct Peer
```

- `network_interface_index` `usize`: The position of its interface in the slice the server was started with.
- `static_key` `NoiseKey`: The client's authenticated Noise static key, zeroed on plaintext interfaces.
- `address` `PeerAddress`: The socket address the connection was accepted from.

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

## PeerAddress

The socket address a peer connected from, as its connection was accepted.

```rust
pub enum PeerAddress
```

- `Unknown`: The engine reported no address the library knows.
- `Ip` = `(SocketAddr)`: An IPv4 or IPv6 address and port, with the IPv6 scope.
- `Unix`: An AF_UNIX peer, which binds no path.

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

## ReplyPart

One part of a reply body, laid out as `ucall_reply_part_t`, so a slice of parts is the C array: bytes and paths borrow for `'b`, copied before the call returns, and descriptors for `'f`, read until the reply is final.

```rust
pub enum ReplyPart<'b, 'f>
```

- `Bytes` = `ucall_reply_part_bytes_k`: Bytes in memory, copied before the call returns.
- `File` = `ucall_reply_part_file_k`: A range of a regular file opened with `O_DIRECT`, which the event loop reads unbuffered
after the call returns, so it stays open until the reply is final.
- `Path` = `ucall_reply_part_path_k`: A range of a file the event loop opens beneath one of the interface's
[`Interface::reply_directories`](https://www.unum.cloud/docs/ucall/rust/serving.md#reply_directories), and closes once it is sent.

Implements: `Clone`, `Copy`, [`Debug`](https://www.unum.cloud/docs/ucall/rust/serving.md#Debug), `From<&'b IoSlice<'_>>`

## Request

One received HTTP request, retained until a reply consumes it.

```rust
pub struct Request
```

It owns its received bytes, so it may move to any thread and be replied to there after its
handler returned. Dropping it unreplied answers 500. Reads the library refuses, which happens
only on a thread whose CPU cannot run the server's class, read as empty.

```
use std::sync::mpsc;
use ucall::*;

let (handed, received) = mpsc::channel::&#x3C;Request>();
let mut server = Server::new(&#x26;[Interface::new("127.0.0.1:0")])?;
server.get("/users/{name}", move |request: Request| handed.send(request).unwrap())?;
let address = server.address(0).unwrap().to_owned();

// The worker outlives the scope, as the sender it waits on lives in the server's handler.
std::thread::spawn(move || {
    for request in received {
        let name = request.path_parameter("name").unwrap_or_default();
        let greeting = [b"Hello, ", name].concat();
        let _ = request.reply(HTTPStatus::OK, &#x26;[], &#x26;greeting);
    }
});
let mut client = Client::new(ClientOptions { connections: 1, ..Default::default() })?;
std::thread::scope(|scope| {
    let polling = scope.spawn(|| server.take_calls(0));
    let response = client.get(format!("http://{address}/users/ada?verbose=1"));
    server.stop();
    polling.join().unwrap()?;
    assert_eq!(response?.body(), b"Hello, ada");
    Ok::&#x3C;(), Error>(())
})?;
```

Implements: `Send`, `Drop`

## verb

The HTTP method, which tells apart the requests of a [`Verb::Any`](https://www.unum.cloud/docs/ucall/rust/serving.md#Any) endpoint.

```rust
pub fn verb(&self) -> Verb
```

## path

The request path as sent, still percent-encoded, without the query.

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

## path_parameter

The value the endpoint's template captured under `name`, case-sensitive.

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

## query

The value of query parameter `name`, case-sensitive and still percent-encoded.

```rust
pub fn query(&self, name: &str) -> Option<&[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])> + '_
```

## query_string

The query after the first `?`, still percent-encoded, or empty without one.

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

## body

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

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

## peer

The interface the request arrived on and the sender's authenticated static key, readable after its connection closed.

```rust
pub fn peer(&self) -> Peer
```

## path_segments_count

Path components in order, as sent and still percent-encoded.

```rust
pub fn path_segments_count(&self) -> usize
```

## path_segment

Path component `index`, counting from zero, or [`None`](https://www.unum.cloud/docs/ucall/rust/noise-keys.md#None) past the last one.

```rust
pub fn path_segment(&self, index: usize) -> Option<&[u8]>
```

## parts_count

Parts in a `multipart/form-data` body, zero for any other body.

```rust
pub fn parts_count(&self) -> usize
```

## part

Part `index` of a `multipart/form-data` body, its payload undecoded.

```rust
pub fn part(&self, index: usize) -> Result<RequestPart<'_>>
```

Fails with [`Status::InvalidArgument`](https://www.unum.cloud/docs/ucall/rust/errors.md#InvalidArgument) past the last part, and with [`Status::Malformed`](https://www.unum.cloud/docs/ucall/rust/errors.md#Malformed)
when the body is no well-formed multipart body.

## detach

Hands the request to a thread that may wait, where it gains the replies that may finish later; the polling thread itself must never wait on them.

```rust
pub fn detach(self) -> DetachedRequest
```

## reply

Replies with [`status`](#status), full header lines like "content-type: text/plain", and [`body`](https://www.unum.cloud/docs/ucall/rust/calling.md#body).

```rust
pub fn reply(self, status: HTTPStatus, headers: &[BytesView<'_>], body: &[u8]) -> Result<()>
```

Content-Length, Date and Server are generated. A closed connection still consumes the
request and reports [`Status::ConnectionClosed`](https://www.unum.cloud/docs/ucall/rust/errors.md#ConnectionClosed); any other failure keeps it, and dropping
it then answers 500.

## reply_error

Replies with an error [`status`](#status), 400 or above, and a plain-text `message`.

```rust
pub fn reply_error(self, status: HTTPStatus, message: &str) -> Result<()>
```

## RequestPart

One part of a `multipart/form-data` request body, borrowing the received bytes.

```rust
pub struct RequestPart<'request>
```

- `name` `&'request [u8]`: The name its `Content-Disposition` gives.
- `content_type` `&'request [u8]`: Its `Content-Type`, empty when absent.
- `data` `&'request [u8]`: Its payload, undecoded.

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

## BytesView

A borrowed header line, laid out so a slice of them is lent to the library without copying.

```rust
pub struct BytesView<'bytes>
```

Implements: `Clone`, `Copy`, `From<&'bytes [u8]>`, `From<&'bytes str>`, `From<&'bytes [u8; LENGTH]>`, [`Debug`](https://www.unum.cloud/docs/ucall/rust/serving.md#Debug)

## as_bytes

The bytes this view borrows.

```rust
pub fn as_bytes(&self) -> &'bytes [u8]
```

## Deferred

A reply still sending a file or path part: it owns the outcome the event loop makes final, and the descriptors its parts borrow stay open for `'f`, until it is final.

```rust
pub struct Deferred<'f>
```

[`Deferred::status`](#status) polls without blocking, [`Deferred::wait`](#wait) blocks until the reply is
final, and `.await` polls it from async code. Dropping one that is not final waits for it, so
it belongs off the polling thread, which is the one finishing it.

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

## status

`Err(Status::Deferred)` until the reply is final, then its final status, read without blocking through `ucall_outcome_status`.

```rust
pub fn status(&self) -> Result<(), Status>
```

## wait

Blocks until the reply is final, through `ucall_outcome_wait`, and reports how it ended.

```rust
pub fn wait(self) -> Result<()>
```

## HTTPStatus

An HTTP status code, for replies and received responses alike; codes without a constant here are just as valid.

```rust
pub struct HTTPStatus(pub u32);
```

- `0` `u32`

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

## OK

200, the request succeeded.

```rust
pub const OK: HTTPStatus = _;
```

## CREATED

201, a resource was created.

```rust
pub const CREATED: HTTPStatus = _;
```

## NO_CONTENT

204, success with an empty body.

```rust
pub const NO_CONTENT: HTTPStatus = _;
```

## PARTIAL_CONTENT

206, success with the requested range of the body.

```rust
pub const PARTIAL_CONTENT: HTTPStatus = _;
```

## MOVED_PERMANENTLY

301, the resource has a new permanent URL.

```rust
pub const MOVED_PERMANENTLY: HTTPStatus = _;
```

## FOUND

302, the resource is temporarily at a different URL.

```rust
pub const FOUND: HTTPStatus = _;
```

## NOT_MODIFIED

304, the cached copy is still fresh.

```rust
pub const NOT_MODIFIED: HTTPStatus = _;
```

## BAD_REQUEST

400, the request is malformed.

```rust
pub const BAD_REQUEST: HTTPStatus = _;
```

## UNAUTHORIZED

401, authentication is required or failed.

```rust
pub const UNAUTHORIZED: HTTPStatus = _;
```

## FORBIDDEN

403, authenticated but not permitted.

```rust
pub const FORBIDDEN: HTTPStatus = _;
```

## NOT_FOUND

404, no endpoint matches the path.

```rust
pub const NOT_FOUND: HTTPStatus = _;
```

## METHOD_NOT_ALLOWED

405, the endpoint exists, but not for this verb.

```rust
pub const METHOD_NOT_ALLOWED: HTTPStatus = _;
```

## TIMEOUT

408, the client took too long to send the request.

```rust
pub const TIMEOUT: HTTPStatus = _;
```

## CONFLICT

409, the request conflicts with the resource's state.

```rust
pub const CONFLICT: HTTPStatus = _;
```

## PRECONDITION_FAILED

412, a request condition does not match the resource.

```rust
pub const PRECONDITION_FAILED: HTTPStatus = _;
```

## PAYLOAD_TOO_LARGE

413, the request body is too large.

```rust
pub const PAYLOAD_TOO_LARGE: HTTPStatus = _;
```

## TOO_MANY_REQUESTS

429, the rate limit is exceeded.

```rust
pub const TOO_MANY_REQUESTS: HTTPStatus = _;
```

## INTERNAL_ERROR

500, a handler failed or dropped its request unanswered.

```rust
pub const INTERNAL_ERROR: HTTPStatus = _;
```

## NOT_IMPLEMENTED

501, the verb or feature is not supported.

```rust
pub const NOT_IMPLEMENTED: HTTPStatus = _;
```

## BAD_GATEWAY

502, an upstream answered with something no HTTP peer would send.

```rust
pub const BAD_GATEWAY: HTTPStatus = _;
```

## SERVICE_UNAVAILABLE

503, the server is overloaded, like a batch endpoint whose queue is full.

```rust
pub const SERVICE_UNAVAILABLE: HTTPStatus = _;
```

## GATEWAY_TIMEOUT

504, an upstream dependency timed out.

```rust
pub const GATEWAY_TIMEOUT: HTTPStatus = _;
```

## VERSION_NOT_SUPPORTED

505, the request names an HTTP version the server does not speak.

```rust
pub const VERSION_NOT_SUPPORTED: HTTPStatus = _;
```

## PercentMode

Which part of a URL [`decode_percent`](#decode_percent) reads, as only a query spells a space `+`.

```rust
pub enum PercentMode
```

- `Path`: A path, where `+` stays itself.
- `Query`: A query, where `+` is a space.

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

## Wait

A [`Deferred`](https://www.unum.cloud/docs/ucall/rust/errors.md#Deferred) being awaited: every poll reads its status, and one not yet final asks to be polled again.

```rust
pub struct Wait<'f>(_);
```

The library has no wake-up, so an executor re-polls it while other tasks run, spinning only
when nothing else is ready; [`Deferred::wait`](#wait) blocks on the CPU class's waiter.

Implements: `Future`

## decode_percent

Decodes the `%XX` escapes of `encoded`, like a [`Request::query`](#query) value, and `+` as a space in [`PercentMode::Query`](#Query).

```rust
pub fn decode_percent(encoded: &[u8], mode: PercentMode) -> Result<Vec<u8>>
```

Fails with [`Status::InvalidArgument`](https://www.unum.cloud/docs/ucall/rust/errors.md#InvalidArgument) on a `%` without two hexadecimal digits.

```
use ucall::{decode_percent, PercentMode};

assert_eq!(decode_percent(b"caf%C3%A9+au+lait", PercentMode::Query)?, "café au lait".as_bytes());
assert!(decode_percent(b"100%", PercentMode::Path).is_err());
```

