github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/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, b, err 29 } 30 if err = b.Close(); err != nil { 31 return nil, b, 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 for outgoing client requests. It 59 // includes any headers that the standard http.Transport adds, such as 60 // 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 defer pr.Close() 99 defer pw.Close() 100 dr := &delegateReader{c: make(chan io.Reader)} 101 102 t := &http.Transport{ 103 Dial: func(net, addr string) (net.Conn, error) { 104 return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil 105 }, 106 } 107 defer t.CloseIdleConnections() 108 109 // Wait for the request before replying with a dummy response: 110 go func() { 111 req, err := http.ReadRequest(bufio.NewReader(pr)) 112 if err == nil { 113 // Ensure all the body is read; otherwise 114 // we'll get a partial dump. 115 io.Copy(ioutil.Discard, req.Body) 116 req.Body.Close() 117 } 118 dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n") 119 }() 120 121 _, err := t.RoundTrip(reqSend) 122 123 req.Body = save 124 if err != nil { 125 return nil, err 126 } 127 dump := buf.Bytes() 128 129 // If we used a dummy body above, remove it now. 130 // TODO: if the req.ContentLength is large, we allocate memory 131 // unnecessarily just to slice it off here. But this is just 132 // a debug function, so this is acceptable for now. We could 133 // discard the body earlier if this matters. 134 if dummyBody { 135 if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 { 136 dump = dump[:i+4] 137 } 138 } 139 return dump, nil 140 } 141 142 // delegateReader is a reader that delegates to another reader, 143 // once it arrives on a channel. 144 type delegateReader struct { 145 c chan io.Reader 146 r io.Reader // nil until received from c 147 } 148 149 func (r *delegateReader) Read(p []byte) (int, error) { 150 if r.r == nil { 151 r.r = <-r.c 152 } 153 return r.r.Read(p) 154 } 155 156 // Return value if nonempty, def otherwise. 157 func valueOrDefault(value, def string) string { 158 if value != "" { 159 return value 160 } 161 return def 162 } 163 164 var reqWriteExcludeHeaderDump = map[string]bool{ 165 "Host": true, // not in Header map anyway 166 "Content-Length": true, 167 "Transfer-Encoding": true, 168 "Trailer": true, 169 } 170 171 // DumpRequest returns the given request in its HTTP/1.x wire 172 // representation. It should only be used by servers to debug client 173 // requests. The returned representation is an approximation only; 174 // some details of the initial request are lost while parsing it into 175 // an http.Request. In particular, the order and case of header field 176 // names are lost. The order of values in multi-valued headers is kept 177 // intact. HTTP/2 requests are dumped in HTTP/1.x form, not in their 178 // original binary representations. 179 // 180 // If body is true, DumpRequest also returns the body. To do so, it 181 // consumes req.Body and then replaces it with a new io.ReadCloser 182 // that yields the same bytes. If DumpRequest returns an error, 183 // the state of req is undefined. 184 // 185 // The documentation for http.Request.Write details which fields 186 // of req are included in the dump. 187 func DumpRequest(req *http.Request, body bool) (dump []byte, err error) { 188 save := req.Body 189 if !body || req.Body == nil { 190 req.Body = nil 191 } else { 192 save, req.Body, err = drainBody(req.Body) 193 if err != nil { 194 return nil, err 195 } 196 } 197 198 var b bytes.Buffer 199 200 // By default, print out the unmodified req.RequestURI, which 201 // is always set for incoming server requests. But because we 202 // previously used req.URL.RequestURI and the docs weren't 203 // always so clear about when to use DumpRequest vs 204 // DumpRequestOut, fall back to the old way if the caller 205 // provides a non-server Request. 206 reqURI := req.RequestURI 207 if reqURI == "" { 208 reqURI = req.URL.RequestURI() 209 } 210 211 fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), 212 reqURI, req.ProtoMajor, req.ProtoMinor) 213 214 absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://") 215 if !absRequestURI { 216 host := req.Host 217 if host == "" && req.URL != nil { 218 host = req.URL.Host 219 } 220 if host != "" { 221 fmt.Fprintf(&b, "Host: %s\r\n", host) 222 } 223 } 224 225 chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked" 226 if len(req.TransferEncoding) > 0 { 227 fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ",")) 228 } 229 if req.Close { 230 fmt.Fprintf(&b, "Connection: close\r\n") 231 } 232 233 err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump) 234 if err != nil { 235 return 236 } 237 238 io.WriteString(&b, "\r\n") 239 240 if req.Body != nil { 241 var dest io.Writer = &b 242 if chunked { 243 dest = NewChunkedWriter(dest) 244 } 245 _, err = io.Copy(dest, req.Body) 246 if chunked { 247 dest.(io.Closer).Close() 248 io.WriteString(&b, "\r\n") 249 } 250 } 251 252 req.Body = save 253 if err != nil { 254 return 255 } 256 dump = b.Bytes() 257 return 258 } 259 260 // errNoBody is a sentinel error value used by failureToReadBody so we 261 // can detect that the lack of body was intentional. 262 var errNoBody = errors.New("sentinel error value") 263 264 // failureToReadBody is a io.ReadCloser that just returns errNoBody on 265 // Read. It's swapped in when we don't actually want to consume 266 // the body, but need a non-nil one, and want to distinguish the 267 // error from reading the dummy body. 268 type failureToReadBody struct{} 269 270 func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody } 271 func (failureToReadBody) Close() error { return nil } 272 273 // emptyBody is an instance of empty reader. 274 var emptyBody = ioutil.NopCloser(strings.NewReader("")) 275 276 // DumpResponse is like DumpRequest but dumps a response. 277 func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) { 278 var b bytes.Buffer 279 save := resp.Body 280 savecl := resp.ContentLength 281 282 if !body { 283 // For content length of zero. Make sure the body is an empty 284 // reader, instead of returning error through failureToReadBody{}. 285 if resp.ContentLength == 0 { 286 resp.Body = emptyBody 287 } else { 288 resp.Body = failureToReadBody{} 289 } 290 } else if resp.Body == nil { 291 resp.Body = emptyBody 292 } else { 293 save, resp.Body, err = drainBody(resp.Body) 294 if err != nil { 295 return nil, err 296 } 297 } 298 err = resp.Write(&b) 299 if err == errNoBody { 300 err = nil 301 } 302 resp.Body = save 303 resp.ContentLength = savecl 304 if err != nil { 305 return nil, err 306 } 307 return b.Bytes(), nil 308 }