如何在 Go 中的ServeHTTP
函数中计算发送和接收的字节数?
计数需要相对准确。跳过连接建立并不理想,但可以接受。但必须包含标头。
它还需要快速。迭代通常太慢。
计数本身不需要在ServeHTTP
内发生,只要给定连接的计数可以提供给ServeHTTP
。
这也不能破坏HTTPS或HTTP/2。
我尝试过的事情
通过迭代Request
标头,可以粗略、缓慢地估计收到的字节。这太慢了,Go 标准库删除并组合了标头,所以它也不准确。
我尝试编写一个拦截Listener
,它创建了一个内部tls.Listen
或net.Listen
监听器,其Accept()
函数从内部监听器的Accept()
中获取net.Conn
,然后将其包装在一个拦截net.Conn
中,其Read
和Write
函数调用实net.Conn
并计算它们的读取和写入次数。然后,可以通过互斥共享变量使这些计数可用于ServeHTTP
函数。
问题是,拦截Conn
破坏了 HTTP/2,因为 Go 的内部库将net.Conn
转换为*tls.Conn
(例如 https://golang.org/src/net/http/server.go#L1730),并且在 Go 中似乎不可能包装对象,同时仍然使该强制转换成功(如果是,它将解决这个问题)。
通过计算写入ResponseWriter
的内容,可以相对准确地计算发送的字节数。还可以通过Request.Body
在 HTTP 正文中计算接收的字节数。这里的关键问题似乎是快速准确地计算请求标头字节数。虽然同样,计算连接建立字节也是理想的。
这可能吗?如何?
我认为这是可能的,但我不能说我已经做到了。但是,基于浏览HTTP服务器和TLS侦听器的stdlib实现,我不明白为什么它不应该是不可能的;密钥是在 TLS之前而不是之后包装连接。这还可以为您提供更准确的线路字节计数,而不是解密字节计数。
你已经有一个拦截Listener
,你只需要把它插入正确的位置。与其将Listener
传递给http.Serve
(或插入它的任何位置),不如先将其传递给tls.NewListener
,后者将其包装在TLS处理程序中,然后将结果(该结果将是一个TLS侦听器(使Go的HTTP/2支持满意)传递到HTTP服务器。
当然,如果你想要一个解密字节数而不是有线字节,你可能就是 SOL - 包装net.Conn
不会让你到达那里。你可能必须尽你所能来计算标题和正文。