]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/net/http/httputil/dump.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / net / http / httputil / dump.go
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 }