http2-client-grpc- Implement gRPC-over-HTTP2 clients.

Safe HaskellNone




A module adding support for gRPC over HTTP2.

This module provides helpers to encode gRPC queries on top of HTTP2 client.

The helpers we provide for streaming RPCs (streamReply, streamRequest, steppedBiDiStream) are a subset of what gRPC allows: the gRPC definition of streaming RPCs offers a large amount of valid behaviors regarding timing of headers, trailers, and end of streams.

The limitations of these functions should be clear from the type signatures. But in general, the design is to only allow synchronous state machines. Such state-machines cannot immediately react to server-sent messages but must wait for the client code to poll for some server-sent information. In short, these handlers prevents programs from observing intermediary steps which may be valid applications by gRPC standards. Simply put, it is not possibly to simultaneously wait for some information from the server and send some information. For instance, in a client-streaming RPC, the server is allowed to send trailers at any time, even before receiving any input message. The streamRequest functions disallows reading trailers until the client code is done sending requests. A result from this design choice is to offer a simple programming surface for the most common use cases. Further, these simple state-machines require little runtime overhead.

A more general handler generalHandler is provided which runs two thread in parallel. This handler allows to send an receive messages concurrently using one loop each, which allows to circumvent the limitations of the above handlers (but at a cost: complexity and threading overhead). It also means that a sending action may be stuck indefinitely on flow-control and cannot be cancelled without killing the RPC thread. You see where we are going: the more elaborate the semantics, the more a programmer has to think.

Though, all hope of expressing wacky application semantics is not lost: it is always possible to write its own RPC function. Writing one's own RPC function allows to leverage the specific semantics of the RPC call to save some overhead (much like the three above streaming helpers assume a simple behavior from the server). Hence, it is generally a good idea to take inspiration from the existing RPC functions and learn how to write one.


Building blocks.

data RPC s (m :: Symbol) #

A proxy type for giving static information about RPCs.



newtype RPCCall s (meth :: Symbol) a Source #

Newtype helper used to uniformize all type of streaming modes when passing arguments to the open call.

type Authority = HeaderValue #

The HTTP2-Authority portion of an URL (e.g., "").

newtype Timeout #

Timeout in seconds.


Timeout Int 

open Source #


:: (Service s, HasMethod s meth) 
=> Http2Client

A connected HTTP2 client.

-> Authority

The HTTP2-Authority portion of the URL (e.g., "").

-> HeaderList

A set of HTTP2 headers (e.g., for adding authentication headers).

-> Timeout

Timeout in seconds.

-> Encoding

Compression used for encoding.

-> Decoding

Compression allowed for decoding

-> RPCCall s meth a

The actual RPC handler.

-> ClientIO (Either TooMuchConcurrency a) 

Main handler to perform gRPC calls to a service.

type RawReply a = Either ErrorCode (CIHeaderList, Maybe CIHeaderList, Either String a) Source #

A reply.

This reply object contains a lot of information because a single gRPC call returns a lot of data. A future version of the library will have a proper data structure with properly named-fields on the reply object.

For now, remember: - 1st item: initial HTTP2 response - 2nd item: second (trailers) HTTP2 response - 3rd item: proper gRPC answer


singleRequest Source #


:: (Service s, HasMethod s meth) 
=> RPC s meth

RPC to call.

-> MethodInput s meth

RPC's input.

-> RPCCall s meth (RawReply (MethodOutput s meth)) 

gRPC call for an unary request.

streamReply Source #


:: (Service s, HasMethod s meth, MethodStreamingType s meth ~ ServerStreaming) 
=> RPC s meth

RPC to call.

-> a

An initial state.

-> MethodInput s meth

The input.

-> (a -> HeaderList -> MethodOutput s meth -> ClientIO a)

A state-passing handler that is called with the message read.

-> RPCCall s meth (a, HeaderList, HeaderList) 

gRPC call for Server Streaming.

streamRequest Source #


:: (Service s, HasMethod s meth, MethodStreamingType s meth ~ ClientStreaming) 
=> RPC s meth

RPC to call.

-> a

An initial state.

-> (a -> ClientIO (a, Either StreamDone (CompressMode, MethodInput s meth)))

A state-passing action to retrieve the next message to send to the server.

-> RPCCall s meth (a, RawReply (MethodOutput s meth)) 

gRPC call for Client Streaming.

steppedBiDiStream Source #


:: (Service s, HasMethod s meth, MethodStreamingType s meth ~ BiDiStreaming) 
=> RPC s meth

RPC to call.

-> a

An initial state.

-> RunBiDiStep s meth a

The program.

-> RPCCall s meth a 

gRPC call for a stepped bidirectional stream.

This helper limited.

See BiDiStep and RunBiDiStep to understand the type of programs one can write with this function.

generalHandler Source #


:: (Service s, HasMethod s meth) 
=> RPC s meth

RPC to call.

-> a

An initial state for the incoming loop.

-> (a -> IncomingEvent s meth a -> ClientIO a)

A state-passing function for the incoming loop.

-> b

An initial state for the outgoing loop.

-> (b -> ClientIO (b, OutgoingEvent s meth b))

A state-passing function for the outgoing loop.

-> RPCCall s meth (a, b) 

General RPC handler for decorrelating the handling of received headers/trailers from the sending of messages.

There is no constraints on the stream-arity of the RPC. It requires a bit of viligence to avoid breaking the gRPC semantics but this one is easy to pay attention to.

This handler runs two loops concurrently: One loop accepts and chunks messages from the HTTP2 stream, then return events and stops on Trailers or Invalid. The other loop waits for messages to send to the server or finalize and returns.

data StreamDone Source #



data BiDiStep s meth a Source #



Finalize and return the current state.

SendInput !CompressMode !(MethodInput s meth)

Sends a single message.

WaitOutput (HandleMessageStep s meth a) (HandleTrailersStep a)

Wait for information from the server, handlers can modify the state.

type RunBiDiStep s meth a = a -> ClientIO (a, BiDiStep s meth a) Source #

State-based function.

type HandleMessageStep s meth a = HeaderList -> a -> MethodOutput s meth -> ClientIO a Source #

Handler for received message.

type HandleTrailersStep a = HeaderList -> a -> HeaderList -> ClientIO a Source #

Handler for received trailers.

data IncomingEvent s meth a Source #

An event for the incoming loop of generalHandler.


Headers HeaderList

The server sent some initial metadata with the headers.

RecvMessage (MethodOutput s meth)

The server send a message.

Trailers HeaderList

The server send final metadata (the loop stops).

Invalid SomeException

Something went wrong (the loop stops).

data OutgoingEvent s meth b Source #

An event for the outgoing loop of generalHandler.



The client is done with the RPC (the loop stops).

SendMessage CompressMode (MethodInput s meth)

The client sends a message to the server.


data InvalidState Source #

Exception raised when a ServerStreaming RPC results in an invalid state machine.


InvalidState String 

data InvalidParse Source #

Exception raised when a BiDiStreaming RPC results in an invalid parse.


InvalidParse String 

Compression of individual messages.

data Compression #

Opaque type for handling compression.

So far, only "pure" compression algorithms are supported. TODO: suport IO-based compression implementations once we move from Builder.

gzip :: Compression #

Use gzip as compression.

uncompressed :: Compression #

Do not compress.


type HeaderList = [Header] #

Header list.