diff --git a/README.md b/README.md index 6e65f0187..e85811205 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ var socket = new WebSocket('ws://' + window.location.host); var connection = new sharedb.Connection(socket); ``` -The native Websocket object that you feed to ShareDB's `Connection` constructor **does not** handle reconnections. +The native Websocket object that you feed to ShareDB's `Connection` constructor **does not** handle reconnections. The easiest way is to give it a WebSocket object that does reconnect. There are plenty of example on the web. The most important thing is that the custom reconnecting websocket, must have the same API as the native rfc6455 version. @@ -124,6 +124,12 @@ For transports other than WebSockets, expose a duplex stream that writes and reads JavaScript objects. Then pass that stream directly into `share.listen`. +The `listen` method accepts the following arguments: + + - `stream` - the stream to listen to for messages from the client + - `req` (optional) - an initial request which is passed through to any `connect` middleware. This is useful for inspecting cookies; Express session; etc. on the request object in the middleware + - `clientId` (optional) - an identifier for the connecting client. This will be prepended to the connection ID, and can be used to identify the source client of an operation. The `src` field will look like `:` + ### Middlewares Middlewares let you hook into the ShareDB server pipeline. In diff --git a/lib/agent.js b/lib/agent.js index d1a944de4..80b2982d8 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -11,11 +11,12 @@ var types = require('./types'); * @param {Backend} backend * @param {Duplex} stream connection to a client */ -function Agent(backend, stream) { +function Agent(backend, stream, clientIdPrefix) { this.backend = backend; this.stream = stream; - this.clientId = hat(); + clientIdPrefix = typeof clientIdPrefix === 'string' ? (clientIdPrefix + ':') : ''; + this.clientId = clientIdPrefix + hat(); this.connectTime = Date.now(); // We need to track which documents are subscribed by the client. This is a diff --git a/lib/backend.js b/lib/backend.js index 3156cfc82..b376b34df 100644 --- a/lib/backend.js +++ b/lib/backend.js @@ -109,7 +109,7 @@ Backend.prototype.close = function(callback) { finish(); }; -Backend.prototype.connect = function(connection, req) { +Backend.prototype.connect = function(connection, req, clientId) { var socket = new StreamSocket(); if (connection) { connection.bindToSocket(socket); @@ -117,7 +117,7 @@ Backend.prototype.connect = function(connection, req) { connection = new Connection(socket); } socket._open(); - var agent = this.listen(socket.stream, req); + var agent = this.listen(socket.stream, req, clientId); // Store a reference to the agent on the connection for convenience. This is // not used internal to ShareDB, but it is handy for server-side only user // code that may cache state on the agent and read it in middleware @@ -125,16 +125,19 @@ Backend.prototype.connect = function(connection, req) { return connection; }; -/** A client has connected through the specified stream. Listen for messages. - * - * The optional second argument (req) is an initial request which is passed - * through to any connect() middleware. This is useful for inspecting cookies - * or an express session or whatever on the request object in your middleware. - * - * (The agent is available through all middleware) +/** + * @param stream - the stream to listen to for messages from the client + * @param req (optional) - an initial request which is passed through to any connect() + * middleware. This is useful for inspecting cookies or an express + * session or whatever on the request object in your middleware + * @param clientId (optional) - an identifier for the connecting client. This will be + * prepended to the connection ID, and can be used to identify the source client + * of an operation. The src field will look like: : + * @returns agent - the instance of the connected agent, which is available through all + * middleware */ -Backend.prototype.listen = function(stream, req) { - var agent = new Agent(this, stream); +Backend.prototype.listen = function(stream, req, clientId) { + var agent = new Agent(this, stream, clientId); this.trigger(this.MIDDLEWARE_ACTIONS.connect, agent, {stream: stream, req: req}, function(err) { if (err) return agent.close(err); agent._open(); diff --git a/test/client/connection.js b/test/client/connection.js index 677e87e44..73da8a9c8 100644 --- a/test/client/connection.js +++ b/test/client/connection.js @@ -65,6 +65,38 @@ describe('client connection', function() { connection.socket.onerror({message: 'Test'}); }); + describe('specifying a custom client ID suffix', function () { + it('can have a custom client ID suffix specified', function (done) { + this.backend.use('connect', function (request, next) { + var idSegments = request.agent.clientId.split(':'); + expect(idSegments[0]).equal('abc'); + done(); + }); + + this.backend.connect(undefined, undefined, 'abc'); + }); + + it('ignores an empty client ID suffix', function (done) { + this.backend.use('connect', function (request, next) { + var idSegments = request.agent.clientId.split(':'); + expect(idSegments.length).equal(1); + done(); + }); + + this.backend.connect(); + }); + + it('ignores a non-string client ID suffix', function (done) { + this.backend.use('connect', function (request, next) { + var idSegments = request.agent.clientId.split(':'); + expect(idSegments.length).equal(1); + done(); + }); + + this.backend.connect(undefined, undefined, 123); + }); + }); + describe('backend.agentsCount', function() { it('updates after connect and connection.close()', function(done) { var backend = this.backend;