How to play with HTTP/2 in Ballerina
Writing a Ballerina HTTP/2 client and server
NOTE: All the Ballerina codes in this article are tested and compatible with Ballerina version 1.1.0
What is Ballerina
Ballerina is a cloud-native programming language whose syntax and run-time address the difficult problems of integration.
What is HTTP/2
HTTP/2 is the second major version of the application protocol, which was officially standardized in response to Google’s HTTP-compatible SPDY protocol. These 2 articles explain everything clearly.
In a nutshell, HTTP/2 has been released to address the major problems of HTTP/1.1
- HTTP/2 is binary instead of textual like HTTP/1.1 — this makes it transfer and parsing of data over HTTP/2 inherently more machine-friendly, thus faster, more efficient and less error-prone.
- HTTP/2 is fully multiplexed allowing multiple files and requests to be transferred at the same time, as opposed to HTTP/1.1 which only accepted one single request/connection at a time.
- HTTP/2 uses the same connection for transferring different files and requests, avoiding the overhead operation of opening a new connection for every file which needs to be transferred between a client and a server.
- HTTP/2 has header compression built-in which is another way of removing several of the overheads associated with HTTP/1.1 having to retrieve several different resources from the same or multiple web servers.
- HTTP/2 allows servers to push required resources proactively rather than waiting for the client browser to request files when it thinks they are needed.
Prerequisites
Before starting, you have to setup your machine with Ballerina. Please refer to Ballerina’s Getting Started Guide.
Once you have successfully installed Ballerina, execute ballerina -v
command to ensure it works. This should display the Ballerina version you installed.
Let’s Start
The Ballerina supports HTTP/2 transport since Ballerina version 0.970.0 and the support has been optimized and improved a lot as of today. The only difference that you can see, between an HTTP/1.1 and HTTP/2 server or client of Ballerina, is the httpVersion: "2.0"
annotation at the Server/Client endpoint initialization.
As an example following code samples demonstrate the difference between HTTP/1.1 client and HTTP/2 client endpoint initialization.
http:Client clientEP = new("http://localhost:9095");
If we add httpVersion: "2.0"
annotation to endpoint configuration it would simply become an HTTP/2 supported client as follows.
http:Client clientEP = new("http://localhost:9095",
{ httpVersion: "2.0" });
HTTP/2 Server
The following code represents how to start a simple Ballerina HTTP/2 server.
This starts a server which accepts any request either in HTTP/1.1 or in HTTP/2.
Even though the line #3 says that this service should be an HTTP/2 service, according to HTTP/2 specification (RFC-7540), it should accept both HTTP/1.1 and HTTP/2 requests, since the client may not aware of the service whether it supports HTTP/2 and vice versa.
The next 2 subtopics explain the possible scenarios clearly.
HTTP/1.1 request -> HTTP/2 service
Let’s see how to invoke the HTTP/2 service by an HTTP/1.1 request.
$ curl -v http://localhost:9095/hello/sayHello
Following is the response you get as the result for the above curl
command. According to that, it acts as a general HTTP/1.1 request and an HTTP/1.1 response. The highlighted segments show that.
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9095 (#0)
> GET /hello/sayHello HTTP/1.1
> Host: localhost:9095
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/plain
< content-length: 19
< server: ballerina/1.1.0
< date: Tue, 7 May 2019 10:55:18 +0530
<
* Connection #0 to host localhost left intact
Ballerina HTTP/2 Response !
HTTP/2 request -> HTTP/2 service
Now let’s see how to invoke the HTTP/2 service by an HTTP/2 request. To have curl
commands with HTTP/2 support, we use a special flag, which is --http2
. Refer the below guide to activate it.
$ curl --http2 -v http://localhost:9095/hello/sayHello
Following is the response you get as the result for the above curl
command. According to that, first, it sends 3 special headers which are “Connection”, “Upgrade” and “HTTP2-Settings”. These headers make the request an HTTP/2 request and with that, the server will identify and distinguish the HTTP/2 request. As the response, it has received “HTTP/1.1 101 Switching Protocols” in header mentioning that the server supports HTTP/2. Finally, as the response, it has received the actual payload with 200 OK. The highlighted segments show that.
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9095 (#0)
> GET /hello/sayHello HTTP/1.1
> Host: localhost:9095
> User-Agent: curl/7.46.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQAAP__
>
< HTTP/1.1 101 Switching Protocols
< connection: upgrade
< upgrade: h2c
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* TCP_NODELAY set
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
< HTTP/2.0 200
< content-type:text/plain
< server:ballerina/0.980.0
< date:Wed, 18 Jul 2018 13:52:49 +0530
<
* Connection #0 to host localhost left intact
Ballerina HTTP/2 Response !
It is as simple as that !
HTTP/2 Client
The following code represents how to create a simple Ballerina HTTP/2 client. This client acts as the HTTP/2 curl
command which was demonstrated previously.
$ curl --http2 -v http://localhost:9095/hello/sayHello
Beyond line #8 of the code, it is for processing the response received. With that, you check the response received, whether it is an actual HTTP response or an error.
You are done. Writing a Ballerina HTTP/2 client and server is simple as that !
HTTP/2 Server-Push
This is the most interesting part of HTTP/2. As you already know, this allows servers to push required resources proactively rather than waiting for the client browser to request files when it thinks they are needed.
Let’s see, how you can do it simply with Ballerina.
HTTP/2 Server
The implementation of HTTP/2 server with Ballerina is explained step by step in this section.
The following code block represents how you can send a promise to the client. It notifies the client that the server promises to send a response after the primary response is sent.
http:PushPromise promise = new(path = "/resource", method = "GET");
checkpanic caller->promise(promise);
Then, the following code block represents, how you can push the response for the promise, to the client. It sends a response to the client, referring already sent promise.
http:Response push = new;
checkpanic caller->pushPromisedResponse(promise, push);
Finally, the behavior of the following sample HTTP/2 server is as follows:
- Sends 2 promises
- Sends the response for the received request
- Sends 2 push responses for the sent 2 promises
HTTP/2 Client
The implementation of HTTP/2 client with Ballerina is explained step by step in this section.
The following code block represents how you can invoke the server with an HTTP/2 request and get a reference of http:HttpFuture
object. It is used for actions like checking whether promises exists, retrieving the promises, etc.
http:Request serviceReq = new;
http:HttpFuture httpFuture = checkpanic clientEP->submit("GET", "/hello/sayHello", serviceReq);
Then, you can get the response for the original request as follows. Here we use the reference of http:HttpFuture
object, which was received for the initial submit a request we sent to the server.
http:Response response = checkpanic clientEP->getResponse(httpFuture);
Then, you can check whether there are any promises exist for the sent request as follows. Here also we use the reference of http:HttpFuture
object.
boolean hasPromise = clientEP->hasPromise(httpFuture);
The following code block represents, how you can get the promise message sent by the server. Here also we use the reference of http:HttpFuture
object.
http:PushPromise pushPromise = checkpanic clientEP-> getNextPromise(httpFuture);
Finally, the behavior of the following sample HTTP/2 is client as follows:
- Submits a request and get a
http:HttpFuture
object - Gets the response for the sent request
- Checks whether promises are exist
- Gets the promise message
- Gets the response for the promise received
- Checks whether more promises exist and if yes, continue from STEP 4
You are done. Writing a Ballerina HTTP/2 client and server with push promises is simple as that !
Happy coding with Ballerina !