diff options
Diffstat (limited to 'src/pkg/http/httptest')
| -rw-r--r-- | src/pkg/http/httptest/recorder.go | 44 | ||||
| -rw-r--r-- | src/pkg/http/httptest/server.go | 106 |
2 files changed, 112 insertions, 38 deletions
diff --git a/src/pkg/http/httptest/recorder.go b/src/pkg/http/httptest/recorder.go index ec7bde8aa..0dd19a617 100644 --- a/src/pkg/http/httptest/recorder.go +++ b/src/pkg/http/httptest/recorder.go @@ -14,20 +14,17 @@ import ( // ResponseRecorder is an implementation of http.ResponseWriter that // records its mutations for later inspection in tests. type ResponseRecorder struct { - Code int // the HTTP response code from WriteHeader - Header http.Header // if non-nil, the headers to populate - Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to - Flushed bool - - FakeRemoteAddr string // the fake RemoteAddr to return, or "" for DefaultRemoteAddr - FakeUsingTLS bool // whether to return true from the UsingTLS method + Code int // the HTTP response code from WriteHeader + HeaderMap http.Header // the HTTP response headers + Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to + Flushed bool } // NewRecorder returns an initialized ResponseRecorder. func NewRecorder() *ResponseRecorder { return &ResponseRecorder{ - Header: http.Header(make(map[string][]string)), - Body: new(bytes.Buffer), + HeaderMap: make(http.Header), + Body: new(bytes.Buffer), } } @@ -35,29 +32,9 @@ func NewRecorder() *ResponseRecorder { // an explicit DefaultRemoteAddr isn't set on ResponseRecorder. const DefaultRemoteAddr = "1.2.3.4" -// RemoteAddr returns the value of rw.FakeRemoteAddr, if set, else -// returns DefaultRemoteAddr. -func (rw *ResponseRecorder) RemoteAddr() string { - if rw.FakeRemoteAddr != "" { - return rw.FakeRemoteAddr - } - return DefaultRemoteAddr -} - -// UsingTLS returns the fake value in rw.FakeUsingTLS -func (rw *ResponseRecorder) UsingTLS() bool { - return rw.FakeUsingTLS -} - -// SetHeader populates rw.Header, if non-nil. -func (rw *ResponseRecorder) SetHeader(k, v string) { - if rw.Header != nil { - if v == "" { - rw.Header.Del(k) - } else { - rw.Header.Set(k, v) - } - } +// Header returns the response headers. +func (rw *ResponseRecorder) Header() http.Header { + return rw.HeaderMap } // Write always succeeds and writes to rw.Body, if not nil. @@ -65,6 +42,9 @@ func (rw *ResponseRecorder) Write(buf []byte) (int, os.Error) { if rw.Body != nil { rw.Body.Write(buf) } + if rw.Code == 0 { + rw.Code = http.StatusOK + } return len(buf), nil } diff --git a/src/pkg/http/httptest/server.go b/src/pkg/http/httptest/server.go index 86c9eb435..8e385d045 100644 --- a/src/pkg/http/httptest/server.go +++ b/src/pkg/http/httptest/server.go @@ -7,9 +7,13 @@ package httptest import ( + "crypto/rand" + "crypto/tls" "fmt" "http" "net" + "os" + "time" ) // A Server is an HTTP server listening on a system-chosen port on the @@ -17,22 +21,69 @@ import ( type Server struct { URL string // base URL of form http://ipaddr:port with no trailing slash Listener net.Listener + TLS *tls.Config // nil if not using using TLS } -// 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 := new(Server) +// historyListener keeps track of all connections that it's ever +// accepted. +type historyListener struct { + net.Listener + history []net.Conn +} + +func (hs *historyListener) Accept() (c net.Conn, err os.Error) { + c, err = hs.Listener.Accept() + if err == nil { + hs.history = append(hs.history, c) + } + return +} + +func newLocalListener() net.Listener { 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)) } } - ts.Listener = l + return l +} + +// 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 := new(Server) + l := newLocalListener() + ts.Listener = &historyListener{l, make([]net.Conn, 0)} ts.URL = "http://" + l.Addr().String() server := &http.Server{Handler: handler} - go server.Serve(l) + go server.Serve(ts.Listener) + return ts +} + +// 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 { + l := newLocalListener() + ts := new(Server) + + cert, err := tls.X509KeyPair(localhostCert, localhostKey) + if err != nil { + panic(fmt.Sprintf("httptest: NewTLSServer: %v", err)) + } + + ts.TLS = &tls.Config{ + Rand: rand.Reader, + Time: time.Seconds, + NextProtos: []string{"http/1.1"}, + Certificates: []tls.Certificate{cert}, + } + tlsListener := tls.NewListener(l, ts.TLS) + + ts.Listener = &historyListener{tlsListener, make([]net.Conn, 0)} + ts.URL = "https://" + l.Addr().String() + server := &http.Server{Handler: handler} + go server.Serve(ts.Listener) return ts } @@ -40,3 +91,46 @@ func NewServer(handler http.Handler) *Server { func (s *Server) Close() { s.Listener.Close() } + +// CloseClientConnections closes any currently open HTTP connections +// to the test Server. +func (s *Server) CloseClientConnections() { + hl, ok := s.Listener.(*historyListener) + if !ok { + return + } + for _, conn := range hl.history { + conn.Close() + } +} + +// localhostCert is a PEM-encoded TLS cert with SAN DNS names +// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end +// of ASN.1 time). +var localhostCert = []byte(`-----BEGIN CERTIFICATE----- +MIIBwTCCASugAwIBAgIBADALBgkqhkiG9w0BAQUwADAeFw0xMTAzMzEyMDI1MDda +Fw00OTEyMzEyMzU5NTlaMAAwggCdMAsGCSqGSIb3DQEBAQOCAIwAMIIAhwKCAIB6 +oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZLKq2sM3gRaimsktIw +nNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0SjdZ7vTPnFDPNsHGe +KBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBA6NPME0wDgYDVR0PAQH/ +BAQDAgCgMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwGwYDVR0RBBQw +EoIJMTI3LjAuMC4xggVbOjoxXTALBgkqhkiG9w0BAQUDggCBAHC3gbdvc44vs+wD +g2kONiENnx8WKc0UTGg/TOXS3gaRb+CUIQtHWja65l8rAfclEovjHgZ7gx8brO0W +JuC6p3MUAKsgOssIrrRIx2rpnfcmFVMzguCmrMNVmKUAalw18Yp0F72xYAIitVQl +kJrLdIhBajcJRYu/YGltHQRaXuVt +-----END CERTIFICATE----- +`) + +// localhostKey is the private key for localhostCert. +var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIBkgIBAQKCAIB6oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZL +Kq2sM3gRaimsktIwnNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0S +jdZ7vTPnFDPNsHGeKBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBAwKC +AIBRwh7Bil5Z8cYpZZv7jdQxDvbim7Z7ocRdeDmzZuF2I9RW04QyHHPIIlALnBvI +YeF1veASz1gEFGUjzmbUGqKYSbCoTzXoev+F4bmbRxcX9sOmtslqvhMSHRSzA5NH +aDVI3Hn4wvBVD8gePu8ACWqvPGbCiql11OKCMfjlPn2uuwJAx/24/F5DjXZ6hQQ7 +HxScOxKrpx5WnA9r1wZTltOTZkhRRzuLc21WJeE3M15QUdWi3zZxCKRFoth65HEs +jy9YHQJAnPueRI44tz79b5QqVbeaOMUr7ZCb1Kp0uo6G+ANPLdlfliAupwij2eIz +mHRJOWk0jBtXfRft1McH2H51CpXAyw== +-----END RSA PRIVATE KEY----- +`) |
