mirror of
https://github.com/golang/go
synced 2024-11-25 03:17:58 -07:00
net/http/httputil: fix race in DumpRequestOut
Fixes #2715 R=golang-dev, adg CC=golang-dev https://golang.org/cl/5614043
This commit is contained in:
parent
548206e869
commit
b2935330b0
@ -5,8 +5,8 @@
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -47,40 +47,59 @@ func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
// DumpRequestOut is like DumpRequest but includes
|
||||
// headers that the standard http.Transport adds,
|
||||
// such as User-Agent.
|
||||
func DumpRequestOut(req *http.Request, body bool) (dump []byte, err error) {
|
||||
func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
|
||||
save := req.Body
|
||||
if !body || req.Body == nil {
|
||||
req.Body = nil
|
||||
} else {
|
||||
var err error
|
||||
save, req.Body, err = drainBody(req.Body)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
dialed := false
|
||||
// Use the actual Transport code to record what we would send
|
||||
// on the wire, but not using TCP. Use a Transport with a
|
||||
// customer dialer that returns a fake net.Conn that waits
|
||||
// for the full input (and recording it), and then responds
|
||||
// with a dummy response.
|
||||
var buf bytes.Buffer // records the output
|
||||
pr, pw := io.Pipe()
|
||||
dr := &delegateReader{c: make(chan io.Reader)}
|
||||
// Wait for the request before replying with a dummy response:
|
||||
go func() {
|
||||
http.ReadRequest(bufio.NewReader(pr))
|
||||
dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\n\r\n")
|
||||
}()
|
||||
|
||||
t := &http.Transport{
|
||||
Dial: func(net, addr string) (c net.Conn, err error) {
|
||||
if dialed {
|
||||
return nil, errors.New("unexpected second dial")
|
||||
}
|
||||
c = &dumpConn{
|
||||
Writer: &b,
|
||||
Reader: strings.NewReader("HTTP/1.1 500 Fake Error\r\n\r\n"),
|
||||
}
|
||||
return
|
||||
Dial: func(net, addr string) (net.Conn, error) {
|
||||
return &dumpConn{io.MultiWriter(pw, &buf), dr}, nil
|
||||
},
|
||||
}
|
||||
|
||||
_, err = t.RoundTrip(req)
|
||||
_, err := t.RoundTrip(req)
|
||||
|
||||
req.Body = save
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
dump = b.Bytes()
|
||||
return
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// delegateReader is a reader that delegates to another reader,
|
||||
// once it arrives on a channel.
|
||||
type delegateReader struct {
|
||||
c chan io.Reader
|
||||
r io.Reader // nil until received from c
|
||||
}
|
||||
|
||||
func (r *delegateReader) Read(p []byte) (int, error) {
|
||||
if r.r == nil {
|
||||
r.r = <-r.c
|
||||
}
|
||||
return r.r.Read(p)
|
||||
}
|
||||
|
||||
// Return value if nonempty, def otherwise.
|
||||
|
Loading…
Reference in New Issue
Block a user