Skip to content

Instantly share code, notes, and snippets.

@dcormier
Last active November 21, 2016 17:53
Show Gist options
  • Save dcormier/44180556bdebad30730256af4cca9a07 to your computer and use it in GitHub Desktop.
Save dcormier/44180556bdebad30730256af4cca9a07 to your computer and use it in GitHub Desktop.
Demonstration of an intermittent failure of a socket.io client package
/*
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)
}
@dcormier
Copy link
Author

This is a test to reproduce hesh915/go-socket.io-client#7.

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