-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrequest.go
132 lines (119 loc) · 3.6 KB
/
request.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
package apitestr
import (
"bytes"
"context"
"fmt"
"github.com/tomwright/apitestr/check"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
// RequestInitFunc defines the structure of the functions that can be used to initialise a request
type RequestInitFunc func(ctx context.Context, req *http.Request, data map[string]interface{}) (*http.Request, error)
// RequestReplacements runs a find and replace in the request body, headers and url path with the given replacements in `data`
func RequestReplacements(ctx context.Context, req *http.Request, data map[string]interface{}) (*http.Request, error) {
req, err := RequestURLReplacements(ctx, req, data)
if err != nil {
return req, err
}
req, err = RequestHeaderReplacements(ctx, req, data)
if err != nil {
return req, err
}
req, err = RequestBodyReplacements(ctx, req, data)
if err != nil {
return req, err
}
return req, err
}
// RequestSchemeReplacements runs a find and replace in the request url scheme with the given replacements in `data`
func RequestURLReplacements(ctx context.Context, req *http.Request, data map[string]interface{}) (*http.Request, error) {
urlStr := req.URL.String()
for k, v := range data {
vStr, err := getReplacementValue(ctx, v)
if err != nil {
return nil, err
}
urlStr = strings.Replace(urlStr, k, vStr, -1)
}
var err error
req.URL, err = url.Parse(urlStr)
if err != nil {
return nil, err
}
return req, nil
}
// RequestHeaderReplacements runs a find and replace in the request headers with the given replacements in `data`
func RequestHeaderReplacements(ctx context.Context, req *http.Request, data map[string]interface{}) (*http.Request, error) {
for k, v := range data {
vStr, err := getReplacementValue(ctx, v)
if err != nil {
return nil, err
}
for headerIndex, headerVals := range req.Header {
for headerValIndex, h := range headerVals {
req.Header[headerIndex][headerValIndex] = strings.Replace(h, k, vStr, -1)
}
}
}
return req, nil
}
// RequestBodyReplacements runs a find and replace in the request body with the given replacements in `data`
func RequestBodyReplacements(ctx context.Context, req *http.Request, data map[string]interface{}) (*http.Request, error) {
if req.Body == nil {
return req, nil
}
if len(data) == 0 {
return req, nil
}
bodyData, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, fmt.Errorf("could not read body: %w", err)
}
bodyStr := string(bodyData)
for k, v := range data {
vStr, err := getReplacementValue(ctx, v)
if err != nil {
return nil, err
}
bodyStr = strings.Replace(bodyStr, k, vStr, -1)
}
newBodyBuffer := bytes.NewBuffer([]byte(bodyStr))
req.ContentLength = int64(newBodyBuffer.Len())
buf := newBodyBuffer.Bytes()
req.GetBody = func() (io.ReadCloser, error) {
r := bytes.NewReader(buf)
return ioutil.NopCloser(r), nil
}
req.Body = ioutil.NopCloser(newBodyBuffer)
return req, nil
}
func getReplacementValue(ctx context.Context, val interface{}) (string, error) {
var valStr string
switch valOfType := val.(type) {
case string:
valStr = valOfType
case []byte:
valStr = string(valOfType)
case nil:
valStr = ""
default:
return "", fmt.Errorf("unhandled replacement value type of `%T` with value `%v`", val, val)
}
if strings.HasPrefix(valStr, "$.") {
dataVal := check.DataIDFromContext(ctx, strings.TrimLeft(valStr, "$."))
switch valOfType := dataVal.(type) {
case string:
valStr = valOfType
case []byte:
valStr = string(valOfType)
case nil:
valStr = ""
default:
return "", fmt.Errorf("unhandled replacement variable value type of `%T` with value `%v`", val, val)
}
}
return valStr, nil
}