github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/gmhttp/httptest/recorder.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package httptest
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"net/textproto"
    12  	"strconv"
    13  	"strings"
    14  
    15  	http "github.com/hxx258456/ccgo/gmhttp"
    16  
    17  	"golang.org/x/net/http/httpguts"
    18  )
    19  
    20  // ResponseRecorder is an implementation of http.ResponseWriter that
    21  // records its mutations for later inspection in tests.
    22  type ResponseRecorder struct {
    23  	// Code is the HTTP response code set by WriteHeader.
    24  	//
    25  	// Note that if a Handler never calls WriteHeader or Write,
    26  	// this might end up being 0, rather than the implicit
    27  	// http.StatusOK. To get the implicit value, use the Result
    28  	// method.
    29  	Code int
    30  
    31  	// HeaderMap contains the headers explicitly set by the Handler.
    32  	// It is an internal detail.
    33  	//
    34  	// Deprecated: HeaderMap exists for historical compatibility
    35  	// and should not be used. To access the headers returned by a handler,
    36  	// use the Response.Header map as returned by the Result method.
    37  	HeaderMap http.Header
    38  
    39  	// Body is the buffer to which the Handler's Write calls are sent.
    40  	// If nil, the Writes are silently discarded.
    41  	Body *bytes.Buffer
    42  
    43  	// Flushed is whether the Handler called Flush.
    44  	Flushed bool
    45  
    46  	result      *http.Response // cache of Result's return value
    47  	snapHeader  http.Header    // snapshot of HeaderMap at first Write
    48  	wroteHeader bool
    49  }
    50  
    51  // NewRecorder returns an initialized ResponseRecorder.
    52  func NewRecorder() *ResponseRecorder {
    53  	return &ResponseRecorder{
    54  		HeaderMap: make(http.Header),
    55  		Body:      new(bytes.Buffer),
    56  		Code:      200,
    57  	}
    58  }
    59  
    60  // DefaultRemoteAddr is the default remote address to return in RemoteAddr if
    61  // an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
    62  const DefaultRemoteAddr = "1.2.3.4"
    63  
    64  // Header implements http.ResponseWriter. It returns the response
    65  // headers to mutate within a handler. To test the headers that were
    66  // written after a handler completes, use the Result method and see
    67  // the returned Response value's Header.
    68  func (rw *ResponseRecorder) Header() http.Header {
    69  	m := rw.HeaderMap
    70  	if m == nil {
    71  		m = make(http.Header)
    72  		rw.HeaderMap = m
    73  	}
    74  	return m
    75  }
    76  
    77  // writeHeader writes a header if it was not written yet and
    78  // detects Content-Type if needed.
    79  //
    80  // bytes or str are the beginning of the response body.
    81  // We pass both to avoid unnecessarily generate garbage
    82  // in rw.WriteString which was created for performance reasons.
    83  // Non-nil bytes win.
    84  func (rw *ResponseRecorder) writeHeader(b []byte, str string) {
    85  	if rw.wroteHeader {
    86  		return
    87  	}
    88  	if len(str) > 512 {
    89  		str = str[:512]
    90  	}
    91  
    92  	m := rw.Header()
    93  
    94  	_, hasType := m["Content-Type"]
    95  	hasTE := m.Get("Transfer-Encoding") != ""
    96  	if !hasType && !hasTE {
    97  		if b == nil {
    98  			b = []byte(str)
    99  		}
   100  		m.Set("Content-Type", http.DetectContentType(b))
   101  	}
   102  
   103  	rw.WriteHeader(200)
   104  }
   105  
   106  // Write implements http.ResponseWriter. The data in buf is written to
   107  // rw.Body, if not nil.
   108  func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
   109  	rw.writeHeader(buf, "")
   110  	if rw.Body != nil {
   111  		rw.Body.Write(buf)
   112  	}
   113  	return len(buf), nil
   114  }
   115  
   116  // WriteString implements io.StringWriter. The data in str is written
   117  // to rw.Body, if not nil.
   118  func (rw *ResponseRecorder) WriteString(str string) (int, error) {
   119  	rw.writeHeader(nil, str)
   120  	if rw.Body != nil {
   121  		rw.Body.WriteString(str)
   122  	}
   123  	return len(str), nil
   124  }
   125  
   126  func checkWriteHeaderCode(code int) {
   127  	// Issue 22880: require valid WriteHeader status codes.
   128  	// For now we only enforce that it's three digits.
   129  	// In the future we might block things over 599 (600 and above aren't defined
   130  	// at https://httpwg.org/specs/rfc7231.html#status.codes)
   131  	// and we might block under 200 (once we have more mature 1xx support).
   132  	// But for now any three digits.
   133  	//
   134  	// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
   135  	// no equivalent bogus thing we can realistically send in HTTP/2,
   136  	// so we'll consistently panic instead and help people find their bugs
   137  	// early. (We can't return an error from WriteHeader even if we wanted to.)
   138  	if code < 100 || code > 999 {
   139  		panic(fmt.Sprintf("invalid WriteHeader code %v", code))
   140  	}
   141  }
   142  
   143  // WriteHeader implements http.ResponseWriter.
   144  func (rw *ResponseRecorder) WriteHeader(code int) {
   145  	if rw.wroteHeader {
   146  		return
   147  	}
   148  
   149  	checkWriteHeaderCode(code)
   150  	rw.Code = code
   151  	rw.wroteHeader = true
   152  	if rw.HeaderMap == nil {
   153  		rw.HeaderMap = make(http.Header)
   154  	}
   155  	rw.snapHeader = rw.HeaderMap.Clone()
   156  }
   157  
   158  // Flush implements http.Flusher. To test whether Flush was
   159  // called, see rw.Flushed.
   160  func (rw *ResponseRecorder) Flush() {
   161  	if !rw.wroteHeader {
   162  		rw.WriteHeader(200)
   163  	}
   164  	rw.Flushed = true
   165  }
   166  
   167  // Result returns the response generated by the handler.
   168  //
   169  // The returned Response will have at least its StatusCode,
   170  // Header, Body, and optionally Trailer populated.
   171  // More fields may be populated in the future, so callers should
   172  // not DeepEqual the result in tests.
   173  //
   174  // The Response.Header is a snapshot of the headers at the time of the
   175  // first write call, or at the time of this call, if the handler never
   176  // did a write.
   177  //
   178  // The Response.Body is guaranteed to be non-nil and Body.Read call is
   179  // guaranteed to not return any error other than io.EOF.
   180  //
   181  // Result must only be called after the handler has finished running.
   182  func (rw *ResponseRecorder) Result() *http.Response {
   183  	if rw.result != nil {
   184  		return rw.result
   185  	}
   186  	if rw.snapHeader == nil {
   187  		rw.snapHeader = rw.HeaderMap.Clone()
   188  	}
   189  	res := &http.Response{
   190  		Proto:      "HTTP/1.1",
   191  		ProtoMajor: 1,
   192  		ProtoMinor: 1,
   193  		StatusCode: rw.Code,
   194  		Header:     rw.snapHeader,
   195  	}
   196  	rw.result = res
   197  	if res.StatusCode == 0 {
   198  		res.StatusCode = 200
   199  	}
   200  	res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode))
   201  	if rw.Body != nil {
   202  		res.Body = io.NopCloser(bytes.NewReader(rw.Body.Bytes()))
   203  	} else {
   204  		res.Body = http.NoBody
   205  	}
   206  	res.ContentLength = parseContentLength(res.Header.Get("Content-Length"))
   207  
   208  	if trailers, ok := rw.snapHeader["Trailer"]; ok {
   209  		res.Trailer = make(http.Header, len(trailers))
   210  		for _, k := range trailers {
   211  			k = http.CanonicalHeaderKey(k)
   212  			if !httpguts.ValidTrailerHeader(k) {
   213  				// Ignore since forbidden by RFC 7230, section 4.1.2.
   214  				continue
   215  			}
   216  			vv, ok := rw.HeaderMap[k]
   217  			if !ok {
   218  				continue
   219  			}
   220  			vv2 := make([]string, len(vv))
   221  			copy(vv2, vv)
   222  			res.Trailer[k] = vv2
   223  		}
   224  	}
   225  	for k, vv := range rw.HeaderMap {
   226  		if !strings.HasPrefix(k, http.TrailerPrefix) {
   227  			continue
   228  		}
   229  		if res.Trailer == nil {
   230  			res.Trailer = make(http.Header)
   231  		}
   232  		for _, v := range vv {
   233  			res.Trailer.Add(strings.TrimPrefix(k, http.TrailerPrefix), v)
   234  		}
   235  	}
   236  	return res
   237  }
   238  
   239  // parseContentLength trims whitespace from s and returns -1 if no value
   240  // is set, or the value if it's >= 0.
   241  //
   242  // This a modified version of same function found in net/http/transfer.go. This
   243  // one just ignores an invalid header.
   244  func parseContentLength(cl string) int64 {
   245  	cl = textproto.TrimString(cl)
   246  	if cl == "" {
   247  		return -1
   248  	}
   249  	n, err := strconv.ParseUint(cl, 10, 63)
   250  	if err != nil {
   251  		return -1
   252  	}
   253  	return int64(n)
   254  }