response.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package echo
  2. import (
  3. "bufio"
  4. "net"
  5. "net/http"
  6. )
  7. type (
  8. // Response wraps an http.ResponseWriter and implements its interface to be used
  9. // by an HTTP handler to construct an HTTP response.
  10. // See: https://golang.org/pkg/net/http/#ResponseWriter
  11. Response struct {
  12. echo *Echo
  13. beforeFuncs []func()
  14. afterFuncs []func()
  15. Writer http.ResponseWriter
  16. Status int
  17. Size int64
  18. Committed bool
  19. }
  20. )
  21. // NewResponse creates a new instance of Response.
  22. func NewResponse(w http.ResponseWriter, e *Echo) (r *Response) {
  23. return &Response{Writer: w, echo: e}
  24. }
  25. // Header returns the header map for the writer that will be sent by
  26. // WriteHeader. Changing the header after a call to WriteHeader (or Write) has
  27. // no effect unless the modified headers were declared as trailers by setting
  28. // the "Trailer" header before the call to WriteHeader (see example)
  29. // To suppress implicit response headers, set their value to nil.
  30. // Example: https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
  31. func (r *Response) Header() http.Header {
  32. return r.Writer.Header()
  33. }
  34. // Before registers a function which is called just before the response is written.
  35. func (r *Response) Before(fn func()) {
  36. r.beforeFuncs = append(r.beforeFuncs, fn)
  37. }
  38. // After registers a function which is called just after the response is written.
  39. // If the `Content-Length` is unknown, none of the after function is executed.
  40. func (r *Response) After(fn func()) {
  41. r.afterFuncs = append(r.afterFuncs, fn)
  42. }
  43. // WriteHeader sends an HTTP response header with status code. If WriteHeader is
  44. // not called explicitly, the first call to Write will trigger an implicit
  45. // WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
  46. // used to send error codes.
  47. func (r *Response) WriteHeader(code int) {
  48. if r.Committed {
  49. r.echo.Logger.Warn("response already committed")
  50. return
  51. }
  52. for _, fn := range r.beforeFuncs {
  53. fn()
  54. }
  55. r.Status = code
  56. r.Writer.WriteHeader(code)
  57. r.Committed = true
  58. }
  59. // Write writes the data to the connection as part of an HTTP reply.
  60. func (r *Response) Write(b []byte) (n int, err error) {
  61. if !r.Committed {
  62. r.WriteHeader(http.StatusOK)
  63. }
  64. n, err = r.Writer.Write(b)
  65. r.Size += int64(n)
  66. for _, fn := range r.afterFuncs {
  67. fn()
  68. }
  69. return
  70. }
  71. // Flush implements the http.Flusher interface to allow an HTTP handler to flush
  72. // buffered data to the client.
  73. // See [http.Flusher](https://golang.org/pkg/net/http/#Flusher)
  74. func (r *Response) Flush() {
  75. r.Writer.(http.Flusher).Flush()
  76. }
  77. // Hijack implements the http.Hijacker interface to allow an HTTP handler to
  78. // take over the connection.
  79. // See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker)
  80. func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  81. return r.Writer.(http.Hijacker).Hijack()
  82. }
  83. // CloseNotify implements the http.CloseNotifier interface to allow detecting
  84. // when the underlying connection has gone away.
  85. // This mechanism can be used to cancel long operations on the server if the
  86. // client has disconnected before the response is ready.
  87. // See [http.CloseNotifier](https://golang.org/pkg/net/http/#CloseNotifier)
  88. func (r *Response) CloseNotify() <-chan bool {
  89. return r.Writer.(http.CloseNotifier).CloseNotify()
  90. }
  91. func (r *Response) reset(w http.ResponseWriter) {
  92. r.beforeFuncs = nil
  93. r.afterFuncs = nil
  94. r.Writer = w
  95. r.Size = 0
  96. r.Status = http.StatusOK
  97. r.Committed = false
  98. }