HTTP2.jl

A HTTP2 support library for Julia
Popularity
10 Stars
Updated Last
2 Years Ago
Started In
May 2016

HTTP2

Build Status HTTPClient

A HTTP2 support library that handles frames, streams and connections.

julia> Pkg.add("HTTP2")
julia> using HTTP2

Simple Servers and Clients

The library can directly create simple servers and clients. For full support of HTTP/2 Upgrade and HTTPS, use HttpServer.jl and Requests.jl.

You only use this library directly if you need low-level functionality. An example for the server is as follows. The code will be explained in the next section.

server = listen(port)

println("Server started.")
while(true)
    buffer = accept(server)
    println("Processing a connection ...")

    connection = Session.new_connection(buffer; isclient=false)
    ## Recv the client preface, and send an empty SETTING frame.

    headers_evt = Session.take_evt!(connection)
    stream_identifier = headers_evt.stream_identifier

    sending_headers = Headers(":status" => "200",
                              "server" => "HTTP2.jl",
                              "date" => Dates.format(now(Dates.UTC), Dates.RFC1123Format),
                              "content-type" => "text/html; charset=UTF-8")

    Session.put_act!(connection, Session.ActSendHeaders(stream_identifier, sending_headers, false))
    Session.put_act!(connection, Session.ActSendData(stream_identifier, body, true))

    ## We are done!
end

A client can be started in a similar way. Again the code will be explained in the next section.

buffer = connect(dest, port)

## Create a HTTPConnection object
connection = Session.new_connection(buffer; isclient=true)

## Create a request with headers
headers = Headers(":method" => "GET",
                  ":path" => url,
                  ":scheme" => "http",
                  ":authority" => "127.0.0.1:9000",
                  "accept" => "*/*",
                  "accept-encoding" => "gzip, deflate",
                  "user-agent" => "HTTP2.jl")

Session.put_act!(connection, Session.ActSendHeaders(Session.next_free_stream_identifier(connection), headers, true))

return (Session.take_evt!(connection).headers, Session.take_evt!(connection).data)

Connection Lifecycle

HTTP/2 is a binary protocol, and can handle multiple requests and responses in one connection. As a result, you cannot read and write directly in the stream like HTTP/1.1. Instead, you talk with the connection through channels. The main interface of low-level HTTP/2 support resides in HTTP2.Session module.

import HTTP2.Session

Create a Buffer

First you need to create a buffer that the connection can read and write. A normal TCP connection from a client is usually like this:

buffer = connect(dest, port)

While for server, it usually looks like this:

server = listen(port)
buffer = accept(server)

You can also use MbedTLS.jl or other TLS library to get a buffer over TLS and initialize a HTTPS connection.

Initialize the Connection

After getting the buffer, we can start to initialize the connection.

connection = Session.new_connection(buffer; isclient=true)

isclient key indicates whether you are a server or a client. This is needed because the server and client uses different stream identifiers.

Another important key to note is skip_preface. For a normal HTTP/2 connection, this is usually set to false. However, if you are doing HTTP/2 protocol upgrade (in which case the HTTP/2 preface should be skipped), you should set this key to true.

Initialize a New Stream

You don't need to do anything in particular to initialize a new stream, because they are solely identified by its identifier. To get a new stream identifier, call the next_free_stream_identifier(connection) function.

Send and Receive Headers and Data

The connection is then alive, and you can start to send or receive headers and data through the connection. Those are done by the take_evt! and put_act! functions. take_evt!(connection) waits and return an event struct from the connection. put_act!(connection, action) put a new action to the connection and returns immediately.

Actions

  • ActPromise(stream_identifier, promised_stream_identifier, headers): This is usually sent from a server which sends a push promise. stream_identifier is the main stream identifier, promised_stream_identifier is the promised stream identifier that is going to be pushed, and headers are a Headers struct that sends the requests.
  • ActSendHeaders(stream_identifier, headers, is_end_stream): This can be used to send request headers, response headers, or other headers specified in the HTTP specification. If there's no more headers or data to be sent in the stream, is_end_stream should set to true.
  • ActSendData(stream_identifier, data, is_end_stream): This can be used to send request body, response body, or if a protocol switch is initialized, other specified protocol data. If there's no more headers or data to be sent in the stream, is_end_stream should set to true.

Events

  • EvtPromise(stream_identifier, promised_stream_identifier, headers): This event is triggered when a push promise is received. The struct is similar to ActPromise.
  • EvtRecvHeaders(stream_identifier, headers, is_end_stream): This event is triggered when a header is received. The struct is similar to ActSendHeaders.
  • EvtRecvData(stream_identifier, data, is_end_stream): This event is triggered when data is received in a stream. The struct is similar to ActSendData.
  • EvtGoaway(): This event is triggered when the whole connection is about to be closed.

Close the Connection

The connection can be closed using close(connection).

Frame

You can do using HTTP2.Frame to import the library. After that, a encode function and a decode function are available. The encode function takes a typed frame into its binary form. The decode function takes an IO buffer, and returns a typed frame.

For details about frames, see the HTTP2 Specification.