How to play Go Lang with HTTP

Writing Go Lang HTTP/1.1 & HTTP/2 client and server

Chanaka Lakmal
7 min readMay 5, 2019

Introduction

In this article, we will implement HTTP client and server with both HTTP/1.1 and HTTP/2 protocols; and with Go programming language.

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. Following articles explain everything about HTTP/2 clearly.

Prerequisites

Before starting, you have to setup your machine with Go Lang. Please refer Go Getting Started Guide.

Once you have successfully installed Go, execute go version command to ensure it works. This should display the Go version you installed.

NOTE: All the Go codes in this article are tested and compatible with Go version 1.11.5

HTTP/1.1 Server

The following code represents how to start a simple HTTP/1.1 server in Go. Here, we have implemented a Go echo server, which echoes back the requests.

The main function starts the HTTP(S) service on the port 9191, with the path hello/sayHello. The echoPayload handler function is responsible for the echo logic, which reads the incoming payload and replies with the same payload implementation. You can modify the response by updating the echoPayload handler as you wish.

Since we are starting an HTTP(S) service here, we have to provide a server certificate and a server key. So, both of them are saved inside a directory called cert as server.crt and server.key.

If interested to learn more on the SSL certificates, the following article clearly explains how to work with SSL certificates, private keys, and CSRs.

A sample self-signed certificate and a key file can be found below.

./cert/server.crt

-----BEGIN CERTIFICATE-----
MIID+zCCAuOgAwIBAgIJAPsvGCCAC2i+MA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
VQQGEwJMSzEQMA4GA1UECAwHV2VzdGVybjEQMA4GA1UEBwwHQ29sb21ibzESMBAG
A1UECgwJTERDTEFLTUFMMRQwEgYDVQQLDAtFbmdpbmVlcmluZzESMBAGA1UEAwwJ
bG9jYWxob3N0MSIwIAYJKoZIhvcNAQkBFhNsZGNsYWttYWxAZ21haWwuY29tMB4X
DTE5MDQyMDA1MjczM1oXDTIwMDQxOTA1MjczM1owgZMxCzAJBgNVBAYTAkxLMRAw
DgYDVQQIDAdXZXN0ZXJuMRAwDgYDVQQHDAdDb2xvbWJvMRIwEAYDVQQKDAlMRENM
QUtNQUwxFDASBgNVBAsMC0VuZ2luZWVyaW5nMRIwEAYDVQQDDAlsb2NhbGhvc3Qx
IjAgBgkqhkiG9w0BCQEWE2xkY2xha21hbEBnbWFpbC5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC9PKAOlJcOBUI9CGnVjMjQHNRqYv01CaUdC4/e
YFyegxLpoMpYvEC+nYlHT2j7BOhQBV+TkH1D4YOK2WP3V0FLv5hM7Nxsgf25WNHa
zi2DTBvcBgB9sDJA/avIvF+63+Btnyggp3xq6MaHy5DNH0kPnSiPiy7PRKToEUn6
oqPnB10rRBFZqs3ePmEDxVL3T/TUZSXR3P95fV1vDCqrJbr3YwWOzFCq8kEJFslK
B7GSEKpPgmK0g5krmAQqUOuCJ3/xFlCP4trKg/lvSJZ5S/LZD5teDDg6Ax3Mvthj
kMh9/OM5GGTTjRwhct9dHjFI8POj+TMbLZvoPVXjsmATEgtLAgMBAAGjUDBOMB0G
A1UdDgQWBBQ1CmWXmrHOv6b8f763/bk80EpbajAfBgNVHSMEGDAWgBQ1CmWXmrHO
v6b8f763/bk80EpbajAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAH
D51Uoe2K4N9/GxRgww5mMW2dUJ7Hc/tGsr/J1fNqHY8SXNAn5i+GwI+xBvwxFHL3
KZHbfq7eYDE5EItt3cZp5ySSscdTEay9ReH2+8k32gpH46CMwPV3XvtQuBVVAC4u
szrq1eWKhYI2zf4iUVpwvq89OynVGIp0atng+q3A2cBhi3NGo6Ho1s2rywQyqiq8
up4PUSVQ6WBoJFx5PEEDxD84VMS7Pan6dT34b9n56tq5R06retZTUZ8jMM88CGX4
88pSPU+XImp6DdNVBmW6Lz76jiSNHLkZGm4jumjeyUGzBjBEBOgSegeWlinMtWE9
gaVxeUHrqHk8xzwJ4oIu
-----END CERTIFICATE-----

