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