mirror of
https://github.com/golang/go
synced 2024-11-25 05:38:01 -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
|
package httputil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -47,40 +47,59 @@ func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
|
|||||||
// DumpRequestOut is like DumpRequest but includes
|
// DumpRequestOut is like DumpRequest but includes
|
||||||
// headers that the standard http.Transport adds,
|
// headers that the standard http.Transport adds,
|
||||||
// such as User-Agent.
|
// 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
|
save := req.Body
|
||||||
if !body || req.Body == nil {
|
if !body || req.Body == nil {
|
||||||
req.Body = nil
|
req.Body = nil
|
||||||
} else {
|
} else {
|
||||||
|
var err error
|
||||||
save, req.Body, err = drainBody(req.Body)
|
save, req.Body, err = drainBody(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var b bytes.Buffer
|
// Use the actual Transport code to record what we would send
|
||||||
dialed := false
|
// 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{
|
t := &http.Transport{
|
||||||
Dial: func(net, addr string) (c net.Conn, err error) {
|
Dial: func(net, addr string) (net.Conn, error) {
|
||||||
if dialed {
|
return &dumpConn{io.MultiWriter(pw, &buf), dr}, nil
|
||||||
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
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = t.RoundTrip(req)
|
_, err := t.RoundTrip(req)
|
||||||
|
|
||||||
req.Body = save
|
req.Body = save
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
dump = b.Bytes()
|
return buf.Bytes(), nil
|
||||||
return
|
}
|
||||||
|
|
||||||
|
// 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.
|
// Return value if nonempty, def otherwise.
|
||||||
|
Loading…
Reference in New Issue
Block a user