...

Source file src/net/http/httputil/dump.go

Documentation: net/http/httputil

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

View as plain text