diff --git a/main.go b/main.go index bf03b74..d975097 100644 --- a/main.go +++ b/main.go @@ -52,6 +52,7 @@ var ( // Command line flags. httpMethod string postBody string + repeat int followRedirects bool onlyHeader bool insecure bool @@ -72,6 +73,7 @@ const maxRedirects = 10 func init() { flag.StringVar(&httpMethod, "X", "GET", "HTTP method to use") flag.StringVar(&postBody, "d", "", "the body of a POST or PUT request; from file use @filename") + flag.IntVar(&repeat, "r", 0, "Repeat the same request N times on the same connection") flag.BoolVar(&followRedirects, "L", false, "follow 30x redirects") flag.BoolVar(&onlyHeader, "I", false, "don't read body of request") flag.BoolVar(&insecure, "k", false, "allow insecure SSL connections") @@ -127,8 +129,11 @@ func main() { } url := parseURL(args[0]) + tr := newTransport(url) - visit(url) + for i := repeat + 1; i > 0; i-- { + visit(tr, url) + } } // readClientCert - helper function to read client certificate @@ -197,9 +202,42 @@ func headerKeyValue(h string) (string, string) { return strings.TrimRight(h[:i], " "), strings.TrimLeft(h[i:], " :") } +func newTransport(url *url.URL) *http.Transport { + tr := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + + switch url.Scheme { + case "https": + host, _, err := net.SplitHostPort(url.Host) + if err != nil { + host = url.Host + } + + tr.TLSClientConfig = &tls.Config{ + ServerName: host, + InsecureSkipVerify: insecure, + Certificates: readClientCert(clientCertFile), + } + + // Because we create a custom TLSClientConfig, we have to opt-in to HTTP/2. + // See https://github.com/golang/go/issues/14275 + err = http2.ConfigureTransport(tr) + if err != nil { + log.Fatalf("failed to prepare transport for HTTP/2: %v", err) + } + } + + return tr +} + // visit visits a url and times the interaction. // If the response is a 30x, visit follows the redirect. -func visit(url *url.URL) { +func visit(tr *http.Transport, url *url.URL) { req := newRequest(httpMethod, url, postBody) var tStart, tDNSStart, tDNSEnd, tConnectStart, tConnectEnd, tTLSStart, tTLSEnd, tConnected, tTTBF, tDone time.Time @@ -229,35 +267,6 @@ func visit(url *url.URL) { } 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, - } - - switch url.Scheme { - case "https": - host, _, err := net.SplitHostPort(req.Host) - if err != nil { - host = req.Host - } - - tr.TLSClientConfig = &tls.Config{ - ServerName: host, - InsecureSkipVerify: insecure, - Certificates: readClientCert(clientCertFile), - } - - // Because we create a custom TLSClientConfig, we have to opt-in to HTTP/2. - // See https://github.com/golang/go/issues/14275 - err = http2.ConfigureTransport(tr) - if err != nil { - log.Fatalf("failed to prepare transport for HTTP/2: %v", err) - } - } - client := &http.Client{ Transport: tr, CheckRedirect: func(req *http.Request, via []*http.Request) error { @@ -363,7 +372,7 @@ func visit(url *url.URL) { log.Fatalf("maximum number of redirects (%d) followed", maxRedirects) } - visit(loc) + visit(tr, loc) } }