Debugging HTTP Client requests with Go

Featured image for sharing metadata for article

Sometimes when you're integrating with APIs or web services, you need to debug the HTTP requests/responses that are occuring.

In some toolchains it's part of configuration, but in Go it's unfortunately not part of the standard library to globally turn on logging for the http.Client.

That being said, the Go standard library has net/http/httputil has the handy helper methods httputil.DumpRequestOut and httputil.DumpResponse which we can use alongside an implementation of http.RoundTripper to provide a logging HTTP client.

We can wrap this together with:

package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
)

type loggingTransport struct{}

func (s *loggingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
	bytes, _ := httputil.DumpRequestOut(r, true)

	resp, err := http.DefaultTransport.RoundTrip(r)
	// err is returned after dumping the response

	respBytes, _ := httputil.DumpResponse(resp, true)
	bytes = append(bytes, respBytes...)

	fmt.Printf("%s\n", bytes)

	return resp, err
}

func main() {
	client := http.Client{
		Transport: &loggingTransport{},
	}

	baseUrl := "https://..." // this would be filled in with i.e. a Request Bin style application

	resp, err := client.PostForm(baseUrl, url.Values{
		"name":    {"foo"},
		"content": {"baz", "bing"},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	fmt.Printf("resp.Status: %v\n", resp.Status)

	resp, err = client.Get(must(url.JoinPath(baseUrl, "/user")))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	fmt.Printf("resp.Status: %v\n", resp.Status)

	// preferably don't mutate the global client!
	http.DefaultClient = &client

	resp, err = http.DefaultClient.Get(must(url.JoinPath(baseUrl, "/from-default-client")))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	fmt.Printf("resp.Status: %v\n", resp.Status)
}

func must(s string, err error) string {
	if err != nil {
		log.Fatal(err)
	}
	return s
}

This then outputs:

Example output
POST / HTTP/1.1
Host: enzzopego73pb.x.pipedream.net
User-Agent: Go-http-client/1.1
Content-Length: 33
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip

content=baz&content=bing&name=fooHTTP/2.0 200 OK
Content-Length: 16
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Sat, 11 Mar 2023 10:06:43 GMT
X-Pd-Status: sent to primary
X-Powered-By: Express

{"success":true}
resp.Status: 200 OK
GET /user HTTP/1.1
Host: enzzopego73pb.x.pipedream.net
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

HTTP/2.0 200 OK
Content-Length: 16
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Sat, 11 Mar 2023 10:06:43 GMT
X-Pd-Status: sent to primary
X-Powered-By: Express

{"success":true}
resp.Status: 200 OK
GET /from-default-client HTTP/1.1
Host: enzzopego73pb.x.pipedream.net
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

HTTP/2.0 200 OK
Content-Length: 16
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Sat, 11 Mar 2023 10:06:43 GMT
X-Pd-Status: sent to primary
X-Powered-By: Express

{"success":true}
resp.Status: 200 OK

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #go.

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.