Skip to content

Commit fc11285

Browse files
fix(eio): close HTTP requests with invalid content type
Before this commit, after the initial handshake, an HTTP request with a "application/octet-stream" content type would trigger a "invalid content" error at the Engine.IO level, but would not be properly closed, which could possibly lead to resource exhaustion. This behavior was introduced in [1] (engine.io@4.1.0, January 2021). [1]: 663d326
1 parent b059af6 commit fc11285

4 files changed

Lines changed: 57 additions & 3 deletions

File tree

packages/engine.io/lib/transports-uws/polling.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ export class Polling extends Transport {
136136
const isBinary = "application/octet-stream" === req.headers["content-type"];
137137

138138
if (isBinary && this.protocol === 4) {
139-
return this.onError("invalid content");
139+
this.onError("invalid content");
140+
return res.writeStatus("400 Bad Request").end();
140141
}
141142

142143
this.dataReq = req;

packages/engine.io/lib/transports/polling.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ export class Polling extends Transport {
122122
const isBinary = "application/octet-stream" === req.headers["content-type"];
123123

124124
if (isBinary && this.protocol === 4) {
125-
return this.onError("invalid content");
125+
this.onError("invalid content");
126+
return res.writeHead(400).end();
126127
}
127128

128129
this.dataReq = req;

packages/engine.io/test/common.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,32 @@ exports.listen = (opts, fn) => {
6060
return e;
6161
};
6262

63+
exports.listenAsync = function listenAsync(opts = {}) {
64+
return new Promise((resolve) => {
65+
const engine = exports.listen(opts, (port) => {
66+
resolve({
67+
port,
68+
close: () => {
69+
engine.close();
70+
if (engine.httpServer) {
71+
engine.httpServer.close();
72+
}
73+
},
74+
});
75+
});
76+
});
77+
};
78+
79+
exports.runHandshake = async function runHandshake(port) {
80+
const res = await fetch(
81+
`http://localhost:${port}/engine.io/?EIO=4&transport=polling`,
82+
);
83+
const data = await res.text();
84+
return {
85+
sid: JSON.parse(data.substring(1)).sid,
86+
};
87+
};
88+
6389
exports.ClientSocket = Socket;
6490

6591
exports.createPartialDone = (done, count) => {

packages/engine.io/test/server.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ const path = require("path");
77
const exec = require("child_process").exec;
88
const zlib = require("zlib");
99
const { Server, Socket, attach } = require("..");
10-
const { ClientSocket, listen, createPartialDone } = require("./common");
10+
const {
11+
ClientSocket,
12+
listen,
13+
listenAsync,
14+
runHandshake,
15+
createPartialDone,
16+
} = require("./common");
1117
const expect = require("expect.js");
1218
const request = require("superagent");
1319
const cookieMod = require("cookie");
@@ -1458,6 +1464,26 @@ describe("server", () => {
14581464
},
14591465
);
14601466

1467+
it("should abort the polling data request if the content type is invalid", async () => {
1468+
const { port, close } = await listenAsync();
1469+
const { sid } = await runHandshake(port);
1470+
1471+
const res = await fetch(
1472+
`http://localhost:${port}/engine.io/?EIO=4&transport=polling&sid=${sid}`,
1473+
{
1474+
method: "POST",
1475+
headers: {
1476+
"content-type": "application/octet-stream",
1477+
},
1478+
body: Buffer.of(1, 2, 3),
1479+
},
1480+
);
1481+
1482+
expect(res.status).to.eql(400);
1483+
1484+
close();
1485+
});
1486+
14611487
// tests https://github.com/LearnBoost/engine.io-client/issues/207
14621488
// websocket test, transport error
14631489
it("should trigger transport close before open for ws", (done) => {

0 commit comments

Comments
 (0)