# Requests

> UCall for Mojo

Each handler runs on the thread polling the event loop and owns its `Request`:

```mojo
def greet(var request: Request) raises:
    var verbose = request.query("verbose")          # Optional[Span[Byte, ...]], still percent-encoded
    var agent = request.header("user-agent")        # case-insensitive
    if not request.path_parameter("name"):          # case-sensitive
        request^.reply_error(HTTPStatus.BAD_REQUEST, "no name")
        return
    request^.reply(HTTPStatus.OK, [BytesView.of("content-type: text/plain")], "Hello!".as_bytes())
```

Every read borrows the received bytes with the request's origin.
`fields_count` and `field` walk every header line in arrival order, `path_segments_count` and `path_segment` walk the path, `parts_count` and `part` walk a `multipart/form-data` body, and `peer` reports the interface, the authenticated Noise key and the socket address.
`reply`, `reply_error` and `detach` consume the request, and a request dropped without them answers 500, which is also what a handler raising past it answers.

`from ucall import Peer, RequestPart, Request, …`

## Peer

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

```mojo
struct Peer
```

- `network_interface_index` `Int`: The position of its interface among those 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.

Conforms to: `Copyable`, `Deinitable`, `ImplicitlyCopyable`, `Movable`

## RequestPart

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

```mojo
struct RequestPart[origin: Origin[mut=origin.mut]]
```

- `name` `Span[UInt8, origin]`: The name its `Content-Disposition` gives.
- `content_type` `Span[UInt8, origin]`: Its `Content-Type`, empty when absent.
- `data` `Span[UInt8, origin]`: Its payload, undecoded.

Conforms to: `Copyable`, `Deinitable`, `ImplicitlyCopyable`, `Movable`

## Request

One received request, owned by its handler on the polling thread.

```mojo
struct Request
```

