You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

165 lines
5.7 KiB

  1. // Copyright 2018 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package httpreplay provides an API for recording and replaying traffic
  15. // from HTTP-based Google API clients.
  16. //
  17. // To record:
  18. // 1. Call NewRecorder to get a Recorder.
  19. // 2. Use its Client method to obtain an HTTP client to use when making API calls.
  20. // 3. Close the Recorder when you're done. That will save the log of interactions
  21. // to the file you provided to NewRecorder.
  22. //
  23. // To replay:
  24. // 1. Call NewReplayer with the same filename you used to record to get a Replayer.
  25. // 2. Call its Client method and use the client to make the same API calls.
  26. // You will get back the recorded responses.
  27. // 3. Close the Replayer when you're done.
  28. //
  29. // This package is EXPERIMENTAL and is subject to change or removal without notice.
  30. // It requires Go version 1.8 or higher.
  31. package httpreplay
  32. // TODO(jba): add examples.
  33. import (
  34. "context"
  35. "net/http"
  36. "cloud.google.com/go/httpreplay/internal/proxy"
  37. "google.golang.org/api/option"
  38. htransport "google.golang.org/api/transport/http"
  39. )
  40. // A Recorder records HTTP interactions.
  41. type Recorder struct {
  42. proxy *proxy.Proxy
  43. }
  44. // NewRecorder creates a recorder that writes to filename. The file will
  45. // also store initial state that can be retrieved to configure replay.
  46. //
  47. // You must call Close on the Recorder to ensure that all data is written.
  48. func NewRecorder(filename string, initial []byte) (*Recorder, error) {
  49. p, err := proxy.ForRecording(filename, 0)
  50. if err != nil {
  51. return nil, err
  52. }
  53. p.Initial = initial
  54. return &Recorder{proxy: p}, nil
  55. }
  56. // RemoveRequestHeaders will remove request headers matching patterns from the log,
  57. // and skip matching them during replay.
  58. //
  59. // Pattern is taken literally except for *, which matches any sequence of characters.
  60. func (r *Recorder) RemoveRequestHeaders(patterns ...string) {
  61. r.proxy.RemoveRequestHeaders(patterns)
  62. }
  63. // ClearHeaders will replace the value of request and response headers that match
  64. // any of the patterns with CLEARED, on both recording and replay.
  65. // Use ClearHeaders when the header information is secret or may change from run to
  66. // run, but you still want to verify that the headers are being sent and received.
  67. //
  68. // Pattern is taken literally except for *, which matches any sequence of characters.
  69. func (r *Recorder) ClearHeaders(patterns ...string) {
  70. r.proxy.ClearHeaders(patterns)
  71. }
  72. // RemoveQueryParams will remove URL query parameters matching patterns from the log,
  73. // and skip matching them during replay.
  74. //
  75. // Pattern is taken literally except for *, which matches any sequence of characters.
  76. func (r *Recorder) RemoveQueryParams(patterns ...string) {
  77. r.proxy.RemoveQueryParams(patterns)
  78. }
  79. // ClearQueryParams will replace the value of URL query parametrs that match any of
  80. // the patterns with CLEARED, on both recording and replay.
  81. // Use ClearQueryParams when the parameter information is secret or may change from
  82. // run to run, but you still want to verify that it are being sent.
  83. //
  84. // Pattern is taken literally except for *, which matches any sequence of characters.
  85. func (r *Recorder) ClearQueryParams(patterns ...string) {
  86. r.proxy.ClearQueryParams(patterns)
  87. }
  88. // Client returns an http.Client to be used for recording. Provide authentication options
  89. // like option.WithTokenSource as you normally would, or omit them to use Application Default
  90. // Credentials.
  91. func (r *Recorder) Client(ctx context.Context, opts ...option.ClientOption) (*http.Client, error) {
  92. return proxyClient(ctx, r.proxy, opts...)
  93. }
  94. func proxyClient(ctx context.Context, p *proxy.Proxy, opts ...option.ClientOption) (*http.Client, error) {
  95. trans, err := htransport.NewTransport(ctx, p.Transport(), opts...)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return &http.Client{Transport: trans}, nil
  100. }
  101. // Close closes the Recorder and saves the log file.
  102. func (r *Recorder) Close() error {
  103. return r.proxy.Close()
  104. }
  105. // A Replayer replays previously recorded HTTP interactions.
  106. type Replayer struct {
  107. proxy *proxy.Proxy
  108. }
  109. // NewReplayer creates a replayer that reads from filename.
  110. func NewReplayer(filename string) (*Replayer, error) {
  111. p, err := proxy.ForReplaying(filename, 0)
  112. if err != nil {
  113. return nil, err
  114. }
  115. return &Replayer{proxy: p}, nil
  116. }
  117. // Client returns an HTTP client for replaying. The client does not need to be
  118. // configured with credentials for authenticating to a server, since it never
  119. // contacts a real backend.
  120. func (r *Replayer) Client(ctx context.Context) (*http.Client, error) {
  121. return proxyClient(ctx, r.proxy, option.WithoutAuthentication())
  122. }
  123. // Initial returns the initial state saved by the Recorder.
  124. func (r *Replayer) Initial() []byte {
  125. return r.proxy.Initial
  126. }
  127. // IgnoreHeader will not use h when matching requests.
  128. func (r *Replayer) IgnoreHeader(h string) {
  129. r.proxy.IgnoreHeader(h)
  130. }
  131. // Close closes the replayer.
  132. func (r *Replayer) Close() error {
  133. return r.proxy.Close()
  134. }
  135. // DebugHeaders helps to determine whether a header should be ignored.
  136. // When true, if requests have the same method, URL and body but differ
  137. // in a header, the first mismatched header is logged.
  138. func DebugHeaders() {
  139. proxy.DebugHeaders = true
  140. }
  141. // Supported reports whether httpreplay is supported in the current version of Go.
  142. // For Go 1.8 and above, the answer is true.
  143. func Supported() bool { return true }