connect-go icon indicating copy to clipboard operation
connect-go copied to clipboard

Incorrect client disconnection error codes

Open emcfarlane opened this issue 2 years ago • 4 comments

Describe the bug

On client disconnections for both HTTP1 and HTTP2 transports we should detect the client has disconnected and use an appropriate error code. Currently disconnects can see a connect.CodeUknown causing erroneous error reporting. To detect a client disconnect we could check the context for cancellation (see docs from deprecated http.CloseNotifier).

To Reproduce

Create a connection and close the underlying net.Conn to replicate a dropped client connection:

var conn client.Conn // Captured client connection.
transport := server.Transport()
dialContext := transport.DialTLSContext
transport.DialTLSContext = func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
  conn, _ = dialContext(ctx, network, addr, cfg) // Capture the client connection.
  return conn, nil
}
stream := ... // Create stream
stream.Send(nil) // Send headers
conn.Close() // Abruptly close
// Check handler error codes

Handler errors will be (only checked the envelope flow):

  • http1/read: invalid_argument: protocol error: incomplete envelope: unexpected EOF
  • http1/write: unknown: write envelope: io: read/write on closed pipe
  • http2/read: invalid_argument: protocol error: incomplete envelope: client disconnected
  • http2/write: unknown: write envelope: client disconnected

In all cases checking the context error will return a context.Cancelled error. The http2 client disconnect string is from this error.

Environment (please complete the following information):

  • connect-go version or commit: v0.1.12
  • go version: go version go1.18.3 darwin/amd64

Additional context

Thanks to the reports from:

  • @pqn https://bufbuild.slack.com/archives/CRZ680FUH/p1701370334963849
  • @njiang747 https://bufbuild.slack.com/archives/CRZ680FUH/p1700005011055009

emcfarlane avatar Dec 01 '23 18:12 emcfarlane

Related issue: https://github.com/golang/go/issues/52183 . Which seems to showclient disconnects can be reported without a context cancellation.

emcfarlane avatar Dec 04 '23 20:12 emcfarlane

FWIW, closing the actual connection is not the same as a cancellation with http/2. With http/2, the client aborts the stream, but the connection remains open. With http/1.1, since multiplexing a single connection is not supported, the only way a client can cancel is to disconnect.

Though perhaps the server handler doesn't care about distinguishing the two cases -- in other words, maybe it doesn't care to distinguish the client intentionally cancelling a single stream vs. a network partition implicitly cancelling all streams (without the client's intent).

jhump avatar Dec 05 '23 16:12 jhump

Hi, just wanted to check in on the prioritization of this, particularly for the http2 case.

pqn avatar Dec 18 '23 17:12 pqn

@pqn, looks like @emcfarlane has opened a pull request, so hopefully it can be addressed in the next release.

jhump avatar Jan 04 '24 18:01 jhump

This should be addressed in v1.15.0 (resolved in #659).

jhump avatar Mar 12 '24 13:03 jhump