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  }