]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: register HTTP/2 before listening in ListenAndServe
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 3 Nov 2015 19:00:37 +0000 (11:00 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 4 Nov 2015 16:25:18 +0000 (16:25 +0000)
Change-Id: Icf9b6802945051aa484fb9ebcce71704f5655474
Reviewed-on: https://go-review.googlesource.com/16630
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/go/build/deps_test.go
src/net/http/export_test.go
src/net/http/httptest/server.go
src/net/http/internal/testcert.go [new file with mode: 0644]
src/net/http/serve_test.go
src/net/http/server.go

index 9746c167ab0bd81ae7ac041a11a59dd8b3a12b49..a3fb9208be773073b1408997e2d787ab7d11d2ce 100644 (file)
@@ -361,7 +361,7 @@ var pkgDeps = map[string][]string{
        "net/http/cgi":       {"L4", "NET", "OS", "crypto/tls", "net/http", "regexp"},
        "net/http/cookiejar": {"L4", "NET", "net/http"},
        "net/http/fcgi":      {"L4", "NET", "OS", "net/http", "net/http/cgi"},
-       "net/http/httptest":  {"L4", "NET", "OS", "crypto/tls", "flag", "net/http"},
+       "net/http/httptest":  {"L4", "NET", "OS", "crypto/tls", "flag", "net/http", "net/http/internal"},
        "net/http/httputil":  {"L4", "NET", "OS", "net/http", "net/http/internal"},
        "net/http/pprof":     {"L4", "OS", "html/template", "net/http", "runtime/pprof", "runtime/trace"},
        "net/rpc":            {"L4", "NET", "encoding/gob", "html/template", "net/http"},
index 68fbd438263c78c08d805e3c3b931a3bb156a820..e530f7e578838d823996ff36ab06f2e495cef714 100644 (file)
@@ -132,3 +132,5 @@ var ExportCloseWriteAndWait = (*conn).closeWriteAndWait
 var ExportErrRequestCanceled = errRequestCanceled
 
 var ExportServeFile = serveFile
+
+func SetTestHookServerServe(fn func(*Server, net.Listener)) { testHookServerServe = fn }
index 4a45b2b9402362d98d74b6b88b308cf3f181963a..fabfeca9434f3bf4cd32e060f2a28fc4577ae864 100644 (file)
@@ -14,6 +14,7 @@ import (
        "log"
        "net"
        "net/http"
+       "net/http/internal"
        "os"
        "runtime"
        "sync"
@@ -107,7 +108,7 @@ func (s *Server) StartTLS() {
        if s.URL != "" {
                panic("Server already started")
        }
-       cert, err := tls.X509KeyPair(localhostCert, localhostKey)
+       cert, err := tls.X509KeyPair(internal.LocalhostCert, internal.LocalhostKey)
        if err != nil {
                panic(fmt.Sprintf("httptest: NewTLSServer: %v", err))
        }
@@ -289,39 +290,3 @@ func (s *Server) forgetConn(c net.Conn) {
                s.wg.Done()
        }
 }
-
-// localhostCert is a PEM-encoded TLS cert with SAN IPs
-// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
-// generated from src/crypto/tls:
-// go run generate_cert.go  --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
-var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
-MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
-MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
-MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
-iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4
-iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul
-rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO
-BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
-AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA
-AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9
-tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs
-h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM
-fblo6RBxUQ==
------END CERTIFICATE-----`)
-
-// localhostKey is the private key for localhostCert.
-var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9
-SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZB
-l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
-AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
-3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
-uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
-qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
-jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
-fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
-fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU
-y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX
-qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo
-f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA==
------END RSA PRIVATE KEY-----`)
diff --git a/src/net/http/internal/testcert.go b/src/net/http/internal/testcert.go
new file mode 100644 (file)
index 0000000..4078909
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
+// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
+// generated from src/crypto/tls:
+// go run generate_cert.go  --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
+var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
+MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
+MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
+MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4
+iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul
+rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO
+BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
+AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA
+AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9
+tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs
+h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM
+fblo6RBxUQ==
+-----END CERTIFICATE-----`)
+
+// LocalhostKey is the private key for localhostCert.
+var LocalhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9
+SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZB
+l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
+AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
+3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
+uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
+qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
+jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
+fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
+fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU
+y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX
+qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo
+f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA==
+-----END RSA PRIVATE KEY-----`)
index f3454848b7577c26c85ce1c5f07d1a7ff3f503a4..4fe40da9aeeeb53761fb0da29fadb8e97c928f00 100644 (file)
@@ -1069,7 +1069,8 @@ func TestTLSServer(t *testing.T) {
        })
 }
 
