@@ -12,9 +12,10 @@ import * as querystring from "querystring";
1212import { Emitter } from "vs/base/common/event" ;
1313import { sanitizeFilePath } from "vs/base/common/extpath" ;
1414import { UriComponents , URI } from "vs/base/common/uri" ;
15+ import { generateUuid } from "vs/base/common/uuid" ;
1516import { getMachineId } from 'vs/base/node/id' ;
1617import { IPCServer , ClientConnectionEvent , StaticRouter } from "vs/base/parts/ipc/common/ipc" ;
17- import { mkdirp } from "vs/base/node/pfs" ;
18+ import { mkdirp , rimraf } from "vs/base/node/pfs" ;
1819import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner" ;
1920import { IConfigurationService } from "vs/platform/configuration/common/configuration" ;
2021import { ConfigurationService } from "vs/platform/configuration/node/configurationService" ;
@@ -56,7 +57,7 @@ import { Connection, ManagementConnection, ExtensionHostConnection } from "vs/se
5657import { ExtensionEnvironmentChannel , FileProviderChannel , } from "vs/server/src/channel" ;
5758import { TelemetryClient } from "vs/server/src/insights" ;
5859import { Protocol } from "vs/server/src/protocol" ;
59- import { AuthType , getMediaMime , getUriTransformer } from "vs/server/src/util" ;
60+ import { AuthType , getMediaMime , getUriTransformer , tmpdir } from "vs/server/src/util" ;
6061
6162export enum HttpCode {
6263 Ok = 200 ,
@@ -391,6 +392,11 @@ export class MainServer extends Server {
391392 private readonly services = new ServiceCollection ( ) ;
392393 private readonly servicesPromise : Promise < void > ;
393394
395+ public readonly _onProxyConnect = new Emitter < net . Socket > ( ) ;
396+ private proxyPipe = path . join ( tmpdir , "tls-proxy" ) ;
397+ private _proxyServer ?: Promise < net . Server > ;
398+ private readonly proxyTimeout = 5000 ;
399+
394400 public constructor ( options : ServerOptions , args : ParsedArgs ) {
395401 super ( options ) ;
396402 this . servicesPromise = this . initializeServices ( args ) ;
@@ -407,7 +413,7 @@ export class MainServer extends Server {
407413 }
408414
409415 protected async handleWebSocket ( socket : net . Socket , parsedUrl : url . UrlWithParsedQuery ) : Promise < void > {
410- const protocol = new Protocol ( socket , {
416+ const protocol = new Protocol ( await this . createProxy ( socket ) , {
411417 reconnectionToken : < string > parsedUrl . query . reconnectionToken || "" ,
412418 reconnection : parsedUrl . query . reconnection === "true" ,
413419 skipWebSocketFrames : parsedUrl . query . skipWebSocketFrames === "true" ,
@@ -592,4 +598,79 @@ export class MainServer extends Server {
592598 private async getDebugPort ( ) : Promise < number | undefined > {
593599 return undefined ;
594600 }
601+
602+ /**
603+ * Since we can't pass TLS sockets to children, use this to proxy the socket
604+ * and pass a non-TLS socket.
605+ */
606+ private createProxy = async ( socket : net . Socket ) : Promise < net . Socket > => {
607+ if ( ! ( socket instanceof tls . TLSSocket ) ) {
608+ return socket ;
609+ }
610+
611+ await this . startProxyServer ( ) ;
612+
613+ return new Promise ( ( resolve , reject ) => {
614+ const timeout = setTimeout ( ( ) => {
615+ listener . dispose ( ) ;
616+ socket . destroy ( ) ;
617+ proxy . destroy ( ) ;
618+ reject ( new Error ( "TLS socket proxy timed out" ) ) ;
619+ } , this . proxyTimeout ) ;
620+
621+ const listener = this . _onProxyConnect . event ( ( connection ) => {
622+ connection . once ( "data" , ( data ) => {
623+ if ( ! socket . destroyed && ! proxy . destroyed && data . toString ( ) === id ) {
624+ clearTimeout ( timeout ) ;
625+ listener . dispose ( ) ;
626+ [ [ proxy , socket ] , [ socket , proxy ] ] . forEach ( ( [ a , b ] ) => {
627+ a . pipe ( b ) ;
628+ a . on ( "error" , ( ) => b . destroy ( ) ) ;
629+ a . on ( "close" , ( ) => b . destroy ( ) ) ;
630+ a . on ( "end" , ( ) => b . end ( ) ) ;
631+ } ) ;
632+ resolve ( connection ) ;
633+ }
634+ } ) ;
635+ } ) ;
636+
637+ const id = generateUuid ( ) ;
638+ const proxy = net . connect ( this . proxyPipe ) ;
639+ proxy . once ( "connect" , ( ) => proxy . write ( id ) ) ;
640+ } ) ;
641+ }
642+
643+ private async startProxyServer ( ) : Promise < net . Server > {
644+ if ( ! this . _proxyServer ) {
645+ this . _proxyServer = new Promise ( async ( resolve ) => {
646+ this . proxyPipe = await this . findFreeSocketPath ( this . proxyPipe ) ;
647+ await mkdirp ( tmpdir ) ;
648+ await rimraf ( this . proxyPipe ) ;
649+ const proxyServer = net . createServer ( ( p ) => this . _onProxyConnect . fire ( p ) ) ;
650+ proxyServer . once ( "listening" , resolve ) ;
651+ proxyServer . listen ( this . proxyPipe ) ;
652+ } ) ;
653+ }
654+ return this . _proxyServer ;
655+ }
656+
657+ private async findFreeSocketPath ( basePath : string , maxTries : number = 100 ) : Promise < string > {
658+ const canConnect = ( path : string ) : Promise < boolean > => {
659+ return new Promise ( ( resolve ) => {
660+ const socket = net . connect ( path ) ;
661+ socket . once ( "error" , ( ) => resolve ( false ) ) ;
662+ socket . once ( "connect" , ( ) => {
663+ socket . destroy ( ) ;
664+ resolve ( true ) ;
665+ } ) ;
666+ } ) ;
667+ } ;
668+
669+ let i = 0 ;
670+ let path = basePath ;
671+ while ( await canConnect ( path ) && i < maxTries ) {
672+ path = `${ basePath } -${ ++ i } ` ;
673+ }
674+ return path ;
675+ }
595676}
0 commit comments