Skip to content

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

@alexg-axis

Description

@alexg-axis

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.FeatureRequestIssues asking for a new feature that does not need a proposal.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions