wsrpc

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 4, 2024 License: MIT Imports: 7 Imported by: 0

README

WSRPC

License Build Status Go Report Card Go Reference Version

WSRPC is simple package to allow bidirectional RPC over a WebSocket connection.
The library already provides WebSocket adapters for Gorilla, Fiber, and FastHTTP; for all other cases, it should be quite easy to implement the WSConn interface.

Installation:

go get github.com/catamat/wsrpc@latest

Examples:

Gorilla server
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/catamat/wsrpc"
	"github.com/catamat/wsrpc/gorillaws"
	"github.com/gorilla/websocket"
)

// Definition of the API that the client can call on the server.
type ServerAPI struct{}

func (s *ServerAPI) Hello(args string, reply *string) error {
	*reply = "I am the server " + args
	return nil
}

// Define an upgrader to handle upgrading HTTP connections to WebSocket.
var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// Handler for the WebSocket connection.
func wsHandler(w http.ResponseWriter, r *http.Request) {
	// Upgrade the HTTP connection to a WebSocket connection
	wsConn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Fatalf("Error upgrading connection to WebSocket: %v", err)
		return
	}
	defer wsConn.Close()

	// Wrap the WebSocket connection using gorillaws.Conn
	wsAdapter := &gorillaws.Conn{Conn: wsConn}

	// Create server connection
	serverConn := wsrpc.NewWebSocketConn(wsAdapter)

	// Create the WSRPC server
	wsrpcServer, err := wsrpc.NewServer(serverConn)
	if err != nil {
		log.Fatalf("Error creating server: %v", err)
		return
	}
	defer wsrpcServer.Close()

	// Register the service on the server for calls from the client
	err = wsrpcServer.Register(&ServerAPI{})
	if err != nil {
		log.Fatalf("Error registering service on server: %v", err)
		return
	}

	// Server calls the Hello method on the client
	var reply string
	err = wsrpcServer.Call("ClientAPI.Hello", "called by server", &reply)
	if err != nil {
		log.Fatalf("Error in RPC call from server to client: %v", err)
		return
	}

	log.Println("Response from client:", reply)

	// Wait until the connection is closed
	<-wsrpcServer.Done()
}

func main() {
	http.HandleFunc("/ws", wsHandler)

	fmt.Println("Server listening on :50505")
	err := http.ListenAndServe(":50505", nil)
	if err != nil {
		log.Fatal("Error starting HTTP server:", err)
	}
}
Fiber server
package main

import (
	"fmt"
	"log"

	"github.com/catamat/wsrpc"
	"github.com/catamat/wsrpc/fiberws"
	"github.com/gofiber/contrib/websocket"
	"github.com/gofiber/fiber/v2"
)

// Definition of the API that the client can call on the server.
type ServerAPI struct{}

func (s *ServerAPI) Hello(args string, reply *string) error {
	*reply = "I am the server " + args
	return nil
}

func main() {
	app := fiber.New(fiber.Config{
		DisableStartupMessage: true,
	})

	// Define an upgrader to handle upgrading HTTP connections to WebSocket.
	app.Use("/ws", func(c *fiber.Ctx) error {
		if websocket.IsWebSocketUpgrade(c) {
			return c.Next()
		}
		return fiber.ErrUpgradeRequired
	})

	// Handler for the WebSocket connection.
	app.Get("/ws", websocket.New(func(c *websocket.Conn) {
		defer c.Close()

		// Wrap the WebSocket connection using fiberws.Conn
		wsAdapter := &fiberws.Conn{Conn: c}

		// Create server connection
		serverConn := wsrpc.NewWebSocketConn(wsAdapter)

		// Create the WSRPC server
		wsrpcServer, err := wsrpc.NewServer(serverConn)
		if err != nil {
			log.Fatalf("Error creating server: %v", err)
			return
		}
		defer wsrpcServer.Close()

		// Register the service on the server for calls from the client
		err = wsrpcServer.Register(&ServerAPI{})
		if err != nil {
			log.Fatalf("Error registering service on server: %v", err)
			return
		}

		// Server calls the Hello method on the client
		var reply string
		err = wsrpcServer.Call("ClientAPI.Hello", "called by server", &reply)
		if err != nil {
			log.Fatalf("Error in RPC call from server to client: %v", err)
			return
		}

		log.Println("Response from client:", reply)

		// Wait until the connection is closed
		<-wsrpcServer.Done()
	}))

	fmt.Println("Server listening on :50505")
	err := app.Listen(":50505")
	if err != nil {
		log.Fatal("Error starting HTTP server:", err)
	}
}
Gorilla client
package main

import (
	"github.com/catamat/wsrpc"
	"github.com/catamat/wsrpc/gorillaws"
	"github.com/gorilla/websocket"
	"log"
	"time"
)

// Definition of the API that the server can call on the client.
type ClientAPI struct{}

func (c *ClientAPI) Hello(args string, reply *string) error {
	*reply = "I am the client " + args
	return nil
}

func main() {
	retryDelay := 10 * time.Second

	for {
		err := connect()
		if err != nil {
			log.Println("Error connecting to server:", err)

			// Wait before attempting to reconnect
			time.Sleep(retryDelay)
			continue
		}
	}
}

