-
Notifications
You must be signed in to change notification settings - Fork 15
/
matchers.go
129 lines (106 loc) · 3.7 KB
/
matchers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package govcr
import (
"net/http"
"net/url"
"github.com/seborama/govcr/v13/cassette/track"
)
// RequestMatcher is a function that performs request comparison.
// request comparison involves the HTTP request and the track request recorded on cassette.
type RequestMatcher func(httpRequest, trackRequest *track.Request) bool
// RequestMatchers is a collection of RequestMatcher's.
type RequestMatchers []RequestMatcher
// Add a set of RequestMatcher's to this RequestMatchers collection.
func (rm RequestMatchers) Add(reqMatchers ...RequestMatcher) RequestMatchers {
return append(rm, reqMatchers...)
}
// Match returns true if all RequestMatcher's in RequestMatchers return true, thereby indicating that
// the trackRequest matches the httpRequest.
// When no matchers are supplied, Match returns false.
func (rm RequestMatchers) Match(httpRequest, trackRequest *track.Request) bool {
for _, matcher := range rm {
if !matcher(httpRequest, trackRequest) {
return false
}
}
return len(rm) != 0
}
// NewStrictRequestMatchers creates a new default sets of RequestMatcher's.
func NewStrictRequestMatchers() RequestMatchers {
return RequestMatchers{
DefaultHeaderMatcher,
DefaultMethodMatcher,
DefaultURLMatcher,
DefaultBodyMatcher,
DefaultTrailerMatcher,
}
}
// NewMethodURLRequestMatchers creates a new default set of RequestMatcher's based on Method and URL.
func NewMethodURLRequestMatchers() RequestMatchers {
return RequestMatchers{
DefaultMethodMatcher,
DefaultURLMatcher,
}
}
// DefaultHeaderMatcher is the default implementation of HeaderMatcher.
func DefaultHeaderMatcher(httpRequest, trackRequest *track.Request) bool {
return areHTTPHeadersEqual(httpRequest.Header, trackRequest.Header)
}
// DefaultMethodMatcher is the default implementation of MethodMatcher.
func DefaultMethodMatcher(httpRequest, trackRequest *track.Request) bool {
return httpRequest.Method == trackRequest.Method
}
// DefaultURLMatcher is the default implementation of URLMatcher.
// nolint: gocyclo,gocognit
func DefaultURLMatcher(httpRequest, trackRequest *track.Request) bool {
httpURL := httpRequest.URL
if httpURL == nil {
httpURL = &url.URL{}
}
trackURL := trackRequest.URL
if trackURL == nil {
trackURL = &url.URL{}
}
return httpURL.Scheme == trackURL.Scheme &&
httpURL.Opaque == trackURL.Opaque &&
httpURL.User.String() == trackURL.User.String() &&
httpURL.Host == trackURL.Host &&
httpURL.Path == trackURL.Path &&
httpURL.RawPath == trackURL.RawPath &&
httpURL.ForceQuery == trackURL.ForceQuery &&
httpURL.RawQuery == trackURL.RawQuery &&
httpURL.Fragment == trackURL.Fragment
}
// DefaultBodyMatcher is the default implementation of BodyMatcher.
func DefaultBodyMatcher(httpRequest, trackRequest *track.Request) bool {
return string(httpRequest.Body) == string(trackRequest.Body)
}
// DefaultTrailerMatcher is the default implementation of TrailerMatcher.
func DefaultTrailerMatcher(httpRequest, trackRequest *track.Request) bool {
return areHTTPHeadersEqual(httpRequest.Trailer, trackRequest.Trailer)
}
// nolint: gocyclo,gocognit
func areHTTPHeadersEqual(httpHeaders1, httpHeaders2 http.Header) bool {
if len(httpHeaders1) != len(httpHeaders2) {
return false
}
for httpHeaderKey, httpHeaderValues := range httpHeaders1 {
trackHeaderValues, ok := httpHeaders2[httpHeaderKey]
if !ok || len(httpHeaderValues) != len(trackHeaderValues) {
return false
}
// "postal" sorting algo
m := make(map[string]int)
for _, httpHeaderValue := range httpHeaderValues {
m[httpHeaderValue]++ // put mail in inbox
}
for _, trackHeaderValue := range trackHeaderValues {
m[trackHeaderValue]-- // pop mail from inbox
}
for _, count := range m {
if count != 0 {
return false
}
}
}
return true
}