Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
108 lines (97 sloc) 2.77 KB
// +build !js
package rpc
import (
log ""
// Server is a JSON RPC 2.0 server implementation over WebSockets. It accepts
// requests from a client for adding orders to the 0x Mesh network.
type Server struct {
mut sync.Mutex
addr string
listenerAddr net.Addr
rpcHandler RPCHandler
listener net.Listener
rpcServer *rpc.Server
// NewServer creates and returns a new server which will listen for new
// connections on the given addr and use the rpcHandler to handle incoming
// requests.
func NewServer(addr string, rpcHandler RPCHandler) (*Server, error) {
return &Server{
addr: addr,
rpcHandler: rpcHandler,
}, nil
// Listen causes the server to listen for new connections. You can call Close to
// stop listening. Listen blocks until there is an error or the given context is
// canceled.
func (s *Server) Listen(ctx context.Context) error {
rpcService := &rpcService{
rpcHandler: s.rpcHandler,
s.rpcServer = rpc.NewServer()
if err := s.rpcServer.RegisterName("mesh", rpcService); err != nil {
log.WithField("error", err.Error()).Error("could not register RPC service")
return err
listener, err := net.Listen("tcp4", s.addr)
if err != nil {
log.WithField("error", err.Error()).Error("could not start listener")
return err
s.listener = listener
// Close the server when the context is canceled.
go func() {
_ = s.listener.Close()
if err := http.Serve(s.listener, s.rpcServer.WebsocketHandler([]string{"*"})); err != nil {
// HACK(albrow): http.Serve doesn't accept a context. This means that
// everytime we close the context for our rpc.Server, we see a "use of
// closed network connection" error.
if isClosedNetworkConnectionErr(err) {
// Check whether the context is canceled in order to determine whether we
// are in the process of tearing down the server.
select {
case <-ctx.Done():
// If we are tearing down the server, this is okay and we don't need to
// return the error.
return nil
// If we are not tearing down the server, the error is not expected, and
// we should return it.
return err
return err
return nil
func isClosedNetworkConnectionErr(err error) bool {
if opErr, ok := err.(*net.OpError); ok {
if strings.Contains(opErr.Error(), "use of closed network connection") {
return true
return false
// Addr returns the address the server is listening on or nil if it has not yet
// started listening.
func (s *Server) Addr() net.Addr {
defer s.mut.Unlock()
if s.listener == nil {
return nil
return s.listener.Addr()
You can’t perform that action at this time.