# flare.grpc.client

> A module of flare_mlake's API.

The gRPC unary client over the HTTP/2 ``HttpClient`` path.

Composes on the existing :class:`flare.http.HttpClient` HTTP/2 surface
rather than introducing a new transport: a unary call is one POST whose
body is the length-prefix-message (LPM) request frame and whose response
body is the LPM reply frame, with ``grpc-status`` / ``grpc-message`` read
from the response header set. The flare H2 client appends trailing
HEADERS into the same header list as the initial response HEADERS, so a
real gRPC server's status trailer is read transparently here.

Scope: unary (one request frame, one reply frame). Server-streaming,
client-streaming, and bidi are future phases on the same H2 stream
machinery; this module ships the unary path so the common request/reply
RPC works end to end today.

```mojo
from flare.grpc import GrpcClient

var ch = GrpcClient("http://127.0.0.1:50051")  # h2c cleartext
var reply = ch.call("/echo.EchoService/Echo", "hello".as_bytes())
if reply.is_ok():
    print(len(reply.message), "bytes back")
```

## Structs

### `GrpcCallResult`

```mojo
struct GrpcCallResult
```

The outcome of a unary call.

Fields:
    status: the RPC :class:`GrpcStatus` (code + message), read from
        ``grpc-status`` / ``grpc-message`` in the response headers.
    message: the LPM-unwrapped reply payload bytes (empty on a
        non-OK status or a trailers-only response).
    http_status: the HTTP/2 ``:status`` (200 for a well-formed gRPC
        response regardless of the RPC status code).
    headers: the full response header set (initial HEADERS + merged
        trailing HEADERS), for reading response metadata.

Implements: `Movable`

Fields:

- `status: GrpcStatus`
- `message: List[UInt8]`
- `http_status: Int`
- `headers: HeaderMap`

#### `is_ok`

```mojo
def is_ok(self) -> Bool
```

True iff the RPC status code is ``OK`` (0).

### `GrpcClient`

```mojo
struct GrpcClient
```

A blocking gRPC unary client over HTTP/2.

Wraps an :class:`HttpClient` configured for HTTP/2: cleartext h2c via
prior knowledge (``cleartext=True``, the default, for an ``http://``
base) or HTTP/2 over TLS via ALPN (``cleartext=False`` for an
``https://`` base). Response auto-decompression is disabled because
gRPC carries its own ``grpc-encoding`` independent of HTTP
``Content-Encoding``.

Implements: `Movable`

#### `__init__`

```mojo
def __init__(out self, base_url: String, *, cleartext: Bool = True)
```

Create a channel to ``base_url`` (origin only, e.g. ``http://host:port``). ``cleartext`` selects h2c prior knowledge vs HTTP/2 over TLS.

#### `__enter__`

```mojo
def __enter__(var self) -> Self
```

#### `call`

```mojo
def call(self, service_method: String, request: Span[UInt8], metadata: GrpcMetadata = GrpcMetadata(), timeout_ms: Int = Int(0)) -> GrpcCallResult
```

Invoke a unary RPC.

Args:

- `service_method`: The fully-qualified method path
    (``/package.Service/Method``); a leading ``/`` is added
    if missing.
- `request`: The serialised request message bytes (LPM-wrapped
    here; the caller passes raw protobuf / codec bytes).
- `metadata`: Optional initial metadata. Text entries become
    request headers verbatim; binary ``-bin`` entries are
    base64-encoded onto their ``-bin`` header per the gRPC
    wire format.
- `timeout_ms`: Optional call deadline in milliseconds. When
    ``> 0`` it is emitted as the ``grpc-timeout`` header
    (``<n>m`` millisecond form) so a deadline-aware server
    can abort the RPC and reply ``DEADLINE_EXCEEDED``.

Raises: NetworkError: on transport failure (connection / H2 error).

#### `call_server_streaming`

```mojo
def call_server_streaming(self, service_method: String, request: Span[UInt8], metadata: GrpcMetadata = GrpcMetadata()) -> GrpcServerStream
```

Open a server-streaming RPC: send one request message, then receive N reply messages.

The returned :class:`GrpcServerStream` yields reply payloads one
at a time via ``recv()`` (``None`` at end of stream) as the
server's DATA frames arrive -- only one message plus the
partial-frame reassembly buffer are ever held in memory. Read
the final ``grpc-status`` with ``status()`` after ``recv()``
returns ``None``.

Args:

- `service_method`: ``/package.Service/Method`` (leading ``/``
    added if missing).
- `request`: The single request message bytes (LPM-framed here).
- `metadata`: Optional initial metadata (text entries only).

#### `call_client_streaming`

```mojo
def call_client_streaming(self, service_method: String, metadata: GrpcMetadata = GrpcMetadata()) -> GrpcBidiStream
```

Open a client-streaming RPC: the request stream stays OPEN so the caller pumps request messages with ``send()`` then half-closes with ``close_send()``, after which ``recv()`` drains the (typically single) reply message and ``status()`` the final status.

Implemented on the same OPEN-stream machinery as :meth:`call_bidi`
(a client-streaming RPC is a bidi RPC the server answers only
after the request half-closes).

#### `call_bidi`

```mojo
def call_bidi(self, service_method: String, metadata: GrpcMetadata = GrpcMetadata()) -> GrpcBidiStream
```

Open a bidirectional-streaming RPC. The returned :class:`GrpcBidiStream` keeps the request side OPEN: pump request messages with ``send()``, half-close with ``close_send()``, and drain replies with ``recv()`` (then ``status()``).

Args:

- `service_method`: ``/package.Service/Method`` (leading ``/``
    added if missing).
- `metadata`: Optional initial metadata (text entries only).
