Skip to content

Commit 487ad1d

Browse files
ant-druhaTylerLeonhardt
authored andcommitted
Adds ability to use separate pipes for reading and writing (#785)
* Adds ability to use separate pipes for reading and writing Some clients (e.g. Java on Windows) have blocking File I/O API, thus it is not possible for them to use single named pipe for reading and writing asynchronously. It will add the ability to use separate pipes for reading and writing when the client specifies the "-SplitInOutPipes" switch for the startup script. * Use Path.DirectorySeparatorChar as read/write pipe name separator for transport config endpoint * WritePipeServer: pipe direction should be "Out" * Organize startup script parameters by parameter sets * EditorServiceTransportType: Introduce separate properties for read/write pipes * [ #785 PR ] Cleanup: refactorings, code style and reformat * remove e in excpe
1 parent fa1a4f5 commit 487ad1d

File tree

5 files changed

+284
-123
lines changed

5 files changed

+284
-123
lines changed

module/PowerShellEditorServices/PowerShellEditorServices.psm1

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,37 @@ function Start-EditorServicesHost {
3131
[string]
3232
$HostVersion,
3333

34+
[Parameter(ParameterSetName="Stdio",Mandatory=$true)]
3435
[switch]
3536
$Stdio,
3637

38+
[Parameter(ParameterSetName="NamedPipe",Mandatory=$true)]
39+
[ValidateNotNullOrEmpty()]
3740
[string]
3841
$LanguageServiceNamedPipe,
3942

43+
[Parameter(ParameterSetName="NamedPipe")]
4044
[string]
4145
$DebugServiceNamedPipe,
4246

47+
[Parameter(ParameterSetName="NamedPipeSimplex",Mandatory=$true)]
48+
[ValidateNotNullOrEmpty()]
49+
[string]
50+
$LanguageServiceInNamedPipe,
51+
52+
[Parameter(ParameterSetName="NamedPipeSimplex",Mandatory=$true)]
53+
[ValidateNotNullOrEmpty()]
54+
[string]
55+
$LanguageServiceOutNamedPipe,
56+
57+
[Parameter(ParameterSetName="NamedPipeSimplex")]
58+
[string]
59+
$DebugServiceInNamedPipe,
60+
61+
[Parameter(ParameterSetName="NamedPipeSimplex")]
62+
[string]
63+
$DebugServiceOutNamedPipe,
64+
4365
[ValidateNotNullOrEmpty()]
4466
[string]
4567
$BundledModulesPath,
@@ -99,19 +121,32 @@ function Start-EditorServicesHost {
99121
$debugServiceConfig =
100122
Microsoft.PowerShell.Utility\New-Object Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportConfig
101123

102-
if ($Stdio.IsPresent) {
103-
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Stdio
104-
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Stdio
105-
}
106-
107-
if ($LanguageServiceNamedPipe) {
108-
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
109-
$languageServiceConfig.Endpoint = "$LanguageServiceNamedPipe"
110-
}
111-
112-
if ($DebugServiceNamedPipe) {
113-
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
114-
$debugServiceConfig.Endpoint = "$DebugServiceNamedPipe"
124+
switch ($PSCmdlet.ParameterSetName) {
125+
"Stdio" {
126+
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Stdio
127+
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Stdio
128+
break
129+
}
130+
"NamedPipe" {
131+
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
132+
$languageServiceConfig.InOutPipeName = "$LanguageServiceNamedPipe"
133+
if ($DebugServiceNamedPipe) {
134+
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
135+
$debugServiceConfig.InOutPipeName = "$DebugServiceNamedPipe"
136+
}
137+
break
138+
}
139+
"NamedPipeSimplex" {
140+
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
141+
$languageServiceConfig.InPipeName = $LanguageServiceInNamedPipe
142+
$languageServiceConfig.OutPipeName = $LanguageServiceOutNamedPipe
143+
if ($DebugServiceInNamedPipe -and $DebugServiceOutNamedPipe) {
144+
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
145+
$debugServiceConfig.InPipeName = $DebugServiceInNamedPipe
146+
$debugServiceConfig.OutPipeName = $DebugServiceOutNamedPipe
147+
}
148+
break
149+
}
115150
}
116151

117152
if ($DebugServiceOnly.IsPresent) {

module/PowerShellEditorServices/Start-EditorServices.ps1

Lines changed: 133 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# Services GitHub repository:
1616
#
1717
# https://github.com/PowerShell/PowerShellEditorServices/blob/master/module/PowerShellEditorServices/Start-EditorServices.ps1
18-
18+
[CmdletBinding(DefaultParameterSetName="NamedPipe")]
1919
param(
2020
[Parameter(Mandatory=$true)]
2121
[ValidateNotNullOrEmpty()]
@@ -65,14 +65,37 @@ param(
6565
[switch]
6666
$ConfirmInstall,
6767

68+
[Parameter(ParameterSetName="Stdio",Mandatory=$true)]
6869
[switch]
6970
$Stdio,
7071

72+
[Parameter(ParameterSetName="NamedPipe")]
7173
[string]
7274
$LanguageServicePipeName = $null,
7375

76+
[Parameter(ParameterSetName="NamedPipe")]
77+
[string]
78+
$DebugServicePipeName = $null,
79+
80+
[Parameter(ParameterSetName="NamedPipeSimplex")]
81+
[switch]
82+
$SplitInOutPipes,
83+
84+
[Parameter(ParameterSetName="NamedPipeSimplex")]
85+
[string]
86+
$LanguageServiceInPipeName,
87+
88+
[Parameter(ParameterSetName="NamedPipeSimplex")]
7489
[string]
75-
$DebugServicePipeName = $null
90+
$LanguageServiceOutPipeName,
91+
92+
[Parameter(ParameterSetName="NamedPipeSimplex")]
93+
[string]
94+
$DebugServiceInPipeName = $null,
95+
96+
[Parameter(ParameterSetName="NamedPipeSimplex")]
97+
[string]
98+
$DebugServiceOutPipeName = $null
7699
)
77100

78101
$DEFAULT_USER_MODE = "600"
@@ -173,8 +196,7 @@ function New-NamedPipeName {
173196

174197
# We try 10 times to find a valid pipe name
175198
for ($i = 0; $i -lt 10; $i++) {
176-
# add a guid to make the pipe unique
177-
$PipeName = "PSES_$([guid]::NewGuid())"
199+
$PipeName = "PSES_$([System.IO.Path]::GetRandomFileName())"
178200

179201
if ((Test-NamedPipeName -PipeName $PipeName)) {
180202
return $PipeName
@@ -246,6 +268,37 @@ function Set-NamedPipeMode {
246268
LogSection "Console Encoding"
247269
Log $OutputEncoding
248270

271+
function Test-NamedPipeName-OrCreate-IfNull {
272+
param(
273+
[string]
274+
$PipeName
275+
)
276+
if (-not $PipeName) {
277+
$PipeName = New-NamedPipeName
278+
}
279+
else {
280+
if (-not (Test-NamedPipeName -PipeName $PipeName)) {
281+
ExitWithError "Pipe name supplied is already taken: $PipeName"
282+
}
283+
}
284+
return $PipeName
285+
}
286+
287+
function Set-PipeFileResult {
288+
param (
289+
[Hashtable]
290+
$ResultTable,
291+
[string]
292+
$PipeNameKey,
293+
[string]
294+
$PipeNameValue
295+
)
296+
$ResultTable[$PipeNameKey] = Get-NamedPipePath -PipeName $PipeNameValue
297+
if ($IsLinux -or $IsMacOS) {
298+
Set-NamedPipeMode -PipeFile $ResultTable[$PipeNameKey]
299+
}
300+
}
301+
249302
# Add BundledModulesPath to $env:PSModulePath
250303
if ($BundledModulesPath) {
251304
$env:PSModulePath = $env:PSModulePath.TrimEnd([System.IO.Path]::PathSeparator) + [System.IO.Path]::PathSeparator + $BundledModulesPath
@@ -266,81 +319,93 @@ try {
266319

267320
Microsoft.PowerShell.Core\Import-Module PowerShellEditorServices -ErrorAction Stop
268321

269-
# Locate available port numbers for services
270-
# There could be only one service on Stdio channel
271-
272-
$languageServiceTransport = $null
273-
$debugServiceTransport = $null
274-
275-
if ($Stdio.IsPresent) {
276-
$languageServiceTransport = "Stdio"
277-
$debugServiceTransport = "Stdio"
278-
}
279-
else {
280-
$languageServiceTransport = "NamedPipe"
281-
$debugServiceTransport = "NamedPipe"
282-
if (-not $LanguageServicePipeName) {
283-
$LanguageServicePipeName = New-NamedPipeName
284-
}
285-
else {
286-
if (-not (Test-NamedPipeName -PipeName $LanguageServicePipeName)) {
287-
ExitWithError "Pipe name supplied is already taken: $LanguageServicePipeName"
288-
}
289-
}
290-
if (-not $DebugServicePipeName) {
291-
$DebugServicePipeName = New-NamedPipeName
292-
}
293-
else {
294-
if (-not (Test-NamedPipeName -PipeName $DebugServicePipeName)) {
295-
ExitWithError "Pipe name supplied is already taken: $DebugServicePipeName"
296-
}
297-
}
298-
}
299-
300322
if ($EnableConsoleRepl) {
301323
Write-Host "PowerShell Integrated Console`n"
302324
}
303325

304-
# Create the Editor Services host
305-
Log "Invoking Start-EditorServicesHost"
306-
$editorServicesHost =
307-
Start-EditorServicesHost `
308-
-HostName $HostName `
309-
-HostProfileId $HostProfileId `
310-
-HostVersion $HostVersion `
311-
-LogPath $LogPath `
312-
-LogLevel $LogLevel `
313-
-AdditionalModules $AdditionalModules `
314-
-LanguageServiceNamedPipe $LanguageServicePipeName `
315-
-DebugServiceNamedPipe $DebugServicePipeName `
316-
-Stdio:$Stdio.IsPresent`
317-
-BundledModulesPath $BundledModulesPath `
318-
-EnableConsoleRepl:$EnableConsoleRepl.IsPresent `
319-
-DebugServiceOnly:$DebugServiceOnly.IsPresent `
320-
-WaitForDebugger:$WaitForDebugger.IsPresent
321-
322-
# TODO: Verify that the service is started
323-
Log "Start-EditorServicesHost returned $editorServicesHost"
324-
325326
$resultDetails = @{
326-
"status" = "started";
327-
"languageServiceTransport" = $languageServiceTransport;
328-
"debugServiceTransport" = $debugServiceTransport;
327+
"status" = "not started";
328+
"languageServiceTransport" = $PSCmdlet.ParameterSetName;
329+
"debugServiceTransport" = $PSCmdlet.ParameterSetName;
329330
};
330331

331-
if ($LanguageServicePipeName) {
332-
$resultDetails["languageServicePipeName"] = Get-NamedPipePath -PipeName $LanguageServicePipeName
333-
if ($IsLinux -or $IsMacOS) {
334-
Set-NamedPipeMode -PipeFile $resultDetails["languageServicePipeName"]
332+
# Create the Editor Services host
333+
Log "Invoking Start-EditorServicesHost"
334+
# There could be only one service on Stdio channel
335+
# Locate available port numbers for services
336+
switch ($PSCmdlet.ParameterSetName) {
337+
"Stdio" {
338+
$editorServicesHost = Start-EditorServicesHost `
339+
-HostName $HostName `
340+
-HostProfileId $HostProfileId `
341+
-HostVersion $HostVersion `
342+
-LogPath $LogPath `
343+
-LogLevel $LogLevel `
344+
-AdditionalModules $AdditionalModules `
345+
-Stdio `
346+
-BundledModulesPath $BundledModulesPath `
347+
-EnableConsoleRepl:$EnableConsoleRepl.IsPresent `
348+
-DebugServiceOnly:$DebugServiceOnly.IsPresent `
349+
-WaitForDebugger:$WaitForDebugger.IsPresent
350+
break
335351
}
336-
}
337-
if ($DebugServicePipeName) {
338-
$resultDetails["debugServicePipeName"] = Get-NamedPipePath -PipeName $DebugServicePipeName
339-
if ($IsLinux -or $IsMacOS) {
340-
Set-NamedPipeMode -PipeFile $resultDetails["debugServicePipeName"]
352+
"NamedPipeSimplex" {
353+
$LanguageServiceInPipeName = Test-NamedPipeName-OrCreate-IfNull $LanguageServiceInPipeName
354+
$LanguageServiceOutPipeName = Test-NamedPipeName-OrCreate-IfNull $LanguageServiceOutPipeName
355+
$DebugServiceInPipeName = Test-NamedPipeName-OrCreate-IfNull $DebugServiceInPipeName
356+
$DebugServiceOutPipeName = Test-NamedPipeName-OrCreate-IfNull $DebugServiceOutPipeName
357+
358+
$editorServicesHost = Start-EditorServicesHost `
359+
-HostName $HostName `
360+
-HostProfileId $HostProfileId `
361+
-HostVersion $HostVersion `
362+
-LogPath $LogPath `
363+
-LogLevel $LogLevel `
364+
-AdditionalModules $AdditionalModules `
365+
-LanguageServiceInNamedPipe $LanguageServiceInPipeName `
366+
-LanguageServiceOutNamedPipe $LanguageServiceOutPipeName `
367+
-DebugServiceInNamedPipe $DebugServiceInPipeName `
368+
-DebugServiceOutNamedPipe $DebugServiceOutPipeName `
369+
-BundledModulesPath $BundledModulesPath `
370+
-EnableConsoleRepl:$EnableConsoleRepl.IsPresent `
371+
-DebugServiceOnly:$DebugServiceOnly.IsPresent `
372+
-WaitForDebugger:$WaitForDebugger.IsPresent
373+
374+
Set-PipeFileResult $resultDetails "languageServiceReadPipeName" $LanguageServiceInPipeName
375+
Set-PipeFileResult $resultDetails "languageServiceWritePipeName" $LanguageServiceOutPipeName
376+
Set-PipeFileResult $resultDetails "debugServiceReadPipeName" $DebugServiceInPipeName
377+
Set-PipeFileResult $resultDetails "debugServiceWritePipeName" $DebugServiceOutPipeName
378+
break
379+
}
380+
Default {
381+
$LanguageServicePipeName = Test-NamedPipeName-OrCreate-IfNull $LanguageServicePipeName
382+
$DebugServicePipeName = Test-NamedPipeName-OrCreate-IfNull $DebugServicePipeName
383+
384+
$editorServicesHost = Start-EditorServicesHost `
385+
-HostName $HostName `
386+
-HostProfileId $HostProfileId `
387+
-HostVersion $HostVersion `
388+
-LogPath $LogPath `
389+
-LogLevel $LogLevel `
390+
-AdditionalModules $AdditionalModules `
391+
-LanguageServiceNamedPipe $LanguageServicePipeName `
392+
-DebugServiceNamedPipe $DebugServicePipeName `
393+
-BundledModulesPath $BundledModulesPath `
394+
-EnableConsoleRepl:$EnableConsoleRepl.IsPresent `
395+
-DebugServiceOnly:$DebugServiceOnly.IsPresent `
396+
-WaitForDebugger:$WaitForDebugger.IsPresent
397+
398+
Set-PipeFileResult $resultDetails "languageServicePipeName" $LanguageServicePipeName
399+
Set-PipeFileResult $resultDetails "debugServicePipeName" $DebugServicePipeName
400+
break
341401
}
342402
}
343403

404+
# TODO: Verify that the service is started
405+
Log "Start-EditorServicesHost returned $editorServicesHost"
406+
407+
$resultDetails["status"] = "started"
408+
344409
# Notify the client that the services have started
345410
WriteSessionFile $resultDetails
346411

src/PowerShellEditorServices.Host/EditorServicesHost.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ public class EditorServiceTransportConfig
4444
/// For Stdio it's ignored.
4545
/// For NamedPipe it's the pipe name.
4646
/// </summary>
47-
public string Endpoint { get; set; }
47+
public string InOutPipeName { get; set; }
48+
public string OutPipeName { get; set; }
49+
public string InPipeName { get; set; }
50+
internal string Endpoint => OutPipeName != null && InPipeName != null ? $"In pipe: {InPipeName} Out pipe: {OutPipeName}" : $" InOut pipe: {InOutPipeName}";
4851
}
4952

5053
/// <summary>
@@ -463,7 +466,15 @@ private IServerListener CreateServiceListener(MessageProtocolType protocol, Edit
463466

464467
case EditorServiceTransportType.NamedPipe:
465468
{
466-
return new NamedPipeServerListener(protocol, config.Endpoint, this.logger);
469+
if (config.OutPipeName !=null && config.InPipeName !=null)
470+
{
471+
this.logger.Write(LogLevel.Verbose, $"Creating NamedPipeServerListener for ${protocol} protocol with two pipes: In: '{config.InPipeName}'. Out: '{config.OutPipeName}'");
472+
return new NamedPipeServerListener(protocol, config.InPipeName, config.OutPipeName, this.logger);
473+
}
474+
else
475+
{
476+
return new NamedPipeServerListener(protocol, config.InOutPipeName, this.logger);
477+
}
467478
}
468479

469480
default:

0 commit comments

Comments
 (0)