github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/pkg/net/http/httputil/dump.go (about) 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. 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 // 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 29 } 30 if err = b.Close(); err != nil { 31 return nil, nil, err 32 } 33 return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil 34 } 35 36 // dumpConn is a net.Conn which writes to Writer and reads from Reader 37 type dumpConn struct { 38 io.Writer 39 io.Reader 40 } 41 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 } 48 49 type neverEnding byte 50 51 func (b neverEnding) Read(p []byte) (n int, err error) { 52 for i := range p { 53 p[i] = byte(b) 54 } 55 return len(p), nil 56 } 57 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 69 } 70 } else { 71 var err error 72 save, req.Body, err = drainBody(req.Body) 73 if err != nil { 74 return nil, err 75 } 76 } 77 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" 89 } 90 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") 103 }() 104 105 t := &http.Transport{ 106 Dial: func(net, addr string) (net.Conn, error) { 107 return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil 108 }, 109 } 110 defer t.CloseIdleConnections() 111 112 _, err := t.RoundTrip(reqSend) 113 114 req.Body = save 115 if err != nil { 116 return nil, err 117 } 118 dump := buf.Bytes() 119 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] 128 } 129 } 130 return dump, nil 131 } 132 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 138 } 139 140 func (r *delegateReader) Read(p []byte) (int, error) { 141 if r.r == nil { 142 r.r = <-r.c 143 } 144 return r.r.Read(p) 145 } 146 147 // Return value if nonempty, def otherwise. 148 func valueOrDefault(value, def string) string { 149 if value != "" { 150 return value 151 } 152 return def 153 } 154 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, 160 } 161 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 167 } 168 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 184 } 185 } 186 187 var b bytes.Buffer 188 189 fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), 190 req.URL.RequestURI(), req.ProtoMajor, req.ProtoMinor) 191 192 host := req.Host 193 if host == "" && req.URL != nil { 194 host = req.URL.Host 195 } 196 if host != "" { 197 fmt.Fprintf(&b, "Host: %s\r\n", host) 198 } 199 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, ",")) 203 } 204 if req.Close { 205 fmt.Fprintf(&b, "Connection: close\r\n") 206 } 207 208 err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump) 209 if err != nil { 210 return 211 } 212 213 io.WriteString(&b, "\r\n") 214 215 if req.Body != nil { 216 var dest io.Writer = &b 217 if chunked { 218 dest = NewChunkedWriter(dest) 219 } 220 _, err = io.Copy(dest, req.Body) 221 if chunked { 222 dest.(io.Closer).Close() 223 io.WriteString(&b, "\r\n") 224 } 225 } 226 227 req.Body = save 228 if err != nil { 229 return 230 } 231 dump = b.Bytes() 232 return 233 } 234 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") 238 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{} 244 245 func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody } 246 func (failureToReadBody) Close() error { return nil } 247 248 var emptyBody = ioutil.NopCloser(strings.NewReader("")) 249 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 255 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 264 } 265 } 266 err = resp.Write(&b) 267 if err == errNoBody { 268 err = nil 269 } 270 resp.Body = save 271 resp.ContentLength = savecl 272 if err != nil { 273 return nil, err 274 } 275 return b.Bytes(), nil 276 }