Skip to content

net/http/fcgi: request context not canceled on aborted connection #71344

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

Open
alexg-axis opened this issue Jan 20, 2025 · 8 comments
Open

net/http/fcgi: request context not canceled on aborted connection #71344

alexg-axis opened this issue Jan 20, 2025 · 8 comments
Labels
BugReport Issues describing a possible bug in the Go implementation. FeatureRequest Issues asking for a new feature that does not need a proposal. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.

Comments

@alexg-axis
Copy link

alexg-axis commented Jan 20, 2025

Go version

go version go1.23.4 darwin/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/Users/user/Library/Caches/go-build'
GOENV='/Users/user/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/user/go/pkg/mod'
GOOS='darwin'
GOPATH='/Users/user/go'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/Cellar/go/1.23.4/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/local/Cellar/go/1.23.4/libexec/pkg/tool/darwin_amd64'
GOVCS=''
GOVERSION='go1.23.4'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/user/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/Users/user/Development/temp/go-build2053259560=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

Start an FCGI server.

listener, err := net.Listen("tcp", ":0")
defer listener.Close()

fcgi.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  // Emulate some slow operation, waiting for the context to cancel
  <-r.Context().Done()
  fmt.Println("Done with slow operation")
}))

Using an FCGI client, or a reverse proxy like Apache with FCGI support, issue a request to the server and then close the request. Unfortunately the code for this can get quite verbose as the package only implements the server parts and I'm not sure it provides much context in this case.

What did you see happen?

The string Done with slow operation is never printed as the context is never canceled.

What did you expect to see?

According to the documentation on the http request's context field, the context should be set and canceled when the connection closes or the server closes. As I haven't found any documentation in the fcgi package stating otherwise, I expect the same to be true.

From what I can tell, the issue comes from the fcgi package never setting a context that corresponds to the incoming connection. It relies on the default context.Background() returned by http.Request.Context if it's nil.

httpReq, err := cgi.RequestFromMap(req.params)
if err != nil {
// there was an error reading the request
r.WriteHeader(http.StatusInternalServerError)
c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
} else {
httpReq.Body = body
withoutUsedEnvVars := filterOutUsedEnvVars(req.params)
envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars)
httpReq = httpReq.WithContext(envVarCtx)
c.handler.ServeHTTP(r, httpReq)

This makes the fcgi package difficult to use when web clients are involved as there's seemingly no way to react on aborted / closed requests, making it difficult to stop ongoing work.

@alexg-axis alexg-axis changed the title net/http/fcgi: Request context not canceled when peer connection is closed net/http/fcgi: Request context never canceled (uses context.Background) Jan 20, 2025
@gabyhelp gabyhelp added the BugReport Issues describing a possible bug in the Go implementation. label Jan 20, 2025
@seankhliao seankhliao changed the title net/http/fcgi: Request context never canceled (uses context.Background) net/http/fcgi: request context not canceled on aborted connection Jan 20, 2025
@seankhliao seankhliao added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jan 20, 2025
@seankhliao
Copy link
Member

I suppose it can be wired up to FCGI_ABORT_REQUEST, but is there a server implementation that supports it? nginx apparently doesn't.

@seankhliao seankhliao added the FeatureRequest Issues asking for a new feature that does not need a proposal. label Jan 20, 2025
@alexg-axis
Copy link
Author

That would only help the case of graceful shutdowns (which I can see as a feature request). It will not solve the issue of a connection getting killed, be it by a user's client or a reverse proxy. That is the issue I'm seeing as a bug - the context has no correlation to the request or underlying transport.

@seankhliao
Copy link
Member

FastCGI multiplexes multiple client->proxy connections to fewer proxy->fastcgi connections.
If the proxy never informs the fastcgi application that the client->proxy connection was terminated (with the aborted message), then the fastcgi application won't know about it, since it's not tied to the lifetime of the connection between the proxy->fastcgi

@alexg-axis
Copy link
Author

Thanks for the quick response. There would still be an issue with the proxy shutting down, right? Also, as far as I can tell, it isn't mandatory to multiplex requests, so non-multiplexed connections getting killed is still an issue. But I think you're right in that it won't help solve the case when an end-user kills a request when using a multiplexing reverse proxy, that would probably require FCGI_ABORT_REQUEST.

@seankhliao
Copy link
Member

While it is possible for the proxy to open a new connection per client connection, I believe none of them will do so as then fastcgi would be pointless. Multiplexing is an inherent part of the protocol, requests sent/received are framed by unique request IDs over the underlying connection.

@alexg-axis
Copy link
Author

From what I can gather nginx doesn't support multiplexing. It doesn't seem to be supported by mod_fastcgi or mod_proxy_fcgi either. Lighttpd doesn't support multiplexing either. Granted, a lot of search results are rather old and things might have changed. HAProxy does support multiplexing.

Nginx can be instructed to keep a connection alive, though. As can Apache. But none of them use it as a default behavior as far as I can tell.

I'd therefore argue that closing connections is a valid and common use case of FCGI and as the fcgi package doesn't document it as a limitation and as it reuses the http package's request type that does document the expectations of the context, this is is an issue.

@alexg-axis
Copy link
Author

@seankhliao Have you had the chance to reconsider this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BugReport Issues describing a possible bug in the Go implementation. FeatureRequest Issues asking for a new feature that does not need a proposal. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

3 participants