Built on top of gorilla toolkit's websocket package, this library allows caller to create and control a pool of websockets
NOTE: This package is still under development.
To import the package into your application:
import "github.com/3cb/ssc"To install the package on your system:
go get github.com/3cb/sscFirst create an instance of ssc.Pool by calling ssc.NewPool() which takes a configuration object as a parameter:
sockets:= []string{
"wss://api.example.com/ws1",
"wss://api.example.com/ws2",
"wss://api.example.com/ws3",
}
pool:=ssc.NewPool(sockets, time.Second*45)
err:=pool.Start()
iferr!=nil {
log.Fatalf("Error starting new Pool.")
}The above example starts a pool of websocket server connections. In order to start an empty pool which is ready for client websockets to connect, send an empty slice for the first parameter:
pool, err:=ssc.NewPool([]string{}, time.Second*45])
err:=pool.Start()
iferr!=nil {
log.Fatalf("Error starting new Pool.")
}To add individual client connections call pool.AddClientSocket() from a route handler:
funcmain() {
r:=mux.NewRouter() // gorilla mux packagepool:=ssc.NewPool([]string{}, time.Second*30)
err:=pool.Start()
iferr!=nil {
log.Fatalf("Error starting new Pool.")
}
upgrader:=&websocket.Upgrader{} // gorilla websocket packager.Handle("/". wsHandler(pool, upgrader))
log.Fatal(http.ListenAndServe(":3000", r))
}
funcwsHandler(pool*ssc.Pool, upgrader*websocket.Upgrader) http.Handler {
returnhttp.HandlerFunc(func(w http.ResponseWriter, r*http.Request) {
err:=pool.AddClientSocket("clientID", upgrader, w, r)
iferr!=nil {
// handle error
}
// do other stuff
})
}Caller application can also add individual server connections with pool.AddServerSocket:
err:=pool.AddServerSocket(URL)
iferr!=nil {
// handle error
}Data is moved in instances of the Message type.
typeMessagestruct {
// ID is the raw URL string for server connections and is set by caller application for client connections.// The read goroutine sets the ID equal to the ID of the Socket before sending it to the pool to be sent to the outbound channel.// Caller app must be careful to change the ID on the Message if it needs to send it to a specific socket in a Pool of client socketsIDstring// Type are defined in the websocket protocol: https://tools.ietf.org/html/rfc6455// They are also described here: http://www.gorillatoolkit.org/pkg/websocket#constantsTypeint// Payload is any message serialized into a slice of bytesPayload []byte
}For writing to sockets from the caller application there are a few options:
// Write sends message to specific socket based on the id string// returns an error if Message.ID parameter is empty or not in stackerr:=pool.Write(&Message{ID: "client1", Type: websocket.TextMessage, Payload: []byte("Hello!")})
iferr!=nil {
// handle error
}
//WriteAll sends message to all sockets in `Pool` regardless of the ID string in Messagepool.WriteAll(&Message{Type: websocket.TextMessage, Payload: []byte("Hello!")})
// Another way to send a Message to all connected websockets is to send directly into the Pool.Inbound channelpool.Inbound<-&Message{Type: websocket.TextMessage, Payload: []byte("Hello!")}ssc provides a couple of convenience methods as well:
// Count returns the number of connections active in the `Pool`count:=pool.Count()
// List returns a slice of ID strings for the connections active in the poolconnections:=pool.List()Lastly, if the caller application has no more need for the Pool it can use:
pool.Stop()The above will cleanup all running goroutines. This method will shutdown all read/write goroutines, wait for confirmation, and then shut down all control goroutines that are running.
Usage for server WebSockets here: https://github.com/3cb/gemini_clone/tree/go_stream
Usage for client WebSockets here: https://github.com/3cb/seattle911 and here: https://github.com/3cb/melbourne_parking
-> MORE TESTS?