log/request_size.go (27 lines of code) (raw):
package log
import "net/http"
const (
// newLineBytes is the number of bytes for a newline:
// HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
//
// protocol elements except the entity-body (see appendix 19.3 for
// tolerant applications).
newLineBytes = 2
// requestLineSpaceBytes is the number of bytes in a separator between fields in request line.
requestLineSpaceBytes = 1
// headerSeparatorBytes is the number of bytes in a separator between header and its value `X: Y`.
headerSeparatorBytes = 2
hostHeaderExtraBytes = len("Host: ")
)
// computeApproximateRequestSize will calculate the approximate request size,
// including the request line, headers and ContentLength (if set).
// Inspired by: https://github.com/prometheus/client_golang/blob/331dfab0cc853dca0242a0d96a80184087a80c1d/prometheus/promhttp/instrument_server.go#L409
// but more accurate as it includes whitespace.
// We do this because it makes verification easier for test data, as we can simply use `len(string)`.
func computeApproximateRequestSize(r *http.Request, bodyByteCount int64) int64 {
var s int64
// Request Line
s += int64(len(r.Method) + requestLineSpaceBytes)
if r.URL != nil {
s += int64(len(r.URL.String()) + requestLineSpaceBytes)
}
s += int64(len(r.Proto) + newLineBytes)
// Headers
if r.Host != "" {
s += int64(hostHeaderExtraBytes + len(r.Host) + newLineBytes)
}
for name, values := range r.Header {
for _, value := range values {
s += int64(len(name) + headerSeparatorBytes + len(value) + newLineBytes)
}
}
// Include the trailing newline after the request headers.
s += newLineBytes
// Body
s += bodyByteCount
return s
}