refactor to use http agent interface for tunnels

This commit is contained in:
Roman Shtylman
2018-05-15 14:49:19 -04:00
parent 30fd566c3a
commit c27100b98e
19 changed files with 1041 additions and 1345 deletions

158
lib/Client.test.js Normal file
View File

@@ -0,0 +1,158 @@
import assert from 'assert';
import http from 'http';
import { Duplex } from 'stream';
import EventEmitter from 'events';
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();
});
});