-func TestAutomaticHTTP2(t *testing.T) {
+func TestAutomaticHTTP2_Serve(t *testing.T) {
+       defer afterTest(t)
        ln := newLocalListener(t)
        ln.Close() // immediately (not a defer!)
        var s Server
@@ -1082,6 +1083,65 @@ func TestAutomaticHTTP2(t *testing.T) {
        }
 }
 
+func TestAutomaticHTTP2_ListenAndServe(t *testing.T) {
+       defer afterTest(t)
+       defer SetTestHookServerServe(nil)
+       cert, err := tls.X509KeyPair(internal.LocalhostCert, internal.LocalhostKey)
+       if err != nil {
+               t.Fatal(err)
+       }
+       var ok bool
+       var s *Server
+       const maxTries = 5
+       var ln net.Listener
+Try:
+       for try := 0; try < maxTries; try++ {
+               ln = newLocalListener(t)
+               addr := ln.Addr().String()
+               ln.Close()
+               t.Logf("Got %v", addr)
+               lnc := make(chan net.Listener, 1)
+               SetTestHookServerServe(func(s *Server, ln net.Listener) {
+                       lnc <- ln
+               })
+               s = &Server{
+                       Addr: addr,
+                       TLSConfig: &tls.Config{
+                               Certificates: []tls.Certificate{cert},
+                       },
+               }
+               errc := make(chan error, 1)
+               go func() { errc <- s.ListenAndServeTLS("", "") }()
+               select {
+               case err := <-errc:
+                       t.Logf("On try #%v: %v", try+1, err)
+                       continue
+               case ln = <-lnc:
+                       ok = true
+                       t.Logf("Listening on %v", ln.Addr().String())
+                       break Try
+               }
+       }
+       if !ok {
+               t.Fatal("Failed to start up after %d tries", maxTries)
+       }
+       defer ln.Close()
+       c, err := tls.Dial("tcp", ln.Addr().String(), &tls.Config{
+               InsecureSkipVerify: true,
+               NextProtos:         []string{"h2", "http/1.1"},
+       })
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer c.Close()
+       if got, want := c.ConnectionState().NegotiatedProtocol, "h2"; got != want {
+               t.Errorf("NegotiatedProtocol = %q; want %q", got, want)
+       }
+       if got, want := c.ConnectionState().NegotiatedProtocolIsMutual, true; got != want {
+               t.Errorf("NegotiatedProtocolIsMutual = %v; want %v", got, want)
+       }
+}
+
 type serverExpectTest struct {
        contentLength    int // of request body
        chunked          bool
index 979b2eb1e54fac0aacd8de808e50f41db5cd3047..af3b28fa33f5e3250e9b361e6501f3bd0bace790 100644 (file)
@@ -1899,17 +1899,20 @@ func (srv *Server) ListenAndServe() error {
        return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
 }
 
+var testHookServerServe func(*Server, net.Listener) // used if non-nil
+
 // Serve accepts incoming connections on the Listener l, creating a
 // new service goroutine for each. The service goroutines read requests and
 // then call srv.Handler to reply to them.
 // Serve always returns a non-nil error.
 func (srv *Server) Serve(l net.Listener) error {
        defer l.Close()
+       if fn := testHookServerServe; fn != nil {
+               fn(srv, l)
+       }
        var tempDelay time.Duration // how long to sleep on accept failure
-       srv.nextProtoOnce.Do(srv.setNextProtoDefaults)
-       if srv.nextProtoErr != nil {
-               // Error from http2 ConfigureServer (e.g. bad ciphersuites)
-               return srv.nextProtoErr
+       if err := srv.setupHTTP2(); err != nil {
+               return err
        }
        for {
                rw, e := l.Accept()
@@ -2044,9 +2047,16 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
        if addr == "" {
                addr = ":https"
        }
+
+       // Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig
+       // before we clone it and create the TLS Listener.
+       if err := srv.setupHTTP2(); err != nil {
+               return err
+       }
+
        config := cloneTLSConfig(srv.TLSConfig)
-       if config.NextProtos == nil {
-               config.NextProtos = []string{"http/1.1"}
+       if !strSliceContains(config.NextProtos, "http/1.1") {
+               config.NextProtos = append(config.NextProtos, "http/1.1")
        }
 
        if len(config.Certificates) == 0 || certFile != "" || keyFile != "" {
@@ -2067,9 +2077,15 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
        return srv.Serve(tlsListener)
 }
 
-// setNextProtoDefaults configures HTTP/2.
-// It must only be called via srv.nextProtoOnce.
-func (srv *Server) setNextProtoDefaults() {
+func (srv *Server) setupHTTP2() error {
+       srv.nextProtoOnce.Do(srv.onceSetNextProtoDefaults)
+       return srv.nextProtoErr
+}
+
+// onceSetNextProtoDefaults configures HTTP/2, if the user hasn't
+// configured otherwise. (by setting srv.TLSNextProto non-nil)
+// It must only be called via srv.nextProtoOnce (use srv.setupHTTP2).
+func (srv *Server) onceSetNextProtoDefaults() {
        // Enable HTTP/2 by default if the user hasn't otherwise
        // configured their TLSNextProto map.
        if srv.TLSNextProto == nil {
@@ -2304,3 +2320,12 @@ func numLeadingCRorLF(v []byte) (n int) {
        return
 
 }
+
+func strSliceContains(ss []string, s string) bool {
+       for _, v := range ss {
+               if v == s {
+                       return true
+               }
+       }
+       return false
+}