sox/request_trace.go
2026-01-17 11:22:16 -07:00

73 lines
1.9 KiB
Go

package main
import (
"context"
"crypto/tls"
"io"
"net/http"
"net/http/httptrace"
"net/url"
"time"
)
type RequestTrace struct {
Started time.Time
DNSStart time.Time
DNSDone time.Time
ConnectStart time.Time
ConnectDone time.Time
TLSStart time.Time
TLSDone time.Time
WroteHeaders time.Time
FirstByte time.Time
AllDone time.Time
}
func (t RequestTrace) ConnectionReady() time.Time {
if t.TLSDone.After(t.ConnectDone) {
return t.TLSDone
}
return t.ConnectDone
}
func TraceRequest(uri *url.URL) (RequestTrace, error) {
var trace RequestTrace
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
ctx = httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
GotConn: func(info httptrace.GotConnInfo) { trace.Started = time.Now() },
DNSStart: func(info httptrace.DNSStartInfo) { trace.DNSStart = time.Now() },
DNSDone: func(info httptrace.DNSDoneInfo) { trace.DNSDone = time.Now() },
ConnectStart: func(network, addr string) { trace.ConnectStart = time.Now() },
ConnectDone: func(network, addr string, err error) { trace.ConnectDone = time.Now() },
TLSHandshakeStart: func() { trace.TLSStart = time.Now() },
TLSHandshakeDone: func(tls.ConnectionState, error) { trace.TLSDone = time.Now() },
WroteHeaders: func() { trace.WroteHeaders = time.Now() },
GotFirstResponseByte: func() { trace.FirstByte = time.Now() },
})
req, err := http.NewRequest("GET", uri.String(), nil)
req = req.WithContext(ctx)
if err != nil {
return trace, err
}
t := http.DefaultTransport.(*http.Transport).Clone()
t.DisableKeepAlives = true
c := &http.Client{Transport: t}
resp, err := c.Do(req)
if err != nil {
return trace, err
}
defer resp.Body.Close()
io.Copy(io.Discard, resp.Body)
trace.AllDone = time.Now()
return trace, nil
}