// Copyright 2011 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. // Implementation of Server package httptest import ( "crypto/tls" "flag" "fmt" "net" "net/http" "os" "sync" ) // A Server is an HTTP server listening on a system-chosen port on the // local loopback interface, for use in end-to-end HTTP tests. type Server struct { URL string // base URL of form http://ipaddr:port with no trailing slash Listener net.Listener // TLS is the optional TLS configuration, populated with a new config // after TLS is started. If set on an unstarted server before StartTLS // is called, existing fields are copied into the new config. TLS *tls.Config // Config may be changed after calling NewUnstartedServer and // before Start or StartTLS. Config *http.Server // wg counts the number of outstanding HTTP requests on this server. // Close blocks until all requests are finished. wg sync.WaitGroup } // historyListener keeps track of all connections that it's ever // accepted. type historyListener struct { net.Listener sync.Mutex // protects history history []net.Conn } func (hs *historyListener) Accept() (c net.Conn, err error) { c, err = hs.Listener.Accept() if err == nil { hs.Lock() hs.history = append(hs.history, c) hs.Unlock() } return } func newLocalListener() net.Listener { if *serve != "" { l, err := net.Listen("tcp", *serve) if err != nil { panic(fmt.Sprintf("httptest: failed to listen on %v: %v", *serve, err)) } return l } l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { if l, err = net.Listen("tcp6", "[::1]:0"); err != nil { panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err)) } } return l } // When debugging a particular http server-based test, // this flag lets you run // go test -run=BrokenTest -httptest.serve=127.0.0.1:8000 // to start the broken server so you can interact with it manually. var serve = flag.String("httptest.serve", "", "if non-empty, httptest.NewServer serves on this address and blocks") // NewServer starts and returns a new Server. // The caller should call Close when finished, to shut it down. func NewServer(handler http.Handler) *Server { ts := NewUnstartedServer(handler) ts.Start() return ts } // NewUnstartedServer returns a new Server but doesn't start it. // // After changing its configuration, the caller should call Start or // StartTLS. // // The caller should call Close when finished, to shut it down. func NewUnstartedServer(handler http.Handler) *Server { return &Server{ Listener: newLocalListener(), Config: &http.Server{Handler: handler}, } } // Start starts a server from NewUnstartedServer. func (s *Server) Start() { if s.URL != "" { panic("Server already started") } s.Listener = &historyListener{Listener: s.Listener} s.URL = "http://" + s.Listener.Addr().String() s.wrapHandler() go s.Config.Serve(s.Listener) if *serve != "" { fmt.Fprintln(os.Stderr, "httptest: serving on", s.URL) select {} } } // StartTLS starts TLS on a server from NewUnstartedServer. func (s *Server) StartTLS() { if s.URL != "" { panic("Server already started") } cert, err := tls.X509KeyPair(localhostCert, localhostKey) if err != nil { panic(fmt.Sprintf("httptest: NewTLSServer: %v", err)) } existingConfig := s.TLS s.TLS = new(tls.Config) if existingConfig != nil { *s.TLS = *existingConfig } if s.TLS.NextProtos == nil { s.TLS.NextProtos = []string{"http/1.1"} } if len(s.TLS.Certificates) == 0 { s.TLS.Certificates = []tls.Certificate{cert} } tlsListener := tls.NewListener(s.Listener, s.TLS) s.Listener = &historyListener{Listener: tlsListener} s.URL = "https://" + s.Listener.Addr().String() s.wrapHandler() go s.Config.Serve(s.Listener) } func (s *Server) wrapHandler() { h := s.Config.Handler if h == nil { h = http.DefaultServeMux } s.Config.Handler = &waitGroupHandler{ s: s, h: h, } } // NewTLSServer starts and returns a new Server using TLS. // The caller should call Close when finished, to shut it down. func NewTLSServer(handler http.Handler) *Server { ts := NewUnstartedServer(handler) ts.StartTLS() return ts } // Close shuts down the server and blocks until all outstanding // requests on this server have completed. func (s *Server) Close() { s.Listener.Close() s.wg.Wait() s.CloseClientConnections() if t, ok := http.DefaultTransport.(*http.Transport); ok { t.CloseIdleConnections() } } // CloseClientConnections closes any currently open HTTP connections // to the test Server. func (s *Server) CloseClientConnections() { hl, ok := s.Listener.(*historyListener) if !ok { return } hl.Lock() for _, conn := range hl.history { conn.Close() } hl.Unlock() } // waitGroupHandler wraps a handler, incrementing and decrementing a // sync.WaitGroup on each request, to enable Server.Close to block // until outstanding requests are finished. type waitGroupHandler struct { s *Server h http.Handler // non-nil } func (h *waitGroupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.s.wg.Add(1) defer h.s.wg.Done() // a defer, in case ServeHTTP below panics h.h.ServeHTTP(w, r) } // localhostCert is a PEM-encoded TLS cert with SAN IPs // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end // of ASN.1 time). // 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-----`)