Mojo · Calling

Calling

from ucall import ClientOptions, ClientCallOptions, ClientCall, … 1.0.0-rc.1 · 60202332

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.

API

StructClientOptionsWhat a client may hold open at once, how long it waits, and who it is under Noise.1
struct ClientOptions

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

Fields

connectionsIntConnections open at once; zero fails with INVALID_ARGUMENT.
pipelined_callsIntCalls one connection may carry at once; zero means one.
callsIntCalls admitted at once, unretrieved completions included; zero means connections times pipelined_calls.
connect_timeout_microsecondsIntTime budget for establishing a connection; zero means 5 seconds.
call_timeout_microsecondsIntTime budget for a full exchange, including queueing and redirects; zero means 30 seconds.
noise_private_keyOptional[SIMD[.uint8, 32]]The client identity for Noise handshakes.
peer_authorizerPeerAuthorizerVets each server's authenticated static key; required for Noise XX calls.
backendBackendThe requested I/O backend.
cpu_classCPUClassA ceiling on the CPU class; UNKNOWN takes what the CPU offers.
logLogWhere log lines go; the default keeps the client silent.
max_response_bytesIntThe largest response a call buffers; zero takes the largest socket receive buffer.

Conforms to

Copyable, Deinitable, Movable
__init__Every default but the connection budget.
def __init__(out self, *, connections: Int)

Parameters

connectionsIntConnections open at once.
StructClientCallOptionsOne call's time budget, retries, redirects and security, each zero field taking its default.1
struct ClientCallOptions

Fields

timeout_microsecondsIntTime budget including queueing and redirects; zero takes the client's call timeout.
max_retriesIntConnection attempts made again before sending the request; zero means 3.
max_redirectsIntRedirect hops followed before failing; zero means 5.
securitySecurityThe transport security; an "http+noise://" URL implies Noise XX.
noise_private_keyOptional[SIMD[.uint8, 32]]The private key this call authenticates with in place of the client's own.
noise_peer_keyOptional[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.
def __init__(out self)
StructClientCallOne HTTP call, owning everything the library reads until the call completes.5
struct ClientCall

Fields

verbVerbThe HTTP method.
urlStringThe full URL, "http://" or "http+noise://".
headersList[String]Full header lines like "accept: text/plain"; Host, Content-Length and User-Agent are generated.
bodyList[UInt8]The request body; empty sends no payload.
optionsClientCallOptionsThe call's own time budget, retries, redirects and security.

Conforms to

Copyable, Deinitable, Movable
getA GET of url with no headers.
def get(url: String) -> Self
postA POST of body to url with no headers.
def post(url: String, body: Span[UInt8]) -> Self
putA PUT of body to url with no headers.
def put(url: String, body: Span[UInt8]) -> Self
deleteA DELETE of url with no headers.
def delete(url: String) -> Self
patchA PATCH of body to url with no headers.
def patch(url: String, body: Span[UInt8]) -> Self
StructCompletedOne finished call: the context it was submitted with, and its response or failure.1
struct Completed

Fields

contextUInt64The context passed to Client.submit.
responseOptional[Response]The received response whatever its HTTP status, or None when none arrived.
errorOptional[Error]Why no response arrived, or None when one did.

Conforms to

Deinitable, Movable
__init__
def __init__(out self, context: UInt64, var response: Optional[Response], error: Optional[Error])
StructClientAn HTTP client, created, submitted to, polled, and dropped on one thread.10
struct Client

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

Fields

libraryLibraryThe loaded library, held so the release can still reach ucall_client_free.
handleAddressThe ucall_client_t.
serverOptional[ArcPointer[_ServerCore]]The server whose event loop drives a bound client, kept alive while the client is.
slotsList[_CallSlot]One per admitted call, sized once, so the inputs the library reads never move.
freeList[Int]Slots holding no call.
retrievedList[CompletedCallArguments]The library's completion records, one per slot.
keptList[Completed]Completions call retrieved for other calls, handed out by the next take_completed first.
call_timeout_microsecondsIntThe resolved time budget of one exchange, which 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.
def __init__(out self, library: Library, options: ClientOptions)
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.

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

Parameters

libraryLibraryThe loaded library, kept so the shared object stays mapped.
optionsClientOptionsThe budgets, timeouts and identity.
serverServerThe server whose loop drives it, kept alive while the client is.
event_loopIntWhich of the server's loops.

Raises

Error with INVALID_ARGUMENT for a zero connection budget, or 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.
def __deinit__(deinit self)
submitAdmits request without driving the network; its completion carries context. Calls ucall_client_call.
def submit(mut self, var request: ClientCall, context: UInt64 = UInt64(0))

Success guarantees exactly one completion from take_completed.

Parameters

requestClientCallvarThe call, owned until it completes.
contextUInt64Handed back with the completion.

Raises

Error with CONTENDED while every call slot holds an unretrieved call, or INVALID_ARGUMENT for a malformed URL.
take_completedDrives 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.
def take_completed(mut self, mut completed: List[Completed], timeout_microseconds: Int = Int(0))

Completions kept by call come first, without waiting.

Parameters

completedList[Completed]mutCleared, then filled with what finished, empty when nothing did in time.
timeout_microsecondsIntThe longest wait; zero performs one nonblocking round.

Raises

Error if the polling itself fails; a call's own failure arrives in its Completed.
callSubmits request and polls until it finishes, keeping other calls' completions for the next take_completed. Calls ucall_client_call and ucall_client_take_completed.
def call(mut self, var request: ClientCall) -> Response

Returns

The response, whatever its HTTP status.

Raises

Error if the call is refused or its exchange fails.
getFetches url and polls until the response arrives.
def get(mut self, url: String) -> Response
postPosts body to url and polls until the response arrives.
def post(mut self, url: String, body: Span[UInt8]) -> Response
putPuts body at url and polls until the response arrives.
def put(mut self, url: String, body: Span[UInt8]) -> Response
deleteDeletes url and polls until the response arrives.
def delete(mut self, url: String) -> Response
patchPatches url with body and polls until the response arrives.
def patch(mut self, url: String, body: Span[UInt8]) -> Response
StructResponseA received HTTP response, freed once dropped.7
struct Response

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

Fields

libraryLibraryThe loaded library, held so the release can still reach ucall_client_response_free.
handleAddressThe ucall_received_http_response_t.

Conforms to

Deinitable, Movable
__init__
def __init__(out self, library: Library, handle: Int)
__deinit__Frees the response. Calls ucall_client_response_free.
def __deinit__(deinit self)
status_codeThe status code the server sent. Calls ucall_client_response_status.
def status_code(self) -> HTTPStatus
bodyThe response body, decoded from chunked framing if it arrived that way. Calls ucall_client_response_body.
def body(ref self) -> Span[UInt8, origin_of(self)]
fields_countHeader field lines in arrival order, repeated names included. Calls ucall_client_response_fields_count.
def fields_count(self) -> Int
fieldHeader field line index: its lowercased name and its trimmed value. Calls ucall_client_response_field.
def field(ref self, index: Int) -> Tuple[Span[UInt8, origin_of(self)], Span[UInt8, origin_of(self)]]

Raises

Error with NOT_FOUND past the last line.