# Calling

> UCall for Mojo

```mojo
var client = Client(library, ClientOptions(connections=2))
var response = client.get("http://127.0.0.1:8545/hello")
print(response.status_code().code, response.header("content-length").value())

client.submit(ClientCall.post("http://127.0.0.1:8545/echo", "ping".as_bytes()), 7)
var completed = List[Completed]()
client.take_completed(completed, timeout_microseconds=1_000_000)
for position in range(len(completed)):  # `Completed` is not `Copyable`, so it is read in place
    ref done = completed[position]
    print(done.context, done.response.value().body())
```

`submit` admits a call without driving the network, `take_completed` drives every admitted call and hands back what finished, and `call`, with `get`, `post`, `put`, `delete` and `patch`, submits one and polls until its own response arrives.
A client owns every input until its call completes, in slots sized once by the resolved call budget.
`Client(server, options, event_loop=0)` binds the client to a server's event loop instead, so that loop's `take_call` drives both, which is how `mojo/test.mojo` serves and calls on one thread.

`from ucall import ClientOptions, ClientCallOptions, ClientCall, …`

## ClientOptions

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

```mojo
struct ClientOptions
```

`connections` is required; every other zero field takes its documented default.

- `connections` `Int`: Connections open at once; zero fails with [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/ucall/mojo/errors.md#INVALID_ARGUMENT).
- `pipelined_calls` `Int`: Calls one connection may carry at once; zero means one.
- `calls` `Int`: Calls admitted at once, unretrieved completions included; zero means `connections` times `pipelined_calls`.
- `connect_timeout_microseconds` `Int`: Time budget for establishing a connection; zero means 5 seconds.
- `call_timeout_microseconds` `Int`: Time budget for a full exchange, including queueing and redirects; zero means 30 seconds.
- `noise_private_key` `Optional[SIMD[.uint8, 32]]`: The client identity for Noise handshakes.
- `peer_authorizer` `PeerAuthorizer`: Vets each server's authenticated static key; required for Noise XX calls.
- `backend` `Backend`: The requested I/O backend.
- `cpu_class` `CPUClass`: A ceiling on the CPU class; [`UNKNOWN`](https://www.unum.cloud/docs/ucall/mojo/threads-and-polling.md#UNKNOWN) takes what the CPU offers.
- `log` `Log`: Where log lines go; the default keeps the client silent.
- `max_response_bytes` `Int`: The largest response a call buffers; zero takes the largest socket receive buffer.

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

## __init__

Every default but the connection budget.

```mojo
def __init__(out self, *, connections: Int)
```

- `connections`: Connections open at once.

## ClientCallOptions

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

```mojo
struct ClientCallOptions
```

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

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

## __init__

Every default.

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

## ClientCall

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

```mojo
struct ClientCall
```

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

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

## get

A GET of `url` with no headers.

```mojo
def get(url: String) -> Self
```

## post

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

```mojo
def post(url: String, body: Span[UInt8]) -> Self
```

## put

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

```mojo
def put(url: String, body: Span[UInt8]) -> Self
```

## delete

A DELETE of `url` with no headers.

```mojo
def delete(url: String) -> Self
```

## patch

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

```mojo
def patch(url: String, body: Span[UInt8]) -> Self
```

## Completed

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

```mojo
struct Completed
```

- `context` `UInt64`: The context passed to [`Client.submit`](#submit).
- `response` `Optional[Response]`: The received response whatever its HTTP status, or `None` when none arrived.
- `error` `Optional[Error]`: Why no response arrived, or `None` when one did.

Conforms to: `Deinitable`, `Movable`

## __init__

```mojo
def __init__(out self, context: UInt64, var response: Optional[Response], error: Optional[Error])
```

## Client

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

```mojo
struct Client
```

Dropping it cancels the calls in flight, while retrieved responses stay readable.

- `library` `Library`: The loaded library, held so the release can still reach `ucall_client_free`.
- `handle` `Address`: The `ucall_client_t`.
- `server` `Optional[ArcPointer[_ServerCore]]`: The server whose event loop drives a bound client, kept alive while the client is.
- `slots` `List[_CallSlot]`: One per admitted call, sized once, so the inputs the library reads never move.
- `free` `List[Int]`: Slots holding no call.
- `retrieved` `List[CompletedCallArguments]`: The library's completion records, one per slot.
- `kept` `List[Completed]`: Completions [`call`](#call) retrieved for other calls, handed out by the next [`take_completed`](#take_completed) first.
- `call_timeout_microseconds` `Int`: The resolved time budget of one exchange, which [`call`](#call) waits in.

Conforms to: `Deinitable`, `Movable`

## __init__

Starts a client with an engine of its own; it holds no connection until a call is polled. Calls `ucall_client_init`.

```mojo
def __init__(out self, library: Library, options: ClientOptions)
```

```mojo
def __init__(out self, server: Server, options: ClientOptions, *, event_loop: Int = Int(0))
```

Starts a client driven by one of `server`'s event loops, sharing its engine. Calls `ucall_client_init`.

```mojo
def __init__(
    out self,
    library: Library,
    options: ClientOptions,
    server: Optional[ArcPointer[_ServerCore]],
    event_loop: Int
)
```

- `library`: The loaded library, kept so the shared object stays mapped.
- `options`: The budgets, timeouts and identity.
- `server`: The server whose loop drives it, kept alive while the client is.
- `event_loop`: Which of the server's loops.

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) for a zero connection budget, or [`UNSUPPORTED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#UNSUPPORTED) when no backend serves the configuration.

## __deinit__

Cancels the calls in flight and frees the client before the inputs they borrowed. Calls `ucall_client_free`.

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

## submit

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

```mojo
def submit(mut self, var request: ClientCall, context: UInt64 = UInt64(0))
```

Success guarantees exactly one completion from [`take_completed`](#take_completed).

- `request` (var): The call, owned until it completes.
- `context`: Handed back with the completion.

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) with [`CONTENDED`](https://www.unum.cloud/docs/ucall/mojo/errors.md#CONTENDED) while every call slot holds an unretrieved call, or [`INVALID_ARGUMENT`](https://www.unum.cloud/docs/ucall/mojo/errors.md#INVALID_ARGUMENT) for a malformed URL.

## take_completed

Drives every admitted call on this thread and refills `completed` with finished ones, waiting up to `timeout_microseconds` for the first. Calls `ucall_client_take_completed`.

```mojo
def take_completed(mut self, mut completed: List[Completed], timeout_microseconds: Int = Int(0))
```

Completions kept by [`call`](#call) come first, without waiting.

- `completed` (mut): Cleared, then filled with what finished, empty when nothing did in time.
- `timeout_microseconds`: The longest wait; zero performs one nonblocking round.

Raises: [`Error`](https://www.unum.cloud/docs/ucall/mojo/errors.md#Error) if the polling itself fails; a call's own failure arrives in its [`Completed`](#Completed).

## call

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

```mojo
def call(mut self, var request: ClientCall) -> Response
```

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

## get

Fetches `url` and polls until the response arrives.

```mojo
def get(mut self, url: String) -> Response
```

## post

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

```mojo
def post(mut self, url: String, body: Span[UInt8]) -> Response
```

## put

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

```mojo
def put(mut self, url: String, body: Span[UInt8]) -> Response
```

## delete

Deletes `url` and polls until the response arrives.

```mojo
def delete(mut self, url: String) -> Response
```

## patch

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

```mojo
def patch(mut self, url: String, body: Span[UInt8]) -> Response
```

## Response

A received HTTP response, freed once dropped.

```mojo
struct Response
```

It owns its received bytes, so it stays readable after its client is dropped.

- `library` `Library`: The loaded library, held so the release can still reach `ucall_client_response_free`.
- `handle` `Address`: The `ucall_received_http_response_t`.

Conforms to: `Deinitable`, `Movable`

## __init__

```mojo
def __init__(out self, library: Library, handle: Int)
```

## __deinit__

Frees the response. Calls `ucall_client_response_free`.

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

## status_code

The status code the server sent. Calls `ucall_client_response_status`.

```mojo
def status_code(self) -> HTTPStatus
```

## body

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

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

## header

The value of header `name`, matched case-insensitively, or `None` without one. Calls `ucall_client_response_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_client_response_fields_count`.

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

## field

Header field line `index`: its lowercased name and its trimmed value. Calls `ucall_client_response_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.

