-
-
Save dcormier/44180556bdebad30730256af4cca9a07 to your computer and use it in GitHub Desktop.
Demonstration of an intermittent failure of a socket.io client package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This is a demonstration of an intermittent failure when using github.com/zhouhui8915/go-socket.io-client | |
to connect to a socket.io server. | |
This file contains two tests that start serving socket.io, then attempt to connect to it | |
concurrently with 10 instances of a socket.io client. | |
These packages are required to run these tests: | |
github.com/googollee/go-socket.io | |
github.com/graarh/golang-socketio | |
github.com/zhouhui8915/go-socket.io-client | |
The test using github.com/graarh/golang-socketio (TestGraarh) passes reliably. | |
The test using github.com/zhouhui8915/go-socket.io-client (TestZhouhui) fails reliably. | |
In every case where this client fails, the server actually *does* accept the connection as expected. | |
The error returned by `socketio_client.NewClient` is "EOF". This is probably the `io.EOF` value. | |
The github.com/zhouhui8915/go-socket.io-client package also fails to connect when not trying to | |
connect clients concurrently, but it's much more rare. If you change that test to pass `false` to | |
the `testConnect` function and run it repeatedly, you will eventually witness this failure. | |
When the client fails to connect, it almost always takes 25 seconds to fail (the default | |
`pingInterval` value). There is a much more rare failure that happens almost immediately, with the | |
same error, but I'm not able to quickly reproduce this. Change `true` to `false` in the `TestZhouhui` | |
function and run the test enough times to cause it to happen. | |
I am unable to cause a similar (or any other) failure when using github.com/graarh/golang-socketio | |
or the official Node.js socket.io client. This leads me to believe it is a problem with the | |
github.com/zhouhui8915/go-socket.io-client package rather than an issue with the server being used. | |
I suspect that the client is expecting data to be immediately available to be read, but in fact it | |
takes a moment for the server to send it. | |
*/ | |
package main | |
import ( | |
"fmt" | |
"net" | |
"net/http" | |
"net/http/httptest" | |
"strconv" | |
"sync" | |
"sync/atomic" | |
"testing" | |
"github.com/googollee/go-socket.io" | |
"github.com/graarh/golang-socketio" | |
"github.com/graarh/golang-socketio/transport" | |
"github.com/zhouhui8915/go-socket.io-client" | |
) | |
func testServe(t *testing.T) (*socketio.Server, net.Addr) { | |
sioServer, err := socketio.NewServer(nil) | |
if err != nil { | |
t.Fatalf("[server] Could not create server: %v", err) | |
} | |
count := uint32(0) | |
sioServer.On("connection", func(so socketio.Socket) { | |
t.Logf("[server] Client #%02d connected: %s", atomic.AddUint32(&count, 1), so.Id()) | |
}) | |
mux := http.NewServeMux() | |
mux.Handle("/socket.io/", sioServer) | |
testServer := httptest.NewServer(mux) | |
addr := testServer.Listener.Addr() | |
t.Logf("[server] Listening on %s", addr) | |
return sioServer, addr | |
} | |
type prep interface { | |
URL() string | |
} | |
type urlPrep struct { | |
url string | |
} | |
func (up *urlPrep) URL() string { | |
return up.url | |
} | |
type prepFunc func(t *testing.T, addr net.Addr) prep | |
type connectFunc func(prepData prep) error | |
func testConnect(t *testing.T, prepFunc prepFunc, connectFunc connectFunc, concurrent bool) { | |
_, addr := testServe(t) | |
prepData := prepFunc(t, addr) | |
var waitForClients *sync.WaitGroup | |
if concurrent { | |
waitForClients = new(sync.WaitGroup) | |
} | |
for i := 1; i <= 10; i++ { | |
if concurrent { | |
waitForClients.Add(1) | |
} | |
doIt := func(i int) { | |
if concurrent { | |
defer waitForClients.Done() | |
} | |
t.Logf("[client] #%02d connecting...", i) | |
err := connectFunc(prepData) | |
if err != nil { | |
t.Errorf("[client] #%02d could not connect to %s: %v", i, prepData.URL(), err) | |
return | |
} | |
t.Logf("[client] #%02d connected", i) | |
} | |
if concurrent { | |
go doIt(i) | |
} else { | |
doIt(i) | |
} | |
} | |
if concurrent { | |
t.Log("Waiting for client connection attempts to finish") | |
waitForClients.Wait() | |
} | |
} | |
func graarhPrep(t *testing.T, addr net.Addr) prep { | |
host, port, err := net.SplitHostPort(addr.String()) | |
if err != nil { | |
t.Fatalf(`Could not split "%s" into host and port: %v`, addr, err) | |
} | |
p, err := strconv.Atoi(port) | |
if err != nil { | |
t.Fatalf(`Could not convert port "%s" to int: %v`, port, err) | |
} | |
return &urlPrep{ | |
url: gosocketio.GetUrl(host, p, false), | |
} | |
} | |
func graarhConnect(prepData prep) error { | |
tport := transport.GetDefaultWebsocketTransport() | |
_, err := gosocketio.Dial(prepData.URL(), tport) | |
return err | |
} | |
func TestGraarh(t *testing.T) { | |
testConnect(t, graarhPrep, graarhConnect, true) | |
} | |
type zhouhui struct { | |
*urlPrep | |
opts *socketio_client.Options | |
} | |
func zhouhuiPrep(t *testing.T, addr net.Addr) prep { | |
return &zhouhui{ | |
urlPrep: &urlPrep{ | |
url: fmt.Sprintf("http://%s", addr), | |
}, | |
opts: &socketio_client.Options{ | |
Transport: "websocket", | |
}, | |
} | |
} | |
func zhouhuiConnect(prepData prep) error { | |
data := prepData.(*zhouhui) | |
_, err := socketio_client.NewClient(data.url, data.opts) | |
return err | |
} | |
func TestZhouhui(t *testing.T) { | |
testConnect(t, zhouhuiPrep, zhouhuiConnect, true) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is a test to reproduce hesh915/go-socket.io-client#7.