mirror of
https://github.com/PowerShell/PowerShell.git
synced 2024-11-23 01:34:19 +08:00
Support Unix-Socket in WebCmdlets (#19343)
This commit is contained in:
parent
8a89fad7df
commit
e8ce6a5b1f
@ -1050,6 +1050,7 @@ ubuntu22.04
|
|||||||
uint
|
uint
|
||||||
un-versioned
|
un-versioned
|
||||||
unicode
|
unicode
|
||||||
|
UnixSocket
|
||||||
unregister-event
|
unregister-event
|
||||||
unregister-packagesource
|
unregister-packagesource
|
||||||
unregister-psrepository
|
unregister-psrepository
|
||||||
|
@ -1161,8 +1161,9 @@ function Publish-PSTestTools {
|
|||||||
$tools = @(
|
$tools = @(
|
||||||
@{ Path="${PSScriptRoot}/test/tools/TestAlc"; Output="library" }
|
@{ Path="${PSScriptRoot}/test/tools/TestAlc"; Output="library" }
|
||||||
@{ Path="${PSScriptRoot}/test/tools/TestExe"; Output="exe" }
|
@{ Path="${PSScriptRoot}/test/tools/TestExe"; Output="exe" }
|
||||||
@{ Path="${PSScriptRoot}/test/tools/WebListener"; Output="exe" }
|
|
||||||
@{ Path="${PSScriptRoot}/test/tools/TestService"; Output="exe" }
|
@{ Path="${PSScriptRoot}/test/tools/TestService"; Output="exe" }
|
||||||
|
@{ Path="${PSScriptRoot}/test/tools/UnixSocket"; Output="exe" }
|
||||||
|
@{ Path="${PSScriptRoot}/test/tools/WebListener"; Output="exe" }
|
||||||
)
|
)
|
||||||
|
|
||||||
$Options = Get-PSOptions -DefaultToNew
|
$Options = Get-PSOptions -DefaultToNew
|
||||||
|
@ -11,6 +11,7 @@ using System.Management.Automation;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
@ -366,14 +367,9 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
[Parameter(Mandatory = true, ParameterSetName = "CustomMethodNoProxy")]
|
[Parameter(Mandatory = true, ParameterSetName = "CustomMethodNoProxy")]
|
||||||
[Alias("CM")]
|
[Alias("CM")]
|
||||||
[ValidateNotNullOrEmpty]
|
[ValidateNotNullOrEmpty]
|
||||||
public virtual string CustomMethod
|
public virtual string CustomMethod { get => _customMethod; set => _customMethod = value.ToUpperInvariant(); }
|
||||||
{
|
|
||||||
get => _custommethod;
|
|
||||||
|
|
||||||
set => _custommethod = value.ToUpperInvariant();
|
private string _customMethod;
|
||||||
}
|
|
||||||
|
|
||||||
private string _custommethod;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the PreserveHttpMethodOnRedirect property.
|
/// Gets or sets the PreserveHttpMethodOnRedirect property.
|
||||||
@ -381,6 +377,13 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public virtual SwitchParameter PreserveHttpMethodOnRedirect { get; set; }
|
public virtual SwitchParameter PreserveHttpMethodOnRedirect { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the UnixSocket property.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[ValidateNotNullOrEmpty]
|
||||||
|
public virtual UnixDomainSocketEndPoint UnixSocket { get; set; }
|
||||||
|
|
||||||
#endregion Method
|
#endregion Method
|
||||||
|
|
||||||
#region NoProxy
|
#region NoProxy
|
||||||
@ -1019,6 +1022,8 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
WebSession.MaximumRedirection = MaximumRedirection;
|
WebSession.MaximumRedirection = MaximumRedirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebSession.UnixSocket = UnixSocket;
|
||||||
|
|
||||||
WebSession.SkipCertificateCheck = SkipCertificateCheck.IsPresent;
|
WebSession.SkipCertificateCheck = SkipCertificateCheck.IsPresent;
|
||||||
|
|
||||||
// Store the other supplied headers
|
// Store the other supplied headers
|
||||||
|
@ -7,6 +7,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -33,6 +34,7 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
private bool _noProxy;
|
private bool _noProxy;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private TimeSpan _connectionTimeout;
|
private TimeSpan _connectionTimeout;
|
||||||
|
private UnixDomainSocketEndPoint? _unixSocket;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains true if an existing HttpClient had to be disposed and recreated since the WebSession was last used.
|
/// Contains true if an existing HttpClient had to be disposed and recreated since the WebSession was last used.
|
||||||
@ -144,6 +146,8 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
|
|
||||||
internal TimeSpan ConnectionTimeout { set => SetStructVar(ref _connectionTimeout, value); }
|
internal TimeSpan ConnectionTimeout { set => SetStructVar(ref _connectionTimeout, value); }
|
||||||
|
|
||||||
|
internal UnixDomainSocketEndPoint UnixSocket { set => SetClassVar(ref _unixSocket, value); }
|
||||||
|
|
||||||
internal bool NoProxy
|
internal bool NoProxy
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
@ -195,7 +199,18 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
|
|
||||||
private HttpClient CreateHttpClient()
|
private HttpClient CreateHttpClient()
|
||||||
{
|
{
|
||||||
HttpClientHandler handler = new();
|
SocketsHttpHandler handler = new();
|
||||||
|
|
||||||
|
if (_unixSocket is not null)
|
||||||
|
{
|
||||||
|
handler.ConnectCallback = async (context, token) =>
|
||||||
|
{
|
||||||
|
Socket socket = new(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
|
||||||
|
await socket.ConnectAsync(_unixSocket).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return new NetworkStream(socket, ownsSocket: false);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
handler.CookieContainer = Cookies;
|
handler.CookieContainer = Cookies;
|
||||||
handler.AutomaticDecompression = DecompressionMethods.All;
|
handler.AutomaticDecompression = DecompressionMethods.All;
|
||||||
@ -204,9 +219,9 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
{
|
{
|
||||||
handler.Credentials = Credentials;
|
handler.Credentials = Credentials;
|
||||||
}
|
}
|
||||||
else
|
else if (UseDefaultCredentials)
|
||||||
{
|
{
|
||||||
handler.UseDefaultCredentials = UseDefaultCredentials;
|
handler.Credentials = CredentialCache.DefaultCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_noProxy)
|
if (_noProxy)
|
||||||
@ -220,13 +235,12 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
|
|
||||||
if (Certificates is not null)
|
if (Certificates is not null)
|
||||||
{
|
{
|
||||||
handler.ClientCertificates.AddRange(Certificates);
|
handler.SslOptions.ClientCertificates = new X509CertificateCollection(Certificates);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_skipCertificateCheck)
|
if (_skipCertificateCheck)
|
||||||
{
|
{
|
||||||
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; };
|
||||||
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.AllowAutoRedirect = _allowAutoRedirect;
|
handler.AllowAutoRedirect = _allowAutoRedirect;
|
||||||
@ -235,9 +249,9 @@ namespace Microsoft.PowerShell.Commands
|
|||||||
handler.MaxAutomaticRedirections = MaximumRedirection;
|
handler.MaxAutomaticRedirections = MaximumRedirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.SslProtocols = (SslProtocols)_sslProtocol;
|
handler.SslOptions.EnabledSslProtocols = (SslProtocols)_sslProtocol;
|
||||||
|
|
||||||
// Check timeout setting (in seconds instead of milliseconds as in HttpWebRequest)
|
// Check timeout setting (in seconds)
|
||||||
return new HttpClient(handler)
|
return new HttpClient(handler)
|
||||||
{
|
{
|
||||||
Timeout = _connectionTimeout
|
Timeout = _connectionTimeout
|
||||||
|
@ -4465,6 +4465,26 @@ Describe 'Invoke-WebRequest and Invoke-RestMethod support Cancellation through C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Describe "Web cmdlets Unix Sockets tests" -Tags "CI", "RequireAdminOnWindows" {
|
||||||
|
BeforeAll {
|
||||||
|
$unixSocket = Get-UnixSocketName
|
||||||
|
$WebListener = Start-UnixSocket $unixSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Execute Invoke-WebRequest with -UnixSocket" {
|
||||||
|
$uri = Get-UnixSocketUri
|
||||||
|
$result = Invoke-WebRequest $uri -UnixSocket $unixSocket
|
||||||
|
$result.StatusCode | Should -Be "200"
|
||||||
|
$result.Content | Should -Be "Hello World Unix Socket."
|
||||||
|
}
|
||||||
|
|
||||||
|
It "Execute Invoke-RestMethod with -UnixSocket" {
|
||||||
|
$uri = Get-UnixSocketUri
|
||||||
|
$result = Invoke-RestMethod $uri -UnixSocket $unixSocket
|
||||||
|
$result | Should -Be "Hello World Unix Socket."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Describe 'Invoke-WebRequest and Invoke-RestMethod support OperationTimeoutSeconds' -Tags "CI", "RequireAdminOnWindows" {
|
Describe 'Invoke-WebRequest and Invoke-RestMethod support OperationTimeoutSeconds' -Tags "CI", "RequireAdminOnWindows" {
|
||||||
BeforeAll {
|
BeforeAll {
|
||||||
$oldProgress = $ProgressPreference
|
$oldProgress = $ProgressPreference
|
||||||
|
17
test/tools/Modules/UnixSocket/README.md
Normal file
17
test/tools/Modules/UnixSocket/README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# UnixSocket Module
|
||||||
|
|
||||||
|
A PowerShell module for managing the UnixSocket App.
|
||||||
|
|
||||||
|
## Running UnixSocket
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Import-Module .\build.psm1
|
||||||
|
Publish-PSTestTools
|
||||||
|
$Listener = Start-UnixSocket
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stopping UnixSocket
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Stop-UnixSocket
|
||||||
|
```
|
17
test/tools/Modules/UnixSocket/UnixSocket.psd1
Normal file
17
test/tools/Modules/UnixSocket/UnixSocket.psd1
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@{
|
||||||
|
ModuleVersion = '1.0.0'
|
||||||
|
GUID = '86471f04-5b94-4136-a299-caf98464a06b'
|
||||||
|
Author = 'PowerShell'
|
||||||
|
Description = 'An UnixSocket Server for testing purposes'
|
||||||
|
RootModule = 'UnixSocket.psm1'
|
||||||
|
RequiredModules = @()
|
||||||
|
FunctionsToExport = @(
|
||||||
|
'Get-UnixSocket'
|
||||||
|
'Get-UnixSocketName'
|
||||||
|
'Get-UnixSocketUri'
|
||||||
|
'Start-UnixSocket'
|
||||||
|
'Stop-UnixSocket'
|
||||||
|
)
|
||||||
|
AliasesToExport = @()
|
||||||
|
CmdletsToExport = @()
|
||||||
|
}
|
125
test/tools/Modules/UnixSocket/UnixSocket.psm1
Normal file
125
test/tools/Modules/UnixSocket/UnixSocket.psm1
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# Licensed under the MIT License.
|
||||||
|
|
||||||
|
Class UnixSocket
|
||||||
|
{
|
||||||
|
[System.Management.Automation.Job]$Job
|
||||||
|
|
||||||
|
UnixSocket () { }
|
||||||
|
|
||||||
|
[String] GetStatus()
|
||||||
|
{
|
||||||
|
return $this.Job.JobStateInfo.State
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnixSocket]$UnixSocket
|
||||||
|
|
||||||
|
function Get-UnixSocket
|
||||||
|
{
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||||
|
[OutputType([UnixSocket])]
|
||||||
|
param()
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
return [UnixSocket]$Script:UnixSocket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Start-UnixSocket
|
||||||
|
{
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||||
|
[OutputType([UnixSocket])]
|
||||||
|
param([string] $socketPath)
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
$runningListener = Get-UnixSocket
|
||||||
|
if ($null -ne $runningListener -and $runningListener.GetStatus() -eq 'Running')
|
||||||
|
{
|
||||||
|
return $runningListener
|
||||||
|
}
|
||||||
|
|
||||||
|
$initTimeoutSeconds = 25
|
||||||
|
$appExe = (Get-Command UnixSocket).Path
|
||||||
|
$initCompleteMessage = 'Now listening on'
|
||||||
|
$sleepMilliseconds = 100
|
||||||
|
|
||||||
|
$Job = Start-Job {
|
||||||
|
$path = Split-Path -Parent (Get-Command UnixSocket).Path -Verbose
|
||||||
|
Push-Location $path -Verbose
|
||||||
|
'appEXE: {0}' -f $using:appExe
|
||||||
|
$env:ASPNETCORE_ENVIRONMENT = 'Development'
|
||||||
|
& $using:appExe $using:socketPath
|
||||||
|
}
|
||||||
|
|
||||||
|
$Script:UnixSocket = [UnixSocket]@{
|
||||||
|
Job = $Job
|
||||||
|
}
|
||||||
|
|
||||||
|
# Count iterations of $sleepMilliseconds instead of using system time to work around possible CI VM sleep/delays
|
||||||
|
$sleepCountRemaining = $initTimeoutSeconds * 1000 / $sleepMilliseconds
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Start-Sleep -Milliseconds $sleepMilliseconds
|
||||||
|
$initStatus = $Job.ChildJobs[0].Output | Out-String
|
||||||
|
$isRunning = $initStatus -match $initCompleteMessage
|
||||||
|
$sleepCountRemaining--
|
||||||
|
}
|
||||||
|
while (-not $isRunning -and $sleepCountRemaining -gt 0)
|
||||||
|
|
||||||
|
if (-not $isRunning)
|
||||||
|
{
|
||||||
|
$jobErrors = $Job.ChildJobs[0].Error | Out-String
|
||||||
|
$jobOutput = $Job.ChildJobs[0].Output | Out-String
|
||||||
|
$jobVerbose = $Job.ChildJobs[0].Verbose | Out-String
|
||||||
|
$Job | Stop-Job
|
||||||
|
$Job | Remove-Job -Force
|
||||||
|
$message = 'UnixSocket did not start before the timeout was reached.{0}Errors:{0}{1}{0}Output:{0}{2}{0}Verbose:{0}{3}' -f ([System.Environment]::NewLine), $jobErrors, $jobOutput, $jobVerbose
|
||||||
|
throw $message
|
||||||
|
}
|
||||||
|
return $Script:UnixSocket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Stop-UnixSocket
|
||||||
|
{
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||||
|
[OutputType([Void])]
|
||||||
|
param()
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
$Script:UnixSocket.Job | Stop-Job -PassThru | Remove-Job
|
||||||
|
$Script:UnixSocket = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-UnixSocketName {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
process {
|
||||||
|
return [System.IO.Path]::Join([System.IO.Path]::GetTempPath(), [System.IO.Path]::ChangeExtension([System.IO.Path]::GetRandomFileName(), "sock"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-UnixSocketUri {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([Uri])]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
process {
|
||||||
|
$runningListener = Get-UnixSocket
|
||||||
|
if ($null -eq $runningListener -or $runningListener.GetStatus() -ne 'Running')
|
||||||
|
{
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
$Uri = [System.UriBuilder]::new()
|
||||||
|
$Uri.Host = '127.0.0.0'
|
||||||
|
|
||||||
|
return [Uri]$Uri.ToString()
|
||||||
|
}
|
||||||
|
}
|
27
test/tools/UnixSocket/UnixSocket.cs
Normal file
27
test/tools/UnixSocket/UnixSocket.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using static Microsoft.AspNetCore.Hosting.WebHostBuilderKestrelExtensions;
|
||||||
|
|
||||||
|
namespace UnixSocket
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
WebApplicationBuilder builder = WebApplication.CreateBuilder();
|
||||||
|
builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
{
|
||||||
|
options.ListenUnixSocket(args[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
app.MapGet("/", () => "Hello World Unix Socket.");
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
test/tools/UnixSocket/UnixSocket.csproj
Normal file
14
test/tools/UnixSocket/UnixSocket.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<Import Project="..\..\Test.Common.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Description>A very simple ASP.NET Core app to provide an UnixSocket server for testing.</Description>
|
||||||
|
<AssemblyName>UnixSocket</AssemblyName>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TieredCompilation>true</TieredCompilation>
|
||||||
|
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
|
||||||
|
<RuntimeIdentifiers>win7-x86;win7-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
Loading…
Reference in New Issue
Block a user