./cert/server.key

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9PKAOlJcOBUI9
CGnVjMjQHNRqYv01CaUdC4/eYFyegxLpoMpYvEC+nYlHT2j7BOhQBV+TkH1D4YOK
2WP3V0FLv5hM7Nxsgf25WNHazi2DTBvcBgB9sDJA/avIvF+63+Btnyggp3xq6MaH
y5DNH0kPnSiPiy7PRKToEUn6oqPnB10rRBFZqs3ePmEDxVL3T/TUZSXR3P95fV1v
DCqrJbr3YwWOzFCq8kEJFslKB7GSEKpPgmK0g5krmAQqUOuCJ3/xFlCP4trKg/lv
SJZ5S/LZD5teDDg6Ax3MvthjkMh9/OM5GGTTjRwhct9dHjFI8POj+TMbLZvoPVXj
smATEgtLAgMBAAECggEAbaS2yDvn2cPKQTqit4y+vXY2zP1V4GkaNd4BGcOTZnRj
fOIg25EXoln8tEiadva888BpREKvkakUYlraxPDVcGIuiEOk42nd7Io97R0Q2cY7
ThxcJHb2ZxmTctdSUCBvFJTm1ySzve3pOb0ExRSfbGCOo7zs/kKzmZKK3qFlffGS
Ga9O7hyLOuXPU22CM+5Lq0JPTER73z0DpAweZc0L14j6dzhcG3qUwk0K6K47VZgE
NhEORul7xDj91bh2iEoSbaQe8HxLaMQoMXOC/9oey2UKKTe9WZE3+XCvg+vkw/sS
biQ+b4EZ9LuhAhCZ0UE6+y7PZY+8G/YsbGg0Zo8cAQKBgQDyTuG47rWBgbdHsEB/
MSKGU6w+a1SdLk6jG+Enji5Q624/h0xt5nF9ah2eRf3Rlhn9WEKM/uE9ouEODBKE
8rnIDsjufEMI8moPEloRBSsxPNw+fNMSSCZjL+qPtTJUbRio7WA23sfdnE57ygBa
wlPQ9UBBWSm2se4veEZtHjtngQKBgQDH7gnH5Att6ZYazRTgD72g0C5v1l4LYVEQ
jxdBcs6TJA8wHfifZ45F67W95QunmM813UxfS+ybdjytlb8/lmi2BnK6lDx5HWIL
31jnbg2CxCrNv9oZLjKVDmkp4WUcEp5W33R1/MGDTRfyARP+6QYQO/ATMdqtm5Uu
cD6clrL4ywKBgQCQ0niy0WmGaAMlQ8CoxLM/2c6+1+OQtlalwkoGHEKudqhELBeQ
MAVw0fW13Vtg4vfRpejQ4J26+xjMDocbEv/bBIsvjvF57XlaXLucJJy2Jwv0BSMa
cCkRa1gkYEYek74DaSzyXqDSYVO/RPKFTFRQNeUbqbD20s3rbVWablFPAQKBgB5y
zUCJJYh2w6qPQzegjhO4wOm9bxMyngL0l+ka0AUuv7VnSx8TyWIytLoX8P90UVJ1
wpTc3ksK5dDV9ot7n7ThJIXv34nehLkkKckNRLd+oro1FsUw+PkkebWsIxb0avL2
EymI9fvGOPhdW6s91/OO/VAfDpvUDxNEevSkKtujAoGAcMOsXtn/UyT3Lssxgla3
K+DCaFhAQPSUXOmpZwEbQ0yQlksDe4flsam8bEDI5D5iHx1ziSfh583qJl3BEZ5u
VZTEO2YLvT9QRz7pv2qspqj7nzSyBU2BFAajq43/G1b8FHfVgN+YdVtzVrigfql5
2a+JxOxFfpjnGQ7RfSxSb+Q=
-----END PRIVATE KEY-----

Let’s see how to start and test the service after configuring all of the above.

  • Run the service with the command below:
$ go run http_server.go
  • You can expect the following output:
Go Backend: { HTTPVersion = 1 }; serving on https://localhost:9191/hello/sayHello
  • Invoke the service with an HTTP/1.1 POST request with a small text payload (insecure mode and secure mode):
