import * as vscode from 'vscode' ; import * as net from 'net' ; import { Disposable } from '../utils/dispose' ; import { WSServer } from './wsServer' ; import { HttpServer } from './httpServer' ; import { StatusBarNotifier } from './serverUtils/statusBarNotifier' ; import { AutoRefreshPreview , SettingUtil , Settings , } from '../utils/settingsUtil' ; import { DONT_SHOW_AGAIN , LIVE_PREVIEW_SERVER_ON , UriSchemes , } from '../utils/constants' ; import TelemetryReporter from 'vscode-extension-telemetry' ; import { EndpointManager } from '../infoManagers/endpointManager' ; import { WorkspaceManager } from '../infoManagers/workspaceManager' ; import { ConnectionManager } from '../infoManagers/connectionManager' ; import { serverMsg } from '../manager' ; import { PathUtil } from '../utils/pathUtil' ; export class Server extends Disposable { private readonly _httpServer : HttpServer ; private readonly _wsServer : WSServer ; private readonly _statusBar : StatusBarNotifier ; private _isServerOn = false ; private _wsConnected = false ; private _httpConnected = false ; private readonly _watcher ; constructor ( private readonly _extensionUri , _reporter , _endpointManager , _workspaceManager , private readonly _connectionManager , _userDataDir ) { super ( ) ; this . _httpServer = this . _register ( new HttpServer ( _extensionUri , _reporter , _endpointManager , _workspaceManager , _connectionManager ) ) ; this . _watcher = vscode . workspace . createFileSystemWatcher ( '**' ) ; this . _wsServer = this . _register ( new WSServer ( _reporter , _endpointManager , _workspaceManager , _connectionManager ) ) ; this . _statusBar = this . _register ( new StatusBarNotifier ( _extensionUri ) ) ; const notUserDataDirChange = function ( file ) { return ( file . scheme != UriSchemes . vscode_userdata && ( ! _userDataDir || ! PathUtil . PathBeginsWith ( file . fsPath , _userDataDir ) ) ) ; } ; this . _register ( vscode . workspace . onDidChangeTextDocument ( ( e ) => { if ( e . contentChanges && e . contentChanges . length > 0 && ( e . document . uri . scheme == UriSchemes . file || e . document . uri . scheme == UriSchemes . untitled ) && this . _reloadOnAnyChange ) { this . _wsServer . refreshBrowsers ( ) ; } } ) ) ; this . _register ( this . _watcher . onDidChange ( ( e ) => { if ( this . _reloadOnSave && notUserDataDirChange ( e ) ) { this . _wsServer . refreshBrowsers ( ) ; } } ) ) ; this . _register ( this . _watcher . onDidDelete ( ( e ) => { if ( ( this . _reloadOnAnyChange || this . _reloadOnSave ) && notUserDataDirChange ( e ) ) { this . _wsServer . refreshBrowsers ( ) ; } } ) ) ; this . _register ( this . _watcher . onDidCreate ( ( e ) => { if ( ( this . _reloadOnAnyChange || this . _reloadOnSave ) && notUserDataDirChange ( e ) ) { this . _wsServer . refreshBrowsers ( ) ; } } ) ) ; this . _register ( this . _httpServer . onNewReqProcessed ( ( e ) => { this . _onNewReqProcessed . fire ( e ) ; } ) ) ; this . _register ( this . _wsServer . onConnected ( ( ) => { this . _wsConnected = true ; if ( this . _wsConnected && this . _httpConnected ) { this . connected ( ) ; } } ) ) ; this . _register ( this . _httpServer . onConnected ( ( e ) => { this . _httpConnected = true ; if ( this . _wsConnected && this . _httpConnected ) { this . connected ( ) ; } } ) ) ; vscode . commands . executeCommand ( 'setContext' , LIVE_PREVIEW_SERVER_ON , false ) ; } returns { boolean } public get isRunning ( ) : boolean { return this . _isServerOn ; } description public updateConfigurations ( ) { this . _statusBar . updateConfigurations ( ) ; } private readonly _onNewReqProcessed = this . _register ( new vscode . EventEmitter < serverMsg > ( ) ) ; public readonly onNewReqProcessed = this . _onNewReqProcessed . event ; description public closeServer ( ) { this . _httpServer . close ( ) ; this . _wsServer . close ( ) ; this . _isServerOn = false ; this . _statusBar . ServerOff ( ) ; this . showServerStatusMessage ( 'Server Stopped' ) ; vscode . commands . executeCommand ( 'setContext' , LIVE_PREVIEW_SERVER_ON , false ) ; } description param { number } port returns { boolean } public openServer ( port ) { this . _httpConnected = false ; this . _wsConnected = false ; if ( this . _extensionUri ) { this . findFreePort ( port , ( freePort ) => { this . _httpServer . start ( freePort ) ; this . _wsServer . start ( freePort + 1 ) ; } ) ; return true ; } return false ; } description private get _reloadOnAnyChange ( ) : boolean { return ( SettingUtil . GetConfig ( this . _extensionUri ) . autoRefreshPreview == AutoRefreshPreview . onAnyChange ) ; } description private get _reloadOnSave ( ) : boolean { return ( SettingUtil . GetConfig ( this . _extensionUri ) . autoRefreshPreview == AutoRefreshPreview . onSave ) ; } param startPort param callback private findFreePort ( startPort , callback ) { let port = startPort ; const sock = new net . Socket ( ) ; const host = this . _connectionManager . host ; sock . setTimeout ( 500 ) ; sock . on ( 'connect' , function ( ) { sock . destroy ( ) ; port ++ ; sock . connect ( port , host ) ; } ) ; sock . on ( 'error' , function ( e ) { callback ( port ) ; } ) ; sock . on ( 'timeout' , function ( ) { callback ( port ) ; } ) ; sock . connect ( port , host ) ; } description private connected ( ) { this . _isServerOn = true ; this . _statusBar . ServerOn ( this . _httpServer . port ) ; this . showServerStatusMessage ( `Server Started on Port ${ this . _httpServer . port }` ) ; this . _connectionManager . connected ( this . _httpServer . port , this . _wsServer . wsPort , this . _wsServer . wsPath ) ; vscode . commands . executeCommand ( 'setContext' , LIVE_PREVIEW_SERVER_ON , true ) ; } description param messsage private showServerStatusMessage ( messsage ) { if ( SettingUtil . GetConfig ( this . _extensionUri ) . showServerStatusNotifications ) { vscode . window . showInformationMessage ( messsage , DONT_SHOW_AGAIN ) . then ( ( selection ) => { if ( selection == DONT_SHOW_AGAIN ) { SettingUtil . UpdateSettings ( Settings . showServerStatusNotifications , false ) ; } } ) ; } } }