Replace TLSClientConfig inside the transport of an http.Client?

Issue

Please consider this question.

ourClient is an *http.Client – once it is generated, how can TLSClientConfig (which is of type tls.Config{}) alone be changed of this http.Client?

package main

import (
    "crypto/tls"
    "fmt"
    "net"
    "net/http"
    "time"
)

func main() {
    transport := &http.Transport{
        TLSHandshakeTimeout: 10 * time.Second,
        Dial: (&net.Dialer{
            Timeout:   1 * time.Second,
            KeepAlive: 30 * time.Second,
        }).Dial,
        TLSClientConfig: &tls.Config{},
    }

    ourClient := &http.Client{
        Timeout:   5 * time.Second,
        Transport: transport,
    }

    fmt.Printf("http.Client Timeout %v\n", ourClient.Timeout)
    ourClient.Timeout = 3 * time.Second
    fmt.Printf("http.Client Timeout %v\n", ourClient.Timeout)

    // how do I replace the TLSClientConfig in ourClient
    // newTLSClientConfig := &tls.Config{}
    // ourClient.Transport.TLSClientConfig = newTLSClientConfig
    // ourClient.Transport.TLSClientConfig undefined (type http.RoundTripper has no field or method TLSClientConfig)

    fmt.Printf("http: %#v\n", ourClient)
}

(Also posted as https://go.dev/play/p/v1HJLZ-MhhH)

Solution

http.RoundTripper is an interface – you need to convert it to the concrete implementation using a type assertion, e.g.:

    ...
    newTLSClientConfig := &tls.Config{}
    if tpt, ok := ourClient.Transport.(*http.Transport); ok {
        tpt.TLSClientConfig = newTLSClientConfig
    }
    ...

https://go.dev/play/p/2UinL3Uazid

It’s probably better explained here, but FWIW – http.Client.Transport is declared as a RoundTripper interface and an interface can have many different implementations and none of them are guaranteed by the interface contract to have a TLSClientConfig field. So the Go compiler won’t let you assign newTLSClientConfig field – because it legally can’t.

A type assertion asserts that the concrete implementation of the interface conforms to the type and also assigns the implementation of the interface (http.Transport in this case) to the variable (tpt in this case). So tpt is now guaranteed to be an instance of http.Transport which means you can now assign newTLSClientConfig to the TLSClientConfig field.

If ok had been false it would have meant that the implementation of the RoundTripper interface was not an http.Transport.

Answered By – tonys

Answer Checked By – Pedro (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.