-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net/http: Read deadline and hijack conflict ? (regression in 1.8) #21133
Comments
Go 1.9rc1 is due out imminently, which means the time to report issues with Go 1.8 was about 7-9 months ago. I suggest you try out Go 1.9 and report any bugs you find before it's too late for Go 1.9. Go 1.8 documented: https://golang.org/doc/go1.8#net_http
And https://golang.org/doc/go1.8#net
But your net.Conn implementation looks fine, so I think this is actually a bug in net/http, but it's too late to fix for Go 1.9, so I'l target this at Go 1.10. Using the modified program https://play.golang.org/p/eRBx-bv4Rr with more logging against Go 1.7 vs Go 1.8:
And Go 1.8:
What happens in Go 1.8 is that the Hijack call calls But this As a workaround, you could make your I'll have to think about how to fix this in Go. Too bad we don't have context support in io.Reader. The the Hijack could've just canceled the context of the background 1 byte read, not affecting other deadlines. /cc @tombergan |
Btw, the workaround would look like: // timeoutableConn
type timeoutableConn struct {
net.Conn
hadReadDeadlineInPast int32 // atomic
}
func (c *timeoutableConn) Read(b []byte) (n int, err error) {
if atomic.LoadInt32(&c.hadReadDeadlineInPast) != 1 {
c.Conn.SetReadDeadline(time.Now().Add(5 * time.Second))
}
return c.Conn.Read(b)
}
func (c *timeoutableConn) SetReadDeadline(t time.Time) error {
inPast := int32(0)
if t.Before(time.Now()) {
inPast = 1
}
atomic.StoreInt32(&c.hadReadDeadlineInPast, inPast)
return c.Conn.SetReadDeadline(t)
} I've verified that successfully works around this. |
This is a workaround for this golang bug golang/go#21133
This is a workaround for this golang bug golang/go#21133
This is a workaround for this golang bug golang/go#21133
This is a workaround for this golang bug golang/go#21133
There is no way to have a transparent QuirkConn which provides a workaround of this golang/go#21133, the easiest way is to expose canSetReadDeadline() API to test if we are allowed to call SetReadDealine API without triggering the golang issue.
QuirkConn is added to replace net.Conn as a workaround to a golang bug: golang/go#21133
QuirkConn is added to replace net.Conn as a workaround to a golang bug: golang/go#21133
QuirkConn is added to replace net.Conn as a workaround to a golang bug: golang/go#21133
@bradfitz, thanks. It also works for me. I appreciate you suggesting the workaround. |
QuirkConn is added to replace net.Conn as a workaround to a golang bug: golang/go#21133
QuirkConn is added to replace net.Conn as a workaround to a golang bug: golang/go#21133
QuirkConn is added to replace net.Conn as a workaround to a golang bug: golang/go#21133
QuirkConn is added to replace net.Conn as a workaround to a golang bug: golang/go#21133
Change https://golang.org/cl/62610 mentions this issue: |
@tombergan Could you take a look at the CL? |
Not convinced this is a bug in net/http. Hijack is not the only code being broken by this Conn implementation. Conn clients also expect that:
will not read for more than 30 seconds. But this Conn implementation keeps bumping the deadline forward on every read, so as long as the header is coming in 1 byte every 4.9s, the readHeader could go on forever. If a Conn.Read is going to change the timeout, presumably to keep Read from blocking for too long, it should still use the min of whatever timeout it wants to impose and any previously-set deadline. I would be in favor of simply closing this bug. Moving to Go 1.11 in any event but feel free to close, @tombergan or @bradfitz, if you agree with the rationale here. |
Agreed, will close. We've gone two releases so far with the Conn requirements spelled out in docs and net/http strictly depending on, and we've had minimum fallout, and most custom Conns have simply fixed their implementations given the new docs. |
What version of Go are you using (
go version
)?go version go1.8.3 linux/amd64
What operating system and processor architecture are you using (
go env
)?GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/vadmeste/work/gospace"
GORACE=""
GOROOT="/home/vadmeste/work/go"
GOTOOLDIR="/home/vadmeste/work/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build477756641=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
What did you do?
curl http://localhost:1234
in a separate terminalWhat did you expect to see?
curl http://localhost:1234 returns text immediately
What did you see instead?
curl http://localhost:1234 returns text after 5 secondes
More explanation:
The code represents a simple http server, with a customized net.Conn which set deadline on Read().
The http server has a handler which hijacks the connection and returns some text. However, running
curl http://localhost:1234
doesn't return only after 5 seconds (which is the read timeout value).** The code works with go 1.7.6 but doesn't with go 1.8.3 **
It looks like this commit causes the problem: faf882d
The text was updated successfully, but these errors were encountered: