Marko Lalic: solicit - an HTTP/2 Library for Rust
For quite some time now, I've been following Rust, a new(ish) programming language by Mozilla. It's been around for a while, but recently it's been attracting a lot of buzz. Around the new year, I finally decided to take a deeper look into the language. I find that the best way to learn a new programming language is to simply dive in and try to build something nontrivial. At that point I'd also been interested in learning more about the specifics behind the new HTTP/2 spec, so I decided to combine the two... The result is a (work in progress) client library for HTTP/2 written in Rust, which can be found at this GitHub repository: https://github.com/mlalic/solicit What follows is a description of what I aim to achieve with the library, as well as an overview of its current design. If that doesn't interest you, you can just check out the GitHub repo for some examples or the source code itself. Goals Given that there are already some fairly popular HTTP libraries for Rust (hyper leading the way), I decided that I wouldn't aim to replace them. Rather, the idea is to focus on the lower levels of the HTTP/2 protocol what is essentially another transport protocol and expose an API that allows those libraries to use an HTTP/2 connection to send requests and receive responses. HTTP/2 doesn't change the semantics of the protocol, so once any HTTP/1.1 library gets the raw headers and body, it should be able to represent the response regardless of the fact that it came on an HTTP/2 connection. The same holds for sending requests: only the representation of the request changes, not its content. There are some minor differences, such as HTTP/2 doing away with the reason phrases being part of the response, but those were only meant to be human readable and "MUST NOT" have had any influence on the semantics of the response. Therefore, the primary goal for the library is to provide an API for using an HTTP/2 connection on the lowest level, but making it easy for clients to customize or augment any particular step of handling a response, without having to know the details of functionality below the layer they want to customize. As an example, if one wanted to build a client that sends out progress notifications for downloads, it wouldn't be good if they needed to muck about with raw HTTP/2 frames ideally, they'd only need to implement a new way to handle incoming data chunks for streams. The same applies for implementing a client that streams the download into a file (instead of saving it in memory) or one that sends the chunks to a different component for processing as soon as they're received (e.g. parsing HTML). Since the Rust standard library only exposes blocking IO primitives, I decided to base this library on blocking IO, as well. However, the goal was to make it flexible enough so that clients that would like to allow multiple requests to be sent concurrently on the same HTTP/2 connection can be built, but without the library itself spawning any threads. Finally, on a less conceptual level (and more of an implementation level), the code should have extensive test coverage. Design The library is split into several layers, each of which provides services to the one above it. A brief description of each layer follows. Transport Layer This layer provides the API for raw byte stream communication with the peer. Essentially, it's a wrapper around the particular transport protocol that is used for the HTTP/2 connection. As the HTTP/2 spec allows the transport protocol to be either a cleartext TCP connection or a TLS-protected one, those are the ones that need to be implemented at this level. The API of the layer is represented by a single trait
TransportStream that simply requires io::Read and io::Write to be implemented and (for now) provides a single convenience method based on the underlying io::Read implementation. For a clear-text TCP connection, the only thing that was required to implement it was one line of code: impl TransportStream for TcpStream
HTTP/2 Connection Layer The connection layer provides an API for reading and writing to a raw HTTP/2 connection. This means reading and writing HTTP/2 frames, not arbitrary bytes. Each HTTP/2 frame has to follow the same basic structure(a 9-byte header followed by a sequence of byte representing the payload of the frame). This is represented by the RawFramestruct. Each HTTP/2 frame type can have its own specific format for the content of the payload and assign meaning to the flags found in the header. Therefore, each frame needs to have its own implementation that can parse a RawFrame or return an error if the raw frame does not represent a valid frame of that type. Some common methods that each frame type implementation needs to support are given by the Frametrait that must be implemented by the implementations. Finally, the HttpConnectionstruct handles reading the byte stream from the underlying TransportStream instance, correctly delineating frames and parsing them into the appropriate frame type implementation (by delegating to the correct Frame implementation based on the frame type found in the header). Obviously, varying the implementation of the transport layer is a matter of choosing the appropriate TransportStream implementation. This includes using mock implementations for testing the HttpConnection itself. HTTP/2 Client Connection The ClientConnectionbuilds upon the HttpConnection by using it to expose functionality that is specific to the client side of an HTTP/2 connection. This includes sending requests and reading responses. The struct takes care of creating frames that need to be sent for a given request (including HPACK header encoding) and shipping them off to the server (using the underlying HttpConnection instance). As for handling a response, the ClientConnection expects to get an instance of a struct implementing the Sessiontrait when constructed. This trait essentially defines a number of callbacks: methods that will be called on this instance when the corresponding event occurs in the ClientConnection. Therefore, the ClientConnection understands the semantics of the particular frames that it reads (e.g. performs the decoding of the HEADERS frames' payloads, passing the raw data received in a DATA frame to the session, etc.). However, it does not know anything about streams; it only passes the stream ID up to the session layer, without thinking about whether the stream is open, valid, etc. HTTP/2 Session As mentioned in the previous section, each ClientConnection requires an instance of a struct implementing the Sessiontrait to be passed to it at construction time. The Session trait defines a number of callbacks that are invoked in the appropriate moments during the processing of frames read on a client connection. For example, new_headers (takes a list of heaers and a stream on which they were received) or new_data_chunk (a buffer and a stream on which the data arrived). The session is in charge of handling these events as they arise. It is up to it to decide what (if anything) should be done with the data that it receives. There is a default implementation of the Session trait (unimaginatively named DefaultSession), which provides an implementation that tracks which streams are currently open and delegates the processing of new events on particular streams to instances of a Streamtrait. This allows for client implementations to simply vary the implementation of a Stream that they provide to the DefaultSession in order to change how a particular part of a response is handled, but let the DefaultSession take care of tracking which streams are open and valid. Different implementations of a Session still can exist, if the particular client needs more fine-grained customization over how these events are handled, but for most purposes the default session, along with Stream customization should probably be enough. HTTP/2 Stream Again, as mentioned previously, the DefaultSessionimplementation of the Session trait relies on a Streamtrait providing the handling for events arising on individual streams. This level should be the one that gets customized most often in alternate implementations. A default stream implementation DefaultStreamhandles the messages by building up a response in-memory. Different implementations could instead send progress notifications on an associated channel, for instance. HTTP/2 Client The final layeris that of a client that exposes methods that would usually be expected for a client HTTP library: sending requests, obtaining responses. This layer is not the primary focus, though, and it only exposes two simple implementations that showcase how the lower level APIs can be used to build a client. The second purpose it fulfills is as a "sanity check" that the APIs actually do allow the goals to be met. Therefore, one of the clients allows for requests to be sent from concurrently running threads and the responses to those requests to be obtained asynchronously (in the form of a Future<Response>). However, the responses are basic and very raw: they are stored fully in memory (not available until they've been read until the end) and both the body, as well as the headers, exposed as raw byte sequences. As an example how this clientcan be used (as well as the simpler single-threaded version), check out the Examplessection in the GitHub repository's README file. Future Work Refactoring APIs As the project was started as a Rust learning exercise, the initial APIs are slightly awkward and fairly inefficient to work with. For example, the solicit::hpackmodule requires headers to be represented as a tuple of Vec<u8>, which means that whenever we need to encode a header list, even though their values might be (statically known) byte sequence literals, such as (b":method", b"GET"), we need to make a bunch of to_vec calls on the &[u8]s before passing them to the encoder, leading not only to a really ugly and tedious way of obtaining the encoded representation of the headers, but also to a lot of unnecessary allocations. Another API that will see some refactoring and improvement is the TransportStream, as well as the HttpConnection in order to make it possible to have separate threads perform reads and writes on the connection. Currently, the socket is owned by the HttpConnection instance and all writes and reads require a &mut reference, making this impossible. This change would tackle this by embracing Rust's new try_clone method on the TcpStream into the TransportStream and refactoring the HttpConnection to allow splitting it into a write and read handle. HTTP/2 Features As already mentioned, only a small subset of the full HTTP/2 spec is currently implemented, so, obviously, implementing the rest of it falls under the most important future work. Some near- to mid-term priorities that will be tackled are: - TLS-backed connections: a new
TransportStreamimplementation that wraps a TCP socket into a TLS wrapper. This will probably rely on OpenSSL bindings. - Support for all HTTP/2 frames: only the three vital frames are implemented so far; other frames are a prerequisite to implementing properly closing streams, handling closed streams etc.
- Signaling stream- and connection-level errors to the peer
- Flow control: right now this aspect is completely ignored
- Server push: the idea is that multithreaded clients could handle pushed responses as soon as they arive, whereas single-threaded ones would be allowed to fetch a list of them every so often.
ApologyAs a twisted turn of fate, I need to start my platform with an apology. Last year, I
Slow start today; I was pretty exhausted after yesterday and last night's
work. Somehow though, I got past the burn and made major progress today.
All the complex movement of both the player and the scroll is finished now,
and all that remains is to write interesting spells, and a system for
learning spells, and to balance out the game difficulty.

Between december 9th 2014 and march 9th 2015 I ve been working as an intern for Debian on Improving AppArmor support in Debian.
Progress
Before the internship started, I had no clue about AppArmor at all. Reading the Wikipedia page and the 




I love reading
I was writing in the
My monthly report covers a large part of what I have been doing in the free software world. I write it for
Over the past week I've mailed many of the people who had signed my previous GPG key and who had checked my ID as part of that process. My intention was to ask "Hey you trusted me before, would you sign
It is demanding (1500 meter rise in elevation from the bus stop, crampons must be worn on the glacier) but the view is breathtaking. While we were there, another nearby volcano
This was my third month working on Debian LTS, and the first where
I actually uploaded packages. I also worked on userland packages
for the first time.
In the middle of February I finished and uploaded a security update
for the kernel package (linux-2.6 version 2.6.32-48squeeze11,
me: Who are you?
joeyh: I'm Joey --
There was also a small syntax bugfix, but functionally nothing has changed because bash was doing the right thing even with that small error.
If you have started using Occasional Rsnapshot, you definitely want now Occasional Rsnapshot v1.3.1. If you haven't started and you don't have backups, please start doing backups, and while you're at it, you might want to try Occasional Rsnapshot (with Rsnapshot).