Rebase.
[official-gcc.git] / libgo / go / net / http / httputil / dump.go
blob2a7a413d01a2d02ad6176e1f53e760799eed1290
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.
5 package httputil
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"
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
24 // debugging only.
25 func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) {
26 var buf bytes.Buffer
27 if _, err = buf.ReadFrom(b); err != nil {
28 return nil, nil, err
30 if err = b.Close(); err != nil {
31 return nil, nil, err
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 {
38 io.Writer
39 io.Reader
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 }
49 type neverEnding byte
51 func (b neverEnding) Read(p []byte) (n int, err error) {
52 for i := range p {
53 p[i] = byte(b)
55 return len(p), nil
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) {
62 save := req.Body
63 dummyBody := false
64 if !body || req.Body == nil {
65 req.Body = nil
66 if req.ContentLength != 0 {
67 req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), req.ContentLength))
68 dummyBody = true
70 } else {
71 var err error
72 save, req.Body, err = drainBody(req.Body)
73 if err != nil {
74 return nil, err
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.
82 reqSend := req
83 if req.URL.Scheme == "https" {
84 reqSend = new(http.Request)
85 *reqSend = *req
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
97 pr, pw := io.Pipe()
98 dr := &delegateReader{c: make(chan io.Reader)}
99 // Wait for the request before replying with a dummy response:
100 go func() {
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)
114 req.Body = save
115 if err != nil {
116 return nil, err
118 dump := buf.Bytes()
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.
125 if dummyBody {
126 if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 {
127 dump = dump[:i+4]
130 return dump, nil
133 // delegateReader is a reader that delegates to another reader,
134 // once it arrives on a channel.
135 type delegateReader struct {
136 c chan io.Reader
137 r io.Reader // nil until received from c
140 func (r *delegateReader) Read(p []byte) (int, error) {
141 if r.r == nil {
142 r.r = <-r.c
144 return r.r.Read(p)
147 // Return value if nonempty, def otherwise.
148 func valueOrDefault(value, def string) string {
149 if value != "" {
150 return value
152 return def
155 var reqWriteExcludeHeaderDump = map[string]bool{
156 "Host": true, // not in Header map anyway
157 "Content-Length": true,
158 "Transfer-Encoding": true,
159 "Trailer": 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
164 // the request.
165 func dumpAsReceived(req *http.Request, w io.Writer) error {
166 return nil
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
175 // of req are used.
176 func DumpRequest(req *http.Request, body bool) (dump []byte, err error) {
177 save := req.Body
178 if !body || req.Body == nil {
179 req.Body = nil
180 } else {
181 save, req.Body, err = drainBody(req.Body)
182 if err != nil {
183 return
187 var b bytes.Buffer
189 fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"),
190 req.URL.RequestURI(), req.ProtoMajor, req.ProtoMinor)
192 host := req.Host
193 if host == "" && req.URL != nil {
194 host = req.URL.Host
196 if host != "" {
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, ","))
204 if req.Close {
205 fmt.Fprintf(&b, "Connection: close\r\n")
208 err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump)
209 if err != nil {
210 return
213 io.WriteString(&b, "\r\n")
215 if req.Body != nil {
216 var dest io.Writer = &b
217 if chunked {
218 dest = NewChunkedWriter(dest)
220 _, err = io.Copy(dest, req.Body)
221 if chunked {
222 dest.(io.Closer).Close()
223 io.WriteString(&b, "\r\n")
227 req.Body = save
228 if err != nil {
229 return
231 dump = b.Bytes()
232 return
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) {
252 var b bytes.Buffer
253 save := resp.Body
254 savecl := resp.ContentLength
256 if !body {
257 resp.Body = failureToReadBody{}
258 } else if resp.Body == nil {
259 resp.Body = emptyBody
260 } else {
261 save, resp.Body, err = drainBody(resp.Body)
262 if err != nil {
263 return
266 err = resp.Write(&b)
267 if err == errNoBody {
268 err = nil
270 resp.Body = save
271 resp.ContentLength = savecl
272 if err != nil {
273 return nil, err
275 return b.Bytes(), nil