Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
💫 Easy RPC over RabbitMQ
Easily implement RPC over your RabbitMQ broker with a few lines of code:
import { RpcClient, RpcServer } from 'mqrpc'
const server = new RpcServer({ amqpClient: { amqpUrl: 'amqp://localhost '} })
const client = new RpcClient({ amqpClient: { amqpUrl: 'amqp://localhost '} })
server.register('math.add', (a, b) => a + b)
await server.init()
await client.init()
await client.call('math.add', 2, 2) // 4
MQRPC leverages RabbitMQs Direct reply-to functionality to implement an RPC system with reasonable defaults that work out-of-the box. All you need is a RabbitMQ broker and its URL.
Both Server & Client are designed to be simple to use and thus have a low-surface API. The following type definitions follow TypeScript syntax loosely:
new RpcServer({ amqpClient: AmqpOpts, rpcServer?: ServerOpts })
Instances a new server with the given config. amqpClient
is required:
type AmqpOpts = {
connection?: amqplib.Connection // Pass a live amqplib connection here to re-use it.
amqpUrl?: string // The RabbitMQ URL. Ignored if `connection` is provided.
socketOptions?: object // Customize connection to RabbitMQ.
prefetchCount: number // Customize consumer prefetch count.
}
type ServerOpts = {
rpcExchangeName?: string // Exchange name for server, defaults to 'mqrpc'.
logger?: object // For custom logger injection.
}
Although all configs are optional, one of amqpClient.connection
or amqpClient.amqpUrl
must be passed.
async server.init()
Declares all the exchanges, queues and bindings for the server. Starts listening for calls from clients, so you should call this after registering all available procedures.
server.register(procedure: string, handler: Function)
Registers a new procedure and its handler in the server. The handler can be synchronous or return a Promise.
server.register('util.echo', arg => arg)
server.register('time.now', () => Date.now())
server.register('math.average', (...args) => args.reduce((acc, val) => acc + val) / args.length)
server.register('meaning.of.life', async () => 42)
register
should be called before init
to ensure the server won't receive any unknown calls by clients that are already live.
server.registerDebugProcedures()
Adds a default procedure for debugging, with name mqrpc.echo
, that returns any given argument.
async server.term()
Neatly shut down the server. Closes the AMQP channel and, if one wasn't provided, the connection as well.
new RpcClient({ amqpClient: AmqpOpts, rpcClient?: ClientOpts })
Instances a new client with the given config. amqpClient
is required:
type AmqpOpts = {
connection?: amqplib.Connection // Pass a live amqplib connection here to re-use it.
amqpUrl?: string // The RabbitMQ URL. Ignored if `connection` is provided.
socketOptions?: object // Customize connection to RabbitMQ.
prefetchCount: number // Customize consumer prefetch count.
}
type ClientOpts = {
rpcExchangeName?: string // Exchange name for server, defaults to 'mqrpc'.
logger?: object // For custom logger injection.
ackTimeout?: number // How long should the client wait for a Server to start working on a call. Default 0 (no timeout).
idleTimeout?: number // How long can the server be idle until it is considered "dead". Default 0 (no timeout).
callTimeout?: number // Maximum time from making a call to receiving a reply. Default 900 000 (15 minutes).
}
Although all configs are optional, one of amqpClient.connection
or amqpClient.amqpUrl
must be passed. Every timeout is in milliseconds and will throw TimeoutExpired
when breached. See timeouts below for more info.
async client.init()
Connects to RabbitMQ and starts listening for replies from servers.
async client.call(procedure: string, ...args: any[])
Calls the named procedure
on an RpcServer with the given args
. Resolves to whatever the procedure returns. Rejects if the procedure throws, or there is a connection error or an error in the server.
The following error types may be thrown:
ProcedureFailed
: The most common (hopefully), is thrown when the procedure itself throws. The remote error stack may be included in error.causeStack
.ServerError
: When an error occurs in the server while processing the call. Eg: the requested procedure is not registered.UnparseableContent
: Whatever reply we got from a server could not be parsed.UnknownReply
: Reply was parseable, but the format isn't understood.async client.term()
Neatly shut down the client. Closes the AMQP channel and, if one wasn't provided, the connection as well.
Since it may not be sensible to wait forever for a call to resolve, the client exposes three configurable timeouts that will interrupt a call when expired. These are:
ackTimeout
When a server receives a procedure call it will send an ack
message back to the client, immediately before beginning execution. This signals the client that a server is handling their call. This timeout signals how long to wait until the ack
is received.
This timeout is disabled by default, since it's sensible to expect a server will eventually pick up a client's call. However, it may be used to control for times of high message congestion, for example.
idleTimeout
While the server is executing a procedure, it'll periodically send wait
messages back to the client (behind the scenes). This works as a heartbeat of sorts and indicates to the client that the server hasn't crashed, or in some way disconnected. This timeout indicates how long a server may be silent before aborting the call.
This timeout is disabled by default, since RabbitMQ has its own hearbeat functionality that, in conjunction with its own ack
mode, guarantees at-least-once execution. You may enable this if operating in noAck
mode.
callTimeout
The overall maximum time a call may take to resolve a request. This timeout starts on a procedure call and terminates when a reply is received.
This timeout is 15 minutes by default.
You'll need a local RabbitMQ broker to run the tests. Optionally set env var RABBITMQ_VHOST
to specify a vhost, uses /
by default. Then:
$ yarn test
Feel free to submit PRs. Please include unit tests for any new features.
Because I wanted to try it out ¯\(ツ)/¯
FAQs
💫 Easy RPC over RabbitMQ
The npm package mqrpc receives a total of 1 weekly downloads. As such, mqrpc popularity was classified as not popular.
We found that mqrpc demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.