Created
January 10, 2018 09:09
-
-
Save PierreZ/16eb726a58a7b6eef874d2a315f4f5f3 to your computer and use it in GitHub Desktop.
Small experiment to genererate metrics from an HTTP call, from https://github.com/davecheney/httpstat
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"fmt" | |
"io" | |
"log" | |
"net/http" | |
"net/http/httptrace" | |
"net/url" | |
"os" | |
"strconv" | |
"strings" | |
"time" | |
) | |
func main() { | |
if len(os.Args) != 2 { | |
log.Fatal("Missing remote URL") | |
} | |
url := parseURL(os.Args[1]) | |
visit(url) | |
} | |
func visit(url *url.URL) { | |
req := newRequest("GET", url, "") | |
var t0, t1, t2, t3, t4 time.Time | |
trace := &httptrace.ClientTrace{ | |
DNSStart: func(_ httptrace.DNSStartInfo) { t0 = time.Now() }, | |
DNSDone: func(_ httptrace.DNSDoneInfo) { t1 = time.Now() }, | |
ConnectStart: func(_, _ string) { | |
if t1.IsZero() { | |
// connecting to IP | |
t1 = time.Now() | |
} | |
}, | |
ConnectDone: func(net, addr string, err error) { | |
if err != nil { | |
log.Fatalf("unable to connect to host %v: %v", addr, err) | |
} | |
t2 = time.Now() | |
fmt.Printf("\nConnected to %v\n", url.String()) | |
fmt.Printf("Scheme: %v\n", url.Scheme) | |
fmt.Printf("Host: %v\n", url.Host) | |
fmt.Printf("rawQuery: %v\n", url.Path) | |
}, | |
GotConn: func(_ httptrace.GotConnInfo) { t3 = time.Now() }, | |
GotFirstResponseByte: func() { t4 = time.Now() }, | |
} | |
req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace)) | |
tr := &http.Transport{ | |
Proxy: http.ProxyFromEnvironment, | |
MaxIdleConns: 100, | |
IdleConnTimeout: 90 * time.Second, | |
TLSHandshakeTimeout: 10 * time.Second, | |
ExpectContinueTimeout: 1 * time.Second, | |
} | |
client := &http.Client{ | |
Transport: tr, | |
CheckRedirect: func(req *http.Request, via []*http.Request) error { | |
// always refuse to follow redirects, visit does that | |
// manually if required. | |
return http.ErrUseLastResponse | |
}, | |
} | |
resp, err := client.Do(req) | |
if err != nil { | |
log.Fatalf("failed to read response: %v", err) | |
} | |
// We don't care about the body | |
resp.Body.Close() | |
t5 := time.Now() // after read body | |
if t0.IsZero() { | |
// we skipped DNS | |
t0 = t1 | |
} | |
fmta := func(d time.Duration) string { | |
return fmt.Sprintf("%7dms", int(d/time.Millisecond)) | |
} | |
fmtb := func(d time.Duration) string { | |
return fmt.Sprintf("%-9s", strconv.Itoa(int(d/time.Millisecond))+"ms") | |
} | |
fmt.Printf("\nResponse is a %v\n", resp.Status) | |
fmt.Printf("dns lookup:%v\n", fmta(t1.Sub(t0))) | |
fmt.Printf("tcp connection: %v\n", fmta(t2.Sub(t1))) // tcp connection | |
fmt.Printf("tls handshake: %v\n", fmta(t3.Sub(t2))) // tcp connection | |
fmt.Printf("server processing: %v\n", fmta(t4.Sub(t3))) | |
fmt.Printf("content transfer: %v\n", fmta(t5.Sub(t4))) | |
fmt.Printf("namelookup: %v\n", fmtb(t1.Sub(t0))) | |
fmt.Printf("connect: %v\n", fmtb(t2.Sub(t0))) | |
fmt.Printf("pretransfer: %v\n", fmtb(t3.Sub(t0))) | |
fmt.Printf("starttransfer: %v\n", fmtb(t4.Sub(t0))) | |
fmt.Printf("total: %v\n", fmtb(t5.Sub(t0))) | |
} | |
func parseURL(uri string) *url.URL { | |
if !strings.Contains(uri, "://") && !strings.HasPrefix(uri, "//") { | |
uri = "//" + uri | |
} | |
url, err := url.Parse(uri) | |
if err != nil { | |
log.Fatalf("could not parse url %q: %v", uri, err) | |
} | |
if url.Scheme == "" { | |
url.Scheme = "http" | |
if !strings.HasSuffix(url.Host, ":80") { | |
url.Scheme += "s" | |
} | |
} | |
return url | |
} | |
func newRequest(method string, url *url.URL, body string) *http.Request { | |
req, err := http.NewRequest(method, url.String(), createBody(body)) | |
if err != nil { | |
log.Fatalf("unable to create request: %v", err) | |
} | |
return req | |
} | |
func createBody(body string) io.Reader { | |
if strings.HasPrefix(body, "@") { | |
filename := body[1:] | |
f, err := os.Open(filename) | |
if err != nil { | |
log.Fatalf("failed to open data file %s: %v", filename, err) | |
} | |
return f | |
} | |
return strings.NewReader(body) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment