Skip to content

net/http: Error when setting idleConnectionTimeout #70312

Closed as not planned
Closed as not planned
@khushijain21

Description

@khushijain21

Go version

1.21

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/khushijain/Library/Caches/go-build'
GOENV='/Users/khushijain/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/khushijain/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/khushijain/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.23.1/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.23.1/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.1'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/khushijain/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/khushijain/Documents/elastic-agent-libs/go.mod'
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 arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/cv/_38x0gd965v0156jwblb9x6h0000gn/T/go-build243205887=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

If you set IdleConnectionTImeout timeout on http.Transport to 2s. And send two separate requests with 5 seconds delay between them with custom logging dialer which logs an error received during read and write operations on net.Conn

We receive a use of closed connection error on sending the second request. The error is from the read method on net.Conn. It seems like it was trying to reuse the first connection which was closed due to timeout. We expected Go to internally handle not reusing old connections

How to reproduce

// Set up a test HTTP server
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"status": "ok"}`)
	}))
	defer server.Close()

	logger := logp.NewLogger("test")
	// Set IdleConnTimeout to 2 seconds and a custom dialer
	transport := &http.Transport{
		IdleConnTimeout: 2 * time.Second,
		// uses our implementation of custom dialer
		DialContext: LoggingDialer(NetDialer(10*time.Second), logger).DialContext,
	}

	client := &http.Client{
		Transport: transport,
	}

	// First request to the test server
	resp, err := client.Get(server.URL) //nolint:noctx // It is a test
	require.NoError(t, err, "first request failed")
	_, _ = io.ReadAll(resp.Body)
	resp.Body.Close()

	// Wait for a duration longer than IdleConnTimeout
	waitTime := 6 * time.Second
	time.Sleep(waitTime)

	// Second request to the test server after idle timeout
	resp, err = client.Get(server.URL) //nolint:noctx // It is a test
	require.NoError(t, err, "second request failed")
	_, _ = io.ReadAll(resp.Body)
	resp.Body.Close()

What did you see happen?

use of closed network connection

What did you expect to see?

We expected a new connection would be used to read from the buffer. Instead it reuses an old connection that has already been closed due to timeout

Metadata

Metadata

Assignees

No one assigned

    Labels

    WaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions