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

http: unsolicited idle response #2057

Closed
gopherbot opened this issue Jul 9, 2011 · 13 comments
Closed

http: unsolicited idle response #2057

gopherbot opened this issue Jul 9, 2011 · 13 comments

Comments

@gopherbot
Copy link

by hectorchu:

http://code.google.com/p/go/source/browse/src/pkg/http/transport.go#502

hitting this on Windows as in:
2011/07/09 17:30:28 Unsolicited response received on idle HTTP channel starting
with ""; err=WSARecv tcp 192.168.137.130:55092: The specified network name is
no
 longer available.

Probably because the code doesn't anticipate errors other than EOF and INVAL. Suggest
changing the line a little above it:

if (err == os.EOF || err == os.EINVAL) && !pc.expectingResponse() {

to

if len(pb) == 0 && !pc.expectingResponse() {
@bradfitz
Copy link
Contributor

Comment 1:

Owner changed to @bradfitz.

Status changed to Accepted.

@bradfitz
Copy link
Contributor

Comment 2:

I think I'd rather catch the exact error that Windows generates.
Could you change that line to log.Printf("error %T %#v", err, err) and let me know what
the windows error type is?

@gopherbot
Copy link
Author

Comment 3 by hectorchu:

I don't have the source checked out at the moment, but I believe I can make a pretty
good guess of the type.
package main
import (
    "log"
    "net"
    "os"
)
func main() {
    err := &net.OpError{"WSARecv", "tcp", &net.TCPAddr{net.ParseIP("192.168.137.130"), 55092}, os.Errno(64)}
    log.Printf("err=%v", err)
    log.Printf("error %T %#v", err, err)
}
Outputs:
2011/07/11 23:20:34 err=WSARecv tcp 192.168.137.130:55092: The specified network name is
no longer available.
2011/07/11 23:20:34 error *net.OpError &net.OpError{Op:"WSARecv", Net:"tcp",
Addr:(*net.TCPAddr)(0x10b727f0), Error:64}

@alexbrainman
Copy link
Member

Comment 4:

errno=64 would be
#define ERROR_NETNAME_DELETED 64L
you could add the const to syscall/ztypes_windows.go if it helps you any.

@gopherbot
Copy link
Author

Comment 5 by hectorchu:

I think the problem is broader than Windows.  I'm getting a similar error under Linux:
connection reset by peer (probably ECONNRESET).  Here's the website I'm connecting to,
in a Go program:
package main
import (
    "bytes"
    "http"
    "io"
    "log"
)
func main() {
    r, err := http.Get("https://www.mbtrading.com/secure/getquoteserverxml.asp")
    if err != nil {
        return
    }
    var buf bytes.Buffer
    io.Copy(&buf, r.Body)
    r.Body.Close()
    log.Printf("%s\n", buf.Bytes())
    select {}
}
The disconnect happens after 2 minutes.

@alexbrainman
Copy link
Member

Comment 6:

I agree.
This particular server closes connection in such way that:
err == os.EOF || err == os.EINVAL
in http/transport.go wouldn't catch that case.
On Windows I receive ERROR_NETNAME_DELETED while reading from connection.
On Linux - ECONNRESET.
We need to handle these errors.

@bradfitz
Copy link
Contributor

Comment 7:

Alex, I can fix this if there's an easy way to reproduce.  Is it just the server closing
the connection?  You want to write (at least part of) a unit test that I can finish +
fix the transport?
I guess I have a Windows machine now, so I can experiment.

@alexbrainman
Copy link
Member

Comment 8:

Brad,
I don't know how to reproduce the problem reliably and on all different platforms. But
here I can demonstrate it on Windows. Build this program:
package main
import (
       "fmt"
       "io"
       "net"
       "log"
       "os"
)
func serveClient(fd io.ReadWriteCloser) {
       defer fd.Close()
       var buf [1024]byte
       for {
               // both parties are blocked reading
               n, err := fd.Read(buf[0:])
               if err != nil || n == 0 {
                       fmt.Printf("disconneted: %q\n", err)
                       break
               }
       }
}
func server() {
       const addr = ":0"
       fmt.Printf("running server ...\n")
       l, err := net.Listen("tcp", addr)
       if err != nil {
               log.Fatalf("Listen(%q) failed: %v", addr, err)
       }
       defer l.Close()
       fmt.Printf("listening on %s ...\n", l.Addr().String())
       for {
               fd, err := l.Accept()
               if err != nil {
                       break
               }
               go serveClient(fd)
       }
}
func client(addr string) {
       fmt.Printf("running client ...\n")
       fd, err := net.Dial("tcp", addr)
       if err != nil {
               log.Fatalf("Dial(%q) failed: %v", addr, err)
       }
       defer fd.Close()
       // both parties are blocked reading
       var b [1024]byte
       n, err := fd.Read(b[:])
       if err != nil {
               log.Fatalf("fd.Read() = %d, %v", n, err)
       }
}
func main() {
       if len(os.Args) == 1 {
               server()
       } else {
               client(os.Args[1])
       }
}
and then run it on Windows:
C:\>a.exe
running server ...
listening on 0.0.0.0:1713 ...
then run the same program, but in a new window:
C:\>a.exe localhost:1713
running client ...
Go back to original (server) window and hit Ctrl+C to kill the program. You should see
client program exit with:
2011/07/22 19:33:16 fd.Read() = 0, WSARecv tcp 127.0.0.1:1713: The specified network
name is no longer available.
If you run client program on Linux, the message is:
2011/07/22 19:52:22 fd.Read() = 0, read tcp 10.0.0.13:1713: connection reset by peer
Alex

@gopherbot
Copy link
Author

Comment 9 by hectorchu:

> Is it just the server closing the connection?
Yep, the server is doing a dirty disconnect.  Should be pretty straightforward to write
a Go http server that does something similar.

@rsc
Copy link
Contributor

rsc commented Oct 6, 2011

Comment 10:

Status changed to HelpWanted.

@gopherbot
Copy link
Author

Comment 12 by hectorchu:

The hard close is likely to be caused by the peer setting SO_LINGER l_onoff to 1 (true)
and l_linger to 0 (zero time) (see
http://groups.google.com/group/comp.unix.programmer/msg/a21de3c46091ed9c).  I had
trouble writing a test case that reproduced this on Windows, but at least the post
indicates that ECONNRESET isn't to be completely unexpected.  Anyway this code will
probably change as we're not supposed to use syscall-type errors in general use.

@bradfitz
Copy link
Contributor

bradfitz commented Oct 6, 2011

Comment 13:

I thought I had a fix for this at one point.  Maybe it's on my Windows machine at home,
stranded without a working codereview.py.

@bradfitz
Copy link
Contributor

Comment 14:

This issue was closed by revision c4b9c8b.

Status changed to Fixed.

@golang golang locked and limited conversation to collaborators Jun 24, 2016
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants