mirror of
https://github.com/bitinflow/server.git
synced 2026-03-13 13:35:53 +00:00
158 lines
4.0 KiB
JavaScript
158 lines
4.0 KiB
JavaScript
import assert from 'assert';
|
|
import http from 'http';
|
|
import { Duplex } from 'stream';
|
|
import WebSocket from 'ws';
|
|
import net from 'net';
|
|
|
|
import Client from './Client';
|
|
|
|
class DummySocket extends Duplex {
|
|
constructor(options) {
|
|
super(options);
|
|
}
|
|
|
|
_write(chunk, encoding, callback) {
|
|
callback();
|
|
}
|
|
|
|
_read(size) {
|
|
this.push('HTTP/1.1 304 Not Modified\r\nX-Powered-By: dummy\r\n\r\n\r\n');
|
|
this.push(null);
|
|
}
|
|
}
|
|
|
|
class DummyWebsocket extends Duplex {
|
|
constructor(options) {
|
|
super(options);
|
|
this.sentHeader = false;
|
|
}
|
|
|
|
_write(chunk, encoding, callback) {
|
|
const str = chunk.toString();
|
|
// if chunk contains `GET / HTTP/1.1` -> queue headers
|
|
// otherwise echo back received data
|
|
if (str.indexOf('GET / HTTP/1.1') === 0) {
|
|
const arr = [
|
|
'HTTP/1.1 101 Switching Protocols',
|
|
'Upgrade: websocket',
|
|
'Connection: Upgrade',
|
|
];
|
|
this.push(arr.join('\r\n'));
|
|
this.push('\r\n\r\n');
|
|
}
|
|
else {
|
|
this.push(str);
|
|
}
|
|
callback();
|
|
}
|
|
|
|
_read(size) {
|
|
// nothing to implement
|
|
}
|
|
}
|
|
|
|
class DummyAgent extends http.Agent {
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
createConnection(options, cb) {
|
|
cb(null, new DummySocket());
|
|
}
|
|
}
|
|
|
|
describe('Client', () => {
|
|
it('should handle request', async () => {
|
|
const agent = new DummyAgent();
|
|
const client = new Client({ agent });
|
|
|
|
const server = http.createServer((req, res) => {
|
|
client.handleRequest(req, res);
|
|
});
|
|
|
|
await new Promise(resolve => server.listen(resolve));
|
|
|
|
const address = server.address();
|
|
const opt = {
|
|
host: 'localhost',
|
|
port: address.port,
|
|
path: '/',
|
|
};
|
|
|
|
const res = await new Promise((resolve) => {
|
|
const req = http.get(opt, (res) => {
|
|
resolve(res);
|
|
});
|
|
req.end();
|
|
});
|
|
assert.equal(res.headers['x-powered-by'], 'dummy');
|
|
server.close();
|
|
});
|
|
|
|
it('should handle upgrade', async () => {
|
|
// need a websocket server and a socket for it
|
|
class DummyWebsocketAgent extends http.Agent {
|
|
constructor() {
|
|
super();
|
|
}
|
|
|
|
createConnection(options, cb) {
|
|
cb(null, new DummyWebsocket());
|
|
}
|
|
}
|
|
|
|
const agent = new DummyWebsocketAgent();
|
|
const client = new Client({ agent });
|
|
|
|
const server = http.createServer();
|
|
server.on('upgrade', (req, socket, head) => {
|
|
client.handleUpgrade(req, socket);
|
|
});
|
|
|
|
await new Promise(resolve => server.listen(resolve));
|
|
|
|
const address = server.address();
|
|
|
|
const netClient = await new Promise((resolve) => {
|
|
const newClient = net.createConnection({ port: address.port }, () => {
|
|
resolve(newClient);
|
|
});
|
|
});
|
|
|
|
const out = [
|
|
'GET / HTTP/1.1',
|
|
'Connection: Upgrade',
|
|
'Upgrade: websocket'
|
|
];
|
|
|
|
netClient.write(out.join('\r\n') + '\r\n\r\n');
|
|
|
|
{
|
|
const data = await new Promise((resolve) => {
|
|
netClient.once('data', (chunk) => {
|
|
resolve(chunk.toString());
|
|
});
|
|
});
|
|
const exp = [
|
|
'HTTP/1.1 101 Switching Protocols',
|
|
'Upgrade: websocket',
|
|
'Connection: Upgrade',
|
|
];
|
|
assert.equal(exp.join('\r\n') + '\r\n\r\n', data);
|
|
}
|
|
|
|
{
|
|
netClient.write('foobar');
|
|
const data = await new Promise((resolve) => {
|
|
netClient.once('data', (chunk) => {
|
|
resolve(chunk.toString());
|
|
});
|
|
});
|
|
assert.equal('foobar', data);
|
|
}
|
|
|
|
netClient.destroy();
|
|
server.close();
|
|
});
|
|
});
|