|  | // 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 httptest | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "fmt" | 
|  | "io/ioutil" | 
|  | "net/http" | 
|  | "strconv" | 
|  | "strings" | 
|  |  | 
|  | "golang_org/x/net/http/httpguts" | 
|  | ) | 
|  |  | 
|  | // ResponseRecorder is an implementation of http.ResponseWriter that | 
|  | // records its mutations for later inspection in tests. | 
|  | type ResponseRecorder struct { | 
|  | // 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 NewRecorder() *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. | 
|  | const DefaultRemoteAddr = "1.2.3.4" | 
|  |  | 
|  | // Header returns the response headers. | 
|  | func (rw *ResponseRecorder) Header() http.Header { | 
|  | m := rw.HeaderMap | 
|  | if m == nil { | 
|  | m = make(http.Header) | 
|  | rw.HeaderMap = m | 
|  | } | 
|  | return m | 
|  | } | 
|  |  | 
|  | // 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 (rw *ResponseRecorder) writeHeader(b []byte, str string) { | 
|  | if rw.wroteHeader { | 
|  | return | 
|  | } | 
|  | if len(str) > 512 { | 
|  | str = str[:512] | 
|  | } | 
|  |  | 
|  | m := rw.Header() | 
|  |  | 
|  | _, hasType := m["Content-Type"] | 
|  | hasTE := m.Get("Transfer-Encoding") != "" | 
|  | if !hasType && !hasTE { | 
|  | if b == nil { | 
|  | b = []byte(str) | 
|  | } | 
|  | m.Set("Content-Type", http.DetectContentType(b)) | 
|  | } | 
|  |  | 
|  | rw.WriteHeader(200) | 
|  | } | 
|  |  | 
|  | // Write always succeeds and writes to rw.Body, if not nil. | 
|  | func (rw *ResponseRecorder) Write(buf []byte) (int, error) { | 
|  | rw.writeHeader(buf, "") | 
|  | if rw.Body != nil { | 
|  | rw.Body.Write(buf) | 
|  | } | 
|  | return len(buf), nil | 
|  | } | 
|  |  | 
|  | // WriteString always succeeds and writes to rw.Body, if not nil. | 
|  | func (rw *ResponseRecorder) WriteString(str string) (int, error) { | 
|  | rw.writeHeader(nil, str) | 
|  | if rw.Body != nil { | 
|  | rw.Body.WriteString(str) | 
|  | } | 
|  | return len(str), nil | 
|  | } | 
|  |  | 
|  | // WriteHeader sets rw.Code. After it is called, changing rw.Header | 
|  | // will not affect rw.HeaderMap. | 
|  | func (rw *ResponseRecorder) WriteHeader(code int) { | 
|  | if rw.wroteHeader { | 
|  | return | 
|  | } | 
|  | rw.Code = code | 
|  | rw.wroteHeader = true | 
|  | if rw.HeaderMap == nil { | 
|  | rw.HeaderMap = make(http.Header) | 
|  | } | 
|  | rw.snapHeader = cloneHeader(rw.HeaderMap) | 
|  | } | 
|  |  | 
|  | func cloneHeader(h http.Header) http.Header { | 
|  | h2 := make(http.Header, len(h)) | 
|  | for k, vv := range h { | 
|  | vv2 := make([]string, len(vv)) | 
|  | copy(vv2, vv) | 
|  | h2[k] = vv2 | 
|  | } | 
|  | return h2 | 
|  | } | 
|  |  | 
|  | // Flush sets rw.Flushed to true. | 
|  | func (rw *ResponseRecorder) Flush() { | 
|  | if !rw.wroteHeader { | 
|  | rw.WriteHeader(200) | 
|  | } | 
|  | rw.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 (rw *ResponseRecorder) Result() *http.Response { | 
|  | if rw.result != nil { | 
|  | return rw.result | 
|  | } | 
|  | if rw.snapHeader == nil { | 
|  | rw.snapHeader = cloneHeader(rw.HeaderMap) | 
|  | } | 
|  | res := &http.Response{ | 
|  | Proto:      "HTTP/1.1", | 
|  | ProtoMajor: 1, | 
|  | ProtoMinor: 1, | 
|  | StatusCode: rw.Code, | 
|  | Header:     rw.snapHeader, | 
|  | } | 
|  | rw.result = res | 
|  | if res.StatusCode == 0 { | 
|  | res.StatusCode = 200 | 
|  | } | 
|  | res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode)) | 
|  | if rw.Body != nil { | 
|  | res.Body = ioutil.NopCloser(bytes.NewReader(rw.Body.Bytes())) | 
|  | } else { | 
|  | res.Body = http.NoBody | 
|  | } | 
|  | res.ContentLength = parseContentLength(res.Header.Get("Content-Length")) | 
|  |  | 
|  | if trailers, ok := rw.snapHeader["Trailer"]; ok { | 
|  | res.Trailer = make(http.Header, len(trailers)) | 
|  | for _, k := range trailers { | 
|  | k = http.CanonicalHeaderKey(k) | 
|  | if !httpguts.ValidTrailerHeader(k) { | 
|  | // Ignore since forbidden by RFC 7230, section 4.1.2. | 
|  | continue | 
|  | } | 
|  | vv, ok := rw.HeaderMap[k] | 
|  | if !ok { | 
|  | continue | 
|  | } | 
|  | vv2 := make([]string, len(vv)) | 
|  | copy(vv2, vv) | 
|  | res.Trailer[k] = vv2 | 
|  | } | 
|  | } | 
|  | for k, vv := range rw.HeaderMap { | 
|  | if !strings.HasPrefix(k, http.TrailerPrefix) { | 
|  | continue | 
|  | } | 
|  | if res.Trailer == nil { | 
|  | res.Trailer = make(http.Header) | 
|  | } | 
|  | for _, v := range vv { | 
|  | res.Trailer.Add(strings.TrimPrefix(k, http.TrailerPrefix), v) | 
|  | } | 
|  | } | 
|  | return res | 
|  | } | 
|  |  | 
|  | // 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(cl string) int64 { | 
|  | cl = strings.TrimSpace(cl) | 
|  | if cl == "" { | 
|  | return -1 | 
|  | } | 
|  | n, err := strconv.ParseInt(cl, 10, 64) | 
|  | if err != nil { | 
|  | return -1 | 
|  | } | 
|  | return n | 
|  | } |