Essential Go  Suggest an edit

HTTP Client

Package net/http in standard library provides functionality to make HTTP network requests.

In the examples we use httpbin.org which is a clever service that can return specific HTTP responses, which is useful for demonstrating various aspects of HTTP protocol.

Basic HTTP GET

For simplicity this example uses http.Get(). In real programs you should use custom client with a timeout as described below.

uri := "https://httpbin.org/html"
resp, err := http.Get(uri)
if err != nil {
	log.Fatalf("http.Get() failed with '%s'\n", err)
}

// it's important to close resp.Body or else we'll leak network connection
// it must be done after checking for error because in error case
// resp.Body can be nil
defer resp.Body.Close()
d, err := ioutil.ReadAll(resp.Body)
if err != nil {
	log.Fatalf("ioutil.ReadAll() failed with '%s'\n", err)
}

contentType := resp.Header.Get("Content-Type")
fmt.Printf("http.Get() returned content of type '%s' and size %d bytes.\nStatus code: %d\n", contentType, len(d), resp.StatusCode)

// getting page that doesn't exist return 404
uri = "https://httpbin.org/page-doesnt-exist"
resp, err = http.Get(uri)
if err != nil {
	log.Fatalf("http.Get() failed with '%s'\n", err)
}

contentType = resp.Header.Get("Content-Type")
fmt.Printf("\nhttp.Get() returned content of type '%s' and size %d bytes.\nStatus code: %d\n", contentType, len(d), resp.StatusCode)

// acessing non-existent host fails
uri = "http://website.not-exists.as/index.html"
resp, err = http.Get(uri)
if err != nil {
	fmt.Printf("\nhttp.Get() failed with: '%s'\nresp: %v\n", err, resp)
}
http.Get() returned content of type 'text/html; charset=utf-8' and size 3741 bytes.
Status code: 200

http.Get() returned content of type 'text/html' and size 3741 bytes.
Status code: 404

http.Get() failed with: 'Get http://website.not-exists.as/index.html: dial tcp: lookup website.not-exists.as: no such host'
resp: <nil>

This shows how to make HTTP GET request for a URL (HTML page in this case).

I use uri as variable name because there is net/url package which means using more natural url will lead to naming conflicts when importing net/url in the same file.

When there is no error, http.Get() returns *http.Response with notable fields:

When no error is returned, it’s important to resp.Body.Close() or you will leak resources.

Trying to access page that doesn’t exist on the server returns a response with StatusCode 404 (Not Found).

Trying to access non-existent host will fail, in which case response is nil.

HTTP GET using custom client

http.Get() is just a wrapper that delegates all work http.DefaultClient, which is a package variable of type *http.Client.

It’s best not to use http.Get because default client doesn’t have a timeout, which means it’ll wait forever connecting to slow or buggy or malicious servers.

client := &http.Client{}
client.Timeout = time.Millisecond * 100

uri := "https://httpbin.org/delay/3"
resp, err := client.Get(uri)
if err != nil {
	log.Fatalf("http.Get() failed with '%s'\n", err)
}
2018/10/18 02:24:48 http.Get() failed with 'Get https://httpbin.org/delay/3: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)'
exit status 1

As shown, creating and using custom http.Client is easy.

In this example we set a very short time-out to demonstrate that exceeding this timeout cancels the connection.

In real programs we would use longer timeout, like 15 seconds (the exact timeout depends on particulars of your code).

Basic HTTP POST

client := &http.Client{}
client.Timeout = time.Second * 15

uri := "https://httpbin.org/post"
body := bytes.NewBufferString("text we send")
resp, err := client.Post(uri, "text/plain", body)
if err != nil {
	log.Fatalf("client.Post() failed with '%s'\n", err)
}
defer resp.Body.Close()
d, err := ioutil.ReadAll(resp.Body)
if err != nil {
	log.Fatalf("http.Get() failed with '%s'\n", err)
}
fmt.Printf("http.Post() returned statuts code %d, truncated text:\n%s...\n", resp.StatusCode, string(d)[:93])
http.Post() returned statuts code 200, truncated text:
{
  "args": {}, 
  "data": "text we send", 
  "files": {}, 
  "form": {}, 
  "headers": {
   ...

The simplest way to do POST is using http.Client.Post(url string, contentType string, body io.Reader).

In this example we send raw text. Most of the time the server expects body to be in url-encoded format.

Basic HTTP HEAD

Like HTTP GET but use http.Client.Head(uri string) method.

  ↑ ↓ to navigate     ↵ to select     Esc to close