-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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: make http1 Server always in a Read, like http2 #15224
Comments
CL https://golang.org/cl/21810 mentions this issue. |
Updates #13021 Updates #15224 Change-Id: Ia3cd608bb887fcfd8d81b035fa57bd5eb8edf09b Reviewed-on: https://go-review.googlesource.com/21810 Reviewed-by: Andrew Gerrand <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
Oh right, I remembered why this is tricky or impossible (I tried to do this previous cycles): we can't proactively be in a read because the next HTTP request might use http.Hijacker and want the unperturbed TCPConn. We can't give them back a different concrete type, and we can't read their bytes for them. (Perhaps we could, because we give back a *bufio.Reader where they should expect to find any over-read bytes, but it would be a big enough behavior change to probably violate the Go 1 API contract in practice) What I really need in the net/http package is a way to do WaitRead on the *net.TCPConn, waiting to find out when either the connection is readable or closed (anything that would wake up epoll_wait and friends). Basically I need guts of the net package exposed. @ianlancetaylor, would you be open to an API addition to the net package, either publicly or via some hacky internal mechanism? (I'd feel a little gross having net/http cheat and do things others couldn't, but not that gross, if we consider it a learning experience before either making it public or removing it in 1.8) |
It's not that easy. We use edge-triggered polling. That means that we don't check for whether there is data available. Instead, we arm a trigger, do a non-blocking read, and if that fails, put the goroutine to sleep waiting for the trigger to fire. We don't currently have a mechanism for detecting whether there is data to read without actually reading it. |
Looking at func (fd *netFD) Read(p []byte) (n int, err error) {
if err := fd.readLock(); err != nil {
return 0, err
}
defer fd.readUnlock()
if err := fd.pd.prepareRead(); err != nil {
return 0, err
}
for {
n, err = syscall.Read(fd.sysfd, p)
if err != nil {
n = 0
if err == syscall.EAGAIN {
if err = fd.pd.waitRead(); err == nil {
continue
}
}
}
err = fd.eofError(n, err)
break
}
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("read", err)
}
return
} Perhaps we could guarantee the behavior of a Then the question is what the various kernels do with a zero byte |
On at least some systems the read system call with a length of 0 simply returns 0 without looking at the file descriptor at all. |
At least on Unix systems we can find out whether there is data available to read by using |
New plan! Instead of waiting for readability, I'll just always be in a Read. I forgot that a blocked Read can be interrupted by another goroutine messing with the conn's ReadDeadline. So in the case of a Hijack, I can just SetReadDeadline to zero, wait for the Read to finish, and then proceed with the Hijack, giving the user a net.Conn which won't cause a data race from concurrent Read calls. |
(Then I can fix #15927) |
Wow, that's interesting. I didn't know a SetReadDeadline
will affect existing blocked Read calls because the docs for
SetReadDeadline says: "SetReadDeadline sets the deadline
for future Read calls." If this is a capability to be relied upon,
should we update the docs for net.Conn?
https://golang.org/pkg/net/#Conn
|
IIRC, we have a bunch of tests locking in that behavior, and I think I even fixed some plan9 bugs to make them pass there (or maybe I didn't? @0intro might remember). So maybe we just need to update the docs. I'll file a separate bug for that. |
@bradfitz did you file a separate bug for updating the docs? (I can't find one). I'd like to contribute a comment to that change. |
CL https://golang.org/cl/31173 mentions this issue. |
Changing the net/http Server to always be in a read (like http2) will simplify CloseNotify and make the per-request context cancelation nicely. I'd like the Request's context's Done channel to close when either the ServeHTTP method returns (easy) or when the connection closes.
The text was updated successfully, but these errors were encountered: