mirror of
https://github.com/golang/go
synced 2024-10-05 11:41:22 -06:00
71bf182028
TestAcceptIgnoreSomeErrors was created to test that network accept function ignores some errors. But conditions created by the test also affects network reads. Change the test to ignore these read errors when acceptable. Fixes #10785 Change-Id: I3da85cb55bd3e78c1980ad949e53e82391f9b41e Reviewed-on: https://go-review.googlesource.com/9942 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
166 lines
3.6 KiB
Go
166 lines
3.6 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package net
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func toErrno(err error) (syscall.Errno, bool) {
|
|
operr, ok := err.(*OpError)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
syserr, ok := operr.Err.(*os.SyscallError)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
errno, ok := syserr.Err.(syscall.Errno)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
return errno, true
|
|
}
|
|
|
|
// TestAcceptIgnoreSomeErrors tests that windows TCPListener.AcceptTCP
|
|
// handles broken connections. It verifies that broken connections do
|
|
// not affect future connections.
|
|
func TestAcceptIgnoreSomeErrors(t *testing.T) {
|
|
recv := func(ln Listener, ignoreSomeReadErrors bool) (string, error) {
|
|
c, err := ln.Accept()
|
|
if err != nil {
|
|
// Display windows errno in error message.
|
|
errno, ok := toErrno(err)
|
|
if !ok {
|
|
return "", err
|
|
}
|
|
return "", fmt.Errorf("%v (windows errno=%d)", err, errno)
|
|
}
|
|
defer c.Close()
|
|
|
|
b := make([]byte, 100)
|
|
n, err := c.Read(b)
|
|
if err == nil || err == io.EOF {
|
|
return string(b[:n]), nil
|
|
}
|
|
errno, ok := toErrno(err)
|
|
if ok && ignoreSomeReadErrors && (errno == syscall.ERROR_NETNAME_DELETED || errno == syscall.WSAECONNRESET) {
|
|
return "", nil
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
send := func(addr string, data string) error {
|
|
c, err := Dial("tcp", addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer c.Close()
|
|
|
|
b := []byte(data)
|
|
n, err := c.Write(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n != len(b) {
|
|
return fmt.Errorf(`Only %d chars of string "%s" sent`, n, data)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if envaddr := os.Getenv("GOTEST_DIAL_ADDR"); envaddr != "" {
|
|
// In child process.
|
|
c, err := Dial("tcp", envaddr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fmt.Printf("sleeping\n")
|
|
time.Sleep(time.Minute) // process will be killed here
|
|
c.Close()
|
|
}
|
|
|
|
ln, err := Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer ln.Close()
|
|
|
|
// Start child process that connects to our listener.
|
|
cmd := exec.Command(os.Args[0], "-test.run=TestAcceptIgnoreSomeErrors")
|
|
cmd.Env = append(os.Environ(), "GOTEST_DIAL_ADDR="+ln.Addr().String())
|
|
stdout, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
t.Fatalf("cmd.StdoutPipe failed: %v", err)
|
|
}
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
t.Fatalf("cmd.Start failed: %v\n", err)
|
|
}
|
|
outReader := bufio.NewReader(stdout)
|
|
for {
|
|
s, err := outReader.ReadString('\n')
|
|
if err != nil {
|
|
t.Fatalf("reading stdout failed: %v", err)
|
|
}
|
|
if s == "sleeping\n" {
|
|
break
|
|
}
|
|
}
|
|
defer cmd.Wait() // ignore error - we know it is getting killed
|
|
|
|
const alittle = 100 * time.Millisecond
|
|
time.Sleep(alittle)
|
|
cmd.Process.Kill() // the only way to trigger the errors
|
|
time.Sleep(alittle)
|
|
|
|
// Send second connection data (with delay in a separate goroutine).
|
|
result := make(chan error)
|
|
go func() {
|
|
time.Sleep(alittle)
|
|
err := send(ln.Addr().String(), "abc")
|
|
if err != nil {
|
|
result <- err
|
|
}
|
|
result <- nil
|
|
}()
|
|
defer func() {
|
|
err := <-result
|
|
if err != nil {
|
|
t.Fatalf("send failed: %v", err)
|
|
}
|
|
}()
|
|
|
|
// Receive first or second connection.
|
|
s, err := recv(ln, true)
|
|
if err != nil {
|
|
t.Fatalf("recv failed: %v", err)
|
|
}
|
|
switch s {
|
|
case "":
|
|
// First connection data is received, let's get second connection data.
|
|
case "abc":
|
|
// First connection is lost forever, but that is ok.
|
|
return
|
|
default:
|
|
t.Fatalf(`"%s" received from recv, but "" or "abc" expected`, s)
|
|
}
|
|
|
|
// Get second connection data.
|
|
s, err = recv(ln, false)
|
|
if err != nil {
|
|
t.Fatalf("recv failed: %v", err)
|
|
}
|
|
if s != "abc" {
|
|
t.Fatalf(`"%s" received from recv, but "abc" expected`, s)
|
|
}
|
|
}
|