Skip to content

Store custom client ID on Agent/Connection ID #224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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 `<clientId>:<randomId>`

### Middlewares

Middlewares let you hook into the ShareDB server pipeline. In
Expand Down
5 changes: 3 additions & 2 deletions lib/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 14 additions & 11 deletions lib/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,32 +109,35 @@ 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);
} else {
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
connection.agent = agent;
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: <clientId>:<randomId>
* @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();
Expand Down
32 changes: 32 additions & 0 deletions test/client/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down