Transport.ForceAttemptHTTP2 to enable HTTP/2
package mainimport ("context""crypto/tls""encoding/pem""flag""fmt""io""io/ioutil""log""mime""net""net/http""net/http/httptrace""net/url""os""path""runtime""sort""strconv""strings""time""github.com/fatih/color")const (httpsTemplate = `` +` DNS Lookup TCP Connection TLS Handshake Server Processing Content Transfer` + "\n" +`[%s | %s | %s | %s | %s ]` + "\n" +` | | | | |` + "\n" +` namelookup:%s | | | |` + "\n" +` connect:%s | | |` + "\n" +` pretransfer:%s | |` + "\n" +` starttransfer:%s |` + "\n" +` total:%s` + "\n"httpTemplate = `` +` DNS Lookup TCP Connection Server Processing Content Transfer` + "\n" +`[ %s | %s | %s | %s ]` + "\n" +` | | | |` + "\n" +` namelookup:%s | | |` + "\n" +` connect:%s | |` + "\n" +` starttransfer:%s |` + "\n" +` total:%s` + "\n")var (// Command line flags.httpMethod stringpostBody stringfollowRedirects boolonlyHeader boolinsecure boolhttpHeaders headerssaveOutput booloutputFile stringshowVersion boolclientCertFile stringfourOnly boolsixOnly bool// number of redirects followedredirectsFollowed intversion = "devel" // for -v flag, updated during the release process with -ldflags=-X=main.version=...)const maxRedirects = 10func 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.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")flag.Var(&httpHeaders, "H", "set HTTP header; repeatable: -H 'Accept: ...' -H 'Range: ...'")flag.BoolVar(&saveOutput, "O", false, "save body as remote filename")flag.StringVar(&outputFile, "o", "", "output file for body")flag.BoolVar(&showVersion, "v", false, "print version number")flag.StringVar(&clientCertFile, "E", "", "client cert file for tls config")flag.BoolVar(&fourOnly, "4", false, "resolve IPv4 addresses only")flag.BoolVar(&sixOnly, "6", false, "resolve IPv6 addresses only")flag.Usage = usage}func usage() {fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] URL\n\n", os.Args[0])fmt.Fprintln(os.Stderr, "OPTIONS:")flag.PrintDefaults()fmt.Fprintln(os.Stderr, "")fmt.Fprintln(os.Stderr, "ENVIRONMENT:")fmt.Fprintln(os.Stderr, " HTTP_PROXY proxy for HTTP requests; complete URL or HOST[:PORT]")fmt.Fprintln(os.Stderr, " used for HTTPS requests if HTTPS_PROXY undefined")fmt.Fprintln(os.Stderr, " HTTPS_PROXY proxy for HTTPS requests; complete URL or HOST[:PORT]")fmt.Fprintln(os.Stderr, " NO_PROXY comma-separated list of hosts to exclude from proxy")}func printf(format string, a ...interface{}) (n int, err error) {return fmt.Fprintf(color.Output, format, a...)}func grayscale(code color.Attribute) func(string, ...interface{}) string {return color.New(code + 232).SprintfFunc()}func main() {flag.Parse()if showVersion {fmt.Printf("%s %s (runtime: %s)\n", os.Args[0], version, runtime.Version())os.Exit(0)}if fourOnly && sixOnly {fmt.Fprintf(os.Stderr, "%s: Only one of -4 and -6 may be specified\n", os.Args[0])os.Exit(-1)}args := flag.Args()if len(args) != 1 {flag.Usage()os.Exit(2)}if (httpMethod == "POST" || httpMethod == "PUT") && postBody == "" {log.Fatal("must supply post body using -d when POST or PUT is used")}if onlyHeader {httpMethod = "HEAD"}url := parseURL(args[0])visit(url)}// readClientCert - helper function to read client certificate// from pem formatted filefunc readClientCert(filename string) []tls.Certificate {if filename == "" {return nil}var (pkeyPem []bytecertPem []byte)// read client certificate file (must include client private key and certificate)certFileBytes, err := ioutil.ReadFile(clientCertFile)if err != nil {log.Fatalf("failed to read client certificate file: %v", err)}for {block, rest := pem.Decode(certFileBytes)if block == nil {break}certFileBytes = restif strings.HasSuffix(block.Type, "PRIVATE KEY") {pkeyPem = pem.EncodeToMemory(block)}if strings.HasSuffix(block.Type, "CERTIFICATE") {certPem = pem.EncodeToMemory(block)}}cert, err := tls.X509KeyPair(certPem, pkeyPem)if err != nil {log.Fatalf("unable to load client cert and key pair: %v", err)}return []tls.Certificate{cert}}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 headerKeyValue(h string) (string, string) {i := strings.Index(h, ":")if i == -1 {log.Fatalf("Header '%s' has invalid format, missing ':'", h)}return strings.TrimRight(h[:i], " "), strings.TrimLeft(h[i:], " :")}func dialContext(network string) func(ctx context.Context, network, addr string) (net.Conn, error) {return func(ctx context.Context, _, addr string) (net.Conn, error) {return (&net.Dialer{Timeout: 30 * time.Second,KeepAlive: 30 * time.Second,DualStack: false,}).DialContext(ctx, network, addr)}}// visit visits a url and times the interaction.// If the response is a 30x, visit follows the redirect.func visit(url *url.URL) {req := newRequest(httpMethod, url, postBody)var t0, t1, t2, t3, t4, t5, t6 time.Timetrace := &httptrace.ClientTrace{DNSStart: func(_ httptrace.DNSStartInfo) { t0 = time.Now() },DNSDone: func(_ httptrace.DNSDoneInfo) { t1 = time.Now() },ConnectStart: func(_, _ string) {if t1.IsZero() {// connecting to IPt1 = 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()printf("\n%s%s\n", color.GreenString("Connected to "), color.CyanString(addr))},GotConn: func(_ httptrace.GotConnInfo) { t3 = time.Now() },GotFirstResponseByte: func() { t4 = time.Now() },TLSHandshakeStart: func() { t5 = time.Now() },TLSHandshakeDone: func(_ tls.ConnectionState, _ error) { t6 = 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,ForceAttemptHTTP2: true,}switch {case fourOnly:tr.DialContext = dialContext("tcp4")case sixOnly:tr.DialContext = dialContext("tcp6")}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),}}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)}bodyMsg := readResponseBody(req, resp)resp.Body.Close()t7 := time.Now() // after read bodyif t0.IsZero() {// we skipped DNSt0 = t1}// print status line and headersprintf("\n%s%s%s\n", color.GreenString("HTTP"), grayscale(14)("/"), color.CyanString("%d.%d %s", resp.ProtoMajor, resp.ProtoMinor, resp.Status))names := make([]string, 0, len(resp.Header))for k := range resp.Header {names = append(names, k)}sort.Sort(headers(names))for _, k := range names {printf("%s %s\n", grayscale(14)(k+":"), color.CyanString(strings.Join(resp.Header[k], ",")))}if bodyMsg != "" {printf("\n%s\n", bodyMsg)}fmta := func(d time.Duration) string {return color.CyanString("%7dms", int(d/time.Millisecond))}fmtb := func(d time.Duration) string {return color.CyanString("%-9s", strconv.Itoa(int(d/time.Millisecond))+"ms")}colorize := func(s string) string {v := strings.Split(s, "\n")v[0] = grayscale(16)(v[0])return strings.Join(v, "\n")}fmt.Println()switch url.Scheme {case "https":printf(colorize(httpsTemplate),fmta(t1.Sub(t0)), // dns lookupfmta(t2.Sub(t1)), // tcp connectionfmta(t6.Sub(t5)), // tls handshakefmta(t4.Sub(t3)), // server processingfmta(t7.Sub(t4)), // content transferfmtb(t1.Sub(t0)), // namelookupfmtb(t2.Sub(t0)), // connectfmtb(t3.Sub(t0)), // pretransferfmtb(t4.Sub(t0)), // starttransferfmtb(t7.Sub(t0)), // total)case "http":printf(colorize(httpTemplate),fmta(t1.Sub(t0)), // dns lookupfmta(t3.Sub(t1)), // tcp connectionfmta(t4.Sub(t3)), // server processingfmta(t7.Sub(t4)), // content transferfmtb(t1.Sub(t0)), // namelookupfmtb(t3.Sub(t0)), // connectfmtb(t4.Sub(t0)), // starttransferfmtb(t7.Sub(t0)), // total)}if followRedirects && isRedirect(resp) {loc, err := resp.Location()if err != nil {if err == http.ErrNoLocation {// 30x but no Location to follow, give up.return}log.Fatalf("unable to follow redirect: %v", err)}redirectsFollowed++if redirectsFollowed > maxRedirects {log.Fatalf("maximum number of redirects (%d) followed", maxRedirects)}visit(loc)}}func isRedirect(resp *http.Response) bool {return resp.StatusCode > 299 && resp.StatusCode < 400}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)}for _, h := range httpHeaders {k, v := headerKeyValue(h)if strings.EqualFold(k, "host") {req.Host = vcontinue}req.Header.Add(k, v)}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)}// getFilenameFromHeaders tries to automatically determine the output filename,// when saving to disk, based on the Content-Disposition header.// If the header is not present, or it does not contain enough information to// determine which filename to use, this function returns "".func getFilenameFromHeaders(headers http.Header) string {// if the Content-Disposition header is set parse itif hdr := headers.Get("Content-Disposition"); hdr != "" {// pull the media type, and subsequent params, from// the body of the header fieldmt, params, err := mime.ParseMediaType(hdr)// if there was no error and the media type is attachmentif err == nil && mt == "attachment" {if filename := params["filename"]; filename != "" {return filename}}}// return an empty string if we were unable to determine the filenamereturn ""}// readResponseBody consumes the body of the response.// readResponseBody returns an informational message about the// disposition of the response body's contents.func readResponseBody(req *http.Request, resp *http.Response) string {if isRedirect(resp) || req.Method == http.MethodHead {return ""}w := ioutil.Discardmsg := color.CyanString("Body discarded")if saveOutput || outputFile != "" {filename := outputFileif saveOutput {// try to get the filename from the Content-Disposition header// otherwise fall back to the RequestURIif filename = getFilenameFromHeaders(resp.Header); filename == "" {filename = path.Base(req.URL.RequestURI())}if filename == "/" {log.Fatalf("No remote filename; specify output filename with -o to save response body")}}f, err := os.Create(filename)if err != nil {log.Fatalf("unable to create file %s: %v", filename, err)}defer f.Close()w = fmsg = color.CyanString("Body read")}if _, err := io.Copy(w, resp.Body); err != nil && w != ioutil.Discard {log.Fatalf("failed to read response body: %v", err)}return msg}type headers []stringfunc (h headers) String() string {var o []stringfor _, v := range h {o = append(o, "-H "+v)}return strings.Join(o, " ")}func (h *headers) Set(v string) error {*h = append(*h, v)return nil}func (h headers) Len() int { return len(h) }func (h headers) Swap(i, j int) { h[i], h[j] = h[j], h[i] }func (h headers) Less(i, j int) bool {a, b := h[i], h[j]// server always sorts at the topif a == "Server" {return true}if b == "Server" {return false}endtoend := func(n string) bool {// https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1switch n {case "Connection","Keep-Alive","Proxy-Authenticate","Proxy-Authorization","TE","Trailers","Transfer-Encoding","Upgrade":return falsedefault:return true}}x, y := endtoend(a), endtoend(b)if x == y {// both are of the same classreturn a < b}return x}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。