1
// Copyright 2009 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
21
// drainBody reads all of b to memory and then returns two equivalent
22
// ReadClosers yielding the same bytes.
24
// It returns an error if the initial slurp of all bytes fails. It does not attempt
25
// to make the returned ReadClosers have identical error-matching behavior.
26
func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
28
// No copying needed. Preserve the magic sentinel meaning of NoBody.
29
return http.NoBody, http.NoBody, nil
32
if _, err = buf.ReadFrom(b); err != nil {
35
if err = b.Close(); err != nil {
38
return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
41
// dumpConn is a net.Conn which writes to Writer and reads from Reader
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 }
56
func (b neverEnding) Read(p []byte) (n int, err error) {
63
// DumpRequestOut is like DumpRequest but for outgoing client requests. It
64
// includes any headers that the standard http.Transport adds, such as
66
func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
69
if !body || req.Body == nil {
71
if req.ContentLength != 0 {
72
req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), req.ContentLength))
77
save, req.Body, err = drainBody(req.Body)
83
// Since we're using the actual Transport code to write the request,
84
// switch to http so the Transport doesn't try to do an SSL
85
// negotiation with our dumpConn and its bytes.Buffer & pipe.
86
// The wire format for https and http are the same, anyway.
88
if req.URL.Scheme == "https" {
89
reqSend = new(http.Request)
91
reqSend.URL = new(url.URL)
92
*reqSend.URL = *req.URL
93
reqSend.URL.Scheme = "http"
96
// Use the actual Transport code to record what we would send
97
// on the wire, but not using TCP. Use a Transport with a
98
// custom dialer that returns a fake net.Conn that waits
99
// for the full input (and recording it), and then responds
100
// with a dummy response.
101
var buf bytes.Buffer // records the output
105
dr := &delegateReader{c: make(chan io.Reader)}
107
t := &http.Transport{
108
Dial: func(net, addr string) (net.Conn, error) {
109
return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil
112
defer t.CloseIdleConnections()
114
// Wait for the request before replying with a dummy response:
116
req, err := http.ReadRequest(bufio.NewReader(pr))
118
// Ensure all the body is read; otherwise
119
// we'll get a partial dump.
120
io.Copy(ioutil.Discard, req.Body)
123
dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n")
126
_, err := t.RoundTrip(reqSend)
134
// If we used a dummy body above, remove it now.
135
// TODO: if the req.ContentLength is large, we allocate memory
136
// unnecessarily just to slice it off here. But this is just
137
// a debug function, so this is acceptable for now. We could
138
// discard the body earlier if this matters.
140
if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 {
147
// delegateReader is a reader that delegates to another reader,
148
// once it arrives on a channel.
149
type delegateReader struct {
151
r io.Reader // nil until received from c
154
func (r *delegateReader) Read(p []byte) (int, error) {
161
// Return value if nonempty, def otherwise.
162
func valueOrDefault(value, def string) string {
169
var reqWriteExcludeHeaderDump = map[string]bool{
170
"Host": true, // not in Header map anyway
171
"Transfer-Encoding": true,
175
// DumpRequest returns the given request in its HTTP/1.x wire
176
// representation. It should only be used by servers to debug client
177
// requests. The returned representation is an approximation only;
178
// some details of the initial request are lost while parsing it into
179
// an http.Request. In particular, the order and case of header field
180
// names are lost. The order of values in multi-valued headers is kept
181
// intact. HTTP/2 requests are dumped in HTTP/1.x form, not in their
182
// original binary representations.
184
// If body is true, DumpRequest also returns the body. To do so, it
185
// consumes req.Body and then replaces it with a new io.ReadCloser
186
// that yields the same bytes. If DumpRequest returns an error,
187
// the state of req is undefined.
189
// The documentation for http.Request.Write details which fields
190
// of req are included in the dump.
191
func DumpRequest(req *http.Request, body bool) ([]byte, error) {
194
if !body || req.Body == nil {
197
save, req.Body, err = drainBody(req.Body)
205
// By default, print out the unmodified req.RequestURI, which
206
// is always set for incoming server requests. But because we
207
// previously used req.URL.RequestURI and the docs weren't
208
// always so clear about when to use DumpRequest vs
209
// DumpRequestOut, fall back to the old way if the caller
210
// provides a non-server Request.
211
reqURI := req.RequestURI
213
reqURI = req.URL.RequestURI()
216
fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
217
reqURI, req.ProtoMajor, req.ProtoMinor)
219
absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://")
222
if host == "" && req.URL != nil {
226
fmt.Fprintf(&b, "Host: %s\r\n", host)
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, ","))
235
fmt.Fprintf(&b, "Connection: close\r\n")
238
err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
243
io.WriteString(&b, "\r\n")
246
var dest io.Writer = &b
248
dest = NewChunkedWriter(dest)
250
_, err = io.Copy(dest, req.Body)
252
dest.(io.Closer).Close()
253
io.WriteString(&b, "\r\n")
261
return b.Bytes(), nil
264
// errNoBody is a sentinel error value used by failureToReadBody so we
265
// can detect that the lack of body was intentional.
266
var errNoBody = errors.New("sentinel error value")
268
// failureToReadBody is a io.ReadCloser that just returns errNoBody on
269
// Read. It's swapped in when we don't actually want to consume
270
// the body, but need a non-nil one, and want to distinguish the
271
// error from reading the dummy body.
272
type failureToReadBody struct{}
274
func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody }
275
func (failureToReadBody) Close() error { return nil }
277
// emptyBody is an instance of empty reader.
278
var emptyBody = ioutil.NopCloser(strings.NewReader(""))
280
// DumpResponse is like DumpRequest but dumps a response.
281
func DumpResponse(resp *http.Response, body bool) ([]byte, error) {
285
savecl := resp.ContentLength
288
// For content length of zero. Make sure the body is an empty
289
// reader, instead of returning error through failureToReadBody{}.
290
if resp.ContentLength == 0 {
291
resp.Body = emptyBody
293
resp.Body = failureToReadBody{}
295
} else if resp.Body == nil {
296
resp.Body = emptyBody
298
save, resp.Body, err = drainBody(resp.Body)
304
if err == errNoBody {
308
resp.ContentLength = savecl
312
return b.Bytes(), nil