// 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.package httptestimport ()// ResponseRecorder is an implementation of [http.ResponseWriter] that// records its mutations for later inspection in tests.typeResponseRecorderstruct {// Code is the HTTP response code set by WriteHeader. // // Note that if a Handler never calls WriteHeader or Write, // this might end up being 0, rather than the implicit // http.StatusOK. To get the implicit value, use the Result // method. Code int// HeaderMap contains the headers explicitly set by the Handler. // It is an internal detail. // // Deprecated: HeaderMap exists for historical compatibility // and should not be used. To access the headers returned by a handler, // use the Response.Header map as returned by the Result method. HeaderMap http.Header// Body is the buffer to which the Handler's Write calls are sent. // If nil, the Writes are silently discarded. Body *bytes.Buffer// Flushed is whether the Handler called Flush. Flushed bool result *http.Response// cache of Result's return value snapHeader http.Header// snapshot of HeaderMap at first Write wroteHeader bool}// NewRecorder returns an initialized [ResponseRecorder].func () *ResponseRecorder {return &ResponseRecorder{HeaderMap: make(http.Header),Body: new(bytes.Buffer),Code: 200, }}// DefaultRemoteAddr is the default remote address to return in RemoteAddr if// an explicit DefaultRemoteAddr isn't set on [ResponseRecorder].constDefaultRemoteAddr = "1.2.3.4"// Header implements [http.ResponseWriter]. It returns the response// headers to mutate within a handler. To test the headers that were// written after a handler completes, use the [ResponseRecorder.Result] method and see// the returned Response value's Header.func ( *ResponseRecorder) () http.Header { := .HeaderMapif == nil { = make(http.Header) .HeaderMap = }return}// writeHeader writes a header if it was not written yet and// detects Content-Type if needed.//// bytes or str are the beginning of the response body.// We pass both to avoid unnecessarily generate garbage// in rw.WriteString which was created for performance reasons.// Non-nil bytes win.func ( *ResponseRecorder) ( []byte, string) {if .wroteHeader {return }iflen() > 512 { = [:512] } := .Header() , := ["Content-Type"] := .Get("Transfer-Encoding") != ""if ! && ! {if == nil { = []byte() } .Set("Content-Type", http.DetectContentType()) } .WriteHeader(200)}// Write implements http.ResponseWriter. The data in buf is written to// rw.Body, if not nil.func ( *ResponseRecorder) ( []byte) (int, error) { .writeHeader(, "")if .Body != nil { .Body.Write() }returnlen(), nil}// WriteString implements [io.StringWriter]. The data in str is written// to rw.Body, if not nil.func ( *ResponseRecorder) ( string) (int, error) { .writeHeader(nil, )if .Body != nil { .Body.WriteString() }returnlen(), nil}func checkWriteHeaderCode( int) {// Issue 22880: require valid WriteHeader status codes. // For now we only enforce that it's three digits. // In the future we might block things over 599 (600 and above aren't defined // at https://httpwg.org/specs/rfc7231.html#status.codes) // and we might block under 200 (once we have more mature 1xx support). // But for now any three digits. // // We used to send "HTTP/1.1 000 0" on the wire in responses but there's // no equivalent bogus thing we can realistically send in HTTP/2, // so we'll consistently panic instead and help people find their bugs // early. (We can't return an error from WriteHeader even if we wanted to.)if < 100 || > 999 {panic(fmt.Sprintf("invalid WriteHeader code %v", )) }}// WriteHeader implements [http.ResponseWriter].func ( *ResponseRecorder) ( int) {if .wroteHeader {return }checkWriteHeaderCode() .Code = .wroteHeader = trueif .HeaderMap == nil { .HeaderMap = make(http.Header) } .snapHeader = .HeaderMap.Clone()}// Flush implements [http.Flusher]. To test whether Flush was// called, see rw.Flushed.func ( *ResponseRecorder) () {if !.wroteHeader { .WriteHeader(200) } .Flushed = true}// Result returns the response generated by the handler.//// The returned Response will have at least its StatusCode,// Header, Body, and optionally Trailer populated.// More fields may be populated in the future, so callers should// not DeepEqual the result in tests.//// The Response.Header is a snapshot of the headers at the time of the// first write call, or at the time of this call, if the handler never// did a write.//// The Response.Body is guaranteed to be non-nil and Body.Read call is// guaranteed to not return any error other than [io.EOF].//// Result must only be called after the handler has finished running.func ( *ResponseRecorder) () *http.Response {if .result != nil {return .result }if .snapHeader == nil { .snapHeader = .HeaderMap.Clone() } := &http.Response{Proto: "HTTP/1.1",ProtoMajor: 1,ProtoMinor: 1,StatusCode: .Code,Header: .snapHeader, } .result = if .StatusCode == 0 { .StatusCode = 200 } .Status = fmt.Sprintf("%03d %s", .StatusCode, http.StatusText(.StatusCode))if .Body != nil { .Body = io.NopCloser(bytes.NewReader(.Body.Bytes())) } else { .Body = http.NoBody } .ContentLength = parseContentLength(.Header.Get("Content-Length"))if , := .snapHeader["Trailer"]; { .Trailer = make(http.Header, len())for , := range {for := rangestrings.SplitSeq(, ",") { = http.CanonicalHeaderKey(textproto.TrimString())if !httpguts.ValidTrailerHeader() {// Ignore since forbidden by RFC 7230, section 4.1.2.continue } , := .HeaderMap[]if ! {continue } := make([]string, len())copy(, ) .Trailer[] = } } }for , := range .HeaderMap {if !strings.HasPrefix(, http.TrailerPrefix) {continue }if .Trailer == nil { .Trailer = make(http.Header) }for , := range { .Trailer.Add(strings.TrimPrefix(, http.TrailerPrefix), ) } }return}// parseContentLength trims whitespace from s and returns -1 if no value// is set, or the value if it's >= 0.//// This a modified version of same function found in net/http/transfer.go. This// one just ignores an invalid header.func parseContentLength( string) int64 { = textproto.TrimString()if == "" {return -1 } , := strconv.ParseUint(, 10, 63)if != nil {return -1 }returnint64()}
The pages are generated with Goldsv0.7.9-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.