Skip to content

Commit 7096e98

Browse files
feat: add a "connection_error" event
The "connection_error" event will be emitted when one of the following errors occurs: - Transport unknown - Session ID unknown - Bad handshake method - Bad request - Forbidden - Unsupported protocol version Syntax: ```js server.on("connection_error", (err) => { console.log(err.req); // the request object console.log(err.code); // the error code, for example 1 console.log(err.message); // the error message, for example "Session ID unknown" console.log(err.context); // some additional error context }); ``` Related: - socketio/socket.io#3819 - #576
1 parent 887ba06 commit 7096e98

File tree

4 files changed

+305
-65
lines changed

4 files changed

+305
-65
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,24 @@ The main server/manager. _Inherits from EventEmitter_.
204204
- Fired when a new connection is established.
205205
- **Arguments**
206206
- `Socket`: a Socket object
207+
- `connection_error`
208+
- Fired when an error occurs when establishing the connection.
209+
- **Arguments**
210+
- `error`: an object with following properties:
211+
- `req` (`http.IncomingMessage`): the request that was dropped
212+
- `code` (`Number`): one of `Server.errors`
213+
- `message` (`string`): one of `Server.errorMessages`
214+
- `context` (`Object`): extra info about the error
215+
216+
| Code | Message |
217+
| ---- | ------- |
218+
| 0 | "Transport unknown"
219+
| 1 | "Session ID unknown"
220+
| 2 | "Bad handshake method"
221+
| 3 | "Bad request"
222+
| 4 | "Forbidden"
223+
| 5 | "Unsupported protocol version"
224+
207225

208226
##### Properties
209227

lib/server.js

