2016-09-20 21:00:49 +03:00
|
|
|
# Azure Relay Hybrid Connections for Node.JS
|
|
|
|
|
2016-09-21 16:05:42 +03:00
|
|
|
This repository contains Node packages and samples for the Hybrid Connections feature of the
|
2016-09-20 21:00:49 +03:00
|
|
|
Microsoft Azure Relay, a capability pilar of the Azure Service Bus platform.
|
|
|
|
|
2016-09-21 16:05:42 +03:00
|
|
|
Hybrid Connections is a secure, open-protocol evolution of the existing Service Bus Relay
|
|
|
|
service that has been available in Azure since the beginning and handles millions of connections
|
|
|
|
daily.
|
2016-09-20 21:00:49 +03:00
|
|
|
|
|
|
|
Hybrid Connections allows establishing bi-directional, binary stream communication between
|
|
|
|
two networked applications, whereby either or both of the parties can reside behind NATs or
|
2016-09-21 16:05:42 +03:00
|
|
|
Firewalls. Hybrid Connections is based on HTTP(S) and WebSockets.
|
2016-09-20 21:00:49 +03:00
|
|
|
|
2016-09-21 16:05:42 +03:00
|
|
|
## How it works
|
2016-09-20 21:00:49 +03:00
|
|
|
|
|
|
|
For Node, the code in the repository allows a **publicly discoverable and reachable** WebSocket
|
|
|
|
server to be hosted on any machine that has outbound access to the Internet, and
|
|
|
|
specifically to the Microsoft Azure Relay service in the chosen region, via HTTPS port 443.
|
|
|
|
|
|
|
|
The WebSocket server code will look instantly familiar as it is directly based on and integrated
|
2016-09-21 16:05:42 +03:00
|
|
|
with two of the most popular existing WebSocket packages in the Node universe: "ws" and "websocket".
|
2016-09-20 21:00:49 +03:00
|
|
|
|
|
|
|
``` JS
|
|
|
|
|
|
|
|
require('ws') ==> require('hyco-ws')
|
|
|
|
require('websocket') ==> require('hyco-websocket')
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
As you create a WebSocket server using either of the alternate "hyco-ws" and "hyco-websocket"
|
2016-09-21 16:05:42 +03:00
|
|
|
packages from this repository, the server will not listen on a TCP port on the local network,
|
2016-09-20 21:00:49 +03:00
|
|
|
but rather delegate listening to a configured Hybrid Connection path the Azure Relay service
|
2016-09-21 16:05:42 +03:00
|
|
|
in Service Bus. The delegation happens by ways of opening and maintaining a "control connection"
|
|
|
|
WebSocket that remains opened and reconnects automatically when dropped inadvertently. This
|
|
|
|
listener connection is automatically TLS/SSL protected without you having to juggle any certificates.
|
2016-09-20 21:00:49 +03:00
|
|
|
|
2016-09-21 16:05:42 +03:00
|
|
|
### Servers
|
|
|
|
|
|
|
|
The snippet below shows the "ws"/"hyco-ws" variant of creating a server. The API is usage is
|
|
|
|
completely "normal" except for using the "hyco-ws" package and creating an instance of the
|
2016-09-20 21:00:49 +03:00
|
|
|
*RelayedServer* instead of *Server*. The default underlying *Server* class remains fully available
|
|
|
|
when using "hyco-ws" instead of "ws", meaning you can host a relayed and a local WebSocket
|
|
|
|
server side-by-side from within the same application. The "websocket"/"hyco-websocket"
|
2016-09-21 16:05:42 +03:00
|
|
|
experience is analogous and explained in the package's README.
|
2016-09-20 21:00:49 +03:00
|
|
|
|
|
|
|
``` JS
|
2016-09-20 22:24:31 +03:00
|
|
|
var WebSocket = require('hyco-ws');
|
2016-09-20 21:00:49 +03:00
|
|
|
|
|
|
|
var wss = WebSocket.RelayedServer(
|
|
|
|
{
|
|
|
|
// create the
|
|
|
|
server : WebSocket.createRelayListenUri(ns, path),
|
|
|
|
token: WebSocket.createRelayToken('http://'+ns, keyrule, key)
|
|
|
|
});
|
|
|
|
|
|
|
|
wss.on('connection', function (ws) {
|
|
|
|
console.log('connection accepted');
|
|
|
|
ws.onmessage = function (event) {
|
|
|
|
console.log(JSON.parse(event.data));
|
|
|
|
};
|
|
|
|
ws.on('close', function () {
|
|
|
|
console.log('connection closed');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
wss.on('error', function(err) {
|
|
|
|
console.log('error' + err);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Up to 25 WebSocket listeners can listen concurrently on the same Hybrid Connection path on the
|
|
|
|
Relay; if two or more listeners are connected, the service will automatically balance incoming
|
|
|
|
connection requests across the connected listeners. which also provides an easy failover capability.
|
|
|
|
You don't have to do anything to enable this, just have multiple listeners share the same path.
|
|
|
|
|
|
|
|
Clients connect to the server through the Relay service on the same path the listener is listening
|
2016-09-20 22:24:31 +03:00
|
|
|
on. The client uses the regular WebSocket protocol. WebSocket subprotocols and extensions can
|
|
|
|
be negotiated between client and the Web Socket server end-to-end as you would without the Relay.
|
2016-09-20 21:00:49 +03:00
|
|
|
|
2016-09-21 16:05:42 +03:00
|
|
|
What happens under the covers, as you can find if you poke around in the code of the two packages, is
|
|
|
|
that any connection that is from a client to the Relay service will be announced to the Listener
|
|
|
|
with a control message over the open control channel. The control message contains information about
|
|
|
|
a "rendezvous endpoint" that is valid for a brief period. The server framework will decide whether
|
|
|
|
to accept the incoming connection, potentially including calling some extensibility hooks, and
|
|
|
|
then open an outbound WebSocket to the rendezvous endpoint. The client WebSocket and this "data"
|
|
|
|
WenSocket are then bound into a single end-to-end connection by the Relay service, behaving like
|
|
|
|
a single WebSocket.
|
|
|
|
|
|
|
|
### Clients
|
|
|
|
|
2016-09-20 22:24:31 +03:00
|
|
|
If the Relay requires a sender token (which is the default), that token can be included either
|
2016-09-21 16:05:42 +03:00
|
|
|
as a query parameter ('sb-hc-token') or with the 'ServiceBusAuthorization' HTTP header. The latter is
|
2016-09-20 22:24:31 +03:00
|
|
|
preferred; mostly since URLs end up in many logs.
|
|
|
|
|
|
|
|
``` JS
|
|
|
|
var WebSocket = require('hyco-ws');
|
2016-09-20 21:00:49 +03:00
|
|
|
|
2016-09-20 22:24:31 +03:00
|
|
|
var opt = { headers : { 'ServiceBusAuthorization' : token}};
|
|
|
|
var address = WebSocket.createRelaySendUri(ns, path),
|
2016-09-20 21:00:49 +03:00
|
|
|
|
2016-09-20 22:24:31 +03:00
|
|
|
var client = new WebSocket(address, null, opt);
|
|
|
|
client.on('open', function(wss) {
|
2016-11-01 01:30:07 +03:00
|
|
|
wss.send("Hi!");
|
2016-09-20 22:24:31 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
```
|
2016-09-20 21:00:49 +03:00
|
|
|
|
2016-09-21 16:05:42 +03:00
|
|
|
The standard WebSocket client that is built into current browsers doesn't support setting
|
|
|
|
the headers for the HTTP handshake, so you'll have to use the query string parameter. The
|
|
|
|
snippet below is from the modified "serverstats" sample included in this repo that leans
|
|
|
|
on the similar sample from the "ws" package. The placeholders in the WebSocket URI are
|
|
|
|
replaced with the correct values for namespace, path, and token using a template engine.
|
|
|
|
|
|
|
|
``` HTML
|
|
|
|
<script>
|
|
|
|
function updateStats(memuse) {
|
|
|
|
document.getElementById('rss').innerHTML = memuse.rss;
|
|
|
|
document.getElementById('heapTotal').innerHTML = memuse.heapTotal;
|
|
|
|
document.getElementById('heapUsed').innerHTML = memuse.heapUsed;
|
|
|
|
}
|
|
|
|
|
|
|
|
var host = window.document.location.host.replace(/:.*/, '');
|
2016-09-23 17:07:39 +03:00
|
|
|
var ws = new WebSocket('wss://{{ns}}:443/$hc/{{path}}?'+
|
2016-09-21 16:05:42 +03:00
|
|
|
'sb-hc-action=connect&sb-hc-token={{token}}');
|
|
|
|
ws.onmessage = function (event) {
|
|
|
|
updateStats(JSON.parse(event.data));
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
```
|
|
|
|
|
|
|
|
## packages
|
|
|
|
|
|
|
|
The README documents for the two includes packages discuss the particular additions made
|
|
|
|
to accomodate support for Hybrid Connections. What's common for both libraries is that
|
|
|
|
you can use the 'hyco-ws' and the 'hyco-websocket' packages instead of the 'ws' and 'websocket'
|
|
|
|
without losing any existing functionality. Both packages containe to expose the full and unaltered
|
|
|
|
functionality of their respective base packages.
|
|
|
|
|
|
|
|
* [README for hyco-ws](./hyco-ws/README.md)
|
|
|
|
* [README for hyco-websocket](./hyco-websocket/README.md)
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
The repo contains local examples at the package level and a few global examples.
|
|
|
|
[The global samples in /examples](/examples/README.md) use the latest, public, npm-published versions
|
|
|
|
of the packages and require that you install all dependencies with "npm install".
|
|
|
|
|
|
|
|
The local examples under [hyco-ws](./hyco-ws) and [hyco-websocket](./hyco-websocket) reference the
|
|
|
|
code in your checked out repo.
|
2016-09-20 21:00:49 +03:00
|
|
|
|