Skip to main content
Build a gRPC Processor that handles SIP MESSAGE requests (instant messaging), then register it in the Message Dispatcher next to the Connect processor. A Processor receives every SIP message as a protobuf MessageRequest, decides where it goes, and returns the modified message. It is the last stop before EdgePort sends the message out. Use one when the built-in Connect processor doesn’t fit, for example to build an IM service, SCAIP logic, or an IoT integration.

Prerequisites

  • Node.js and npm.
  • A running Routr core: an EdgePort, a Message Dispatcher, and a Location Service. Running Routr from source gives you all three.
  • Familiarity with the message flow.
The finished example lives in the nodejs-processor template. Run npx degit fonoster/nodejs-processor my-processor to start from it instead of from scratch.

The processor contract

Every Processor implements one RPC, defined in processor.proto:
The request carries everything the Processor needs (sender, listening points, networks), so a Processor holds no state and scales horizontally. You can write one in any language with gRPC support. In Node.js, @routr/processor hands you the request as JSON. This excerpt shows the shape of a REGISTER:

Build the IM processor

1

Create the project

Create a folder and install the Processor library, the Location Service client, and fp-ts for pipe:
2

Start the gRPC server

Create index.js. It connects to the Location Service and starts a Processor on port 51904 that sends REGISTER and MESSAGE to handlers and rejects everything else:
index.js
listen() calls your function once per message with the MessageRequest and a Response. You must answer every call with one of the Response methods:
3

Handle REGISTER

Create handlers.js. The register handler stores the sender’s route in the Location Service under the target AOR and replies 200 OK:
handlers.js
Always pass maxContacts. Over gRPC an omitted value arrives as 0, and the Location Service rejects new contacts once the AOR holds 0 or more of them. Use -1 for no limit.
4

Handle MESSAGE with the Alterations API

Replace the sendMessage stub. It forwards responses back, looks up the callee, and builds the outgoing message by piping the request through alterations. Each alteration takes a MessageRequest and returns a modified copy:
handlers.js
The two branches implement EdgePort cooperation:
  • addSelfVia(route) adds a Via for the egress EdgePort interface.
  • addSelfRecordRoute(route) keeps this EdgePort in the path for the rest of the dialog.
  • addRouteToPeerEdgePort(route) adds a Route to the EdgePort that holds the callee’s connection.
  • addRouteToNextHop(route) adds a Route to the callee.
  • addXEdgePortRef adds the X-Edgeport-Ref header; removeXEdgePortRef strips it on the last hop.
  • decreaseMaxForwards, removeAuthorization, and removeSelfRoutes clean up the request before delivery.
5

Run the processor

Start it:
The library logs a line like starting routr service {"name":"instant-messaging","bindAddr":"0.0.0.0:51904"}.

Register the processor in the Dispatcher

The Message Dispatcher sends each request to the first processor whose matchFunc returns true and whose methods include the request method. Order matters: put specific matches before catch-alls. This dispatcher.yaml sends MESSAGE to the IM processor and everything else to Connect:
dispatcher.yaml
matchFunc is a JavaScript expression evaluated against the JSON MessageRequest. Other useful matches:
Set addr to wherever your processor runs. When you run from source, edit config/dispatcher.yaml and restart with npm run start:dispatcher. On Kubernetes, add the entry under dispatcher.processors in the Helm values.
The Dispatcher is optional. For a single processor you can point EdgePort’s spec.processor.addr straight at it and list the methods it handles in spec.methods.

Verify

Register two softphones (for example Blink) against your EdgePort and send a chat message from one to the other. The message reaches the second phone, and the processor’s console shows the requests it handled. A MESSAGE to an unregistered user returns 404 Not Found.

Pitfalls

  • Keep processors stateless. Store state in the Location Service or the API Server, not in memory, so you can run several replicas.
  • Answer every call. A handler that throws without calling a Response method leaves the transaction hanging; wrap handlers in try/catch and call sendInternalServerError().
  • Alterations don’t mutate. They return a deep copy, so always send the result of pipe, not the original req.

Alterations API

Every alteration and helper exported by @routr/processor.

Build a middleware

Add side effects before or after the processor.

Message Dispatcher

Full Dispatcher configuration.

Message flow

How a request moves through Routr.