github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/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 // drainBody reads all of b to memory and then returns two equivalent 22 // ReadClosers yielding the same bytes. 23 // 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) { 27 if b == http.NoBody { 28 // No copying needed. Preserve the magic sentinel meaning of NoBody. 29 return http.NoBody, http.NoBody, nil 30 } 31 var buf bytes.Buffer 32 if _, err = buf.ReadFrom(b); err != nil { 33 return nil, b, err 34 } 35 if err = b.Close(); err != nil { 36 return nil, b, err 37 } 38 return ioutil.NopCloser(&buf), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil 39 } 40 41 // dumpConn is a net.Conn which writes to Writer and reads from Reader 42 type dumpConn struct { 43 io.Writer 44 io.Reader 45 } 46 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 } 53 54 type neverEnding byte 55 56 func (b neverEnding) Read(p []byte) (n int, err error) { 57 for i := range p { 58 p[i] = byte(b) 59 } 60 return len(p), nil 61 } 62 63 // DumpRequestOut is like DumpRequest but for outgoing client requests. It 64 // includes any headers that the standard http.Transport adds, such as 65 // User-Agent. 66 func DumpRequestOut(req *http.Request, body bool) ([]byte, error) { 67 save := req.Body 68 dummyBody := false 69 if !body || req.Body == nil { 70 req.Body = nil 71 if req.ContentLength != 0 { 72 req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), req.ContentLength)) 73 dummyBody = true 74 } 75 } else { 76 var err error 77 save, req.Body, err = drainBody(req.Body) 78 if err != nil { 79 return nil, err 80 } 81 } 82 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. 87 reqSend := req 88 if req.URL.Scheme == "https" { 89 reqSend = new(http.Request) 90 *reqSend = *req 91 reqSend.URL = new(url.URL) 92 *reqSend.URL = *req.URL 93 reqSend.URL.Scheme = "http" 94 } 95 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 102 pr, pw := io.Pipe() 103 defer pr.Close() 104 defer pw.Close() 105 dr := &delegateReader{c: make(chan io.Reader)} 106 107 t := &http.Transport{ 108 Dial: func(net, addr string) (net.Conn, error) { 109 return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil 110 }, 111 } 112 defer t.CloseIdleConnections() 113 114 // Wait for the request before replying with a dummy response: 115 go func() { 116 req, err := http.ReadRequest(bufio.NewReader(pr)) 117 if err == nil { 118 // Ensure all the body is read; otherwise 119 // we'll get a partial dump. 120 io.Copy(ioutil.Discard, req.Body) 121 req.Body.Close() 122 } 123 dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n") 124 }() 125 126 _, err := t.RoundTrip(reqSend) 127 128 req.Body = save 129 if err != nil { 130 return nil, err 131 } 132 dump := buf.Bytes() 133 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. 139 if dummyBody { 140 if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 { 141 dump = dump[:i+4] 142 } 143 } 144 return dump, nil 145 } 146 147 // delegateReader is a reader that delegates to another reader, 148 // once it arrives on a channel. 149 type delegateReader struct { 150 c chan io.Reader 151 r io.Reader // nil until received from c 152 } 153 154 func (r *delegateReader) Read(p []byte) (int, error) { 155 if r.r == nil { 156 r.r = <-r.c 157 } 158 return r.r.Read(p) 159 } 160 161 // Return value if nonempty, def otherwise. 162 func valueOrDefault(value, def string) string { 163 if value != "" { 164 return value 165 } 166 return def 167 } 168 169 var reqWriteExcludeHeaderDump = map[string]bool{ 170 "Host": true, // not in Header map anyway 171 "Transfer-Encoding": true, 172 "Trailer": true, 173 } 174 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. 183 // 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. 188 // 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) { 192 var err error 193 save := req.Body 194 if !body || req.Body == nil { 195 req.Body = nil 196 } else { 197 save, req.Body, err = drainBody(req.Body) 198 if err != nil { 199 return nil, err 200 } 201 } 202 203 var b bytes.Buffer 204 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 212 if reqURI == "" { 213 reqURI = req.URL.RequestURI() 214 } 215 216 fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), 217 reqURI, req.ProtoMajor, req.ProtoMinor) 218 219 absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://") 220 if !absRequestURI { 221 host := req.Host 222 if host == "" && req.URL != nil { 223 host = req.URL.Host 224 } 225 if host != "" { 226 fmt.Fprintf(&b, "Host: %s\r\n", host) 227 } 228 } 229 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, ",")) 233 } 234 if req.Close { 235 fmt.Fprintf(&b, "Connection: close\r\n") 236 } 237 238 err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump) 239 if err != nil { 240 return nil, err 241 } 242 243 io.WriteString(&b, "\r\n") 244 245 if req.Body != nil { 246 var dest io.Writer = &b 247 if chunked { 248 dest = NewChunkedWriter(dest) 249 } 250 _, err = io.Copy(dest, req.Body) 251 if chunked { 252 dest.(io.Closer).Close() 253 io.WriteString(&b, "\r\n") 254 } 255 } 256 257 req.Body = save 258 if err != nil { 259 return nil, err 260 } 261 return b.Bytes(), nil 262 } 263 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") 267 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{} 273 274 func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody } 275 func (failureToReadBody) Close() error { return nil } 276 277 // emptyBody is an instance of empty reader. 278 var emptyBody = ioutil.NopCloser(strings.NewReader("")) 279 280 // DumpResponse is like DumpRequest but dumps a response. 281 func DumpResponse(resp *http.Response, body bool) ([]byte, error) { 282 var b bytes.Buffer 283 var err error 284 save := resp.Body 285 savecl := resp.ContentLength 286 287 if !body { 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 292 } else { 293 resp.Body = failureToReadBody{} 294 } 295 } else if resp.Body == nil { 296 resp.Body = emptyBody 297 } else { 298 save, resp.Body, err = drainBody(resp.Body) 299 if err != nil { 300 return nil, err 301 } 302 } 303 err = resp.Write(&b) 304 if err == errNoBody { 305 err = nil 306 } 307 resp.Body = save 308 resp.ContentLength = savecl 309 if err != nil { 310 return nil, err 311 } 312 return b.Bytes(), nil 313 }