diff --git a/CHANGELOG.md b/CHANGELOG.md index a12026a..06d927b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## v0.2.3 + +- Добавили поддержку `hot-reload`. +- Добавили параметр запуска `ws-origin`: + Используется для указания, что `WebSocket`-соединение должно использовать тот же `Origin`, что и проксируемый сервер. + - Значение `1` — использовать `Origin` проксируемого сервера (значение по умолчанию). + - Значение `0` — отключить это поведение. + ## v0.2.2 - Убрали node-fetch из зависимостей. diff --git a/package.json b/package.json index 9f47bd4..3ef47c1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@vkontakte/vk-tunnel", "license": "MIT", - "version": "0.2.2", + "version": "0.2.3", "bin": { "vk-tunnel": "./bin/vk-tunnel.js" }, diff --git a/src/constants.ts b/src/constants.ts index 938cd36..c487cc0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -11,6 +11,7 @@ export const DEFAULT_USER_PROXY_APP_SETTINGS = { 'port': Number(process.env.PROXY_PORT ?? 10888), 'host': process.env.PROXY_HOST ?? 'localhost', 'insecure': 0, + 'ws-origin': 1, 'app_id': undefined, 'staging': undefined, 'endpoints': undefined, diff --git a/src/entities/WsProxy.ts b/src/entities/WsProxy.ts index bac1943..63e6966 100644 --- a/src/entities/WsProxy.ts +++ b/src/entities/WsProxy.ts @@ -12,20 +12,30 @@ export class WsProxy { private static DISABLE_COMPRESS = 'gzip;q=0,deflate;q=0'; private connections: Record = {}; - private userSettings: UserProxyAppSettings; - private logger: Logger; - public constructor(userSettings: UserProxyAppSettings, logger: Logger) { - this.userSettings = userSettings; - this.logger = logger; - } + public constructor( + private readonly userSettings: UserProxyAppSettings, + private readonly logger: Logger, + ) {} - private filterHeaders(payload: string, proxyHost: string) { + private transformPayload(payload: string, proxyHost: string) { return payload .replace(/Accept-Encoding:.*/, WsProxy.ACCEPT_ENCODING + ': ' + WsProxy.DISABLE_COMPRESS) .replace(/Host: .*/, 'Host: ' + proxyHost); } + private filterWebSocketHeaders(headers: Record) { + const allowedHeaders = [ + 'Sec-WebSocket-Protocol', + 'Sec-WebSocket-Extensions', + 'Sec-WebSocket-Key', + 'Sec-WebSocket-Version', + ]; + return Object.fromEntries( + Object.entries(headers).filter(([key]) => allowedHeaders.includes(key)), + ); + } + private closeConnection(seq: string) { this.connections[seq].close(); } @@ -33,26 +43,24 @@ export class WsProxy { private createConnection( seq: string, proxiedServerUrl: string, + headers: Record, sendResponseToVkProxyServer: SendResponseToProxyServer, ) { - this.connections[seq] = new WebSocket(proxiedServerUrl, [], {}); - this.connections[seq].on('error', (msg) => { - this.logger.error('Connection error for ' + seq, msg); - }); + const subprotocol = headers['Sec-Websocket-Protocol']; + const host = this.userSettings.wsOrigin ? this.userSettings.host : undefined; + const origin = this.userSettings.wsOrigin + ? `${this.userSettings.wsProtocol}://${this.userSettings.host}:${this.userSettings.port}` + : undefined; - this.connections[seq].on('upgrade', (msg) => { - let response = ['HTTP/1.1 101 Switching Protocols']; - let keys = Object.keys(msg.headers); - for (let i = 0; i < keys.length; i++) { - response.push(`${keys[i]}:${msg.headers[keys[i]]}`); - } - response.push('\n'); - sendResponseToVkProxyServer(seq + MessageType.HTTP + response.join('\n'), () => { - this.logger.debug('send reply upgrade', seq, response.toString()); - }); + const websocket = new WebSocket(proxiedServerUrl, subprotocol, { + host, + origin, + headers: this.filterWebSocketHeaders(headers), }); - this.connections[seq].on('open', () => { + websocket.on('error', (msg) => this.logger.error('Connection error for ' + seq, msg)); + + websocket.on('open', () => { this.connections[seq].on('message', (data) => { this.logger.debug('incoming ws message from service', seq, data); sendResponseToVkProxyServer(`${seq}${MessageType.WEBSOCKET}${data}`, () => { @@ -60,16 +68,30 @@ export class WsProxy { }); }); }); + + websocket.on('upgrade', (msg) => { + const responseHeaders = [ + 'HTTP/1.1 101 Switching Protocols', + ...Object.entries(msg.headers).map(([key, value]) => `${key}: ${value}`), + ]; + const response = responseHeaders.join('\n') + '\n\n'; + + sendResponseToVkProxyServer(seq + MessageType.HTTP + response, () => { + this.logger.debug('send reply upgrade', seq, response.toString()); + }); + }); + + this.connections[seq] = websocket; } public async proxy( request: ProxiedNetworkPacket, sendResponseToVkProxyServer: SendResponseToProxyServer, ) { - const { messageType, payload, isWebsocketUpgrade, seq, endpoint } = request; + const { messageType, payload, isWebsocketUpgrade, seq, endpoint, parsedRequest } = request; if (messageType !== MessageType.HTTP) { - const filteredPayload = this.filterHeaders(payload, this.userSettings.host); + const filteredPayload = this.transformPayload(payload, this.userSettings.host); if (messageType === MessageType.WEBSOCKET_CLOSE) { return this.closeConnection(seq); @@ -82,7 +104,13 @@ export class WsProxy { if (isWebsocketUpgrade) { const proxiedServerUrl = `${this.userSettings.wsProtocol}://${this.userSettings.host}:${this.userSettings.port}${endpoint}`; - this.createConnection(seq, proxiedServerUrl, sendResponseToVkProxyServer); + + this.createConnection( + seq, + proxiedServerUrl, + parsedRequest.headers, + sendResponseToVkProxyServer, + ); } } } diff --git a/src/helpers/getUserProxyAppSettings.ts b/src/helpers/getUserProxyAppSettings.ts index a452308..7254d06 100644 --- a/src/helpers/getUserProxyAppSettings.ts +++ b/src/helpers/getUserProxyAppSettings.ts @@ -16,8 +16,9 @@ export function getUserProxyAppSettings(): UserProxyAppSettings { const { 'http-protocol': httpProtocol, 'ws-protocol': wsProtocol, + 'ws-origin': wsOrigin, ...userSettings } = Object.assign({ ...DEFAULT_USER_PROXY_APP_SETTINGS, ...userConfigFile }, cliArgs); - return { httpProtocol, wsProtocol, ...userSettings }; + return { wsOrigin, httpProtocol, wsProtocol, ...userSettings }; } diff --git a/src/types.ts b/src/types.ts index 070c683..f0f0c0e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,15 +23,17 @@ export interface UserProxyAppSettings { host: string; port: number; insecure: 0 | 1; + wsOrigin: 0 | 1; app_id?: number; staging?: boolean; endpoints?: string[]; } export interface UserProxyAppSettingsArgs - extends Omit { + extends Omit { 'http-protocol': HttpProtocol; 'ws-protocol': WsProtocol; + 'ws-origin': 0 | 1; } export interface TunnelConnectionData {