Source file src/pkg/net/http/httputil/dump.go
1
2
3
4
5 package httputil
6
7 import (
8 "bufio"
9 "bytes"
10 "errors"
11 "fmt"
12 "io"
13 "io/ioutil"
14 "net"
15 "net/http"
16 "net/url"
17 "strings"
18 "time"
19 )
20
21
22
23
24
25
26 func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
27 if b == http.NoBody {
28
29 return http.NoBody, http.NoBody, nil
30 }
31 var buf bytes.Buffer
32 if _, err = buf.ReadFrom(b); err != nil {
33 return nil, b, err
34 }
35 if err = b.Close(); err != nil {
36 return nil, b, err
37 }
38 return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
39 }
40
41
42 type dumpConn struct {
43 io.Writer
44 io.Reader
45 }
46
47 func (c *dumpConn) Close() error { return nil }
48 func (c *dumpConn) LocalAddr() net.Addr { return nil }
49 func (c *dumpConn) RemoteAddr() net.Addr { return nil }
50 func (c *dumpConn) SetDeadline(t time.Time) error { return nil }
51 func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil }
52 func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
53
54 type neverEnding byte
55
56 func (b neverEnding) Read(p []byte) (n int, err error) {
57 for i := range p {
58 p[i] = byte(b)
59 }
60 return len(p), nil
61 }
62
63
64
65
66 func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
67 save := req.Body
68 dummyBody := false
69 if !body || req.Body == nil {
70 req.Body = nil
71 if req.ContentLength != 0 {
72 req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), req.ContentLength))
73 dummyBody = true
74 }
75 } else {
76 var err error
77 save, req.Body, err = drainBody(req.Body)
78 if err != nil {
79 return nil, err
80 }
81 }
82
83
84
85
86
87 reqSend := req
88 if req.URL.Scheme == "https" {
89 reqSend = new(http.Request)
90 *reqSend = *req
91 reqSend.URL = new(url.URL)
92 *reqSend.URL = *req.URL
93 reqSend.URL.Scheme = "http"
94 }
95
96
97
98
99
100
101 var buf bytes.Buffer
102 pr, pw := io.Pipe()
103 defer pr.Close()
104 defer pw.Close()
105 dr := &delegateReader{c: make(chan io.Reader)}
106
107 t := &http.Transport{
108 Dial: func(net, addr string) (net.Conn, error) {
109 return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil
110 },
111 }
112 defer t.CloseIdleConnections()
113
114
115 go func() {
116 req, err := http.ReadRequest(bufio.NewReader(pr))
117 if err == nil {
118
119
120 io.Copy(ioutil.Discard, req.Body)
121 req.Body.Close()
122 }
123 dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n")
124 }()
125
126 _, err := t.RoundTrip(reqSend)
127
128 req.Body = save
129 if err != nil {
130 return nil, err
131 }
132 dump := buf.Bytes()
133
134
135
136
137
138
139 if dummyBody {
140 if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 {
141 dump = dump[:i+4]
142 }
143 }
144 return dump, nil
145 }
146
147
148
149 type delegateReader struct {
150 c chan io.Reader
151 r io.Reader
152 }
153
154 func (r *delegateReader) Read(p []byte) (int, error) {
155 if r.r == nil {
156 r.r = <-r.c
157 }
158 return r.r.Read(p)
159 }
160
161
162 func valueOrDefault(value, def string) string {
163 if value != "" {
164 return value
165 }
166 return def
167 }
168
169 var reqWriteExcludeHeaderDump = map[string]bool{
170 "Host": true,
171 "Transfer-Encoding": true,
172 "Trailer": true,
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 func DumpRequest(req *http.Request, body bool) ([]byte, error) {
192 var err error
193 save := req.Body
194 if !body || req.Body == nil {
195 req.Body = nil
196 } else {
197 save, req.Body, err = drainBody(req.Body)
198 if err != nil {
199 return nil, err
200 }
201 }
202
203 var b bytes.Buffer
204
205
206
207
208
209
210
211 reqURI := req.RequestURI
212 if reqURI == "" {
213 reqURI = req.URL.RequestURI()
214 }
215
216 fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
217 reqURI, req.ProtoMajor, req.ProtoMinor)
218
219 absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://")
220 if !absRequestURI {
221 host := req.Host
222 if host == "" && req.URL != nil {
223 host = req.URL.Host
224 }
225 if host != "" {
226 fmt.Fprintf(&b, "Host: %s\r\n", host)
227 }
228 }
229
230 chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked"
231 if len(req.TransferEncoding) > 0 {
232 fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ","))
233 }
234 if req.Close {
235 fmt.Fprintf(&b, "Connection: close\r\n")
236 }
237
238 err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
239 if err != nil {
240 return nil, err
241 }
242
243 io.WriteString(&b, "\r\n")
244
245 if req.Body != nil {
246 var dest io.Writer = &b
247 if chunked {
248 dest = NewChunkedWriter(dest)
249 }
250 _, err = io.Copy(dest, req.Body)
251 if chunked {
252 dest.(io.Closer).Close()
253 io.WriteString(&b, "\r\n")
254 }
255 }
256
257 req.Body = save
258 if err != nil {
259 return nil, err
260 }
261 return b.Bytes(), nil
262 }
263
264
265
266 var errNoBody = errors.New("sentinel error value")
267
268
269
270
271
272 type failureToReadBody struct{}
273
274 func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody }
275 func (failureToReadBody) Close() error { return nil }
276
277
278 var emptyBody = ioutil.NopCloser(strings.NewReader(""))
279
280
281 func DumpResponse(resp *http.Response, body bool) ([]byte, error) {
282 var b bytes.Buffer
283 var err error
284 save := resp.Body
285 savecl := resp.ContentLength
286
287 if !body {
288
289
290 if resp.ContentLength == 0 {
291 resp.Body = emptyBody
292 } else {
293 resp.Body = failureToReadBody{}
294 }
295 } else if resp.Body == nil {
296 resp.Body = emptyBody
297 } else {
298 save, resp.Body, err = drainBody(resp.Body)
299 if err != nil {
300 return nil, err
301 }
302 }
303 err = resp.Write(&b)
304 if err == errNoBody {
305 err = nil
306 }
307 resp.Body = save
308 resp.ContentLength = savecl
309 if err != nil {
310 return nil, err
311 }
312 return b.Bytes(), nil
313 }
314
View as plain text