In How to consume a REST API in Go a fully working example code is provided to call a public REST API. But if I try the sample this error occurs:
error getting cat fact:
Get "https://catfact.ninja/fact":
proxyconnect tcp: tls: first record does not look like a TLS handshake
The documentation about http states
For control over proxies, TLS configuration, keep-alives, compression, and other settings, create a Transport:
and from the Transport documentation:
// DialContext specifies the dial function
// for creating unencrypted TCP connections.
// If DialContext is nil (and the deprecated Dial
// below is also nil), then the transport dials using
// package net.
//
// DialContext runs concurrently with calls to RoundTrip.
// A RoundTrip call that initiates a dial may end up
// using a connection dialed previously when the
// earlier connection becomes idle before the later
// DialContext completes.
DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
So I assume that I have to configure Dialcontext to enable an insecure connection without TLS
from my client to the proxy. But I do not know how to do this. Reading these:
did not help either. Some have the same error proxyconnect tcp: tls: first record does not look like a TLS handshake
and explain the cause:
This is because the proxy answers with an plain HTTP error to the strange HTTP request (which is actually the start of the TLS handshake).
But Steffen's reply has no sample code how to set up DialContext func(ctx context.Context, network, addr string)
and both Bogdan and cyberdelia suggest to set tls.Config{InsecureSkipVerify: true}
for example like this
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
But the above has not effect. I still get the same error. And the connection is still calling https://*
instead of http://*
This is the sample code and my attempt to include the above recommendations and adapt it:
var tr = &http.Transport{ TLSClientConfig:
&tls.Config{InsecureSkipVerify: true}, }
// lacks DialContext config
var client* http.Client = &http.Client{Transport: tr} // modified * added
// var client *http.Client // code from tutorial
type CatFact struct {
Fact string `json:"fact"`
Length int `json:"length"`
}
func GetCatFact() {
// changed from https to http
url := "http://catfact.ninja/fact"
var catFact CatFact
err := GetJson(url, &catFact)
if err != nil {
fmt.Printf("Error getting cat fact: %s\n", err.Error())
} else {
fmt.Printf("Super Cat Fact: %s\n", catFact.Fact)
}
}
func main() {
client = &http.Client{Timeout: 10 * time.Second}
GetCatFact()
// same error
// proxyconnect tcp: tls: first record does
// not look like a TLS handshake
// still uses https
// for GET catfact.ninja
}
How can the connection be configured to use unencrypted connection from myClient over the proxy to the server? Will setting the DialContext func(ctx context.Context, network, addr string)
help to do this? How can it be done?
If i remember correctly then this question was answered once. Since it is no longer the case i will add a sample to request a CatFact with a customDialContext
See code below or this gist
// Custom dialing function to handle connections
func customDialContext(ctx context.Context, network, addr string)
(net.Conn, error) {
conn, err := net.Dial(network, addr)
return conn, err
}
The main function uses
func main() {
// Create a custom Transport with the desired settings
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: customDialContext,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
// Create a new HTTP client using the custom Transport
client := &http.Client{
Transport: tr,
Timeout: 10 * time.Second,
}
// Call the function to get a cat fact
GetCatFact(client)
}
Helper function GetCatFact()
func GetCatFact(client *http.Client) {
url := "https://catfact.ninja/fact" // Reverted back to https
var catFact CatFact
err := GetJson(url, &catFact, client)
if err != nil {
fmt.Printf("error getting cat fact: %s\n", err.Error())
} else {
fmt.Printf("A super interesting Cat Fact: %s\n", catFact.Fact)
}
}
GetJson
func GetJson(url string, target interface{}, client *http.Client) error {
resp, err := client.Get(url)
if err != nil {
return fmt.Errorf("error sending GET request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("received non-OK HTTP status: %d", resp.StatusCode)
}
err = json.NewDecoder(resp.Body).Decode(target)
if err != nil {
return fmt.Errorf("error decoding JSON response: %w", err)
}
return nil
}
The json response body
{
"fact": "Both humans and cats have identical regions
in the brain responsible for emotion.",
"length": 81
}
and the struct
type CatFact struct {
Fact string `json:"fact"`
Length int `json:"length"`
}