Giter Site home page Giter Site logo

elpheria / rpc-websockets Goto Github PK

View Code? Open in Web Editor NEW
562.0 16.0 112.0 5.7 MB

JSON-RPC 2.0 implementation over WebSockets for Node.js and JavaScript/TypeScript

License: Other

JavaScript 69.48% TypeScript 30.52%
rpc-api rpc-framework rpc-client rpc-server websocket-server rpc aws serverless scalable messaging

rpc-websockets's Introduction


WebSockets for Node.js and JavaScript/TypeScript with JSON RPC 2.0 support on top.




About

The rpc-websockets library enables developers to easily implement their business logic that includes messaging between users, machines or any devices. It provides a possibility to send and receive JSON data through the WebSocket communication protocol in order to support two-way notification push, running RPC methods and firing any types of event signalling. Only clients can call RPC methods and not vice versa at the moment. Both frontend (HTML/JS-based) and backend (Node.js-based) development environments are supported.

rpc-websockets is built on Node.js and supports both LTS and Current versions.

Use the free OSS edition in order to implement and manage your own WebSocket server instances, or subscribe for our Pro plan and have us manage your instances and provide you with management of your methods, events and notifications on an easy-to-use Web Management portal.

Quick start

Install our OSS library in your project:

npm install rpc-websockets

Write your source code using rpc-websockets:

var WebSocket = require('rpc-websockets').Client
var WebSocketServer = require('rpc-websockets').Server

// instantiate Server and start listening for requests
var server = new WebSocketServer({
  port: 8080,
  host: 'localhost'
})

// register an RPC method
server.register('sum', function(params) {
  return params[0] + params[1]
})

// ...and maybe a protected one also
server.register('account', function() {
  return ['confi1', 'confi2']
}).protected()

// create an event
server.event('feedUpdated')

// get events
console.log(server.eventList())

// emit an event to subscribers
server.emit('feedUpdated')

// close the server
server.close()

// instantiate Client and connect to an RPC server
var ws = new WebSocket('ws://localhost:8080')

ws.on('open', function() {
  // call an RPC method with parameters
  ws.call('sum', [5, 3]).then(function(result) {
    require('assert').equal(result, 8)
  })

  // send a notification to an RPC server
  ws.notify('openedNewsModule')

  // subscribe to receive an event
  ws.subscribe('feedUpdated')

  ws.on('feedUpdated', function() {
    updateLogic()
  })

  // unsubscribe from an event
  ws.unsubscribe('feedUpdated')

  // login your client to be able to use protected methods
  ws.login({'username': 'confi1', 'password':'foobar'}).then(function() {
    ws.call('account').then(function(result) {
      require('assert').equal(result, ['confi1', 'confi2'])
    })
  }).catch(function(error) {
    console.log('auth failed')
  })

  // close a websocket connection
  ws.close()
})

Documentation

Please consult our API documentation for both WebSocket server and client JavaScript and TypeScript classes.

OSS Features

Features of the free open-source edition.

OSS Features

All library's open-source features are documented in our API documentation and can be used free of charge. You are free to implement your solutions based on provided methods in any way you are comfortable with, as long as you use our work along our very permissive license conditions.

Pro Features

In order to support your production-ready environments, we can provide you with additional features built on top of our free OSS edition along with the skill set to turn your business case or a Proof-of-Concept idea into reality.

Pro Features

Describe us your use case by contacting us and we will swiftly get back to you with a proposed solution that meets your needs.

Professional support

We offer professional support for rpc-websockets and beyond. We have many years of expertise on building robust, scalable Node.js applications and can help you overcome issues and challenges preventing you to ship your great products. We excel in software architecture and implementation, being able to provide you with development, planning, consulting, training and customization services. Feel free to contact us so we can discuss how to help you finish your products!

Users

rpc-websockets is being actively used in production by multiple companies in a variety of different use cases.


ScratchboxLoom NetworkuniqCastLeapDAOKlika Tech, Inc.Kodebox, Inc.Hey-Group S.A./N.V.Hylo, Inc.Witnet FoundationScale LeapCodice Foundation, Inc.Holo Ltd.Solana Labs, Inc.Filecoin

Sponsors

Become a sponsor and get your logo on project's README on GitHub with a link to your site. Feel free to contact us for the arrangement!

License

This library is licensed under LGPLv3. Please see LICENSE for licensing details.

rpc-websockets's People

Contributors