func connect() error {
	// Connect to the server
	wsConn, _, err := websocket.DefaultDialer.Dial("ws://localhost:50505/ws", nil)
	if err != nil {
		return err
	}
	defer wsConn.Close()
	log.Println("Successfully connected to the server")

	// Wrap the WebSocket connection using gorilla.Conn
	wsAdapter := &gorillaws.Conn{Conn: wsConn}

	// Create client connection
	clientConn := wsrpc.NewWebSocketConn(wsAdapter)

	// Create the WSRPC client
	wsrpcClient, err := wsrpc.NewClient(clientConn)
	if err != nil {
		log.Printf("Error creating client: %v", err)
		return err
	}
	defer wsrpcClient.Close()

	// Register the service on the client for calls from the server
	err = wsrpcClient.Register(&ClientAPI{})
	if err != nil {
		log.Fatalf("Error registering service on client: %v", err)
		return err
	}

	// Client calls the Hello method on the server
	var reply string
	err = wsrpcClient.Call("ServerAPI.Hello", "called by client", &reply)
	if err != nil {
		log.Fatalf("Error in RPC call from client to server: %v", err)
		return err
	}

	log.Println("Response from server:", reply)

	// Wait until the connection is closed
	<-wsrpcClient.Done()

	return nil
}
FastHTTP client
package main

import (
	"log"
	"time"

	"github.com/fasthttp/websocket"
	"github.com/catamat/wsrpc"
	"github.com/catamat/wsrpc/fasthttpws"
)

// Definition of the API that the server can call on the client.
type ClientAPI struct{}

func (c *ClientAPI) Hello(args string, reply *string) error {
	*reply = "I am the client " + args
	return nil
}

func main() {
	retryDelay := 10 * time.Second

	for {
		err := connect()
		if err != nil {
			log.Println("Error connecting to server:", err)

			// Wait before attempting to reconnect
			time.Sleep(retryDelay)
			continue
		}
	}
}

func connect() error {
	// Connect to the server
	wsConn, _, err := websocket.DefaultDialer.Dial("ws://localhost:50505/ws", nil)
	if err != nil {
		return err
	}
	defer wsConn.Close()
	log.Println("Successfully connected to the server")

	// Wrap the WebSocket connection using fasthttpws.Conn
	wsAdapter := &fasthttpws.Conn{Conn: wsConn}

	// Create client connection
	clientConn := wsrpc.NewWebSocketConn(wsAdapter)

	// Create the WSRPC client
	wsrpcClient, err := wsrpc.NewClient(clientConn)
	if err != nil {
		log.Printf("Error creating client: %v", err)
		return err
	}
	defer wsrpcClient.Close()

	// Register the service on the client for calls from the server
	err = wsrpcClient.Register(&ClientAPI{})
	if err != nil {
		log.Fatalf("Error registering service on client: %v", err)
		return err
	}

	// Client calls the Hello method on the server
	var reply string
	err = wsrpcClient.Call("ServerAPI.Hello", "called by client", &reply)
	if err != nil {
		log.Fatalf("Error in RPC call from client to server: %v", err)
		return err
	}

	log.Println("Response from server:", reply)

	// Wait until the connection is closed
	<-wsrpcClient.Done()

	return nil
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewWebSocketConn

func NewWebSocketConn(ws WSConn) net.Conn

NewWebSocketConn creates a new WebSocketConn from a WSConn.

Types

type WSConn

type WSConn interface {
	ReadMessage() (messageType int, p []byte, err error)
	WriteMessage(messageType int, data []byte) error
	Close() error
	LocalAddr() net.Addr
	RemoteAddr() net.Addr
	SetReadDeadline(t time.Time) error
	SetWriteDeadline(t time.Time) error
}

WSConn defines the interface that must be implemented by a WebSocket connection.

type WSRPC

type WSRPC struct {
	// contains filtered or unexported fields
}

WSRPC defines a bidirectional RPC connection over a Yamux session.

func NewClient

func NewClient(conn net.Conn) (*WSRPC, error)

NewClient creates a new client on a net.Conn connection.

func NewServer

func NewServer(conn net.Conn) (*WSRPC, error)

NewServer creates a new server on a net.Conn connection.

func (*WSRPC) Call

func (wsrpc *WSRPC) Call(serviceMethod string, args interface{}, reply interface{}) error

Call calls the named function, waits for it to complete, and returns its error status.

func (*WSRPC) Close

func (wsrpc *WSRPC) Close() error

Close closes the connection.

func (*WSRPC) Done

func (wsrpc *WSRPC) Done() <-chan struct{}

Done returns a channel that is closed when the connection is terminated.

func (*WSRPC) Register

func (wsrpc *WSRPC) Register(rcvr interface{}) error

Register registers the set of methods of the RPC service.

type WebSocketConn

type WebSocketConn struct {
	// contains filtered or unexported fields
}

WebSocketConn adapts a WSConn to a net.Conn.

func (*WebSocketConn) Close

func (c *WebSocketConn) Close() error

func (*WebSocketConn) LocalAddr

func (c *WebSocketConn) LocalAddr() net.Addr

func (*WebSocketConn) Read

func (c *WebSocketConn) Read(b []byte) (n int, err error)

func (*WebSocketConn) RemoteAddr

func (c *WebSocketConn) RemoteAddr() net.Addr

func (*WebSocketConn) SetDeadline

func (c *WebSocketConn) SetDeadline(t time.Time) error

func (*WebSocketConn) SetReadDeadline

func (c *WebSocketConn) SetReadDeadline(t time.Time) error

func (*WebSocketConn) SetWriteDeadline

func (c *WebSocketConn) SetWriteDeadline(t time.Time) error

func (*WebSocketConn) Write

func (c *WebSocketConn) Write(b []byte) (n int, err error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL