httprec.go 6.2 KB

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