$ curl -k -v https://localhost:9191/hello/sayHello -d "Hello Go!"$ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
  • Following is the result you get for the above curl command. The first part of the result has details on TLS handshake with the server and client. The next part of the result has the details on HTTP/1.1 request and HTTP/1.1 response. Finally, it has Hello Go! text which received via the response.
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9191 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=LK; ST=Western; L=Colombo; O=LDCLAKMAL; OU=Engineering; CN=Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* start date: Apr 20 03:03:58 2019 GMT
* expire date: Apr 19 03:03:58 2020 GMT
* issuer: C=LK; ST=Western; L=Colombo; O=LDCLAKMAL; OU=Engineering; CN=Chanaka Lakmal; emailAddress=ldclakmal@gmail.com
* SSL certificate verify result: self signed certificate (18), continuing anyway.
> POST /hello/sayHello HTTP/1.1
> Host: localhost:9191
> User-Agent: curl/7.46.0
> Accept: */*
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 9 out of 9 bytes
< HTTP/1.1 200 OK
< Date: Sat, 20 Apr 2019 06:56:19 GMT
< Content-Length: 10
< Content-Type: text/plain; charset=utf-8
<
Hello Go!
* Connection #0 to host localhost left intact

HTTP/1.1 Client

The following code represents how to start a simple HTTP/1.1 client in Go. Here, the Go client sends an HTTP(S) POST request to https://localhost:9191/hello/sayHello with the text payload of “Hello Go!”.

Since we are starting an HTTP(S) service here also, we have to provide a server certificate and a server key. So, both of them are saved inside a directory called cert as server.crt and server.key. More details on SSL configurations are discussed under the previous section (HTTP/1.1 Server).

Let’s see how to start and test the service after configuring all of the above.

  • In order to run the client, we need to start the server before. Therefore, run the server by referring to the previous section (HTTP/1.1 Server), or start any HTTP(S) server on the port 9191 and the path /hello/sayHello.
$ go run http_client.go
  • You can expect the following output:
Got response 200: HTTP/1.1 Hello Go!

This client simply demonstrates the behavior of the curl command below:

$ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"

The next section describes how to implement an HTTP/2 server and HTTP/2 client with Go. Following two segments are quite the same as the previous sections, but the HTTP version is different.

HTTP/2 Server

The following code represents how to start a simple HTTP/2 server in Go. Here also, we have implemented a Go echo server, which echoes back the requests.

Let’s see how to start and test the service after configuring all of the above.

  • Run the service with the command below:
$ go run http2_server.go
  • You can expect the following output:
Go Backend: { HTTPVersion = 2 }; serving on https://localhost:9191/hello/sayHello

Now, we have successfully started the HTTP/2 server. It can be invoked with either HTTP/1.1 requests or HTTP/2 requests. The HTTP/2 server supports both according to the specification.

  • Invoke the service with an HTTP/1.1 POST request with a small text payload (insecure mode and secure mode):
$ curl -k -v https://localhost:9191/hello/sayHello -d "Hello Go!"$ curl -v --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
  • Now, invoke the service with an HTTP/2 POST request with a small text payload (insecure mode and secure mode):

To have curl commands with HTTP/2 support, we use special flag, which is --http2. Refer the below guide to activate it.

$ curl -k -v --http2 https://localhost:9191/hello/sayHello -d "Hello Go!"$ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"
  • Following is the result you get for the above HTTP/2 curl commands. The first part of the result has details on TLS handshake with the server and client. The next part of the result has the details on HTTP/2 request and HTTP/2 response. Finally, it has Hello Go! text which received via the response.
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9191 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: src/hello/cert/server.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=LK; ST=Western; L=Colombo; O=LDCLAKMAL; OU=Engineering; CN=localhost; emailAddress=ldclakmal@gmail.com
* start date: Apr 20 05:27:33 2019 GMT
* expire date: Apr 19 05:27:33 2020 GMT
* common name: localhost (matched)
* issuer: C=LK; ST=Western; L=Colombo; O=LDCLAKMAL; OU=Engineering; CN=localhost; emailAddress=ldclakmal@gmail.com
* SSL certificate verify ok.
* 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
* Using Stream ID: 1 (easy handle 0x10ddf20)
> POST /hello/sayHello HTTP/1.1
> Host: localhost:9191
> User-Agent: curl/7.46.0
> Accept: */*
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
>
* We are completely uploaded and fine
< HTTP/2.0 200
< content-type:text/plain; charset=utf-8
< content-length:10
< date:Sat, 20 Apr 2019 06:54:50 GMT
<
Hello Go!
* Connection #0 to host localhost left intact

HTTP/2 Client

The following code represents how to start a simple HTTP/2 client in Go. Here, the Go client sends an HTTP(S) POST request to https://localhost:9191/hello/sayHello with the text payload of “Hello Go!”.

Since we are starting an HTTP(S) service here also, we have to provide a server certificate and a server key. So, both of them are saved inside a directory called cert as server.crt and server.key. More details on SSL configurations are discussed under the previous section (HTTP/1.1 Server).

Let’s see how to start and test the service after configuring all of the above.

  • In order to run the client, we need to start the server before. Therefore, run the server by referring to the previous section (HTTP/2 Server), or start any HTTP(S) server on the port 9191 and the path /hello/sayHello.
$ go run http2_client.go
  • You can expect the following output:
Got response 200: HTTP/2 Hello Go!

This client simply demonstrates the behavior of the curl command below:

$ curl -v --http2 --cacert ./cert/server.crt https://localhost:9191/hello/sayHello -d "Hello Go!"

You are done. Playing Go Lang with HTTP is simple as that!

Happy coding with Go !

References

--

--

Chanaka Lakmal

CS PhD Student at UWaterloo | ex-WSO2 (Associate Technical Lead)