package main import ( "fmt" "net/url" "time" ) type TracePlugin struct{} func (p TracePlugin) Name() string { return "trace" } func (p TracePlugin) ShortHelp() string { return "Time an HTTP request" } func (p TracePlugin) DetailedHelp() string { return "trace \n\nTimes an HTTP/HTTPS request to the given URL and reports DNS lookup, connect, TLS negotiation, time to first byte, and time to last byte.\n\nExample:\n trace https://example.com" } func (p TracePlugin) Execute(msg BotMessage, args string) string { uri, err := url.Parse(args) if err != nil || (uri.Scheme != "http" && uri.Scheme != "https") { return "That doesn't look like a valid URL for me to call" } response, err := timeRequest(msg.User.Name, uri) if err != nil { return "Failed to time the request" } return response } func timeRequest(username string, uri *url.URL) (string, error) { trace, err := TraceRequest(uri) if err != nil { return "", err } result := fmt.Sprintf("Hi %s! I've checked %s for you, and here's what I found:

\n", username, uri) result += formatDuration("DNS lookup", trace.DNSStart, trace.DNSDone) result += formatDuration("Connect", trace.ConnectStart, trace.ConnectDone) result += formatDuration("TLS negotiation", trace.TLSStart, trace.TLSDone) result += formatDuration("Sending headers", trace.ConnectionReady(), trace.WroteHeaders) result += formatDuration("Time to first byte", trace.WroteHeaders, trace.FirstByte) result += formatDuration("Time to last byte", trace.WroteHeaders, trace.AllDone) return result, nil } func formatDuration(label string, start, end time.Time) string { dur := end.Sub(start).String() if start.IsZero() || end.IsZero() { dur = "n/a" } return fmt.Sprintf("%s: %s
\n", label, dur) }