-
Notifications
You must be signed in to change notification settings - Fork 2
/
request.go
208 lines (177 loc) · 5.62 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package intercept
import (
"bytes"
"encoding/json"
"encoding/xml"
"io"
"io/ioutil"
"net/http"
"strings"
)
// XMLCharDecoder is a helper type that takes a stream of bytes (not encoded in
// UTF-8) and returns a reader that encodes the bytes into UTF-8. This is done
// because Go's XML library only supports XML encoded in UTF-8.
type XMLCharDecoder func(charset string, input io.Reader) (io.Reader, error)
// ReqModifierFunc represent the function interface for request modifiers.
type ReqModifierFunc func(*RequestModifier)
// Filter defines whether a RequestModifier should be applied or not.
type Filter func(*http.Request) bool
// RequestModifier implements a convenient abstraction to modify an http.Request,
// including methods to read, decode/encode and define JSON/XML/String/Binary bodies
// and modify HTTP headers.
type RequestModifier struct {
// Header exposes the request http.Header type.
Header http.Header
// Request exposes the current http.Request to be modified.
Request *http.Request
}
// NewRequestModifier creates a new request modifier that modifies the given http.Request.
func NewRequestModifier(req *http.Request) *RequestModifier {
return &RequestModifier{Request: req, Header: req.Header}
}
// ReadString reads the whole body of the current http.Request and returns it as string.
func (s *RequestModifier) ReadString() (string, error) {
buf, err := ioutil.ReadAll(s.Request.Body)
if err != nil {
return "", err
}
s.Bytes(buf)
return string(buf), nil
}
// ReadBytes reads the whole body of the current http.Request and returns it as bytes.
func (s *RequestModifier) ReadBytes() ([]byte, error) {
buf, err := ioutil.ReadAll(s.Request.Body)
if err != nil {
return nil, err
}
s.Bytes(buf)
return buf, nil
}
// DecodeJSON reads and parses the current http.Request body and tries to decode it as JSON.
func (s *RequestModifier) DecodeJSON(userStruct interface{}) error {
buf, err := s.ReadBytes()
if err != nil {
return err
}
jsonDecoder := json.NewDecoder(bytes.NewReader(buf))
err = jsonDecoder.Decode(&userStruct)
if err != nil && err != io.EOF {
return err
}
return nil
}
// DecodeXML reads and parses the current http.Request body and tries to decode it as XML.
func (s *RequestModifier) DecodeXML(userStruct interface{}, charsetReader XMLCharDecoder) error {
buf, err := s.ReadBytes()
if err != nil {
return err
}
xmlDecoder := xml.NewDecoder(bytes.NewReader(buf))
if charsetReader != nil {
xmlDecoder.CharsetReader = charsetReader
}
if err := xmlDecoder.Decode(&userStruct); err != nil && err != io.EOF {
return err
}
return nil
}
// Bytes sets the given bytes as http.Request body.
func (s *RequestModifier) Bytes(body []byte) {
s.Request.Body = ioutil.NopCloser(bytes.NewReader(body))
}
// String sets the given string as http.Request body.
func (s *RequestModifier) String(body string) {
if s.Request.Method == "GET" || s.Request.Method == "HEAD" {
return
}
s.Request.Body = ioutil.NopCloser(bytes.NewReader([]byte(body)))
}
// JSON sets the given JSON serializable struct as http.Request body
// defining the proper content length header.
func (s *RequestModifier) JSON(data interface{}) error {
buf := &bytes.Buffer{}
switch data.(type) {
case string:
buf.WriteString(data.(string))
case []byte:
buf.Write(data.([]byte))
default:
if err := json.NewEncoder(buf).Encode(data); err != nil {
return err
}
}
s.Request.Body = ioutil.NopCloser(buf)
s.Request.ContentLength = int64(buf.Len())
s.Request.Header.Set("Content-Type", "application/json")
return nil
}
// XML sets the given XML serializable struct as http.Request body
// defining the proper content length header.
func (s *RequestModifier) XML(data interface{}) error {
buf := &bytes.Buffer{}
switch data.(type) {
case string:
buf.WriteString(data.(string))
case []byte:
buf.Write(data.([]byte))
default:
if err := xml.NewEncoder(buf).Encode(data); err != nil {
return err
}
}
s.Request.Body = ioutil.NopCloser(buf)
s.Request.ContentLength = int64(buf.Len())
s.Request.Header.Set("Content-Type", "application/xml")
return nil
}
// Reader sets the given io.Reader stream as http.Request body
// defining the proper content length header.
func (s *RequestModifier) Reader(body io.Reader) error {
rc, ok := body.(io.ReadCloser)
if !ok && body != nil {
rc = ioutil.NopCloser(body)
}
req := s.Request
if body != nil {
switch v := body.(type) {
case *bytes.Buffer:
req.ContentLength = int64(v.Len())
case *bytes.Reader:
req.ContentLength = int64(v.Len())
case *strings.Reader:
req.ContentLength = int64(v.Len())
}
}
req.Body = rc
return nil
}
// RequestInterceptor interceps a given http.Request using a custom request modifier function.
type RequestInterceptor struct {
Modifier ReqModifierFunc
Filters []Filter
}
// Request intercepts an HTTP request and passes it to the given request modifier function.
func Request(h ReqModifierFunc) *RequestInterceptor {
return &RequestInterceptor{Modifier: h, Filters: []Filter{}}
}
// Filter intercepts an HTTP requests if and only if the given filter returns true.
func (s *RequestInterceptor) Filter(f ...Filter) {
s.Filters = append(s.Filters, f...)
}
// HandleHTTP handles the middleware call chain, intercepting the request data if possible.
// This methods implements the middleware layer compatible interface.
func (s *RequestInterceptor) HandleHTTP(w http.ResponseWriter, r *http.Request, h http.Handler) {
if s.filter(r) {
req := NewRequestModifier(r)
s.Modifier(req)
}
h.ServeHTTP(w, r)
}
func (s RequestInterceptor) filter(req *http.Request) bool {
for _, filter := range s.Filters {
if !filter(req) {
return false
}
}
return true
}