How can sent and received bytes be counted from within a ServeHTTP
function in Go?
The count needs to be relatively accurate. Skipping connection establishment is not ideal, but acceptable. But headers must be included.
It also needs to be fast. Iterating is generally too slow.
The counting itself doesn't need to occur within ServeHTTP
, as long the count for a given connection can be made available to ServeHTTP
.
This must also not break HTTPS or HTTP/2.
Things I've Tried
It's possible to get a rough, slow estimate of received bytes by iterating over the Request
headers. This is far too slow, and the Go standard library removes and combines headers, so it's not accurate either.
I tried writing an intercepting Listener
, which created an internal tls.Listen
or net.Listen
Listener, and whose Accept()
function got a net.Conn
from the internal Listener's Accept()
, and then wrapped that in an intercepting net.Conn
whose Read
and Write
functions call the real net.Conn
and count their reads and writes. It's then possible to make those counts available to the ServeHTTP
function via mutexed shared variables.
The problem is, the intercepting Conn
breaks HTTP/2, because Go's internal libraries cast the net.Conn
as a *tls.Conn
(e.g. https://golang.org/src/net/http/server.go#L1730), and it doesn't appear possible in Go to wrap the object while still making that cast succeed (if it is, it would solve this problem).
Counting sent bytes can be done relatively accurately by counting what is written to the ResponseWriter
. Counting received bytes in the HTTP body is also achievable, via Request.Body
. The critical issue here appears to be quickly and accurately counting request header bytes. Though again, also counting connection establishment bytes would be ideal.
Is this possible? How?