Documentation ¶
Overview ¶
Package replay enables creating and/or replaying canned HTTP server responses.
It is primarily intended to simplify providing canned HTTP responses for unit testing packages that rely on external HTTP services. It may have other uses.
Recordings can be created by either using the HTTP client returned by NewRecordingClient, or by setting the Recording field of the RoundTripper to true. The recording format is intended to be simple, so that they may also be created and edited manually for simple cases.
Recordings are identified uniquely by a pathname derived from HTTP request contents. Paths are constructed as a series of intermediate directories and a file as follows:
HTTP scheme / host(:port) / HTTP method / path / ... / request (. CRC) .json
The period and CRC beteen "request" and ".json" may not be present if a request contained no excluded query parameters, no excluded headers and also no body. Each component is also URL-encoded, if necessary, with url.QueryEscape, to avoid potentially invalid filenames for some platforms. As an example, a GET request for the URL
http://www.example.com/path/to/easy+street
generates the path name
http/www.example.com/GET/path/to/easy%2bstreet/request.json
The RoundTripper will first try to load a canned response from the path with the CRC extension, if a CRC is calculated. If no response is found, it will by default attempt to load the content from a path without the CRC extension. This behavior can be disabled by setting StrictPath to true.
The paths above are relative to the Dir field of RoundTripper, which is also taken as a parameter to the NewClient and NewRecordingClient functions.
The format of the recording files is also intended to be easily human-readable. The first part of the file is a JSON object with fields that will be mapped to the *http.Response object. The JSON object is followed by one newline. Any content after that is the body of the recorded response:
{ "status": "404 Not Found", "status_code": 301, "proto": "HTTP/1.1", "proto_major": 1, "proto_minor": 1, "headers": { "Content-Type": [ "text/plain" ], } } The requested content was not found.
A simple example use case may look something like this:
client := replay.NewClient("testdata") // If allowRecording is false, this will only succeed if a recorded response // exists under the "testdata" directory: res, err := client.Get("https://api.ipify.org?format=json")
Index ¶
Constants ¶
const ( // ModeRecordIfMissing enables playing back recordings that exist, and // recording new responses when a recording isn't found. ModeRecordIfMissing = iota // ModePlaybackOnly enables playing back content only. ModePlaybackOnly // ModeRecordOnly enables recording new content only. ModeRecordOnly )
Variables ¶
This section is empty.
Functions ¶
func NewClient ¶
NewClient returns an *http.Client which will return pre-recorded responses if the exists, or create new recordings if they are missing..
func NewPlaybackOnlyClient ¶
NewPlaybackOnlyClient returns an *http.Client which will only return pre- recorded responses. If no response is found, an error is returned.
func NewRecordOnlyClient ¶
NewRecordOnlyClient returns an *http.Client which will record new responses, even if a pre-recorded response exists.
Types ¶
type Error ¶
type Error struct { // Request is the *http.Request that was being processed when the error // occurred. Request *http.Request // Response is the *http.Response that was being processed when the error // occurred. It will be nil if the error occurred while loading a recording // from the local filesystem (as opposed to saving a new response). Response *http.Response // Err is the underlying error that was encountered while attempting to // manipulate a recording. Err error }
Error is an error that may be returned by RoundTripper, and thus by the *http.Client returned by NewClient or NewRecordingClient. It can be used to differentiate an error encountered when trying to fetch or save a recording in local storage versus errors returned by the http package, such as for URL or network errors.
type PathGenerator ¶
type PathGenerator struct { // OmitHeaders is a set of headers to exclude from path calculations. // Requests with different content in these headers can still return the // same unique path. OmitHeaders StringSet // OmitQuery is a set of query parameters to exclude from path calculations. // Requests with different content in these parameters can still return the // same unique path. OmitQuery StringSet // MungeRequestBody can be used to edit which bytes of the request body // are used to calculate the path CRC. It may be nil or return the same // io.Reader that is passed in. It does not alter the request that is sent // to the server. MungeRequestBody func(*http.Request, io.Reader) io.Reader }
PathGenerator creates a unique path for a given *http.Request.
func NewPathGenerator ¶
func NewPathGenerator() *PathGenerator
NewPathGenerator creates a new generator for recording path names.
func (*PathGenerator) RecordingPath ¶
func (p *PathGenerator) RecordingPath(req *http.Request) (*RecordingPath, error)
RecordingPath returns the unique path for the given request.
func (*PathGenerator) RequestCRC ¶
func (p *PathGenerator) RequestCRC(req *http.Request) (string, error)
RequestCRC generates a checksum based on the contents of any headers, query string parameters and body in the request. Any headers in OmitHeaders or any query string parameters in OmitQuery are not considered. If there are no headers, query string parameters and body to consider, returns an empty string.
type Recording ¶
type Recording struct { Status string `json:"status,omitempty"` StatusCode int `json:"status_code,omitempty"` Proto string `json:"proto,omitempty"` ProtoMajor int `json:"proto_major,omitempty"` ProtoMinor int `json:"proto_minor,omitempty"` Headers http.Header `json:"headers,omitempty"` Body []byte `json:"-"` }
A Recording represents a recorded HTTP server response. The fields map directly to fields in http.Response, except for Body, which is the body of the server response.
func LoadRecording ¶
LoadRecording loads a Recording object from the given file path.
func NewRecording ¶
NewRecording returns a new, populated Recording struct from the given *http.Response. The http.Response Body is read and replaced.
type RecordingPath ¶
type RecordingPath struct {
// contains filtered or unexported fields
}
RecordingPath contains a relative path for a recording.
func (*RecordingPath) GenericPath ¶
func (r *RecordingPath) GenericPath() string
GenericPath returns a generic path for the request. The filename portion is always "request.json", even if a checksum was calculated.
func (*RecordingPath) Path ¶
func (r *RecordingPath) Path() string
Path returns a canonical filename generated for the request. If a checksum can be calculated over the request query parameters, headers and body, the filename portion of the path will be "recording." + checksum + "".json". If there are no query parameters, no headers and no body, the returned path will be GenericPath().
type RoundTripper ¶
type RoundTripper struct { // RoundTripper is the http.RoundTripper used to process HTTP requests if // a recorded response is not available on disk. It will be unused if // Record is false. http.RoundTripper // Dir is the base directory where HTTP responses are read from and recored // to. Dir string // Mode determines if responses are recorded, played back, or recorded only // if missing. Mode int // PathGenerator is used to generate unique paths for retrieving and saving // responses. The paths generated are relative to Dir. *PathGenerator // StrictPath, if true, will prevent RoundTripper from loading responses // without a checksum. The default is to attempt to load a recording from // the path without a checksum in cases where the path including the // checksum does not exist. StrictPath bool }
RoundTripper implemnts a wrapper around an instance of the http.RoundTripper interface type. It attempts toload canned responses from recordings on disk. If one is not found, it can also use the wrapped RoundTripper to fetch the response and record it to disk for later use.
type StringSet ¶
type StringSet map[string]struct{}
StringSet implements a set of string values.
func DefaultOmitHeaders ¶
func DefaultOmitHeaders() StringSet
DefaultOmitHeaders returns a default set of headers to omit from recording path generation.
func NewStringSet ¶
NewStringSet returns a new set initialized with optional values.