Ejemplo n.º 1
0
// RoundTrip handles the actual request; ensuring a connection is
// made, determining which protocol to use, and performing the
// request.
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
	u := req.URL

	// Make sure the URL host contains the port.
	if !strings.Contains(u.Host, ":") {
		switch u.Scheme {
		case "http":
			u.Host += ":80"

		case "https":
			u.Host += ":443"
		}
	}

	conn, tcpConn, err := t.process(req)
	if err != nil {
		return nil, err
	}
	if tcpConn != nil {
		return t.doHTTP(tcpConn, req)
	}

	// The connection has now been established.

	debug.Printf("Requesting %q over SPDY.\n", u.String())

	// Determine the request priority.
	var priority common.Priority
	if t.Priority != nil {
		priority = t.Priority(req.URL)
	} else {
		priority = common.DefaultPriority(req.URL)
	}

	res, err := conn.RequestResponse(req, t.Receiver, priority)
	if conn.Closed() {
		t.connLimit[u.Host] <- struct{}{}
	}
	if err != nil {
		return nil, err
	}

	return res, nil
}
Ejemplo n.º 2
0
// test tests a site to see if it supports various protocols
func (s *site) test(l *os.File) {

	// Check the name resolves, give up if it does not

	s.resolves.ran = true
	_, err := net.LookupHost(s.name)
	if err != nil {
		s.logf(l, "Error resolving name: %s", err)
		s.resolves.yesno = false
		return
	}
	s.resolves.yesno = true

	// See if port 443 is open, give up if it is not

	hostPort := net.JoinHostPort(s.name, "443")

	s.port443Open.ran = true
	c, err := net.DialTimeout("tcp", hostPort, 2*time.Second)
	if err != nil {
		s.logf(l, "TCP dial to port 443 failed: %s", err)
		s.port443Open.yesno = false
		return
	}
	c.Close()
	s.port443Open.yesno = true

	// See if TLS works and if SPDY/3.1 or HTTP/2 offered. Give up if
	// TLS does not work. If the name doesn't work try adding www.  to
	// se if it's a TLS certificate error.

	s.tlsWorks.ran = true
	config := &tls.Config{}
	config.ServerName = s.name
	timeoutDialer := &net.Dialer{}
	timeoutDialer.Timeout = 2 * time.Second
	tc, err := tls.DialWithDialer(timeoutDialer, "tcp", hostPort, config)
	if err != nil {
		s.name = "www." + s.name
		config.ServerName = s.name
		hostPort = net.JoinHostPort(s.name, "443")
		tc, err = tls.DialWithDialer(timeoutDialer, "tcp", hostPort, config)
		if err != nil {
			s.logf(l, "Error performing TLS connection: %s", err)
			s.tlsWorks.yesno = false
			return
		}
	}
	s.tlsWorks.yesno = true

	// Retrieve the list of NPN offered protocols and check for
	// spdy/3.1 and h2

	cs := tc.ConnectionState()
	s.npn = cs.OfferedProtocols
	s.spdyAnnounced.ran = true
	s.spdyAnnounced.yesno = in(cs.OfferedProtocols, "spdy/3.1")
	s.http2Announced.ran = true
	s.http2Announced.yesno = in(cs.OfferedProtocols, "h2")
	tc.Close()

	// See if HTTPS works by performing GET /

	s.httpsWorks.ran = true
	httpTransport := &http.Transport{}
	req, _ := http.NewRequest("GET", "https://"+s.name, nil)
	resp, err := httpTransport.RoundTrip(req)
	if err != nil {
		s.logf(l, "HTTP request failed: %s", err)
		return
	}
	if resp != nil && resp.Body != nil {
		_, _ = ioutil.ReadAll(resp.Body)
		resp.Body.Close()
	}
	httpTransport.CloseIdleConnections()
	s.httpsWorks.yesno = err == nil

	// See if SPDY works by doing GET / over SPDY

	if s.spdyAnnounced.yesno {
		s.spdyWorks.ran = true
		conf := &tls.Config{}
		conf.NextProtos = []string{"spdy/3.1"}
		spdyC, err := tls.DialWithDialer(timeoutDialer, "tcp", hostPort, conf)
		if err == nil {
			spdyC.SetDeadline(time.Now().Add(time.Minute))
			defer spdyC.Close()
			cs = spdyC.ConnectionState()
			if cs.NegotiatedProtocol != "spdy/3.1" {
				s.logf(l, "NegotiatedProtocol not spdy/3.1: %s",
					cs.NegotiatedProtocol)
			} else {
				sc, err := spdy.NewClientConn(spdyC, nil, 3, 1)
				if err == nil {
					defer sc.Close()
					go sc.Run()
					req, _ := http.NewRequest("GET", "https://"+s.name, nil)
					resp, err := sc.RequestResponse(req, nil,
						common.DefaultPriority(req.URL))
					if err != nil {
						s.logf(l, "Failed to do SPDY request: %s", err)
					}
					s.spdyWorks.yesno = err == nil
					if resp != nil && resp.Body != nil {
						_, _ = ioutil.ReadAll(resp.Body)
						resp.Body.Close()
					}
				} else {
					s.logf(l, "Failed create SPDY client connection: %s", err)
				}
			}
		} else {
			s.logf(l, "Failed to dial port 443 for SPDY: %s", err)
		}
	}

	// See if HTTP/2 works by doing GET / using HTTP/2

	if s.http2Announced.yesno {
		s.http2Works.ran = true
		conf := &tls.Config{}
		conf.NextProtos = []string{"h2"}
		h2C, err := tls.DialWithDialer(timeoutDialer, "tcp", hostPort, conf)
		if err == nil {
			h2C.SetDeadline(time.Now().Add(time.Minute))
			defer h2C.Close()
			cs = h2C.ConnectionState()
			if cs.NegotiatedProtocol != "h2" {
				s.logf(l, "NegotiatedProtocol not h2: %s",
					cs.NegotiatedProtocol)
			} else {
				h2t := &http2.Transport{}
				h2c, err := h2t.NewClientConn(h2C)
				if err == nil {
					req, _ := http.NewRequest("GET", "https://"+s.name, nil)
					resp, err := h2c.RoundTrip(req)
					if err != nil {
						s.logf(l, "HTTP/2 RoundTrip failed: %s", err)
					}
					s.http2Works.yesno = err == nil
					if resp != nil && resp.Body != nil {
						_, _ = ioutil.ReadAll(resp.Body)
						resp.Body.Close()
					}
				} else {
					s.logf(l, "Failed create HTTP/2 client connection: %s", err)
				}
				h2t.CloseIdleConnections()
			}
		} else {
			s.logf(l, "Failed to dial port 443 for HTTP/2: %s", err)
		}
	}
}