andrsyash avatar arsac avatar baso53 avatar basz avatar dependabot[bot] avatar dstreet avatar elmanderrr avatar iggyvolz avatar james-aurizn avatar jstarry avatar kalloc avatar lolsborn avatar macalinao avatar marvinthepa avatar mhw0 avatar mkozjak avatar noneghost avatar onlined avatar pfrazee avatar rishabhyadav09 avatar sisou avatar steveluscher avatar syvb avatar trasherdk avatar twiddler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rpc-websockets's Issues

Request headers

I want to do authentication by token in the headers of requests
How to take request headers?

Can I use rpc-websockets in a browser application?

Hi,

If I add the index.browser-bundle.js in the HTML page scripts, how do I instantiate the WebSocket? I tried var ws = WebSocket('ws://localhost:7777'); but I get ws.on is not a function later when I try calling ws.on('open',....)

Thanks!
Dinko

How to correctly handle UnhandledPromiseRejectionWarning?

I have a method registered that looks like this:

_server.register('delete', function(parameters) {
    const { path, headers } = parameters
    return mylibrary.delete(path, headers).catch((e) => {
        const newError = _mapErrorToRestError(e)
        throw newError
    })
})

When there is an error and the throw executes, I get the following in the console:

(node:2212620) UnhandledPromiseRejectionWarning: Error: WebSocket is not open: readyState 3 (CLOSED)
    at WebSocket.send (C:\Users\laughlcl\source\zookeeper\node_modules\rpc-websockets\node_modules\ws\lib\websocket.js:314:19)
    at Server._callee$ (C:\Users\laughlcl\source\zookeeper\node_modules\rpc-websockets\dist\lib\server.js:667:77)
    at tryCatch (C:\Users\laughlcl\source\zookeeper\node_modules\regenerator-runtime\runtime.js:65:40)
    at Generator.invoke [as _invoke] (C:\Users\laughlcl\source\zookeeper\node_modules\regenerator-runtime\runtime.js:299:22)
    at Generator.prototype.(anonymous function) [as next] (C:\Users\laughlcl\source\zookeeper\node_modules\regenerator-runtime\runtime.js:117:21)
    at step (C:\Users\laughlcl\source\zookeeper\node_modules\babel-runtime\helpers\asyncToGenerator.js:17:30)
    at C:\Users\laughlcl\source\zookeeper\node_modules\babel-runtime\helpers\asyncToGenerator.js:28:13

What I am attempting to do is throw an error that the client code can receive and handle. This is working properly as far as I can tell. But, in my server code, I'm getting that console output. Is there something else I need to do to throw an error for the client?

How do I know who sent a notification?

In README there is this line -

  ws.notify('openedNewsModule')

This is not useful if I can't know which client sent the message.
Is there any way of identifying the client besides using different Namespaces for each connection?

Socket id as parameter in registered methods

It would be really helpful if the socket id (or maybe the socket object?) could be used in the registered methods.

Currently, I'm using patch-package to make the necessary changes to get socketId passed as a parameter when the method is called.

rpc.register("sum", (params, socketId) => {
  return params[0] + params[1]
});

Support for server request and client response

We have a case when the server should make requests to the client. At this point this library do not have such functionality, while specification do not restrict regarding this.

Do you interested in merging such PR if we will implement it?

Does it work on Windows?

I paste the example as you have it on your README.md and run it and the only output is
[ 'feedUpdated' ]

Passing client options causes browser WebSocket to fail

When connecting to a client and including an options object:

new Client('ws://localhost', { reconnect: false })

the following error is thrown:

Error: Failed to construct 'WebSocket': The subprotocol '[object Object]' is invalid.

The browser implementation of WebSocket expects a protocol string or array of protocol strings as the second argument to the constructor.

client fails if trying to subscribe to an unregistered event

Investigate if we need an object of events when on server-side, since client's API for subscribe only takes a string parameter.

./node_modules/bluebird/js/release/async.js:61
fn = function () { throw arg; };
                           ^

