Doable. =)
You should set Dial and DialTLS fields on Transport and do checks there.
Hope the code is self explanatory, feel free to ask.
Does not work on playground due to restrictions!
https://play.golang.org/p/4TkczUEnKn
package main
import (
"fmt"
"net/http"
"net"
"time"
"crypto/tls"
)
func DialTLS(network, addr string) (net.Conn, error) {
conn, err := tls.Dial(network, addr, &tls.Config{
InsecureSkipVerify: true,
})
cs := conn.ConnectionState()
fmt.Println(cs.Version, cs.HandshakeComplete)
// insert your check here!
return conn, err
}
func Dial (network, addr string) (net.Conn, error) {
// Copied from DefaultTransport
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}
conn, err := dialer.Dial(network, addr)
fmt.Println("unsecure")
return conn, err
}
func main() {
client := &http.Client{
Timeout: time.Second,
Transport: &http.Transport{
Dial: Dial,
// If DialTLS is set, the Dial hook is not used for HTTPS
// requests and the TLSClientConfig and TLSHandshakeTimeout
// are ignored. The returned net.Conn is assumed to already be
// past the TLS handshake.
// TLSClientConfig: &tls.Config{
// InsecureSkipVerify: true,
// },
DialTLS: DialTLS,
},
}
// Deep in transport it checks "tlsDial := t.DialTLS != nil && cm.targetScheme == "https" && cm.proxyURL == nil"
// https://golang.org/src/net/http/transport.go#L741
resp, err := client.Get("https://www.google.com")
fmt.Println(resp, err, "
")
resp , err = client.Get("http://www.google.com")
fmt.Println(resp, err)
}
UPD
UPD2: Code below is bad idea =). Fallback logic confuses http.Client error = Get https://www.google.com: http: server gave HTTP response to HTTPS client
or I am missing something, well, its up to you now =)
Or even better (depends on your logic).
func DialTLSWithFallback(network, addr string) (net.Conn, error) {
conn, err := tls.Dial(network, addr, &tls.Config{
InsecureSkipVerify: true,
})
if err != nil {
// obviously, usecure
// lets try net.Dial
// and return, so check bellow wont trigger on wrong connection type
return &(net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial(network, addr)
}
cs := conn.ConnectionState()
fmt.Println(cs.Version, cs.HandshakeComplete)
// its "secure" connection
// check is it strong enough here!
return conn, err
}
And you transport shrinks to
Transport: &http.Transport{
Dial: DialTLSWithFallback,
DialTLS: DialTLSWithFallback,
}