@@ -44,6 +44,12 @@ export enum TunnelType {
44
44
Add = 'Add'
45
45
}
46
46
47
+ export enum TunnelCloseReason {
48
+ Other = 'Other' ,
49
+ User = 'User' ,
50
+ AutoForwardEnd = 'AutoForwardEnd' ,
51
+ }
52
+
47
53
export interface ITunnelItem {
48
54
tunnelType : TunnelType ;
49
55
remoteHost : string ;
@@ -426,6 +432,7 @@ export class TunnelModel extends Disposable {
426
432
private restoreListener : IDisposable | undefined ;
427
433
private knownPortsRestoreValue : string | undefined ;
428
434
private unrestoredExtensionTunnels : Map < string , Tunnel > = new Map ( ) ;
435
+ private sessionCachedProperties : Map < string , Partial < TunnelProperties > > = new Map ( ) ;
429
436
430
437
private portAttributesProviders : PortAttributesProvider [ ] = [ ] ;
431
438
@@ -500,11 +507,11 @@ export class TunnelModel extends Disposable {
500
507
this . _onForwardPort . fire ( this . forwarded . get ( key ) ! ) ;
501
508
} ) ) ;
502
509
this . _register ( this . tunnelService . onTunnelClosed ( address => {
503
- return this . onTunnelClosed ( address ) ;
510
+ return this . onTunnelClosed ( address , TunnelCloseReason . Other ) ;
504
511
} ) ) ;
505
512
}
506
513
507
- private async onTunnelClosed ( address : { host : string ; port : number } ) {
514
+ private async onTunnelClosed ( address : { host : string ; port : number } , reason : TunnelCloseReason ) {
508
515
const key = makeAddress ( address . host , address . port ) ;
509
516
if ( this . forwarded . has ( key ) ) {
510
517
this . forwarded . delete ( key ) ;
@@ -631,7 +638,9 @@ export class TunnelModel extends Disposable {
631
638
632
639
const key = makeAddress ( tunnelProperties . remote . host , tunnelProperties . remote . port ) ;
633
640
this . inProgress . set ( key , true ) ;
634
- let tunnel = await this . tunnelService . openTunnel ( addressProvider , tunnelProperties . remote . host , tunnelProperties . remote . port , undefined , localPort , ( ! tunnelProperties . elevateIfNeeded ) ? attributes ?. elevateIfNeeded : tunnelProperties . elevateIfNeeded , tunnelProperties . privacy , attributes ?. protocol ) ;
641
+ tunnelProperties = this . mergeCachedAndUnrestoredProperties ( key , tunnelProperties ) ;
642
+
643
+ const tunnel = await this . tunnelService . openTunnel ( addressProvider , tunnelProperties . remote . host , tunnelProperties . remote . port , undefined , localPort , ( ! tunnelProperties . elevateIfNeeded ) ? attributes ?. elevateIfNeeded : tunnelProperties . elevateIfNeeded , tunnelProperties . privacy , attributes ?. protocol ) ;
635
644
if ( tunnel && tunnel . localAddress ) {
636
645
const matchingCandidate = mapHasAddressLocalhostOrAllInterfaces < CandidatePort > ( this . _candidates ?? new Map ( ) , tunnelProperties . remote . host , tunnelProperties . remote . port ) ;
637
646
const protocol = ( tunnel . protocol ?
@@ -658,37 +667,63 @@ export class TunnelModel extends Disposable {
658
667
await this . storeForwarded ( ) ;
659
668
await this . showPortMismatchModalIfNeeded ( tunnel , localPort , attributes ) ;
660
669
this . _onForwardPort . fire ( newForward ) ;
661
- if ( this . unrestoredExtensionTunnels . has ( key ) ) {
662
- const updateProps = this . unrestoredExtensionTunnels . get ( key ) ;
663
- this . unrestoredExtensionTunnels . delete ( key ) ;
664
- if ( updateProps ) {
665
- tunnel = await this . forward ( {
666
- remote : { host : newForward . remoteHost , port : newForward . remotePort } ,
667
- local : newForward . localPort ,
668
- name : newForward . name ,
669
- privacy : updateProps . privacy ,
670
- elevateIfNeeded : true ,
671
- } ) ;
672
- }
673
- }
674
670
return tunnel ;
675
671
}
676
672
this . inProgress . delete ( key ) ;
677
673
} else {
678
- const newName = attributes ?. label ?? tunnelProperties . name ;
679
- if ( newName !== existingTunnel . name ) {
680
- existingTunnel . name = newName ;
674
+ return this . mergeAttributesIntoExistingTunnel ( existingTunnel , tunnelProperties , attributes ) ;
675
+ }
676
+
677
+ return undefined ;
678
+ }
679
+
680
+ private mergeCachedAndUnrestoredProperties ( key : string , tunnelProperties : TunnelProperties ) : TunnelProperties {
681
+ const map = this . unrestoredExtensionTunnels . has ( key ) ? this . unrestoredExtensionTunnels : ( this . sessionCachedProperties . has ( key ) ? this . sessionCachedProperties : undefined ) ;
682
+ if ( map ) {
683
+ const updateProps = map . get ( key ) ! ;
684
+ map . delete ( key ) ;
685
+ if ( updateProps ) {
686
+ tunnelProperties . name = updateProps . name ?? tunnelProperties . name ;
687
+ tunnelProperties . local = ( ( 'local' in updateProps ) ? updateProps . local : ( ( 'localPort' in updateProps ) ? updateProps . localPort : undefined ) ) ?? tunnelProperties . local ;
688
+ tunnelProperties . privacy = updateProps . privacy ?? tunnelProperties . privacy ;
689
+ }
690
+ }
691
+ return tunnelProperties ;
692
+ }
693
+
694
+ private async mergeAttributesIntoExistingTunnel ( existingTunnel : Tunnel , tunnelProperties : TunnelProperties , attributes : Attributes | undefined ) {
695
+ const newName = attributes ?. label ?? tunnelProperties . name ;
696
+ enum MergedAttributeAction {
697
+ None = 0 ,
698
+ Fire = 1 ,
699
+ Reopen = 2
700
+ }
701
+ let mergedAction = MergedAttributeAction . None ;
702
+ if ( newName !== existingTunnel . name ) {
703
+ existingTunnel . name = newName ;
704
+ mergedAction = MergedAttributeAction . Fire ;
705
+ }
706
+ // Source of existing tunnel wins so that original source is maintained
707
+ if ( ( attributes ?. protocol || ( existingTunnel . protocol !== TunnelProtocol . Http ) ) && ( attributes ?. protocol !== existingTunnel . protocol ) ) {
708
+ tunnelProperties . source = existingTunnel . source ;
709
+ mergedAction = MergedAttributeAction . Reopen ;
710
+ }
711
+ // New privacy value wins
712
+ if ( tunnelProperties . privacy && ( existingTunnel . privacy !== tunnelProperties . privacy ) ) {
713
+ mergedAction = MergedAttributeAction . Reopen ;
714
+ }
715
+ switch ( mergedAction ) {
716
+ case MergedAttributeAction . Fire : {
681
717
this . _onForwardPort . fire ( ) ;
718
+ break ;
682
719
}
683
- if ( ( attributes ?. protocol || ( existingTunnel . protocol !== TunnelProtocol . Http ) ) && ( attributes ?. protocol !== existingTunnel . protocol ) ) {
684
- await this . close ( existingTunnel . remoteHost , existingTunnel . remotePort ) ;
685
- tunnelProperties . source = existingTunnel . source ;
720
+ case MergedAttributeAction . Reopen : {
721
+ await this . close ( existingTunnel . remoteHost , existingTunnel . remotePort , TunnelCloseReason . User ) ;
686
722
await this . forward ( tunnelProperties , attributes ) ;
687
723
}
688
- return mapHasAddressLocalhostOrAllInterfaces ( this . remoteTunnels , tunnelProperties . remote . host , tunnelProperties . remote . port ) ;
689
724
}
690
725
691
- return undefined ;
726
+ return mapHasAddressLocalhostOrAllInterfaces ( this . remoteTunnels , tunnelProperties . remote . host , tunnelProperties . remote . port ) ;
692
727
}
693
728
694
729
async name ( host : string , port : number , name : string ) {
@@ -705,9 +740,18 @@ export class TunnelModel extends Disposable {
705
740
}
706
741
}
707
742
708
- async close ( host : string , port : number ) : Promise < void > {
743
+ async close ( host : string , port : number , reason : TunnelCloseReason ) : Promise < void > {
744
+ const key = makeAddress ( host , port ) ;
745
+ const oldTunnel = this . forwarded . get ( key ) ! ;
746
+ if ( reason === TunnelCloseReason . AutoForwardEnd ) {
747
+ this . sessionCachedProperties . set ( key , {
748
+ local : oldTunnel . localPort ,
749
+ name : oldTunnel . name ,
750
+ privacy : oldTunnel . privacy ,
751
+ } ) ;
752
+ }
709
753
await this . tunnelService . closeTunnel ( host , port ) ;
710
- return this . onTunnelClosed ( { host, port } ) ;
754
+ return this . onTunnelClosed ( { host, port } , reason ) ;
711
755
}
712
756
713
757
address ( host : string , port : number ) : string | undefined {
@@ -929,7 +973,7 @@ export interface IRemoteExplorerService {
929
973
setEditable ( tunnelItem : ITunnelItem | undefined , editId : TunnelEditId , data : IEditableData | null ) : void ;
930
974
getEditableData ( tunnelItem : ITunnelItem | undefined , editId ?: TunnelEditId ) : IEditableData | undefined ;
931
975
forward ( tunnelProperties : TunnelProperties , attributes ?: Attributes | null ) : Promise < RemoteTunnel | undefined > ;
932
- close ( remote : { host : string ; port : number } ) : Promise < void > ;
976
+ close ( remote : { host : string ; port : number } , reason : TunnelCloseReason ) : Promise < void > ;
933
977
setTunnelInformation ( tunnelInformation : TunnelInformation | undefined ) : void ;
934
978
setCandidateFilter ( filter : ( ( candidates : CandidatePort [ ] ) => Promise < CandidatePort [ ] > ) | undefined ) : IDisposable ;
935
979
onFoundNewCandidates ( candidates : CandidatePort [ ] ) : void ;
@@ -991,8 +1035,8 @@ class RemoteExplorerService implements IRemoteExplorerService {
991
1035
return this . tunnelModel . forward ( tunnelProperties , attributes ) ;
992
1036
}
993
1037
994
- close ( remote : { host : string ; port : number } ) : Promise < void > {
995
- return this . tunnelModel . close ( remote . host , remote . port ) ;
1038
+ close ( remote : { host : string ; port : number } , reason : TunnelCloseReason ) : Promise < void > {
1039
+ return this . tunnelModel . close ( remote . host , remote . port , reason ) ;
996
1040
}
997
1041
998
1042
setTunnelInformation ( tunnelInformation : TunnelInformation | undefined ) : void {
0 commit comments