Lines changed: 102 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -109,37 +109,60 @@ class Server extends EventEmitter {
109109
const transport = req._query.transport;
110110
if (!~this.opts.transports.indexOf(transport)) {
111111
debug('unknown transport "%s"', transport);
112-
return fn(Server.errors.UNKNOWN_TRANSPORT, false);
112+
return fn(Server.errors.UNKNOWN_TRANSPORT, { transport });
113113
}
114114

115115
// 'Origin' header check
116116
const isOriginInvalid = checkInvalidHeaderChar(req.headers.origin);
117117
if (isOriginInvalid) {
118+
const origin = req.headers.origin;
118119
req.headers.origin = null;
119120
debug("origin header invalid");
120-
return fn(Server.errors.BAD_REQUEST, false);
121+
return fn(Server.errors.BAD_REQUEST, {
122+
name: "INVALID_ORIGIN",
123+
origin
124+
});
121125
}
122126

123127
// sid check
124128
const sid = req._query.sid;
125129
if (sid) {
126130
if (!this.clients.hasOwnProperty(sid)) {
127131
debug('unknown sid "%s"', sid);
128-
return fn(Server.errors.UNKNOWN_SID, false);
132+
return fn(Server.errors.UNKNOWN_SID, {
133+
sid
134+
});
129135
}
130-
if (!upgrade && this.clients[sid].transport.name !== transport) {
136+
const previousTransport = this.clients[sid].transport.name;
137+
if (!upgrade && previousTransport !== transport) {
131138
debug("bad request: unexpected transport without upgrade");
132-
return fn(Server.errors.BAD_REQUEST, false);
139+
return fn(Server.errors.BAD_REQUEST, {
140+
name: "TRANSPORT_MISMATCH",
141+
transport,
142+
previousTransport
143+
});
133144
}
134145
} else {
135146
// handshake is GET only
136-
if ("GET" !== req.method)
137-
return fn(Server.errors.BAD_HANDSHAKE_METHOD, false);
138-
if (!this.opts.allowRequest) return fn(null, true);
139-
return this.opts.allowRequest(req, fn);
147+
if ("GET" !== req.method) {
148+
return fn(Server.errors.BAD_HANDSHAKE_METHOD, {
149+
method: req.method
150+
});
151+
}
152+
153+
if (!this.opts.allowRequest) return fn();
154+
155+
return this.opts.allowRequest(req, (message, success) => {
156+
if (!success) {
157+
return fn(Server.errors.FORBIDDEN, {
158+
message
159+
});
160+
}
161+
fn();
162+
});
140163
}
141164

142-
fn(null, true);
165+
fn();
143166
}
144167

145168
/**
@@ -186,9 +209,15 @@ class Server extends EventEmitter {
186209
this.prepare(req);
187210
req.res = res;
188211

189-
const callback = (err, success) => {
190-
if (!success) {
191-
sendErrorMessage(req, res, err);
212+
const callback = (errorCode, errorContext) => {
213+
if (errorCode !== undefined) {
214+
this.emit("connection_error", {
215+
req,
216+
code: errorCode,
217+
message: Server.errorMessages[errorCode],
218+
context: errorContext
219+
});
220+
sendErrorMessage(req, res, errorCode, errorContext);
192221
return;
193222
}
194223

@@ -231,6 +260,15 @@ class Server extends EventEmitter {
231260
const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default
232261
if (protocol === 3 && !this.opts.allowEIO3) {
233262
debug("unsupported protocol version");
263+
this.emit("connection_error", {
264+
req,
265+
code: Server.errors.UNSUPPORTED_PROTOCOL_VERSION,
266+
message:
267+
Server.errorMessages[Server.errors.UNSUPPORTED_PROTOCOL_VERSION],
268+
context: {
269+
protocol
270+
}
271+
});
234272
sendErrorMessage(
235273
req,
236274
req.res,
@@ -244,6 +282,15 @@ class Server extends EventEmitter {
244282
id = await this.generateId(req);
245283
} catch (e) {
246284
debug("error while generating an id");
285+
this.emit("connection_error", {
286+
req,
287+
code: Server.errors.BAD_REQUEST,
288+
message: Server.errorMessages[Server.errors.BAD_REQUEST],
289+
context: {
290+
name: "ID_GENERATION_ERROR",
291+
error: e
292+
}
293+
});
247294
sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST);
248295
return;
249296
}
@@ -266,6 +313,15 @@ class Server extends EventEmitter {
266313
}
267314
} catch (e) {
268315
debug('error handshaking to transport "%s"', transportName);
316+
this.emit("connection_error", {
317+
req,
318+
code: Server.errors.BAD_REQUEST,
319+
message: Server.errorMessages[Server.errors.BAD_REQUEST],
320+
context: {
321+
name: "TRANSPORT_HANDSHAKE_ERROR",
322+
error: e
323+
}
324+
});
269325
sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST);
270326
return;
271327
}
@@ -304,9 +360,15 @@ class Server extends EventEmitter {
304360
this.prepare(req);
305361

306362
const self = this;
307-
this.verify(req, true, function(err, success) {
308-
if (!success) {
309-
abortConnection(socket, err);
363+
this.verify(req, true, (errorCode, errorContext) => {
364+
if (errorCode) {
365+
this.emit("connection_error", {
366+
req,
367+
code: errorCode,
368+
message: Server.errorMessages[errorCode],
369+
context: errorContext
370+
});
371+
abortConnection(socket, errorCode, errorContext);
310372
return;
311373
}
312374

@@ -469,52 +531,46 @@ Server.errorMessages = {
469531
/**
470532
* Sends an Engine.IO Error Message
471533
*
472-
* @param {http.ServerResponse} response
473-
* @param {code} error code
534+
* @param req - the request object
535+
* @param res - the response object
536+
* @param errorCode - the error code
537+
* @param errorContext - additional error context
538+
*
474539
* @api private
475540
*/
476541

477-
function sendErrorMessage(req, res, code) {
478-
const headers = { "Content-Type": "application/json" };
479-
480-
const isForbidden = !Server.errorMessages.hasOwnProperty(code);
481-
if (isForbidden) {
482-
res.writeHead(403, headers);
483-
res.end(
484-
JSON.stringify({
485-
code: Server.errors.FORBIDDEN,
486-
message: code || Server.errorMessages[Server.errors.FORBIDDEN]
487-
})
488-
);
489-
return;
490-
}
491-
if (res !== undefined) {
492-
res.writeHead(400, headers);
493-
res.end(
494-
JSON.stringify({
495-
code: code,
496-
message: Server.errorMessages[code]
497-
})
498-
);
499-
}
542+
function sendErrorMessage(req, res, errorCode, errorContext) {
543+
const statusCode = errorCode === Server.errors.FORBIDDEN ? 403 : 400;
544+
const message =
545+
errorContext && errorContext.message
546+
? errorContext.message
547+
: Server.errorMessages[errorCode];
548+
549+
res.writeHead(statusCode, { "Content-Type": "application/json" });
550+
res.end(
551+
JSON.stringify({
552+
code: errorCode,
553+
message
554+
})
555+
);
500556
}
501557

502558
/**
503559
* Closes the connection
504560
*
505561
* @param {net.Socket} socket
506-
* @param {code} error code
562+
* @param {string} errorCode - the error code
563+
* @param {object} errorContext - additional error context
564+
*
507565
* @api private
508566
*/
509567

510-
function abortConnection(socket, code) {
568+
function abortConnection(socket, errorCode, errorContext) {
511569
socket.on("error", () => {
512570
debug("ignoring error from closed connection");
513571
});
514572
if (socket.writable) {
515-
const message = Server.errorMessages.hasOwnProperty(code)
516-
? Server.errorMessages[code]
517-
: String(code || "");
573+
const message = errorContext.message || Server.errorMessages[errorCode];
518574
const length = Buffer.byteLength(message);
519575
socket.write(
520576
"HTTP/1.1 400 Bad Request\r\n" +

test/common.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,14 @@ exports.eioc = eioc;
3434
*/
3535

3636
require("s").extend();
37+
38+
exports.createPartialDone = (done, count) => {
39+
let i = 0;
40+
return () => {
41+
if (++i === count) {
42+
done();
43+
} else if (i > count) {
44+
done(new Error(`partialDone() called too many times: ${i} > ${count}`));
45+
}
46+
};
47+
};

0 commit comments

Comments
 (0)