Skip to content
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: Closing Hijacked Conn doesn't cancel Request Context #22347

Closed
anacrolix opened this issue Oct 20, 2017 · 10 comments
Closed

net/http: Closing Hijacked Conn doesn't cancel Request Context #22347

anacrolix opened this issue Oct 20, 2017 · 10 comments
Labels
Documentation FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@anacrolix
Copy link
Contributor

After a server Handler Hijacks and returns the underlying net.Conn, I'd expect the Handler's Request.Context to be Done when that Conn is closed. It appears that the Context is Done when the original handler routine returns, but closing the Conn after a Hijack no longer triggers it too.

Here's a test demonstrating it. It gets stuck at the linked line. https://github.com/anacrolix/wat/blob/master/http_test.go#L63

$ go version
go version devel +2da8a16cbc Thu Oct 19 23:34:02 2017 +0000 darwin/amd64

@bradfitz
Copy link
Contributor

After a server Handler Hijacks and returns the underlying net.Conn, I'd expect the Handler's Request.Context to be Done when that Conn is closed.

We've never documented that guarantee. In fact, I believe we document something close to the opposite: that once you call Hijack, the net/http package is no longer involved at all, which includes doing magic context cancellations behind your back.

Let us know if there's missing or inconsistent documentation, but I don't think we can change behavior here.

/cc @tombergan

@bradfitz bradfitz added the WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. label Oct 20, 2017
@tombergan
Copy link
Contributor

After a server Handler Hijacks and returns the underlying net.Conn, I'd expect the Handler's Request.Context to be Done when that Conn is closed.

We've never documented that guarantee.

I think we have, actually:

https://golang.org/pkg/net/http/#Request.Context

For incoming server requests, the context is canceled when the client's connection closes, the request is canceled (with HTTP/2), or when the ServeHTTP method returns.

There's no stated exception for hijacked connections so it's reasonable to assume that the above guarantee applies to hijacked connections as well. This would be simple to implement. In conn.hijackLocked, we'd wrap rwc to call cancelCtx() from Close or on error from Read/Write.

The problem is there are users who call Hijack and then typecast the returned net.Conn to *net.TCPConn or *tls.Conn. We haven't documented that this typecast works, but it has worked since the beginning (most likely) and users are relying on it. Wrapping rwc would break those typecasts. Looking through internal google source code, I found at least one major user of Hijack that relies on being able to cast to *net.TCPConn. Outside google code, I notice this. I haven't done a very thorough search ... I'm sure there are other casts like these.

So, we either wrap rwc and break users, or we keep the code as-is and update the documentation. Given the potential breakage, I favor updating the documentation.

If a user wants to cancel req.Context when the connection closes, it's not hard to wrap the returned net.Conn as I described above. Something like:

ctx, cancelCtx := context.WithCancel(req.Context())
req = req.WithContex(ctx)
conn, buff, err := rw.Hijack()
conn = connCancelOnClose{conn, cancelCtx}

type connCancelOnClose struct {  // implements wrappers for Read, Write, Close
  net.Conn
  cancelCtx func()
}

@bradfitz
Copy link
Contributor

In conn.hijackLocked, we'd wrap rwc to call cancelCtx() from Close or on error from Read/Write.

We can't change the concrete type of the net.Conn returned by Hijack, if that's what you're proposing.

@tombergan
Copy link
Contributor

We can't change the concrete type of the net.Conn returned by Hijack, if that's what you're proposing.

I proposed that as a straw man, then argued that it's a bad idea. I don't think we can do anything except clarify the documentation for Request.Context().

@bradfitz
Copy link
Contributor

Sorry, once again I failed at reading. Yeah, documentation SGTM.

@tombergan tombergan self-assigned this Oct 24, 2017
@tombergan tombergan removed the WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. label Oct 24, 2017
@anacrolix
Copy link
Contributor Author

I appreciate clarification that closing the hijacked connection does not cancel the request context. It's a shame this can't be made the case, I think it's another example of the interface type assertion issue that exists elsewhere. It also seems presumptuous of existing users to assume that the net.Conn interface guarantees a specific type, that's why it's an interface in the first place. Clearly here we want to give the hijacker access to the true connection type with everything it implies (for instance TCPConn), but we need to modify a bunch of the methods. I also think we're unable to widen the conn interface passed to the Hijack method because it will cause compile issues. That wouldn't be a problem if functions automatically allowed interface narrowing (so that existing hijackers would just check that the interface methods they require are present and pass type checking).

@ianlancetaylor ianlancetaylor added Documentation NeedsFix The path to resolution is known, but the work has not been done. labels Mar 29, 2018
@ianlancetaylor ianlancetaylor added this to the Go1.11 milestone Mar 29, 2018
@bradfitz bradfitz self-assigned this May 29, 2018
@gopherbot
Copy link

Change https://golang.org/cl/115039 mentions this issue: net/http: document how Hijack and Request.Context interact

@anacrolix
Copy link
Contributor Author

I don't believe the Request Context is canceled when Hijack is called. The new documentation seems to suggest this.

@bradfitz
Copy link
Contributor

@anacrolix, yeah, thanks for catching that. The docs are wrong. I'll try again.

@bradfitz bradfitz reopened this Jun 15, 2018
@gopherbot
Copy link

Change https://golang.org/cl/119235 mentions this issue: net/http: document how Hijack and Request.Context interact, take two

@golang golang locked and limited conversation to collaborators Jun 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Documentation FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

5 participants