@@ -783,6 +783,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
783783 } ) ;
784784 }
785785
786+ public async getRemoteExecServer ( remoteAuthority : string ) : Promise < vscode . ExecServer | undefined > {
787+ const { resolver } = await this . _activateAndGetResolver ( remoteAuthority ) ;
788+ return resolver ?. resolveExecServer ?.( remoteAuthority , { resolveAttempt : 0 } ) ;
789+ }
790+
786791 // -- called by main thread
787792
788793 private async _activateAndGetResolver ( remoteAuthority : string ) : Promise < { authorityPrefix : string ; resolver : vscode . RemoteAuthorityResolver | undefined } > {
@@ -798,99 +803,122 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
798803 return { authorityPrefix, resolver : this . _resolvers [ authorityPrefix ] } ;
799804 }
800805
801- public async $resolveAuthority ( remoteAuthority : string , resolveAttempt : number ) : Promise < Dto < IResolveAuthorityResult > > {
806+ public async $resolveAuthority ( remoteAuthorityChain : string , resolveAttempt : number ) : Promise < Dto < IResolveAuthorityResult > > {
802807 const sw = StopWatch . create ( false ) ;
803- const prefix = ( ) => `[resolveAuthority(${ getRemoteAuthorityPrefix ( remoteAuthority ) } ,${ resolveAttempt } )][${ sw . elapsed ( ) } ms] ` ;
808+ const prefix = ( ) => `[resolveAuthority(${ getRemoteAuthorityPrefix ( remoteAuthorityChain ) } ,${ resolveAttempt } )][${ sw . elapsed ( ) } ms] ` ;
804809 const logInfo = ( msg : string ) => this . _logService . info ( `${ prefix ( ) } ${ msg } ` ) ;
805810 const logError = ( msg : string , err : any = undefined ) => this . _logService . error ( `${ prefix ( ) } ${ msg } ` , err ) ;
811+ const normalizeError = ( err : unknown ) => {
812+ if ( err instanceof RemoteAuthorityResolverError ) {
813+ return {
814+ type : 'error' as const ,
815+ error : {
816+ code : err . _code ,
817+ message : err . _message ,
818+ detail : err . _detail
819+ }
820+ } ;
821+ }
822+ throw err ;
823+ } ;
806824
807- logInfo ( `activating resolver...` ) ;
808- const { authorityPrefix, resolver } = await this . _activateAndGetResolver ( remoteAuthority ) ;
809- if ( ! resolver ) {
810- logError ( `no resolver` ) ;
811- return {
812- type : 'error' ,
813- error : {
814- code : RemoteAuthorityResolverErrorCode . NoResolverFound ,
815- message : `No remote extension installed to resolve ${ authorityPrefix } .` ,
816- detail : undefined
825+ const chain = remoteAuthorityChain . split ( / @ | % 4 0 / g) . reverse ( ) ;
826+ logInfo ( `activating remote resolvers ${ chain . join ( ' -> ' ) } ` ) ;
827+
828+ let resolvers ;
829+ try {
830+ resolvers = await Promise . all ( chain . map ( async remoteAuthority => {
831+ logInfo ( `activating resolver...` ) ;
832+ const { resolver, authorityPrefix } = await this . _activateAndGetResolver ( remoteAuthority ) ;
833+ if ( ! resolver ) {
834+ logError ( `no resolver` ) ;
835+ throw new RemoteAuthorityResolverError ( `No remote extension installed to resolve ${ authorityPrefix } .` , RemoteAuthorityResolverErrorCode . NoResolverFound ) ;
817836 }
818- } ;
837+ return { resolver, authorityPrefix, remoteAuthority } ;
838+ } ) ) ;
839+ } catch ( e ) {
840+ return normalizeError ( e ) ;
819841 }
820842
821843 const intervalLogger = new IntervalTimer ( ) ;
822- try {
823- logInfo ( `setting tunnel factory...` ) ;
824- this . _register ( await this . _extHostTunnelService . setTunnelFactory ( resolver ) ) ;
825-
826- intervalLogger . cancelAndSet ( ( ) => logInfo ( 'waiting...' ) , 1000 ) ;
827- logInfo ( `invoking resolve()...` ) ;
828- performance . mark ( `code/extHost/willResolveAuthority/${ authorityPrefix } ` ) ;
829- const result = await resolver . resolve ( remoteAuthority , { resolveAttempt } ) ;
830- performance . mark ( `code/extHost/didResolveAuthorityOK/${ authorityPrefix } ` ) ;
831- intervalLogger . dispose ( ) ;
832-
833- const tunnelInformation : TunnelInformation = {
834- environmentTunnels : result . environmentTunnels ,
835- features : result . tunnelFeatures
836- } ;
844+ intervalLogger . cancelAndSet ( ( ) => logInfo ( 'waiting...' ) , 1000 ) ;
837845
838- // Split merged API result into separate authority/options
839- const options : ResolvedOptions = {
840- extensionHostEnv : result . extensionHostEnv ,
841- isTrusted : result . isTrusted ,
842- authenticationSession : result . authenticationSessionForInitializingExtensions ? { id : result . authenticationSessionForInitializingExtensions . id , providerId : result . authenticationSessionForInitializingExtensions . providerId } : undefined
843- } ;
846+ let result ! : vscode . ResolverResult ;
847+ let execServer : vscode . ExecServer | undefined ;
848+ for ( const [ i , { authorityPrefix, resolver, remoteAuthority } ] of resolvers . entries ( ) ) {
849+ try {
850+ if ( i === resolvers . length - 1 ) {
851+ logInfo ( `invoking final resolve()...` ) ;
852+ performance . mark ( `code/extHost/willResolveAuthority/${ authorityPrefix } ` ) ;
853+ result = await resolver . resolve ( remoteAuthority , { resolveAttempt, execServer } ) ;
854+ performance . mark ( `code/extHost/didResolveAuthorityOK/${ authorityPrefix } ` ) ;
855+ // todo@connor 4312: we probably need to chain tunnels too, how does this work with 'public' tunnels?
856+ logInfo ( `setting tunnel factory...` ) ;
857+ this . _register ( await this . _extHostTunnelService . setTunnelFactory ( resolver ) ) ;
858+ } else {
859+ logInfo ( `invoking resolveExecServer() for ${ remoteAuthority } ` ) ;
860+ performance . mark ( `code/extHost/willResolveExecServer/${ authorityPrefix } ` ) ;
861+ execServer = await resolver . resolveExecServer ?.( remoteAuthority , { resolveAttempt, execServer } ) ;
862+ if ( ! execServer ) {
863+ throw new RemoteAuthorityResolverError ( `Exec server was not available for ${ remoteAuthority } ` , RemoteAuthorityResolverErrorCode . NoResolverFound ) ; // we did, in fact, break the chain :(
864+ }
865+ performance . mark ( `code/extHost/didResolveExecServerOK/${ authorityPrefix } ` ) ;
866+ }
867+ } catch ( e ) {
868+ performance . mark ( `code/extHost/didResolveAuthorityError/${ authorityPrefix } ` ) ;
869+ logError ( `returned an error` , e ) ;
870+ intervalLogger . dispose ( ) ;
871+ return normalizeError ( e ) ;
872+ }
873+ }
844874
845- // extension are not required to return an instance of ResolvedAuthority or ManagedResolvedAuthority, so don't use `instanceof`
846- logInfo ( `returned ${ ExtHostManagedResolvedAuthority . isManagedResolvedAuthority ( result ) ? 'managed authority' : `${ result . host } :${ result . port } ` } ` ) ;
875+ intervalLogger . dispose ( ) ;
847876
848- let authority : ResolvedAuthority ;
849- if ( ExtHostManagedResolvedAuthority . isManagedResolvedAuthority ( result ) ) {
850- // The socket factory is identified by the `resolveAttempt`, since that is a number which
851- // always increments and is unique over all resolve() calls in a workbench session.
852- const socketFactoryId = resolveAttempt ;
877+ const tunnelInformation : TunnelInformation = {
878+ environmentTunnels : result . environmentTunnels ,
879+ features : result . tunnelFeatures
880+ } ;
853881
854- // There is only on managed socket factory at a time, so we can just overwrite the old one.
855- this . _extHostManagedSockets . setFactory ( socketFactoryId , result . makeConnection ) ;
882+ // Split merged API result into separate authority/options
883+ const options : ResolvedOptions = {
884+ extensionHostEnv : result . extensionHostEnv ,
885+ isTrusted : result . isTrusted ,
886+ authenticationSession : result . authenticationSessionForInitializingExtensions ? { id : result . authenticationSessionForInitializingExtensions . id , providerId : result . authenticationSessionForInitializingExtensions . providerId } : undefined
887+ } ;
856888
857- authority = {
858- authority : remoteAuthority ,
859- connectTo : new ManagedRemoteConnection ( socketFactoryId ) ,
860- connectionToken : result . connectionToken
861- } ;
862- } else {
863- authority = {
864- authority : remoteAuthority ,
865- connectTo : new WebSocketRemoteConnection ( result . host , result . port ) ,
866- connectionToken : result . connectionToken
867- } ;
868- }
889+ // extension are not required to return an instance of ResolvedAuthority or ManagedResolvedAuthority, so don't use `instanceof`
890+ logInfo ( `returned ${ ExtHostManagedResolvedAuthority . isManagedResolvedAuthority ( result ) ? 'managed authority' : `${ result . host } :${ result . port } ` } ` ) ;
869891
870- return {
871- type : 'ok' ,
872- value : {
873- authority : authority as Dto < ResolvedAuthority > ,
874- options,
875- tunnelInformation,
876- }
892+ let authority : ResolvedAuthority ;
893+ if ( ExtHostManagedResolvedAuthority . isManagedResolvedAuthority ( result ) ) {
894+ // The socket factory is identified by the `resolveAttempt`, since that is a number which
895+ // always increments and is unique over all resolve() calls in a workbench session.
896+ const socketFactoryId = resolveAttempt ;
897+
898+ // There is only on managed socket factory at a time, so we can just overwrite the old one.
899+ this . _extHostManagedSockets . setFactory ( socketFactoryId , result . makeConnection ) ;
900+
901+ authority = {
902+ authority : remoteAuthorityChain ,
903+ connectTo : new ManagedRemoteConnection ( socketFactoryId ) ,
904+ connectionToken : result . connectionToken
905+ } ;
906+ } else {
907+ authority = {
908+ authority : remoteAuthorityChain ,
909+ connectTo : new WebSocketRemoteConnection ( result . host , result . port ) ,
910+ connectionToken : result . connectionToken
877911 } ;
878- } catch ( err ) {
879- performance . mark ( `code/extHost/didResolveAuthorityError/${ authorityPrefix } ` ) ;
880- intervalLogger . dispose ( ) ;
881- logError ( `returned an error` , err ) ;
882- if ( err instanceof RemoteAuthorityResolverError ) {
883- return {
884- type : 'error' ,
885- error : {
886- code : err . _code ,
887- message : err . _message ,
888- detail : err . _detail
889- }
890- } ;
891- }
892- throw err ;
893912 }
913+
914+ return {
915+ type : 'ok' ,
916+ value : {
917+ authority : authority as Dto < ResolvedAuthority > ,
918+ options,
919+ tunnelInformation,
920+ }
921+ } ;
894922 }
895923
896924 public async $getCanonicalURI ( remoteAuthority : string , uriComponents : UriComponents ) : Promise < UriComponents | null > {
@@ -1047,6 +1075,7 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic
10471075 getExtensionRegistry ( ) : Promise < ExtensionDescriptionRegistry > ;
10481076 getExtensionPathIndex ( ) : Promise < ExtensionPaths > ;
10491077 registerRemoteAuthorityResolver ( authorityPrefix : string , resolver : vscode . RemoteAuthorityResolver ) : vscode . Disposable ;
1078+ getRemoteExecServer ( authority : string ) : Promise < vscode . ExecServer | undefined > ;
10501079
10511080 onDidChangeRemoteConnectionData : Event < void > ;
10521081 getRemoteConnectionData ( ) : IRemoteConnectionData | null ;
0 commit comments