Error: Failed subscribing to an event with: undefined
    at Client._callee$ (./node_modules/rpc-websockets/dist/lib/client.js:210:39)```

Incorrect index for arguments causes websocket upgrade to fail

this.options = arguments[1]
this.autoconnect = autoconnect
this.ready = false
this.reconnect = reconnect
this.reconnect_interval = reconnect_interval
this.max_reconnects = max_reconnects
this.current_reconnects = 0
this.generate_request_id = generate_request_id || (() => ++this.rpc_id)
if (this.autoconnect)
this._connect(this.address, this.options)
}

Line 83 appears to be causing the websocket upgrade to fail inside the ws library. When I create a new Client, arguments[1] is the websocket URL and not an options object. This results in my URL getting passed into the ws library where it is expecting an options object. Ultimately ws then fails to do the upgrade because it thinks I've specified a protocol, and the server I'm connecting to happens to not return a sec-websocket-protocol header.

https://github.com/websockets/ws/blob/6df06d9751937e1bed526644d468a5be31ef33cc/lib/websocket.js#L611-L621

When I evaluate arguments in my debugger it is set to the following:

0:function _default(address, options) { … }
1:"wss://mywebsocketurl..."
2:Object {autoconnect: true, reconnect: true, reconnect_interval: 1000, …}
3:undefined

Should line 83 really be this?
this.options = arguments[2]

I'm not sure how this is not causing wider spread issues though.

Thanks for looking at it!

rpc-websockets client fails to resolve localhost

Just spent several hours debugging rpc-websockets and eventually uWebsockets because my rpc-websocket client couldn't connect to localhost.

Eventually found the issue, if localhost is resolved as ::1 (localhost of ipv6) The underlying library uWebsockets will produce the following error:

{ message: 'uWs client connection error',
  stack: 'uWs client connection error' }

For now I suggest we link this issue to the bug existing issue in uWebsocket so other people can be aware of this issue.

Conflict with primus

still no idea why, but with this project imported, the primus will stop working. some hidden conflict between them each other happened.

Uncaught TypeError: WebSocketServer is not a constructor

I installed 'rpc-websockets',and wrote my source code follow the quick start,bu i get the follow error.
Uncaught TypeError: WebSocketServer is not a constructor...

var WebSocket = require('rpc-websockets').Client
var WebSocketServer = require('rpc-websockets').Server

// Uncaught TypeError: WebSocketServer is not a constructor...
var server = new WebSocketServer({
port: 8080,
host: 'localhost'
})
...

migrate to babel-preset-env from babel-preset-latest

npm WARN deprecated [email protected]: 💥 preset-latest accomplishes the same task as babel-preset-env. 🙏 Please install it with 'npm install babel-preset-env --save-dev'. '{ "presets": ["latest"] }' to '{ "presets": ["env"] }'. For more info, please check the docs: http://babeljs.io/docs/plugins/preset-env

Browser client

Hi,

Thanks for the great library!
I've written a small Node JS server, and I want to use it from my frontend running in the browser. Can I do that?
I saw a dist/index.browser-bundle.js file and included it in my index.html. How can I instantiate the client using this bundle? Normally I would do:
var WebSocket = require('rpc-websockets').Client
var ws = new WebSocket('ws://localhost:8080')
What's the equivalent if using the browser-bundle?

Thanks!

Dinko

Need help: How to "subscribe" to a feed?

I'm trying to use the this library to get market/trading data from HitBTC. (e.g. subscribing to ticker: https://api.hitbtc.com/#subscribe-to-ticker)

The problem is that I'm not 100% sure how I have to do that.

When I do the following it just prints "true" and doesn't print anything else.

ws.on('open', function() {
  ws.call('subscribeTicker', {
    "symbol": "ETHBTC", 
  }).then(function(result) {
    console.log(result);
  }).catch(console.log);
});

I dug into the code and saw that the library receives the updates from the server after ws.call('subscribeTicker', ...), but the messages don't get forwarded. When I add a console.log(message) here (https://github.com/qaap/rpc-websockets/blob/master/src/lib/client.js#L210) I get this response:

{ jsonrpc: '2.0',
  method: 'ticker',
  params:
   { ask: '0.102482',
     bid: '0.102339',
     last: '0.102483',
     open: '0.111648',
     low: '0.096804',
     high: '0.113327',
     volume: '70636.904',
     volumeQuote: '7539.892416786',
     timestamp: '2018-02-02T22:16:24.866Z',
     symbol: 'ETHBTC' } }
{ channel: 'ticker',
  type: 'update',
  data:
   { ask: '0.102482',
     bid: '0.102341',
     last: '0.102483',
     open: '0.111648',
     low: '0.096804',
     high: '0.113327',
     volume: '70636.904',
     volumeQuote: '7539.892416786',
     timestamp: '2018-02-02T22:16:24.966Z',
     symbol: 'ETHBTC' } }

There is no notification property nor an ID and it doesn't get handled. I hacked something together to expose an additional "callback" function and I was able to use that and it works fine. But that's an ugly hack I would like to avoid.

So my question is: Am I missing a feature of this library? Or did they simply implement their API in a very weird way that is not supported by this library?

Thanks for your help.

TypeScript support

Please add TypeScript support either via type declarations or rewriting in TypeScript.

Thanks!

browser support

We need client support for internet browsers.
Should we rely on an html5 websocket implementation and do a separate browser logic?

public connect-function for autoconnect:false

Hello,

thanks for your work on this Module. I have a question regarding the client options:
You can set autoconnect: false upon intialization of the WebSocket.Client. But where can I can actually call something like connect? In the implementation I only found "_connect", which does not seem to be intended as a public function.

Is there any reason for _connect being private?

migrate ws to 2.x

Currently the test 1 from server spec fails with:

(node:34693) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Not running
    1) should return a new instance


  1 failing

  1) Server should return a new instance:
     Uncaught Error: connect ECONNREFUSED 127.0.0.1:43010
      at Object.exports._errnoException (util.js:1022:11)
      at exports._exceptionWithHostPort (util.js:1045:20)
      at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1087:14)```

