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: Read or Write on expired AND closed connection do not return a timeout error #29876

Closed
empijei opened this Issue Jan 22, 2019 · 1 comment

Comments

Projects
None yet
2 participants
@empijei
Copy link
Contributor

empijei commented Jan 22, 2019

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

$ go version
go version go1.9.4 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
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/REDACTED/go"
GORACE=""
GOROOT="/usr/lib/go-1.9"
GOTOOLDIR="/usr/lib/go-1.9/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build648808165=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

What did you do?

/*
Output
0 read tcp [::1]:35374->[::1]:16080: i/o timeout
0 read tcp [::1]:35374->[::1]:16080: use of closed network connection
*/
func main() {
	sign := make(chan bool)
	l, err := net.Listen("tcp", ":16080")
	check(err)
	go func() {
		defer l.Close()
		c, err := l.Accept()
		check(err)
		c.Close()
		sign <- true
	}()
	c, err := net.Dial("tcp", "localhost:16080")
	check(err)
	c.SetDeadline(time.Now().Add(1 * time.Millisecond))
	time.Sleep(2 * time.Millisecond)
	<-sign
	for {
		time.Sleep(1 * time.Second)
		p := make([]byte, 4)
		n, err := c.Read(p)
		fmt.Println(n, err)
		if netErr, ok := err.(net.Error); ok && !netErr.Temporary() {
			return
		}
		time.Sleep(1 * time.Second)
		c.Close()
	}
}

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
  1. Opened a connection to a remote
  2. Set deadline on it
  3. Deadline expired
  4. Remote closed connection
  5. Try Read
  6. Close connection
  7. Try Read

What did you expect to see?

I would expect to get 2 non-timeout error or 2 timeout ones.

What did you see instead?

  • "i/o timeout" error when just remote closed connection
  • "use of closed connection" error when local connection was also closed

Some additional comments

Looking at the net.Conn documentation, it seems like timing out for the Read might be intended behavior

        // Read can be made to time out and return an Error with Timeout() == true
        // after a fixed time limit
        // [...]
        // A deadline is an absolute time after which I/O operations
        // fail with a timeout (see type Error) instead of
        // blocking. The deadline applies to all future and pending
        // I/O, not just the immediately following call to Read or
        // Write.

This probably means that if the remote closed the connection, a Read or Write would still timeout when attempted.

What seems to be a non-intended behavior is to return a non-timeout error after the local side of the Conn is closed.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

ianlancetaylor commented Jan 22, 2019

It is intentional that when the local side of a network is closed, that an existing Read or Write on that connection should return an error "use of closed network connection". I don't think we want to specify whether you get that error or a timeout error when a deadline has expired. I don't see how that is useful. If there are two possible error conditions, you will get one or the other. We won't specify which one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment