Skip to content

Commit 7f51859

Browse files
knagaitsevhiroppy
authored andcommitted
fix(server): fix header check for socket server (#2077)
* fix(server): fix header check for socket server * test(server): tests to check header error and server methods
1 parent 96b5ab9 commit 7f51859

9 files changed

+177
-17
lines changed

lib/Server.js

+4-7
Original file line numberDiff line numberDiff line change
@@ -677,25 +677,22 @@ class Server {
677677
const SocketServerImplementation = this.socketServerImplementation;
678678
this.socketServer = new SocketServerImplementation(this);
679679

680-
this.socketServer.onConnection((connection) => {
680+
this.socketServer.onConnection((connection, headers) => {
681681
if (!connection) {
682682
return;
683683
}
684684

685-
if (
686-
!this.checkHost(connection.headers) ||
687-
!this.checkOrigin(connection.headers)
688-
) {
685+
if (headers && (!this.checkHost(headers) || !this.checkOrigin(headers))) {
689686
this.sockWrite([connection], 'error', 'Invalid Host/Origin header');
690687

691-
connection.close();
688+
this.socketServer.close(connection);
692689

693690
return;
694691
}
695692

696693
this.sockets.push(connection);
697694

698-
connection.on('close', () => {
695+
this.socketServer.onConnectionClose(connection, () => {
699696
const idx = this.sockets.indexOf(connection);
700697

701698
if (idx >= 0) {

lib/servers/SockJSServer.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,14 @@ module.exports = class SockJSServer extends BaseServer {
5757
connection.close();
5858
}
5959

60-
// f should return the resulting connection
60+
// f should return the resulting connection and, optionally, the connection headers
6161
onConnection(f) {
62-
this.socket.on('connection', f);
62+
this.socket.on('connection', (connection) => {
63+
f(connection, connection.headers);
64+
});
65+
}
66+
67+
onConnectionClose(connection, f) {
68+
connection.on('close', f);
6369
}
6470
};

lib/servers/WebsocketServer.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ module.exports = class WebsocketServer extends BaseServer {
2929

3030
// f should return the resulting connection
3131
onConnection(f) {
32-
this.wsServer.on('connection', f);
32+
this.wsServer.on('connection', (connection, req) => {
33+
f(connection, req.headers);
34+
});
35+
}
36+
37+
onConnectionClose(connection, f) {
38+
connection.on('close', f);
3339
}
3440
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`serverMode option with a bad host header results in an error 1`] = `
4+
Array [
5+
"open",
6+
"{\\"type\\":\\"error\\",\\"data\\":\\"Invalid Host/Origin header\\"}",
7+
"close",
8+
]
9+
`;

test/server/serverMode-option.test.js

+89-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
const request = require('supertest');
77
const sockjs = require('sockjs');
8+
const SockJS = require('sockjs-client/dist/sockjs');
89
const SockJSServer = require('../../lib/servers/SockJSServer');
910
const config = require('../fixtures/simple-config/webpack.config');
1011
const testServer = require('../helpers/test-server');
@@ -77,6 +78,7 @@ describe('serverMode option', () => {
7778

7879
describe('as a class (custom "sockjs" implementation)', () => {
7980
it('uses supplied server implementation', (done) => {
81+
let sockPath;
8082
server = testServer.start(
8183
config,
8284
{
@@ -102,7 +104,7 @@ describe('serverMode option', () => {
102104
prefix: this.server.sockPath,
103105
});
104106

105-
expect(server.options.sockPath).toEqual('/foo/test/bar/');
107+
sockPath = server.options.sockPath;
106108
}
107109

108110
send(connection, message) {
@@ -114,11 +116,20 @@ describe('serverMode option', () => {
114116
}
115117

116118
onConnection(f) {
117-
this.socket.on('connection', f);
119+
this.socket.on('connection', (connection) => {
120+
f(connection, connection.headers);
121+
});
122+
}
123+
124+
onConnectionClose(connection, f) {
125+
connection.on('close', f);
118126
}
119127
},
120128
},
121-
done
129+
() => {
130+
expect(sockPath).toEqual('/foo/test/bar/');
131+
done();
132+
}
122133
);
123134
});
124135
});
@@ -137,4 +148,79 @@ describe('serverMode option', () => {
137148
}).toThrow(/serverMode must be a string/);
138149
});
139150
});
151+
152+
describe('with a bad host header', () => {
153+
beforeAll((done) => {
154+
server = testServer.start(
155+
config,
156+
{
157+
port,
158+
serverMode: class MySockJSServer extends BaseServer {
159+
constructor(serv) {
160+
super(serv);
161+
this.socket = sockjs.createServer({
162+
// Use provided up-to-date sockjs-client
163+
sockjs_url: '/__webpack_dev_server__/sockjs.bundle.js',
164+
// Limit useless logs
165+
log: (severity, line) => {
166+
if (severity === 'error') {
167+
this.server.log.error(line);
168+
} else {
169+
this.server.log.debug(line);
170+
}
171+
},
172+
});
173+
174+
this.socket.installHandlers(this.server.listeningApp, {
175+
prefix: this.server.sockPath,
176+
});
177+
}
178+
179+
send(connection, message) {
180+
connection.write(message);
181+
}
182+
183+
close(connection) {
184+
connection.close();
185+
}
186+
187+
onConnection(f) {
188+
this.socket.on('connection', (connection) => {
189+
f(connection, {
190+
host: null,
191+
});
192+
});
193+
}
194+
195+
onConnectionClose(connection, f) {
196+
connection.on('close', f);
197+
}
198+
},
199+
},
200+
done
201+
);
202+
});
203+
204+
it('results in an error', (done) => {
205+
const data = [];
206+
const client = new SockJS(`http://localhost:${port}/sockjs-node`);
207+
208+
client.onopen = () => {
209+
data.push('open');
210+
};
211+
212+
client.onmessage = (e) => {
213+
data.push(e.data);
214+
};
215+
216+
client.onclose = () => {
217+
data.push('close');
218+
};
219+
220+
setTimeout(() => {
221+
expect(data).toMatchSnapshot();
222+
done();
223+
}, 5000);
224+
});
225+
});
140226
});

test/server/servers/SockJSServer.test.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ describe('SockJSServer', () => {
3535
it('should recieve connection, send message, and close client', (done) => {
3636
const data = [];
3737

38-
socketServer.onConnection((connection) => {
38+
let headers;
39+
socketServer.onConnection((connection, h) => {
40+
headers = h;
3941
data.push('open');
4042
socketServer.send(connection, 'hello world');
4143
setTimeout(() => {
44+
// the server closes the connection with the client
4245
socketServer.close(connection);
4346
}, 1000);
4447
});
@@ -54,10 +57,33 @@ describe('SockJSServer', () => {
5457
};
5558

5659
setTimeout(() => {
60+
expect(headers.host).toMatchSnapshot();
5761
expect(data).toMatchSnapshot();
5862
done();
5963
}, 3000);
6064
});
65+
66+
it('should receive client close event', (done) => {
67+
let receivedClientClose = false;
68+
socketServer.onConnection((connection) => {
69+
socketServer.onConnectionClose(connection, () => {
70+
receivedClientClose = true;
71+
});
72+
});
73+
74+
// eslint-disable-next-line new-cap
75+
const client = new SockJS(`http://localhost:${port}/sockjs-node`);
76+
77+
setTimeout(() => {
78+
// the client closes itself, the server does not close it
79+
client.close();
80+
}, 1000);
81+
82+
setTimeout(() => {
83+
expect(receivedClientClose).toBeTruthy();
84+
done();
85+
}, 3000);
86+
});
6187
});
6288

6389
afterAll((done) => {

test/server/servers/WebsocketServer.test.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ describe('WebsocketServer', () => {
3535
it('should recieve connection, send message, and close client', (done) => {
3636
const data = [];
3737

38-
socketServer.onConnection((connection) => {
38+
let headers;
39+
socketServer.onConnection((connection, h) => {
40+
headers = h;
3941
data.push('open');
4042
socketServer.send(connection, 'hello world');
4143
setTimeout(() => {
44+
// the server closes the connection with the client
4245
socketServer.close(connection);
4346
}, 1000);
4447
});
@@ -55,10 +58,33 @@ describe('WebsocketServer', () => {
5558
};
5659

5760
setTimeout(() => {
61+
expect(headers.host).toMatchSnapshot();
5862
expect(data).toMatchSnapshot();
5963
done();
6064
}, 3000);
6165
});
66+
67+
it('should receive client close event', (done) => {
68+
let receivedClientClose = false;
69+
socketServer.onConnection((connection) => {
70+
socketServer.onConnectionClose(connection, () => {
71+
receivedClientClose = true;
72+
});
73+
});
74+
75+
// eslint-disable-next-line new-cap
76+
const client = new ws(`http://localhost:${port}/ws-server`);
77+
78+
setTimeout(() => {
79+
// the client closes itself, the server does not close it
80+
client.close();
81+
}, 1000);
82+
83+
setTimeout(() => {
84+
expect(receivedClientClose).toBeTruthy();
85+
done();
86+
}, 3000);
87+
});
6288
});
6389

6490
afterAll((done) => {

test/server/servers/__snapshots__/SockJSServer.test.js.snap

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`SockJSServer server should recieve connection, send message, and close client 1`] = `
3+
exports[`SockJSServer server should recieve connection, send message, and close client 1`] = `"localhost:8083"`;
4+
5+
exports[`SockJSServer server should recieve connection, send message, and close client 2`] = `
46
Array [
57
"open",
68
"hello world",

test/server/servers/__snapshots__/WebsocketServer.test.js.snap

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`WebsocketServer server should recieve connection, send message, and close client 1`] = `
3+
exports[`WebsocketServer server should recieve connection, send message, and close client 1`] = `"localhost:8121"`;
4+
5+
exports[`WebsocketServer server should recieve connection, send message, and close client 2`] = `
46
Array [
57
"open",
68
"hello world",

0 commit comments

Comments
 (0)