printer.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package httpexpect
  2. import (
  3. "bytes"
  4. "fmt"
  5. "net/http"
  6. "net/http/httputil"
  7. "strings"
  8. "time"
  9. "github.com/gorilla/websocket"
  10. "moul.io/http2curl/v2"
  11. )
  12. // Printer is used to print requests and responses.
  13. // CompactPrinter, DebugPrinter, and CurlPrinter implement this interface.
  14. type Printer interface {
  15. // Request is called before request is sent.
  16. // It is allowed to read and close request body, or ignore it.
  17. Request(*http.Request)
  18. // Response is called after response is received.
  19. // It is allowed to read and close response body, or ignore it.
  20. Response(*http.Response, time.Duration)
  21. }
  22. // WebsocketPrinter is used to print writes and reads of WebSocket connection.
  23. //
  24. // If WebSocket connection is used, all Printers that also implement WebsocketPrinter
  25. // are invoked on every WebSocket message read or written.
  26. //
  27. // DebugPrinter implements this interface.
  28. type WebsocketPrinter interface {
  29. Printer
  30. // WebsocketWrite is called before writes to WebSocket connection.
  31. WebsocketWrite(typ int, content []byte, closeCode int)
  32. // WebsocketRead is called after reads from WebSocket connection.
  33. WebsocketRead(typ int, content []byte, closeCode int)
  34. }
  35. // CompactPrinter implements Printer.
  36. // Prints requests in compact form. Does not print responses.
  37. type CompactPrinter struct {
  38. logger Logger
  39. }
  40. // NewCompactPrinter returns a new CompactPrinter given a logger.
  41. func NewCompactPrinter(logger Logger) CompactPrinter {
  42. return CompactPrinter{logger}
  43. }
  44. // Request implements Printer.Request.
  45. func (p CompactPrinter) Request(req *http.Request) {
  46. if req != nil {
  47. p.logger.Logf("%s %s", req.Method, req.URL)
  48. }
  49. }
  50. // Response implements Printer.Response.
  51. func (CompactPrinter) Response(*http.Response, time.Duration) {
  52. }
  53. // CurlPrinter implements Printer.
  54. // Uses http2curl to dump requests as curl commands that can be inserted
  55. // into terminal.
  56. type CurlPrinter struct {
  57. logger Logger
  58. }
  59. // NewCurlPrinter returns a new CurlPrinter given a logger.
  60. func NewCurlPrinter(logger Logger) CurlPrinter {
  61. return CurlPrinter{logger}
  62. }
  63. // Request implements Printer.Request.
  64. func (p CurlPrinter) Request(req *http.Request) {
  65. if req != nil {
  66. cmd, err := http2curl.GetCurlCommand(req)
  67. if err != nil {
  68. panic(err)
  69. }
  70. p.logger.Logf("%s", cmd.String())
  71. }
  72. }
  73. // Response implements Printer.Response.
  74. func (CurlPrinter) Response(*http.Response, time.Duration) {
  75. }
  76. // DebugPrinter implements Printer and WebsocketPrinter.
  77. // Uses net/http/httputil to dump both requests and responses.
  78. // Also prints all websocket messages.
  79. type DebugPrinter struct {
  80. logger Logger
  81. body bool
  82. }
  83. // NewDebugPrinter returns a new DebugPrinter given a logger and body
  84. // flag. If body is true, request and response body is also printed.
  85. func NewDebugPrinter(logger Logger, body bool) DebugPrinter {
  86. return DebugPrinter{logger, body}
  87. }
  88. // Request implements Printer.Request.
  89. func (p DebugPrinter) Request(req *http.Request) {
  90. if req == nil {
  91. return
  92. }
  93. dump, err := httputil.DumpRequest(req, p.body)
  94. if err != nil {
  95. panic(err)
  96. }
  97. p.logger.Logf("%s", dump)
  98. }
  99. // Response implements Printer.Response.
  100. func (p DebugPrinter) Response(resp *http.Response, duration time.Duration) {
  101. if resp == nil {
  102. return
  103. }
  104. dump, err := httputil.DumpResponse(resp, p.body)
  105. if err != nil {
  106. panic(err)
  107. }
  108. text := strings.Replace(string(dump), "\r\n", "\n", -1)
  109. lines := strings.SplitN(text, "\n", 2)
  110. p.logger.Logf("%s %s\n%s", lines[0], duration, lines[1])
  111. }
  112. // WebsocketWrite implements WebsocketPrinter.WebsocketWrite.
  113. func (p DebugPrinter) WebsocketWrite(typ int, content []byte, closeCode int) {
  114. b := &bytes.Buffer{}
  115. fmt.Fprintf(b, "-> Sent: %s", wsMessageType(typ))
  116. if typ == websocket.CloseMessage {
  117. fmt.Fprintf(b, " %s", wsCloseCode(closeCode))
  118. }
  119. fmt.Fprint(b, "\n")
  120. if len(content) > 0 {
  121. if typ == websocket.BinaryMessage {
  122. fmt.Fprintf(b, "%v\n", content)
  123. } else {
  124. fmt.Fprintf(b, "%s\n", content)
  125. }
  126. }
  127. fmt.Fprintf(b, "\n")
  128. p.logger.Logf(b.String())
  129. }
  130. // WebsocketRead implements WebsocketPrinter.WebsocketRead.
  131. func (p DebugPrinter) WebsocketRead(typ int, content []byte, closeCode int) {
  132. b := &bytes.Buffer{}
  133. fmt.Fprintf(b, "<- Received: %s", wsMessageType(typ))
  134. if typ == websocket.CloseMessage {
  135. fmt.Fprintf(b, " %s", wsCloseCode(closeCode))
  136. }
  137. fmt.Fprint(b, "\n")
  138. if len(content) > 0 {
  139. if typ == websocket.BinaryMessage {
  140. fmt.Fprintf(b, "%v\n", content)
  141. } else {
  142. fmt.Fprintf(b, "%s\n", content)
  143. }
  144. }
  145. fmt.Fprintf(b, "\n")
  146. p.logger.Logf(b.String())
  147. }