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 // One of the copies, say from b to r2, could be avoided by using a more
22 // elaborate trick where the other copy is made during Request/Response.Write.
23 // This would complicate things too much, given that these functions are for
25 func drainBody(b io
.ReadCloser
) (r1
, r2 io
.ReadCloser
, err error
) {
27 if _
, err
= buf
.ReadFrom(b
); err
!= nil {
30 if err
= b
.Close(); err
!= nil {
33 return ioutil
.NopCloser(&buf
), ioutil
.NopCloser(bytes
.NewReader(buf
.Bytes())), nil
36 // dumpConn is a net.Conn which writes to Writer and reads from Reader
37 type dumpConn
struct {
42 func (c
*dumpConn
) Close() error
{ return nil }
43 func (c
*dumpConn
) LocalAddr() net
.Addr
{ return nil }
44 func (c
*dumpConn
) RemoteAddr() net
.Addr
{ return nil }
45 func (c
*dumpConn
) SetDeadline(t time
.Time
) error
{ return nil }
46 func (c
*dumpConn
) SetReadDeadline(t time
.Time
) error
{ return nil }
47 func (c
*dumpConn
) SetWriteDeadline(t time
.Time
) error
{ return nil }
51 func (b neverEnding
) Read(p
[]byte) (n
int, err error
) {
58 // DumpRequestOut is like DumpRequest but includes
59 // headers that the standard http.Transport adds,
60 // such as User-Agent.
61 func DumpRequestOut(req
*http
.Request
, body
bool) ([]byte, error
) {
64 if !body || req
.Body
== nil {
66 if req
.ContentLength
!= 0 {
67 req
.Body
= ioutil
.NopCloser(io
.LimitReader(neverEnding('x'), req
.ContentLength
))
72 save
, req
.Body
, err
= drainBody(req
.Body
)
78 // Since we're using the actual Transport code to write the request,
79 // switch to http so the Transport doesn't try to do an SSL
80 // negotiation with our dumpConn and its bytes.Buffer & pipe.
81 // The wire format for https and http are the same, anyway.
83 if req
.URL
.Scheme
== "https" {
84 reqSend
= new(http
.Request
)
86 reqSend
.URL
= new(url
.URL
)
87 *reqSend
.URL
= *req
.URL
88 reqSend
.URL
.Scheme
= "http"
91 // Use the actual Transport code to record what we would send
92 // on the wire, but not using TCP. Use a Transport with a
93 // custom dialer that returns a fake net.Conn that waits
94 // for the full input (and recording it), and then responds
95 // with a dummy response.
96 var buf bytes
.Buffer
// records the output
98 dr
:= &delegateReader
{c
: make(chan io
.Reader
)}
99 // Wait for the request before replying with a dummy response:
101 http
.ReadRequest(bufio
.NewReader(pr
))
102 dr
.c
<- strings
.NewReader("HTTP/1.1 204 No Content\r\n\r\n")
105 t
:= &http
.Transport
{
106 Dial
: func(net
, addr
string) (net
.Conn
, error
) {
107 return &dumpConn
{io
.MultiWriter(&buf
, pw
), dr
}, nil
110 defer t
.CloseIdleConnections()
112 _
, err
:= t
.RoundTrip(reqSend
)
120 // If we used a dummy body above, remove it now.
121 // TODO: if the req.ContentLength is large, we allocate memory
122 // unnecessarily just to slice it off here. But this is just
123 // a debug function, so this is acceptable for now. We could
124 // discard the body earlier if this matters.
126 if i
:= bytes
.Index(dump
, []byte("\r\n\r\n")); i
>= 0 {
133 // delegateReader is a reader that delegates to another reader,
134 // once it arrives on a channel.
135 type delegateReader
struct {
137 r io
.Reader
// nil until received from c
140 func (r
*delegateReader
) Read(p
[]byte) (int, error
) {
147 // Return value if nonempty, def otherwise.
148 func valueOrDefault(value
, def
string) string {
155 var reqWriteExcludeHeaderDump
= map[string]bool{
156 "Host": true, // not in Header map anyway
157 "Content-Length": true,
158 "Transfer-Encoding": true,
162 // dumpAsReceived writes req to w in the form as it was received, or
163 // at least as accurately as possible from the information retained in
165 func dumpAsReceived(req
*http
.Request
, w io
.Writer
) error
{
169 // DumpRequest returns the as-received wire representation of req,
170 // optionally including the request body, for debugging.
171 // DumpRequest is semantically a no-op, but in order to
172 // dump the body, it reads the body data into memory and
173 // changes req.Body to refer to the in-memory copy.
174 // The documentation for http.Request.Write details which fields
176 func DumpRequest(req
*http
.Request
, body
bool) (dump
[]byte, err error
) {
178 if !body || req
.Body
== nil {
181 save
, req
.Body
, err
= drainBody(req
.Body
)
189 fmt
.Fprintf(&b
, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req
.Method
, "GET"),
190 req
.URL
.RequestURI(), req
.ProtoMajor
, req
.ProtoMinor
)
193 if host
== "" && req
.URL
!= nil {
197 fmt
.Fprintf(&b
, "Host: %s\r\n", host
)
200 chunked
:= len(req
.TransferEncoding
) > 0 && req
.TransferEncoding
[0] == "chunked"
201 if len(req
.TransferEncoding
) > 0 {
202 fmt
.Fprintf(&b
, "Transfer-Encoding: %s\r\n", strings
.Join(req
.TransferEncoding
, ","))
205 fmt
.Fprintf(&b
, "Connection: close\r\n")
208 err
= req
.Header
.WriteSubset(&b
, reqWriteExcludeHeaderDump
)
213 io
.WriteString(&b
, "\r\n")
216 var dest io
.Writer
= &b
218 dest
= NewChunkedWriter(dest
)
220 _
, err
= io
.Copy(dest
, req
.Body
)
222 dest
.(io
.Closer
).Close()
223 io
.WriteString(&b
, "\r\n")
235 // errNoBody is a sentinel error value used by failureToReadBody so we can detect
236 // that the lack of body was intentional.
237 var errNoBody
= errors
.New("sentinel error value")
239 // failureToReadBody is a io.ReadCloser that just returns errNoBody on
240 // Read. It's swapped in when we don't actually want to consume the
241 // body, but need a non-nil one, and want to distinguish the error
242 // from reading the dummy body.
243 type failureToReadBody
struct{}
245 func (failureToReadBody
) Read([]byte) (int, error
) { return 0, errNoBody
}
246 func (failureToReadBody
) Close() error
{ return nil }
248 var emptyBody
= ioutil
.NopCloser(strings
.NewReader(""))
250 // DumpResponse is like DumpRequest but dumps a response.
251 func DumpResponse(resp
*http
.Response
, body
bool) (dump
[]byte, err error
) {
254 savecl
:= resp
.ContentLength
257 resp
.Body
= failureToReadBody
{}
258 } else if resp
.Body
== nil {
259 resp
.Body
= emptyBody
261 save
, resp
.Body
, err
= drainBody(resp
.Body
)
267 if err
== errNoBody
{
271 resp
.ContentLength
= savecl
275 return b
.Bytes(), nil