Path: blob/main/mitm-socket/test/MitmSocket.test.ts
1030 views
import { Helpers } from '@secret-agent/testing';1import { createPromise } from '@secret-agent/commons/utils';2import * as http2 from 'http2';3import * as stream from 'stream';4import * as WebSocket from 'ws';5import { getTlsConnection, httpGetWithSocket } from '@secret-agent/testing/helpers';6import * as https from 'https';7import { IncomingMessage } from 'http';8import MitmSocket from '../index';9import MitmSocketSession from '../lib/MitmSocketSession';1011afterAll(Helpers.afterAll);12afterEach(Helpers.afterEach);1314let mitmSocketSession: MitmSocketSession;15beforeAll(() => {16mitmSocketSession = new MitmSocketSession('mitmSocket.test', {17clientHelloId: 'chrome-72',18rejectUnauthorized: false,19});20Helpers.onClose(() => mitmSocketSession.close(), true);21});2223test('should be able to send a tls connection', async () => {24const htmlString = 'Secure as anything!';25const server = await Helpers.runHttpsServer((req, res) => {26return res.end(htmlString);27});2829const tlsConnection = getTlsConnection(server.port);30await tlsConnection.connect(mitmSocketSession);31const httpResponse = await httpGetWithSocket(`${server.baseUrl}/any`, {}, tlsConnection.socket);32expect(httpResponse).toBe(htmlString);33});3435test('should handle http2 requests', async () => {36const httpServer = await Helpers.runHttp2Server((request, response) => {37response.end('I am h2');38});39const tlsConnection = getTlsConnection(httpServer.port);4041await tlsConnection.connect(mitmSocketSession);42expect(tlsConnection.alpn).toBe('h2');4344const client = http2.connect('https://secretagent.dev', {45createConnection: () => tlsConnection.socket,46});47closeAfterTest(client);4849const request = client.request({ ':path': '/' });50const httpResponse = await readResponse(request);51expect(httpResponse).toBe('I am h2');52client.destroy();53});5455test('should be able to hit google using a Chrome Emulator', async () => {56const socketSession = new MitmSocketSession('mitmSocket.test', {57clientHelloId: 'chrome-79',58rejectUnauthorized: false,59});60Helpers.needsClosing.push(socketSession);61const tlsConnection = new MitmSocket('1', {62host: 'google.com',63port: '443',64servername: 'google.com',65isSsl: true,66});67Helpers.onClose(async () => tlsConnection.close());6869await tlsConnection.connect(socketSession);70expect(tlsConnection.alpn).toBe('h2');7172const client = http2.connect('https://www.google.com', {73createConnection: () => tlsConnection.socket,74});75closeAfterTest(client);7677const request = client.request({ ':path': '/' });78const httpResponse = await readResponse(request);79expect(httpResponse).toBeTruthy();80expect(httpResponse).toMatch(/<\/body><\/html>$/);81});8283test('should be able to hit gstatic using a Chrome Emulator', async () => {84const tlsConnection = new MitmSocket('optimove', {85host: 'www.gstatic.com',86port: '443',87servername: 'www.gstatic.com',88isSsl: true,89});90Helpers.onClose(async () => tlsConnection.close());9192await tlsConnection.connect(mitmSocketSession);93expect(tlsConnection.alpn).toBe('h2');9495const client = http2.connect('https://www.gstatic.com', {96createConnection: () => tlsConnection.socket,97});98closeAfterTest(client);99100const request = client.request({101':path': '/firebasejs/4.9.1/firebase.js',102});103const httpResponse = await readResponse(request);104expect(httpResponse).toBeTruthy();105});106107test('should be able to hit a server that disconnects', async () => {108const server = await Helpers.runHttpsServer(async (req, res) => {109res.socket.end(110`HTTP/1.1 301 Moved Permanently\r\nContent-Length: 0\r\nConnection: close\r\nLocation: https://www.location2.com\r\n\r\n`,111);112});113114const tlsConnection = new MitmSocket('disconnect', {115host: `localhost`,116port: String(server.port),117servername: 'localhost',118keepAlive: true,119isSsl: true,120});121Helpers.onClose(async () => tlsConnection.close());122123await tlsConnection.connect(mitmSocketSession);124expect(tlsConnection.alpn).toBe('http/1.1');125const request = https.request({126method: 'GET',127path: '/',128host: 'localhost',129port: server.port,130createConnection() {131return tlsConnection.socket;132},133});134135const responsePromise = new Promise<IncomingMessage>(resolve => request.on('response', resolve));136request.end();137const response = await responsePromise;138expect(response.headers).toEqual({139'content-length': '0',140connection: 'close',141location: 'https://www.location2.com',142});143});144145// only test this manually146// eslint-disable-next-line jest/no-disabled-tests147test.skip('should be able to get scripts from unpkg using Chrome emulator', async () => {148const socketSession = new MitmSocketSession('mitmSocket.test', {149clientHelloId: 'chrome-79',150rejectUnauthorized: false,151});152Helpers.needsClosing.push(socketSession);153const tlsConnection = new MitmSocket('3', {154host: 'unpkg.com',155port: '443',156servername: 'unpkg.com',157isSsl: true,158});159Helpers.onClose(async () => tlsConnection.close());160161await tlsConnection.connect(socketSession);162expect(tlsConnection.alpn).toBe('h2');163164const client = http2.connect('https://unpkg.com', {165createConnection: () => tlsConnection.socket,166});167closeAfterTest(client);168169{170const request = client.request({ ':path': '/[email protected]/umd/react.production.min.js' });171const httpResponse = await readResponse(request);172expect(httpResponse).toBeTruthy();173expect(httpResponse).toMatch(/\(function\(/);174}175{176const request = client.request({177':path': '/[email protected]/umd/react-dom.production.min.js',178});179const httpResponse = await readResponse(request);180expect(httpResponse).toBeTruthy();181expect(httpResponse).toMatch(/\(function\(/);182}183});184185test('should handle websockets', async () => {186const htmlString = 'Secure as anything!';187const server = await Helpers.runHttpsServer((req, res) => res.end(htmlString));188const messageCount = 500;189190const wsServer = new WebSocket.Server({ server: server.server });191wsServer.on('connection', async (ws: WebSocket) => {192for (let i = 0; i < messageCount; i += 1) {193await new Promise(resolve =>194ws.send(i, () => {195setTimeout(resolve, 2);196}),197);198}199});200201const tlsConnection = getTlsConnection(server.port, undefined, true);202tlsConnection.connectOpts.keepAlive = true;203await tlsConnection.connect(mitmSocketSession);204205const wsClient = new WebSocket(`wss://localhost:${server.port}`, {206rejectUnauthorized: false,207createConnection: () => tlsConnection.socket,208});209210Helpers.onClose(async () => wsClient.close());211212const messages = [];213const messagePromise = createPromise();214wsClient.on('open', () => {215wsClient.on('message', msg => {216messages.push(msg);217if (messages.length === messageCount) {218messagePromise.resolve();219}220});221});222await messagePromise.promise;223expect(messages.length).toBe(messageCount);224}, 35e3);225226test('should handle upstream disconnects', async () => {227const server = await Helpers.runHttpsServer((req, res) => {228res.connection.end();229});230231const tlsConnection = getTlsConnection(server.port);232await tlsConnection.connect(mitmSocketSession);233234await expect(235httpGetWithSocket(`${server.baseUrl}/any`, {}, tlsConnection.socket),236).rejects.toThrow();237});238239function closeAfterTest(session: http2.ClientHttp2Session) {240Helpers.onClose(() => {241session.destroy();242});243}244245async function readResponse(res: stream.Readable) {246return (await Helpers.readableToBuffer(res)).toString();247}248249250