Skip to content

Commit feeb831

Browse files
author
Alec Gibson
committed
Store custom client ID on Agent/Connection ID
It can be useful to identify which connection submitted what operation. This is already possible to some extent by tracking the ID randomly generated when instantiating `Agent`. However, this ID obviously changes on every connection, and bears no relation to the actual client that is connecting. This change allows the client to specify some ID when connecting, which will be concatenated on to the agent's ID like this: `<randomId>:<clientId>` Keeping the `randomId` ensures that the IDs remain unique, and concatenating the `clientId` allows the consumer to later determine which operations were submitted by the client that matches that ID, by checking the `src` field stored on an operation.
1 parent 762e05d commit feeb831

File tree

3 files changed

+49
-13
lines changed

3 files changed

+49
-13
lines changed

lib/agent.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ var types = require('./types');
1111
* @param {Backend} backend
1212
* @param {Duplex} stream connection to a client
1313
*/
14-
function Agent(backend, stream) {
14+
function Agent(backend, stream, clientIdSuffix) {
1515
this.backend = backend;
1616
this.stream = stream;
1717

18-
this.clientId = hat();
18+
clientIdSuffix = typeof clientIdSuffix === 'string' ? (':' + clientIdSuffix) : '';
19+
this.clientId = hat() + clientIdSuffix;
1920
this.connectTime = Date.now();
2021

2122
// We need to track which documents are subscribed by the client. This is a

lib/backend.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,32 +109,35 @@ Backend.prototype.close = function(callback) {
109109
finish();
110110
};
111111

112-
Backend.prototype.connect = function(connection, req) {
112+
Backend.prototype.connect = function(connection, req, clientId) {
113113
var socket = new StreamSocket();
114114
if (connection) {
115115
connection.bindToSocket(socket);
116116
} else {
117117
connection = new Connection(socket);
118118
}
119119
socket._open();
120-
var agent = this.listen(socket.stream, req);
120+
var agent = this.listen(socket.stream, req, clientId);
121121
// Store a reference to the agent on the connection for convenience. This is
122122
// not used internal to ShareDB, but it is handy for server-side only user
123123
// code that may cache state on the agent and read it in middleware
124124
connection.agent = agent;
125125
return connection;
126126
};
127127

128-
/** A client has connected through the specified stream. Listen for messages.
129-
*
130-
* The optional second argument (req) is an initial request which is passed
131-
* through to any connect() middleware. This is useful for inspecting cookies
132-
* or an express session or whatever on the request object in your middleware.
133-
*
134-
* (The agent is available through all middleware)
128+
/**
129+
* @param stream - the stream to listen to for messages from the client
130+
* @param req (optional) - an initial request which is passed through to any connect()
131+
* middleware(). This is useful for inspecting cookies or an express
132+
* session or whatever on the request object in your middleware
133+
* @param clientId (optional) - an identifier for the connecting client. This will be
134+
* appended to the connection ID, and can be used to identify the source client
135+
* of an operation. It will look like: <randomId>:<clientId>
136+
* @returns agent - the instance of the connected agent, which is available through all
137+
* middleware
135138
*/
136-
Backend.prototype.listen = function(stream, req) {
137-
var agent = new Agent(this, stream);
139+
Backend.prototype.listen = function(stream, req, clientId) {
140+
var agent = new Agent(this, stream, clientId);
138141
this.trigger(this.MIDDLEWARE_ACTIONS.connect, agent, {stream: stream, req: req}, function(err) {
139142
if (err) return agent.close(err);
140143
agent._open();

test/client/connection.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,38 @@ describe('client connection', function() {
6565
connection.socket.onerror({message: 'Test'});
6666
});
6767

68+
describe('specifying a custom client ID suffix', function () {
69+
it('can have a custom client ID suffix specified', function (done) {
70+
this.backend.use('connect', function (request, next) {
71+
var idSegments = request.agent.clientId.split(':');
72+
expect(idSegments[1]).equal('abc');
73+
done();
74+
});
75+
76+
this.backend.connect(undefined, undefined, 'abc');
77+
});
78+
79+
it('ignores an empty client ID suffix', function (done) {
80+
this.backend.use('connect', function (request, next) {
81+
var idSegments = request.agent.clientId.split(':');
82+
expect(idSegments.length).equal(1);
83+
done();
84+
});
85+
86+
this.backend.connect();
87+
});
88+
89+
it('ignores a non-string client ID suffix', function (done) {
90+
this.backend.use('connect', function (request, next) {
91+
var idSegments = request.agent.clientId.split(':');
92+
expect(idSegments.length).equal(1);
93+
done();
94+
});
95+
96+
this.backend.connect(undefined, undefined, 123);
97+
});
98+
});
99+
68100
describe('backend.agentsCount', function() {
69101
it('updates after connect and connection.close()', function(done) {
70102
var backend = this.backend;

0 commit comments

Comments
 (0)