-
Notifications
You must be signed in to change notification settings - Fork 15
/
govcr.go
137 lines (110 loc) · 4.15 KB
/
govcr.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
130
131
132
133
134
135
136
137
package govcr
import (
"fmt"
"net/http"
"os"
"github.com/pkg/errors"
"github.com/seborama/govcr/v13/cassette"
"github.com/seborama/govcr/v13/encryption"
)
// CrypterProvider is the signature of a cipher provider function with default nonce generator.
// Examples are encryption.NewAESGCMWithRandomNonceGenerator and
// encryption.NewChaCha20Poly1305WithRandomNonceGenerator.
type CrypterProvider func(key []byte) (*encryption.Crypter, error)
// CrypterNonceProvider is the signature of a cipher provider function with custom nonce generator.
// Examples are encryption.NewAESGCM and encryption.NewChaCha20Poly1305.
type CrypterNonceProvider func(key []byte, nonceGenerator encryption.NonceGenerator) (*encryption.Crypter, error)
// CassetteLoader helps build a cassette to load in the VCR.
type CassetteLoader struct {
cassetteName string
opts []cassette.Option
}
// NewCassetteLoader creates a new CassetteLoader, initialised with the cassette's name.
func NewCassetteLoader(cassetteName string) *CassetteLoader {
return &CassetteLoader{
cassetteName: cassetteName,
}
}
// WithCipher creates a cassette cryptographer with the specified cipher function
// and key file.
// Using more than one WithCipher* on the same cassette is ambiguous.
func (cb *CassetteLoader) WithCipher(crypter CrypterProvider, keyFile string) *CassetteLoader {
f := func(key []byte, nonceGenerator encryption.NonceGenerator) (*encryption.Crypter, error) {
// a "CrypterProvider" is a CrypterNonceProvider with a pre-defined / default nonceGenerator
return crypter(key)
}
return cb.WithCipherCustomNonce(f, keyFile, nil)
}
// WithCipherCustomNonce creates a cassette cryptographer with the specified key file and
// customer nonce generator.
// Using more than one WithCipher* on the same cassette is ambiguous.
func (cb *CassetteLoader) WithCipherCustomNonce(crypterNonce CrypterNonceProvider, keyFile string, nonceGenerator encryption.NonceGenerator) *CassetteLoader {
cr, err := makeCrypter(crypterNonce, keyFile, nonceGenerator)
if err != nil {
panic(fmt.Sprintf("%+v", err))
}
cb.opts = append(cb.opts, cassette.WithCrypter(cr))
return cb
}
func (cb *CassetteLoader) load() *cassette.Cassette {
if cb == nil {
panic("please select a cassette for the VCR")
}
return cassette.LoadCassette(cb.cassetteName, cb.opts...)
}
func makeCrypter(crypterNonce CrypterNonceProvider, keyFile string, nonceGenerator encryption.NonceGenerator) (*encryption.Crypter, error) {
if crypterNonce == nil {
return nil, errors.New("a cipher must be supplied for encryption, `nil` is not permitted")
}
key, err := os.ReadFile(keyFile)
if err != nil {
return nil, errors.WithStack(err)
}
cr, err := crypterNonce(key, nonceGenerator)
if err != nil {
return nil, errors.WithStack(err)
}
return cr, nil
}
// NewVCR creates a new VCR.
func NewVCR(cassetteLoader *CassetteLoader, settings ...Setting) *ControlPanel {
var vcrSettings VCRSettings
vcrSettings.cassette = cassetteLoader.load()
for _, option := range settings {
option(&vcrSettings)
}
// use a default client if none provided
if vcrSettings.client == nil {
vcrSettings.client = http.DefaultClient
}
// use a default vcrTransport if none provided
if vcrSettings.client.Transport == nil {
vcrSettings.client.Transport = http.DefaultTransport
}
// use a default RequestMatcher if none provided
if vcrSettings.requestMatchers == nil {
vcrSettings.requestMatchers = NewStrictRequestMatchers()
}
// create VCR's HTTP client
vcrClient := &http.Client{
Transport: &vcrTransport{
pcb: &PrintedCircuitBoard{
requestMatchers: vcrSettings.requestMatchers,
trackRecordingMutators: vcrSettings.trackRecordingMutators,
trackReplayingMutators: vcrSettings.trackReplayingMutators,
httpMode: vcrSettings.httpMode,
readOnly: vcrSettings.readOnly,
},
cassette: vcrSettings.cassette,
transport: vcrSettings.client.Transport,
},
// copy the attributes of the original http.Client
CheckRedirect: vcrSettings.client.CheckRedirect,
Jar: vcrSettings.client.Jar,
Timeout: vcrSettings.client.Timeout,
}
// return
return &ControlPanel{
client: vcrClient,
}
}