import { WebSiteManagementModels } from '@azure/arm-appservice' ; import { EventEmitter } from 'events' ; import { createServer , Server , Socket } from 'net' ; import { ParsedSite , pingFunctionApp } from 'vscode-azureappservice' ; import { IActionContext } from 'vscode-azureextensionui' ; import * as websocket from 'websocket' ; import { ext } from '../../extensionVariables' ; export class DebugProxy extends EventEmitter { private _server : Server | undefined ; private _wsclient : websocket . client | undefined ; private _wsconnection : websocket . connection | undefined ; private readonly _site : ParsedSite ; private readonly _port : number ; private readonly _publishCredential : WebSiteManagementModels . User ; private _keepAlive : boolean ; constructor ( site , port , publishCredential ) { super ( ) ; this . _site = site ; this . _port = port ; this . _publishCredential = publishCredential ; this . _keepAlive = true ; this . _server = createServer ( ) ; } public async startProxy ( context ) { if ( ! this . _server ) { this . emit ( 'error' , new Error ( 'Proxy server is not started.' ) ) ; } else { await this . keepAlive ( context ) ; this . _server . on ( 'connection' , ( socket ) => { if ( this . _wsclient ) { ext . outputChannel . appendLog ( `[Proxy Server] The server is already connected. Rejected connection to "${ socket . remoteAddress }:${ socket . remotePort }"` ) ; this . emit ( 'error' , new Error ( `[Proxy Server]  The server is already connected. Rejected connection to "${ socket . remoteAddress }:${ socket . remotePort }"` ) ) ; socket . destroy ( ) ; } else { ext . outputChannel . appendLog ( `[Proxy Server] client connected ${ socket . remoteAddress }:${ socket . remotePort }` ) ; socket . pause ( ) ; this . _wsclient = new websocket . client ( ) ; this . _wsclient . on ( 'connect' , ( connection ) => { ext . outputChannel . appendLog ( '[WebSocket] client connected' ) ; this . _wsconnection = connection ; connection . on ( 'close' , ( ) => { ext . outputChannel . appendLog ( '[WebSocket] client closed' ) ; this . dispose ( ) ; socket . destroy ( ) ; this . emit ( 'end' ) ; } ) ; connection . on ( 'error' , ( err ) => { ext . outputChannel . appendLog ( `[WebSocket] ${ err }` ) ; this . dispose ( ) ; socket . destroy ( ) ; this . emit ( 'error' , err ) ; } ) ; connection . on ( 'message' , ( data ) => { if ( 'binaryData' in data && data . binaryData ) { socket . write ( data . binaryData ) ; } } ) ; socket . resume ( ) ; } ) ; this . _wsclient . on ( 'connectFailed' , ( err ) => { ext . outputChannel . appendLog ( `[WebSocket] ${ err }` ) ; this . dispose ( ) ; socket . destroy ( ) ; this . emit ( 'error' , err ) ; } ) ; this . _wsclient . connect ( `wss://${ this . _site . kuduHostName }/DebugSiteExtension/JavaDebugSiteExtension.ashx` , undefined , undefined , { 'Cache-Control' : 'no-cache' , Pragma : 'no-cache' } , { auth : `${ this . _publishCredential . publishingUserName }:${ this . _publishCredential . publishingPassword }` } ) ; socket . on ( 'data' , ( data ) => { if ( this . _wsconnection ) { this . _wsconnection . send ( data ) ; } } ) ; socket . on ( 'end' , ( ) => { ext . outputChannel . appendLog ( `[Proxy Server] client disconnected ${ socket . remoteAddress }:${ socket . remotePort }` ) ; this . dispose ( ) ; this . emit ( 'end' ) ; } ) ; socket . on ( 'error' , ( err ) => { ext . outputChannel . appendLog ( `[Proxy Server] ${ err }` ) ; this . dispose ( ) ; socket . destroy ( ) ; this . emit ( 'error' , err ) ; } ) ; } } ) ; this . _server . on ( 'listening' , ( ) => { ext . outputChannel . appendLog ( '[Proxy Server] start listening' ) ; this . emit ( 'start' ) ; } ) ; this . _server . listen ( { host : 'localhost' , port : this . _port , backlog : 1 } ) ; } } public dispose ( ) { if ( this . _wsconnection ) { this . _wsconnection . close ( ) ; this . _wsconnection = undefined ; } if ( this . _wsclient ) { this . _wsclient . abort ( ) ; this . _wsclient = undefined ; } if ( this . _server ) { this . _server . close ( ) ; this . _server = undefined ; } this . _keepAlive = false ; } private async keepAlive ( context ) { if ( this . _keepAlive ) { try { await pingFunctionApp ( context , this . _site ) ; setTimeout ( this . keepAlive , 60 * 1000 ) ; } catch ( err ) { ext . outputChannel . appendLog ( `[Proxy Server] ${ err }` ) ; setTimeout ( this . keepAlive , 5 * 1000 ) ; } } } }