[`reply`](#reply), [`reply_error`](#reply_error) and [`detach`](#detach) consume it; one dropped without them answers 500. Reads
borrow the received bytes with the request's origin.

Conforms to: `Deinitable`, `Movable`, `[RequestReads](#RequestReads)`

## __init__

```mojo
def __init__(out self, server: Pointer[Server, ImmUntrackedOrigin], request: Int)
```

## __deinit__

Answers 500 for a request no reply or detach consumed. Calls `ucall_server_reply_error`.

```mojo
def __deinit__(deinit self)
```

## reply

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

```mojo
def reply[origin: ImmOrigin](
    deinit self,
    status: HTTPStatus,
    headers: Span[BytesView[origin]],
    body: Span[UInt8]
)
```

```mojo
def reply(deinit self, status: HTTPStatus, body: Span[UInt8])
```

Replies with [`status`](#status) and [`body`](https://www.unum.cloud/docs/ucall/mojo/calling.md#body) and no header lines of its own. Calls `ucall_server_reply_content`.

A closed connection still consumes the request and raises [`CONNECTION_CLOSED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#CONNECTION_CLOSED); any other
refusal answers 500 before raising.

- `status`: The response status; zero sends 200.
- `headers`: Every header line; Content-Length, Date and Server are generated.
- `body`: The response body.

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) if the reply is refused.

## reply_error

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

```mojo
def reply_error(deinit self, status: HTTPStatus, message: StringSpan)
```

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) if the reply is refused, as [`reply`](#reply) does.

## detach

Hands the request to an owner that may move to any thread and reply after the handler returned, gaining the replies that may finish later. Binding-only.

```mojo
def detach(deinit self) -> DetachedRequest
```

## DetachedRequest

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

```mojo
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`](#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.

Conforms to: `Deinitable`, `Movable`, `[RequestReads](#RequestReads)`

## __init__

```mojo
def __init__(out self, shared: ArcPointer[_ServerCore], request: Int)
```

## __deinit__

Answers 500 for a request dropped unreplied. Calls `ucall_server_reply_error`.

```mojo
def __deinit__(deinit self)
```

## reply

Replies with [`status`](#status), full header lines, and [`body`](https://www.unum.cloud/docs/ucall/mojo/calling.md#body), final on return. Calls `ucall_server_reply_content`.

```mojo
def reply[origin: ImmOrigin](
    deinit self,
    status: HTTPStatus,
    headers: Span[BytesView[origin]],
    body: Span[UInt8]
)
```

```mojo
def reply(deinit self, status: HTTPStatus, body: Span[UInt8])
```

Replies with [`status`](#status) and [`body`](https://www.unum.cloud/docs/ucall/mojo/calling.md#body) and no header lines of its own. Calls `ucall_server_reply_content`.

A refusal that keeps the request answers 500 before raising.

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) if the reply is refused.

## reply_error

Replies with an error [`status`](#status) and a plain-text `message`, final on return. Calls `ucall_server_reply_error`.

```mojo
def reply_error(deinit self, status: HTTPStatus, message: StringSpan)
```

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) if the reply is refused.

## reply_parts

Replies with [`status`](#status), full header lines, and `parts` concatenated into the body, its Content-Length measured. Calls `ucall_server_reply_vectored`.

```mojo
def reply_parts[headers_origin: ImmOrigin, parts_origin: ImmOrigin](
    deinit self,
    status: HTTPStatus,
    headers: Span[BytesView[headers_origin]],
    parts: Span[ReplyPart[parts_origin]]
) -> Optional[Deferred]
```

A part failing before the head left replaces the reply with 404, 416 or 500, and the
[`Deferred`](#Deferred) reports the cause. A refusal on return answers 500 before raising.

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) if the reply is refused or fails.

## 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. Calls `ucall_server_reply_begin`.

```mojo
def reply_stream[origin: ImmOrigin](
    deinit self,
    status: HTTPStatus,
    headers: Span[BytesView[origin]]
) -> ReplyStream
```

A closed connection still ends the stream and raises [`CONNECTION_CLOSED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#CONNECTION_CLOSED); any other failure
answers 500 before raising.

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) if the head is refused.

## body_chunk

Pulls the body bytes decoded since the last pull, waiting for them to arrive; empty once the body ended. Calls `ucall_server_request_body_chunk` and `ucall_outcome_wait`.

```mojo
def body_chunk(ref self) -> Span[UInt8, origin_of(self)]
```

On a buffered endpoint the first pull is the whole body. On the polling thread, where nothing
can arrive while it runs, a pull finding nothing raises [`WOULD_BLOCK`](https://www.unum.cloud/docs/ucall/mojo/errors.md#WOULD_BLOCK) instead.

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) with [`CONNECTION_CLOSED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#CONNECTION_CLOSED) for a body cut short, or [`MALFORMED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#MALFORMED) for a malformed one.

## ReplyPart

One part of a reply body, `ucall_reply_part_t` in C: bytes in memory, or a byte range of a file.

```mojo
struct ReplyPart[origin: ImmOrigin = ImmUntrackedOrigin]
```

Mojo has no unions, so the union's five words are spelled out, as the ABI lays them for each kind
on the little-endian targets this binding runs on.

- `origin` (comptime): The bytes or path a part borrows, named so they outlive the reply call.

- `kind` `c_int`: `ucall_reply_part_kind_t`: 0 for bytes, 1 for a file, 2 for a path.
- `first` `UInt64`
- `second` `UInt64`
- `third` `UInt64`
- `fourth` `UInt64`
- `fifth` `UInt64`

Conforms to: `Copyable`, `Deinitable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`

## bytes

Bytes in memory, copied before the reply call returns.

```mojo
def bytes[bytes_origin: ImmOrigin, //](bytes: Span[UInt8, bytes_origin]) -> ReplyPart[bytes_origin]
```

- `bytes`: The content.

## file

A range of an `O_DIRECT` file, which the event loop reads after the call returns.

```mojo
def file[part_origin: ImmOrigin = ImmUntrackedOrigin](range: FileRange) -> ReplyPart[part_origin]
```

- `part_origin` (comptime): The origin of the parts it shares an array with.
- `range`: The file and the range.

## path

A range of a file beneath one of the interface's reply directories, opened by the event loop.

```mojo
def path[path_origin: ImmOrigin, //](
    directory: Int,
    relative_path: StringSpan[path_origin],
    offset: UInt64 = UInt64(0),
    length: UInt64 = UInt64(0)
) -> ReplyPart[path_origin]
```

- `directory`: The position of the directory among the interface's reply directories.
- `relative_path`: The file beneath it, copied before the call returns.
- `offset`: The range's first byte.
- `length`: The range's byte count; zero sends through the end of the file.

## RequestReads

The reads every received request offers, detached or not, each written once.

```mojo
trait RequestReads
```

## verb

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

```mojo
def verb(self) -> Verb
```

## path

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

```mojo
def path(ref self) -> Span[UInt8, origin_of(self)]
```

## query_string

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

```mojo
def query_string(ref self) -> Span[UInt8, origin_of(self)]
```

## body

The buffered body, decoded from chunked framing if it arrived that way. Calls `ucall_server_request_body`.

```mojo
def body(ref self) -> Span[UInt8, origin_of(self)]
```

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/ucall/mojo/errors.md#INVALID_ARGUMENT) on a streamed endpoint, whose body [`body_chunk`](#body_chunk) pulls.

## path_parameter

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

```mojo
def path_parameter(ref self, name: StringSpan) -> Optional[Span[UInt8, origin_of(self)]]
```

## query

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

```mojo
def query(ref self, name: StringSpan) -> Optional[Span[UInt8, origin_of(self)]]
```

## header

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

```mojo
def header(ref self, name: StringSpan) -> Optional[Span[UInt8, origin_of(self)]]
```

## fields_count

Header field lines in arrival order, repeated names included. Calls `ucall_server_request_fields_count`.

```mojo
def fields_count(self) -> Int
```

## field

Header field line `index`: its name, lowercased as the parser keeps it, and its trimmed value. Calls `ucall_server_request_field`.

```mojo
def field(ref self, index: Int) -> Tuple[Span[UInt8, origin_of(self)], Span[UInt8, origin_of(self)]]
```

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) with [`NOT_FOUND`](https://www.unum.cloud/docs/ucall/mojo/errors.md#NOT_FOUND) past the last line.

## peer

The interface the request arrived on, the sender's authenticated key and its socket address, readable after its connection closed. Calls `ucall_server_request_peer`.

```mojo
def peer(self) -> Peer
```

## path_segments_count

Path components in order, as sent. Calls `ucall_server_request_path_segments_count`.

```mojo
def path_segments_count(self) -> Int
```

## path_segment

Path component `index`, still percent-encoded, or `None` past the last one. Calls `ucall_server_request_path_segment`.

```mojo
def path_segment(ref self, index: Int) -> Optional[Span[UInt8, origin_of(self)]]
```

## parts_count

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

```mojo
def parts_count(self) -> Int
```

## part

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

```mojo
def part(ref self, index: Int) -> RequestPart[origin_of(self)]
```

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/ucall/mojo/errors.md#INVALID_ARGUMENT) past the last part, or [`MALFORMED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#MALFORMED) for a malformed body.

## BytesView

A borrowed run of bytes and its length, `ucall_bytes_view_t` in C, never null-terminated.

```mojo
struct BytesView[origin: ImmOrigin = ImmUntrackedOrigin]
```

Crosses by value, which the two-word argument class handles on both x86-64 and AArch64.

- `origin` (comptime): The borrowed storage, named so it outlives every call this view crosses.

- `data` `Pointer[UInt8, origin]`: The first byte, borrowed for the duration of the call.
- `length` `c_size_t`: How many bytes the run covers, since nothing terminates it.

Conforms to: `Copyable`, `Deinitable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`

## of

Borrows a Mojo string's bytes, which the caller keeps alive across the call.

```mojo
def of[text_origin: ImmOrigin, //](text: StringSpan[text_origin]) -> BytesView[text_origin]
```

```mojo
def of[bytes_origin: ImmOrigin, //](bytes: Span[UInt8, bytes_origin]) -> BytesView[bytes_origin]
```

Borrows a run of raw bytes, such as a request body.

- `text_origin` (comptime): The string's origin, inferred from the argument.
- `text`: The characters to hand over.
- `bytes_origin` (comptime): The run's origin, inferred from the argument.
- `bytes`: The bytes to hand over.

## empty

A run of no bytes, which output slots hold until the library writes them.

```mojo
def empty() -> BytesView
```

It points at a static empty string, since a `Pointer` is never null.

## arguments

The same run with its borrow erased, as the one instantiation the ABI signatures name.

```mojo
def arguments(self) -> BytesView
```

## span

Reads a run the library wrote back as a span living as long as `target`.

```mojo
def span[target: Origin[mut=target.mut]](self) -> Span[UInt8, target]
```

The library writes `{NULL, 0}` for an empty run, and a span of no bytes never reads its
pointer, so the null needs no branch.

- `target` (comptime): The owner whose lifetime bounds the bytes, such as a request or a response.

## HTTPStatus

An HTTP status code a server sends and a client receives, `ucall_http_status_t` in C.

```mojo
struct HTTPStatus
```

Codes absent from the named constants are as valid as the named ones.

- `OK` = `HTTPStatus(UInt32(200))`: Request succeeded.
- `CREATED` = `HTTPStatus(UInt32(201))`: Resource created.
- `NO_CONTENT` = `HTTPStatus(UInt32(204))`: Success with an empty body.
- `PARTIAL_CONTENT` = `HTTPStatus(UInt32(206))`: Success with the requested range of the body.
- `MOVED_PERMANENTLY` = `HTTPStatus(UInt32(301))`: Resource has a new permanent URL.
- `FOUND` = `HTTPStatus(UInt32(302))`: Resource is temporarily at another URL.
- `NOT_MODIFIED` = `HTTPStatus(UInt32(304))`: Cached copy is still fresh.
- `BAD_REQUEST` = `HTTPStatus(UInt32(400))`: Malformed request syntax.
- `UNAUTHORIZED` = `HTTPStatus(UInt32(401))`: Authentication required or failed.
- `FORBIDDEN` = `HTTPStatus(UInt32(403))`: Authenticated, but not permitted.
- `NOT_FOUND` = `HTTPStatus(UInt32(404))`: No endpoint matches the request path.
- `METHOD_NOT_ALLOWED` = `HTTPStatus(UInt32(405))`: Endpoint exists, but not for this verb.
- `TIMEOUT` = `HTTPStatus(UInt32(408))`: Client took too long to send the request.
- `CONFLICT` = `HTTPStatus(UInt32(409))`: Request conflicts with the current resource state.
- `PRECONDITION_FAILED` = `HTTPStatus(UInt32(412))`: A request condition does not match the current resource.
- `PAYLOAD_TOO_LARGE` = `HTTPStatus(UInt32(413))`: Request body is too large.
- `RANGE_NOT_SATISFIABLE` = `HTTPStatus(UInt32(416))`: A requested byte range starts or ends past the end of the resource.
- `TOO_MANY_REQUESTS` = `HTTPStatus(UInt32(429))`: Rate limit exceeded.
- `INTERNAL_ERROR` = `HTTPStatus(UInt32(500))`: Unhandled failure inside an endpoint.
- `NOT_IMPLEMENTED` = `HTTPStatus(UInt32(501))`: Verb or feature not supported.
- `BAD_GATEWAY` = `HTTPStatus(UInt32(502))`: An upstream answered with something no HTTP peer would send.
- `SERVICE_UNAVAILABLE` = `HTTPStatus(UInt32(503))`: Server is overloaded or shutting down.
- `GATEWAY_TIMEOUT` = `HTTPStatus(UInt32(504))`: Upstream dependency timed out.
- `VERSION_NOT_SUPPORTED` = `HTTPStatus(UInt32(505))`: Request names an HTTP version the server does not speak.
- `code` `UInt32`: The numeric status code.

Conforms to: `Copyable`, `Deinitable`, `Equatable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`

## PercentMode

Which part of a URL [`Library.decode_percent`](https://www.unum.cloud/docs/ucall/mojo/design.md#decode_percent) reads, as only a query spells a space '+'.

```mojo
struct PercentMode
```

- `PATH` = `PercentMode(Int32(0))`: A path, where '+' stays itself.
- `QUERY` = `PercentMode(Int32(1))`: A query, where '+' is a space.
- `code` `c_int`: The raw `ucall_percent_mode_t` value the ABI exchanges.

Conforms to: `Copyable`, `Deinitable`, `Equatable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`

## AddressFamily

The family of a peer's socket address, `ucall_address_family_t` in C, one byte wide.

```mojo
struct AddressFamily
```

- `UNKNOWN` = `AddressFamily(UInt8(0))`: No address was read for this peer.
- `IPV4` = `AddressFamily(UInt8(1))`: IPv4, in the first 4 bytes.
- `IPV6` = `AddressFamily(UInt8(2))`: IPv6, in all 16 bytes, with its scope.
- `UNIX` = `AddressFamily(UInt8(3))`: A local AF_UNIX peer, with no address bytes.
- `code` `UInt8`: The raw `ucall_address_family_t` value the ABI exchanges.

Conforms to: `Copyable`, `Deinitable`, `Equatable`, `ImplicitlyCopyable`, `Movable`, `RegisterPassable`, `TrivialRegisterPassable`

## PeerAddress

A peer's socket address as its connection was accepted, `ucall_peer_address_t` in C.

```mojo
struct PeerAddress
```

- `bytes` `SIMD[.uint8, 16]`: The address in network order: IPv4 in the first 4 bytes, IPv6 in all 16.
- `scope` `UInt32`: The IPv6 scope, 0 otherwise.
- `port` `UInt16`: The port in host order.
- `family` `AddressFamily`: Which of the families the address belongs to.
- `reserved` `UInt8`: Zero.

Conforms to: `Copyable`, `Defaultable`, `Deinitable`, `ImplicitlyCopyable`, `Movable`

## __init__

No address, as a failed read leaves it.

```mojo
def __init__(out self)
```

## Deferred

A reply still sending a file or path part, which owns the outcome the event loop makes final.

```mojo
struct Deferred
```

[`status`](#status) reads [`DEFERRED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#DEFERRED) until then without blocking, and [`wait`](#wait) blocks until the reply is
final and raises its failure. Dropping one that is not final waits for it, so it belongs off the
polling thread, which is the one finishing it. The descriptors its parts borrow stay open until
it is final.

- `library` `Library`: The loaded library, held so the wait can still reach `ucall_outcome_wait`.
- `outcome` `OwnedPointer[Outcome]`: The outcome the event loop writes, on the heap since it outlives the verb.
- `symbol` `StaticString`: The entry point the reply went through, carried into its failure.

Conforms to: `Deinitable`, `Movable`

## __init__

A pending outcome at a stable heap address, for a verb that may finish after it returns.

```mojo
def __init__(out self, library: Library, symbol: StringSpan[ImmStaticOrigin])
```

## __deinit__

Waits until the reply is final, then frees its outcome. Calls `ucall_outcome_wait`.

```mojo
def __deinit__(deinit self)
```

## status

[`DEFERRED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#DEFERRED) until the reply is final, then its final status; never blocks. Calls `ucall_outcome_status`.

```mojo
def status(self) -> Status
```

## wait

Blocks until the reply is final, and raises its failure. Calls `ucall_outcome_wait`.

```mojo
def wait(deinit self)
```

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) carrying the reply's failure.

