Tracking which connection is used to make an HTTP request

212 views
Skip to first unread message

Christian Worm Mortensen

unread,
Oct 12, 2022, 12:05:27 PM10/12/22
to golang-nuts
I have created an http.Client with a custom TLS dialer. The custom TLS dialer creates a *tls.Conn and while doing this, it logs out a lot of information such as which server is used.

Now, when I make an HTTP request, I want to log out which dial produced the *tls.Conn the HTTP request ended up using.

Using httptrace.ClientTrace.GotConn it is possible to get the net.Conn that was used by the request. But how do I relate that with which dial was used to create it?

I see four options but they all have drawbacks:

1) I can keep a map from net.Conn to information that identifies the dial. But when can I delete entries from this map? Go does not have weak pointers.
 
2) I can make a my own struct and return from my custom dialer:

type myConn struct {
  net.Conn
  dialInfo string
}

However, that does not work well for TLS. The reason is that the Go HTTP client has special logic in case a custom dialer returns a *tls.Conn. That logic will be disabled.

3) I can log out the address of the net.Conn object both from my custom dialer and from GotConn. I can do that by using something like reflect.ValueOf(conn).Pointer(). However in practice it seems like different net.Conn objects can easily get the same address.

4) I can log out the local and remote IP addresses and port of the net.Conn both from the dialer and from GotConn. It is a lot of information and different connections may use the same IP and ports. But maybe the best I can do?

Any other ideas?

Konstantin Khomoutov

unread,
Oct 25, 2022, 7:43:54 AM10/25/22
to Christian Worm Mortensen, golang-nuts
On Wed, Oct 12, 2022 at 09:01:46AM -0700, Christian Worm Mortensen wrote:

(I have rehashed the options you've presented, to help commenting on them.)

[...]

> 2) I can make a my own struct and return from my custom dialer:
>
> type myConn struct {
> net.Conn
> dialInfo string
> }
>
> However, that does not work well for TLS. The reason is that the Go HTTP
> client has special logic in case a custom dialer returns a *tls.Conn. That
> logic will be disabled.

The Conn funtion of the crypto/tls package allows to "upgrade" a user-supplied
net.Conn to a tls.Conn, so you could probably try to modify your dialer so
that it returns myConn which has, say, LocalAddr method returning a custom
type which implements net.Addr but also conveys some information about itself,
like in

type MyAddr struct {
addr net.TCPAddr
dialInfo string
}

...

func (a *MyAddr) DialInfo() string { return m.dialInfo }

It's clumsy but should work: you could

if laddr, my := someTLSConn.LocalAddr().(*whateverpkg.MyAddr); my {
di := laddr.DialInfo()
}

or type-assert to an interface containing the single DialInfo() method.

> 1) I can keep a map from net.Conn to information that identifies the dial.
> But when can I delete entries from this map? Go does not have weak pointers.

Combined with the idea above, each myConn could keep a reference to the
"registry" which maintains the mapping from the conns to the dial information
blocks, an have myConn deregister in its Close() method.

Alternatively you could use runtime.SetFinalizer() on your custom myConn type
- passing it a reference to that "registry" object which had your myConn
registered after creation.

The type of that registry must be safe for concurrent use in both cases.

Konstantin Khomoutov

unread,
Oct 25, 2022, 7:58:24 AM10/25/22
to Christian Worm Mortensen, golang-nuts
On Tue, Oct 25, 2022 at 03:43:21PM +0400, Konstantin Khomoutov wrote:

>> However, that does not work well for TLS. The reason is that the Go HTTP
>> client has special logic in case a custom dialer returns a *tls.Conn. That
>> logic will be disabled.
>
> The Conn funtion of the crypto/tls package allows to "upgrade" a user-supplied

The function is called Client, sorry for the confusion.

Christian Worm Mortensen

unread,
Oct 30, 2022, 12:47:28 PM10/30/22
to Konstantin Khomoutov, golang-nuts
Hi Konstantin

Thanks for the reply - this is exactly what I needed! It even seems
like tls.Conn has a function NetConn that allows me to get my net.Conn
back again. So for anyone stumbling on this in the future, I think
this is the way to solve it:

* Make the customer TLS dialer dial a TCP connection to the HTTPS
server to get a net.Conn
* Wrap the net.Conn in a custom struct like

type myConn struct {
net.Conn
myInfo myType
}

* Upgrade myConn to a tls.Conn by using the function tls.Client.
* Optionally perform the TLS handshake by calling Handshake or
HandshakeContext on the tls.Conn
* Return the tls.Conn from the dialer
* When a net.Conn is received from GotConn, cast it to tls.Conn, call
NetConn on the result, and cast that to myConn in order to access
myInfo.


Best

Christian
Reply all
Reply to author
Forward
0 new messages