timeout on .call()

Maybe we should have a possibility to set a timeout after which the ws.call method should throw.

No Protocols Passed to `window.WebSocket`

Hello and thanks for this great JSON-RPC 2.0 implementation

Anyway i was looking around your implementation and found that in Client APIs the "protocols" is not being passed to "window.WebSocket".
Here at line 26

constructor(address: string, options: {}, protocols?: string | string[])
{
super()
this.socket = new window.WebSocket(address, protocols)

because protocols is always no passed from here at line 93

export default function(address: string, options: IWSClientAdditionalOptions)
{
return new WebSocketBrowserImpl(address, options)
}

I think we can add the protocols as new argument.

WS connection based context/session data

Is there some way to have authentication mechanism or have session data based on ws connection?

Basically I would like to register methods that are usable anonymously (eg. login) and methods that require authenticated connection (eg. getPermissions). So on client connect, when calling getPermissions I would get some error, but calling it after successful login, I would get my data.

This use-case could be achieved by passing socket (<ws.WebSocket>) to registered method handler as second argument. Then registered methods can set and check custom properties.

Currently only way to achieved this is to return some token with login and require it on getPermissions. But because ws connections are not shared, then if feels strange to pass some session tokens to methods.

Subscribing multiple events at once

Now I'm developing a RPC server by Java and it's using more than thousand events.
I would be gratefull if you could consider about following proposal.

ws.subscribe("event1", "event2",...,"eventN");
or
ws.subscribe(["event1", "event2",...,"eventN"]);

--> {"jsonrpc": "2.0", "method": "rpc.on", params:["event1", "event2",...,"eventN"], "id": 1}
<-- {"jsonrpc": "2.0", "result": {"event1": "ok", "event2": "ok",...,"eventN": "ok"}, "id": 1}

Client.reconnect bug

I'm trying to stop Client form attempting to reconnect by setting Client.reconnect = false. This doesn't work. I think the problem is here:
if (this.reconnect && (this.max_reconnects > this.current_reconnects) || this.max_reconnects === 0)
I think this should be written as: if (this.reconnect && ((this.max_reconnects > this.current_reconnects) || this.max_reconnects === 0))
Can you please fix it? Thanks.

Help regarding setting up the library

Hi Everyone.
I am very new to websockets and RPC protocol. I want to exchange the information between two servers. Came across this library. Does this library solve this purpose?

Anyway, I am using following code snippets for RPC server and client.

Server Side Code :

var WebSocket = require('rpc-websockets').Client
var WebSocketServer = require('rpc-websockets').Server
 
// instantiate Server and start listening for requests
var server = new WebSocketServer({
  port: 8082,
  host: 'localhost'
})
 
// register an RPC method
server.register('sum', function(params) {
  return params[0] + params[1]
})
 
// ...and maybe a protected one also
server.register('account', function() {
  return ['confi1', 'confi2']
}).protected()
 
// create an event
server.event('feedUpdated')
 
// get events
console.log(server.eventList())
 
// emit an event to subscribers
server.emit('feedUpdated')
 
// close the server
server.close()

Client Side Code

var WebSocket = require('rpc-websockets').Client
 
// instantiate Client and connect to an RPC server
var ws = new WebSocket('ws://localhost:8082')

ws.on('open', function() {
  // call an RPC method with parameters
  console.log('Connection opened')
  ws.call('sum', [5, 3]).then(function(result) {
    require('assert').equal(result, 8)
  })
 
  // send a notification to an RPC server
  ws.notify('openedNewsModule')
 
  // subscribe to receive an event
  ws.subscribe('feedUpdated')
 
  ws.on('feedUpdated', function() {
    updateLogic()
  })
 
  // unsubscribe from an event
  ws.unsubscribe('feedUpdated')
 
  // login your client to be able to use protected methods
  ws.login({'username': 'confi1', 'password':'foobar'}).then(function() {
    ws.call('account'),then(function(result) {
      require('assert').equal(result, ['confi1', 'confi2'])
    })
  })
 
  // close a websocket connection
  ws.close()
})

The issue is client is not able to connect to RPC server. ws.open function is not getting triggered. I am not sure whether issue is in server side or client side.

Thanks in advance for looking into the issue.

v6.x Uncaught (in promise) TypeError: cb is not a function

send(
data: any,
options?: { mask?: boolean; binary?: boolean; compress?: boolean; fin?: boolean } |
((err?: Error) => void),
cb?: (err?: Error) => void
)
{
if (
data &&
typeof data === "object" &&
!(data instanceof ArrayBuffer) &&
!(data instanceof Buffer)
)
{
data = CircularJSON.stringify(data)
}
// @ts-ignore
return this._socket.send(data, options, cb)
}

cb is optional parameter here, but this._socket.send require that cb is a function
So we should add default cb function here or require cb parameter

how to use?

What the hell is this? How to use this shit?
How can i separate the client and server side and why is the whole code of both sides in one file on the main page? Thank you very much!

ability to get connection

I want to save some per-connection variables. for example:

class RPCHandler {
  data;
  setMyData(data){
    this.data = data
  }
  getMyData(){
    return data
  }
}

but I can't find anyway to tell apart which connection when my rpc methods was called from.

Not working path in server options

var WebSocketServer = require("rpc-websockets").Server;
var wss = new WebSocketServer({path: "rpc", server: httpsServer });
var express = require('express');
var app = express();
var options = {
  key: fs.readFileSync('server.key').toString(),
 cert: fs.readFileSync('server.crt').toString(),
 }
var httpsServer = https.createServer(options, app);
httpsServer.listen(3000);

register methods via namespaces

The default namespace should be /.

Should each client which is connected to a specific namespace only be provided methods from that namespace?

namespace event emit arguments not correct

Here is the example code:

// server
var WebSocketServer = require('rpc-websockets').Server

var server = new WebSocketServer({
  port: 8080,
  host: 'localhost'
})
const ns = server.of('/test')
ns.event('test');

ns.emit('test', 'aaaa', 'bbbb', 'cccc');
//client
var WebSocket = require('rpc-websockets').Client

var ws = new WebSocket('ws://localhost:8080/test')

ws.on('open', function() {
  ws.subscribe('test');
  ws.on('test', (...args) => console.log(args, args.length));
});

Expected output:
['aaa', 'bbbb', 'cccc'], 3

Giving output:
['a', 'a', 'a', 'a'], 4

no issue when use server.emit

Add an event for client disconnect of namespace on server

Great thanks to this nice library.

I need to do some cleanup job after client disconnect from server. Is it possible to emit an event after client disconnect?

server.on('disconnect', (socket, request) => {
  // do something cleanup job
});

or

namespace.on('disconnect', (socket, request) => {
  // do something cleanup job
});

login middleware authenticates all clients

Hi,
Trying to understand how login middlewere should work.

Came across this code:

// if login middleware returned true, set connection as authenticated
        if (message.method === "rpc.login" && response === true)
            this.authenticated = true

Does it mean that after one client authenticates, all clients are authenticated automatically?

Server should check socket state before calling 'send' method

return socket.send(CircularJSON.stringify(response), msg_options)

In case if client socket closes the connection - 'send' will cause an unhandled rejection, which in some production environments might lead to application crash!

Mentioned problem is relevant to any other socket.send call.

Steps to reproduce:

  1. Call some long running rpc task
  2. Close client connection
  3. After task has finished - unhandledRejection will be triggered

Typo in the README

ws.call('account'),then(function(result) {

should be:

ws.call('account').then(function(result) {

(Comma changed to a period)

`Uncaught TypeError: WebSocketServer is not a constructor` in browser w/ webpack

We're working on a web app using Mithril.js and webpack. Currently getting this error: Uncaught TypeError: WebSocketServer is not a constructor when trying

const WebSocketServer = require('rpc-websockets').Server
const server = new WebSocketServer({port, host})

Locally, the rpc-websocket object looks like:
{ Server: [Getter], Client: [Function: Client] }

In the browser, I get:
{__esModule: true, Client: ƒ} (no Server)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.