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: Server negative timeouts are accepted and break silently #39177

Closed
belimawr opened this issue May 20, 2020 · 10 comments
Closed

net/http: Server negative timeouts are accepted and break silently #39177

belimawr opened this issue May 20, 2020 · 10 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@belimawr
Copy link

I am not completely sure whether it is a bug, an expected behaviour or just a lack of documentation, however, I believe that this behaviour should at least be documented, ideally have the application explicitly failing.

What version of Go are you using (go version)?

$ go version
go version go1.14.3 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/onefootball/.cache/go-build"
GOENV="/home/onefootball/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY="github.com/motain/*"
GONOSUMDB="github.com/motain/*"
GOOS="linux"
GOPATH="/home/onefootball/go"
GOPRIVATE="github.com/motain/*"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/onefootball/sdk/go1.14.3"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/onefootball/sdk/go1.14.3/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/onefootball/test/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build006613611=/tmp/go-build -gno-record-gcc-switches"
GOROOT/bin/go version: go version go1.14.3 linux/amd64
GOROOT/bin/go tool compile -V: compile version go1.14.3
uname -sr: Linux 5.6.13-arch1-1
/usr/lib/libc.so.6: GNU C Library (GNU libc) stable release version 2.31.

What did you do?

Started an HTTP server with negative values for WriteTimeout and ReadTimeout, then executed requests to this HTTP server.

Here is a super simple code that reproduces the issue:

package main

import (
	"log"
	"net/http"
	"time"
)

func main() {
	server := &http.Server{
		WriteTimeout: -time.Second,
		ReadTimeout:  -time.Second,
		Addr:         ":3000",
	}

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Print("Request:", r.URL.String())
	})

	server.Handler = handler

	log.Print("starting HTTP server on port: 3000")
	panic(server.ListenAndServe())
}

What did you expect to see?

One of:

  • An error log or panic on HTTP server start up
  • Any sort of error log when accepting/rejecting requests
  • Documentation on net/http.Server explaining the behaviour when the timeout is negative

What did you see instead?

Nothing on application side, for any caller the only kind of error we see is a "connection reset by peer".

onefootball@millennium-falcon ~ % curl localhost:3000/
curl: (56) Recv failure: Connection reset by peer
@cagedmantis cagedmantis added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label May 21, 2020
@cagedmantis cagedmantis added this to the Backlog milestone May 21, 2020
@cagedmantis
Copy link
Contributor

/cc @bradfitz @bcmills

@odeke-em
Copy link
Member

For an isolated and runnable repro, please see https://play.golang.org/p/uD5-UNDhTyN or inlined below

package main

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"time"
)

func main() {
	cst := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, issue!"))
	}))
	cst.Config.WriteTimeout = -time.Second
	cst.Config.ReadTimeout = -time.Second
	cst.Start()
	defer cst.Close()

	for i := 0; i < 10; i++ {
		res, err := cst.Client().Get(cst.URL)
		if err != nil {
			panic(err)
		}
		_, _ = ioutil.ReadAll(res.Body)
		res.Body.Close()
	}
}

@jharshman
Copy link
Contributor

I'm not sure if setting a negative value for those fields has any practical use. Perhaps testing?

I could see documentation being enough for this but it's also a small code change to not add negative values to the timeouts.

@belimawr
Copy link
Author

I don't see any use of negative values there either. For testing very small, positive values could achieve the same result.

I believe not accepting negative values (or setting them to zero) would make http.Server safer to use. However I wonder how that could affect existing programs and impact the go compatibility promise.

@gopherbot
Copy link

Change https://golang.org/cl/235437 mentions this issue: net/http: add to deadlines only when positive

@neild
Copy link
Contributor

neild commented Oct 17, 2020

I believe the current behavior is consistent with the documentation: A negative timeout gives less than no time in which to perform the operation. This isn't useful, but a 1ns timeout isn't really useful either.

I don't think there's any need to change the package behavior or documentation with regard to negative timeouts.

I do note that there is no documentation of what a zero WriteTimeout means. We should explicitly document that this indicates no timeout.

jalilzade pushed a commit to jalilzade/mockky that referenced this issue Nov 7, 2020
@bradfitz
Copy link
Contributor

bradfitz commented Dec 1, 2020

Sometimes we use negative values to mean "disabled, no timeout" when 0 means "pick some default timeout value for me".

I'm fine with making negative values an error or disabled. I agree we should say what 0 means for ReadTimeout.

@belimawr
Copy link
Author

belimawr commented Dec 2, 2020

Sometimes we use negative values to mean "disabled, no timeout" when 0 means "pick some default timeout value for me".

I'm fine with making negative values an error or disabled. I agree we should say what 0 means for ReadTimeout.

I believe we should say what 0 means for both of them (ReadTimeout and WriteTimeout).

Making negative values an error or disabled are both fine for me. However, I believe that making negative values an error is "safer" in the sense that it's easier to spot errors. What lead me to open this issue was an overflow of time.Duration (int64) that make the timeouts negative without nobody noticing it.

@bcmills
Copy link
Contributor

bcmills commented Dec 2, 2020

@belimawr given that negative values already have a well-defined behavior, that behavior arguably cannot be changed due to the Go 1 compatibility policy.

We can certainly improve the documentation, though.

@bcmills
Copy link
Contributor

bcmills commented Dec 2, 2020

What lead me to open this issue was an overflow of time.Duration (int64) that make the timeouts negative without nobody noticing it.

Arguably the defect there lies with time.Duration rather than net/http.Server. The same class of errors is possible for any code that manipulates Duration values, so the fix should be a general fix in the time package or some (static or dynamic) analysis tool. (#20757 is closely related.)

@golang golang locked and limited conversation to collaborators Apr 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge 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

8 participants