Last active
March 2, 2023 00:24
-
-
Save Tiberriver256/afb649f8543c86515adae3ce99ebbd6c to your computer and use it in GitHub Desktop.
Trying to get Kestrel working in PowerShell...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace Microsoft.AspNetCore.Builder; | |
using namespace Microsoft.AspNetCore.Http; | |
using namespace System; | |
using namespace Microsoft.AspNetCore.Hosting; | |
# If you don't have Visual Studio Installed this should work fine | |
# Install-Package Microsoft.AspNetCore -Source nuget.org -Destination .\.nuget\packages | |
# Install-Package Microsoft.AspNetCore.Server.Kestrel -Source nuget.org -Destination .\.nuget\packages | |
$LibFolder = "" | |
if( -not (Test-Path .\lib)){ | |
# If you do have Visual Studio installed and Install-Package is shot try this | |
Register-PackageSource -Name nuget.org-v2 -ProviderName NuGet -Location https://www.nuget.org/api/v2 | |
Install-Package Microsoft.AspNetCore -Source nuget.org-v2 -Destination .\.nuget\packages | |
Install-Package Microsoft.AspNetCore.Server.Kestrel -Source nuget.org-v2 -Destination .\.nuget\packages | |
$LibFolder = md lib | |
dir .\.nuget\packages\*\lib\*.dll -Recurse | foreach { Move-Item -Path $_.FullName -Destination .\lib\ -Force } | |
dir .\.nuget\packages\*\lib\*.xml -Recurse | foreach { Move-Item -Path $_.FullName -Destination .\lib\ -Force } | |
} else { | |
$LibFolder = Get-Item lib | |
} | |
Read-Host "Manual steps... Move the appropriate dll for libuv for your platform from the .nuget/packages/libuv folder to /lib" | |
[System.Environment]::CurrentDirectory = $LibFolder.FullName | |
Import-Module .\lib\Microsoft.AspNetCore.dll | |
Import-Module .\lib\Microsoft.AspNetCore.Diagnostics.dll | |
Import-Module .\lib\Microsoft.AspNetCore.Hosting.dll | |
Import-Module .\lib\Microsoft.AspNetCore.Server.Kestrel.dll | |
class StartUp { | |
[void] Configure([IApplicationBuilder]$App){ | |
[DeveloperExceptionPageExtensions]::UseDeveloperExceptionPage($App) | |
[RunExtensions]::Run($App, [Microsoft.AspNetCore.Http.RequestDelegate]( | |
[AsyncOps]::CreateAsyncDelegate({ | |
param($Context) | |
Write-Host "Hello!" | |
#return ([Microsoft.AspNetCore.Http.HttpResponseWritingExtensions]::WriteAsync($Context.Response, "Hello World", [Threading.CancellationToken]::None)) | |
}, [Microsoft.AspNetCore.Http.RequestDelegate]))) | |
} | |
} | |
#$Env:ASPNETCORE_ENVIRONMENT = "Development" | |
$WebHostBuilder = $Null | |
[IWebHostBuilder]$WebHostBuilder = [Microsoft.AspNetCore.WebHost]::CreateDefaultBuilder() | |
$WebHostBuilder = [WebHostBuilderExtensions]::UseStartup($WebHostBuilder, [StartUp]) | |
$WebHostBuilder = [WebHostBuilderKestrelExtensions]::UseKestrel($WebHostBuilder) | |
$WebHost = $WebHostBuilder.Build() | |
$Result = $WebHost.StartAsync() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace System.Collections.Generic | |
using namespace System.Collections.ObjectModel | |
using namespace System.Linq.Expressions | |
using namespace System.Management.Automation | |
using namespace System.Management.Automation.Runspaces | |
using namespace System.Threading | |
using namespace System.Threading.Tasks | |
# Static class that facilitates the use of traditional .NET async techniques in PowerShell. | |
class AsyncOps { | |
static [PSTaskFactory] $Factory = [PSTaskFactory]::new(); | |
static [Task] ContinueWithCodeMethod([psobject] $instance, [scriptblock] $continuationAction) { | |
$delegate = [AsyncOps]::CreateAsyncDelegate( | |
$continuationAction, | |
[Action[Task[Collection[psobject]]]]) | |
return [AsyncOps]::PrepareTask($instance.psadapted.ContinueWith($delegate)) | |
} | |
# - Hides the result property on a Task object. This is done because the getter for Result waits | |
# for the task to finish, even if just output to the console. | |
# - Adds ContinueWith code method that wraps scriptblocks with CreateAsyncDelegate | |
static [Task] PrepareTask([Task] $target) { | |
$propertyList = $target.psobject.Properties.Name -notmatch 'Result' -as [string[]] | |
$propertySet = [PSPropertySet]::new('DefaultDisplayPropertySet', $propertyList) -as [PSMemberInfo[]] | |
$standardMembers = [PSMemberSet]::new('PSStandardMembers', $propertySet) | |
$target.psobject.Members.Add($standardMembers) | |
$target.psobject.Methods.Add( | |
[PSCodeMethod]::new( | |
'ContinueWith', | |
[AsyncOps].GetMethod('ContinueWithCodeMethod'))) | |
return $target | |
} | |
static [MulticastDelegate] CreateAsyncDelegate([scriptblock] $function, [type] $delegateType) { | |
return [AsyncOps]::CreateAsyncDelegate($function, $delegateType, [AsyncOps]::Factory) | |
} | |
# Create a delegate from a scriptblock that can be used in threads without runspaces, like those | |
# used in Tasks or AsyncCallbacks. | |
static [MulticastDelegate] CreateAsyncDelegate([scriptblock] $function, [type] $delegateType, [PSTaskFactory] $factory) { | |
$invokeMethod = $delegateType.GetMethod('Invoke') | |
$returnType = $invokeMethod.ReturnType | |
# Create a parameter expression for each parameter the delegate takes. | |
$parameters = $invokeMethod. | |
GetParameters(). | |
ForEach{ [Expression]::Parameter($PSItem.ParameterType, $PSItem.Name) } | |
$scriptParameters = [string]::Empty | |
if ($parameters) { | |
$scriptParameters = '$' + ($invokeMethod.GetParameters().Name -join ', $') | |
} | |
# Allow access to parameters in the following ways: | |
# - By the name given to them by the delegate's invoke method | |
# - $args | |
# - $PSItem/$_ (first parameter only) | |
$preparedScript = | |
'param({0}) process {{ return {{ {1} }}.InvokeReturnAsIs($PSBoundParameters.Values) }}' -f | |
$scriptParameters, | |
$function | |
# Prepare variable and constant expressions. | |
$scriptText = [Expression]::Constant($preparedScript, [string]) | |
$ps = [Expression]::Variable([powershell], 'ps') | |
$collectionResultType = [Collection[psobject]] | |
if ($returnType -ne [void] -and $returnType -ne [Collection[psobject]]) { | |
$collectionResultType = [Collection`1].MakeGenericType($returnType) | |
} | |
$result = [Expression]::Variable($collectionResultType, 'result') | |
$psInput = [Expression]::Variable([Object[]], 'psInput') | |
$guid = [Expression]::Constant($factory.InstanceId, [guid]) | |
$pool = [Expression]::Property( | |
[Expression]::Property( | |
[Expression]::Property($null, [PSTaskFactory], 'Instances'), | |
'Item', | |
$guid), | |
'RunspacePool') | |
# Group the expressions for the body by creating them in a scriptblock. | |
[Expression[]]$expressions = & { | |
[Expression]::Assign($ps, [Expression]::Call([powershell], 'Create', @(), @())) | |
[Expression]::Assign([Expression]::Property($ps, 'RunspacePool'), $pool) | |
[Expression]::Call($ps, 'AddScript', @(), $scriptText) | |
foreach ($parameter in $parameters) { | |
[Expression]::Call($ps, 'AddArgument', @(), $parameter) | |
} | |
$invokeArgs = @() | |
if ($parameters) { | |
[Expression]::Assign( | |
$psInput, | |
[Expression]::NewArrayInit([object], $parameters[0] -as [Expression[]])) | |
$invokeArgs = @($psInput) | |
} | |
$invokeTypeArgs = @() | |
if ($returnType -ne [void] -and $returnType -ne [Collection[psobject]]) { | |
$invokeTypeArgs = @($returnType) | |
} | |
[Expression]::Assign($result, [Expression]::Call($ps, 'Invoke', $invokeTypeArgs, $invokeArgs)) | |
[Expression]::Call($ps, 'Dispose', @(), @()) | |
if ($returnType -ne [void]) { | |
[Expression]::Call( | |
[LanguagePrimitives], | |
'ConvertTo', | |
@($returnType), | |
([Expression]::Property($result, "Item", @([expression]::Parameter([int], "0")) -as [expression[]])) -as [Expression[]]) | |
} else { | |
$result | |
} | |
} | |
$block = [Expression]::Block([ParameterExpression[]]($ps, $result, $psInput), $expressions) | |
$lambda = [Expression]::Lambda( | |
$delegateType, | |
$block, | |
$parameters -as [ParameterExpression[]]) | |
return $lambda.Compile() | |
} | |
} | |
# A TaskFactory implementation that creates tasks that run scriptblocks in a runspace pool. | |
class PSTaskFactory : TaskFactory[Collection[psobject]] { | |
hidden static [Dictionary[guid, PSTaskFactory]] $Instances = [Dictionary[guid, PSTaskFactory]]::new() | |
hidden [RunspacePool] $RunspacePool; | |
hidden [guid] $InstanceId; | |
hidden [bool] $IsDisposed = $false; | |
PSTaskFactory() : base() { | |
$this.Initialize() | |
} | |
PSTaskFactory([CancellationToken] $cancellationToken) : base($cancellationToken) { | |
$this.Initialize() | |
} | |
PSTaskFactory([TaskScheduler] $scheduler) : base($scheduler) { | |
$this.Initialize() | |
} | |
PSTaskFactory( | |
[TaskCreationOptions] $creationOptions, | |
[TaskContinuationOptions] $continuationOptions) | |
: base($creationOptions, $continuationOptions) { | |
$this.Initialize() | |
} | |
PSTaskFactory( | |
[CancellationToken] $cancellationToken, | |
[TaskCreationOptions] $creationOptions, | |
[TaskContinuationOptions] $continuationOptions, | |
[TaskScheduler] $scheduler) | |
: base($cancellationToken, $creationOptions, $continuationAction, $scheduler) { | |
$this.Initialize() | |
} | |
hidden [void] Initialize() { | |
$this.RunspacePool = [runspacefactory]::CreateRunspacePool(1, 4) | |
$this.RunspacePool.Open() | |
$this.InstanceId = [guid]::NewGuid() | |
[PSTaskFactory]::Instances.Add($this.InstanceId, $this) | |
} | |
# Can't implement IDisposable while inheriting a generic class because of a parse error, need | |
# to create an issue. | |
[void] Dispose() { | |
$this.AssertNotDisposed() | |
[PSTaskFactory]::Instances.Remove($this.InstanceId) | |
$this.RunspacePool.Dispose() | |
$this.IsDisposed = $true | |
} | |
hidden [void] AssertNotDisposed() { | |
if ($this.IsDisposed) { | |
throw [InvalidOperationException]::new( | |
'Cannot perform operation because object "PSTaskFactory" has already been disposed.') | |
} | |
} | |
# Shortcut to AsyncOps.CreateAsyncDelegate | |
hidden [MulticastDelegate] Wrap([scriptblock] $function, [type] $delegateType) { | |
$this.AssertNotDisposed() | |
return [AsyncOps]::CreateAsyncDelegate($function, $delegateType, $this) | |
} | |
# The remaining functions implement methods from TaskFactory. All of these methods call the base | |
# method after wrapping the scriptblock to create a delegate that will work in tasks. | |
[Task[Collection[psobject]]] ContinueWhenAll([Task[]] $tasks, [scriptblock] $continuationAction) { | |
$this.AssertNotDisposed() | |
$delegateType = [Func`2].MakeGenericType([Task[]], [Collection[psobject]]) | |
return [AsyncOps]::PrepareTask( | |
([TaskFactory[Collection[psobject]]]$this).ContinueWhenAll( | |
$tasks, | |
$this.Wrap($continuationAction, $delegateType), | |
$this.CancellationToken, | |
$this.ContinuationOptions, | |
[TaskScheduler]::Current)) | |
} | |
[Task[Collection[psobject]]] ContinueWhenAny([Task[]] $tasks, [scriptblock] $continuationAction) { | |
$this.AssertNotDisposed() | |
$delegateType = [Func`2].MakeGenericType([Task], [Collection[psobject]]) | |
return [AsyncOps]::PrepareTask( | |
([TaskFactory[Collection[psobject]]]$this).ContinueWhenAny( | |
$tasks, | |
$this.Wrap($continuationAction, $delegateType), | |
$this.CancellationToken, | |
$this.ContinuationOptions, | |
[TaskScheduler]::Current)) | |
} | |
[Task[Collection[psobject]]] StartNew([scriptblock] $function) { | |
$this.AssertNotDisposed() | |
return [AsyncOps]::PrepareTask( | |
([TaskFactory[Collection[psobject]]]$this).StartNew( | |
$this.Wrap($function, [Func[Collection[psobject]]]), | |
$this.CancellationToken, | |
$this.CreationOptions, | |
[TaskScheduler]::Current)) | |
} | |
[Task[Collection[psobject]]] StartNew([scriptblock] $function, [object] $state) { | |
$this.AssertNotDisposed() | |
return [AsyncOps]::PrepareTask( | |
([TaskFactory[Collection[psobject]]]$this).StartNew( | |
$this.Wrap($function, [Func[object, Collection[psobject]]]), | |
$state, | |
$this.CancellationToken, | |
$this.CreationOptions, | |
[TaskScheduler]::Current)) | |
} | |
} | |
function async { | |
[CmdletBinding()] | |
param( | |
[scriptblock] | |
$ScriptBlock, | |
[Parameter(ValueFromPipeline)] | |
[object] | |
$ArgumentList | |
) | |
process { | |
if (-not $scriptblock) { return } | |
[AsyncOps]::Factory.StartNew($ScriptBlock, $ArgumentList) | |
} | |
} | |
function await { | |
[CmdletBinding()] | |
param( | |
[Parameter(ValueFromPipeline)] | |
[Task] | |
$Task | |
) | |
begin { | |
$taskList = [List[Task]]::new() | |
} | |
process { | |
if ($Task) { $taskList.Add($Task) } | |
} | |
end { | |
if (-not $taskList.Count) { return } | |
$finished = $false | |
while (-not $finished) { | |
$finished = $taskList.TrueForAll({ | |
param([Task]$task) | |
$task.IsCompleted -or | |
$task.IsCanceled -or | |
$task.IsFaulted | |
}) | |
} | |
$taskList.ToArray().ForEach{ | |
if ($PSItem.IsFaulted -and $PSItem.Exception) { | |
if (-not ($exception = $PSItem.Exception.InnerException)) { | |
$exception = $PSItem.Exception | |
} | |
$PSCmdlet.WriteError( | |
[ErrorRecord]::new( | |
$exception, | |
$exception.GetType().Name, | |
[ErrorCategory]::InvalidResult, | |
$PSItem)) | |
} | |
$PSItem.Result | |
} | |
} | |
} | |
function ContinueWith { | |
[CmdletBinding()] | |
param( | |
[scriptblock] | |
$ContinuationAction, | |
[switch] | |
$WhenAny, | |
[Parameter(ValueFromPipeline)] | |
[Task] | |
$Task | |
) | |
begin { | |
$taskList = [List[Task]]::new() | |
} | |
process { | |
if ($Task) { $taskList.Add($Task) } | |
} | |
end { | |
if (-not $taskList.Count) { return } | |
if ($WhenAny.IsPresent) { | |
return [AsyncOps]::Factory.ContinueWhenAny($taskList, $ContinuationAction) | |
} | |
return [AsyncOps]::Factory.ContinueWhenAll($taskList, $ContinuationAction) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment