# Start-WebserverAsync # # the normal "listener.getcontext()"-Method blocks the script until a new # request appears. There is no timeout of other way to cancel the waiting # process and a CTRL-C stops the script but not the listener This sample # uses the $listener.BeginGetContext() method with a callback-function, # which allowes the main process to continue # Attn: Async Calling only works, if the PowerShell Process is not # suspended with a "start-sleep" etc. Callbacks are executed after the # current command is completed. Is it also not Multitasking/threading # # Major work was done from Diagg at https://gist.GitHub.com/Diagg/f57b330f340e4f42fed26dd5759cca05 param ( [uint16]$port=8080 ) function New-ScriptBlockCallback { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param( [parameter(Mandatory)] [ValidateNotNullOrEmpty()] [scriptblock]$Callback ) # Is this type already defined? if (-not ( 'CallbackEventBridge' -as [type])) { Add-Type @' using System; public sealed class CallbackEventBridge { public event AsyncCallback CallbackComplete = delegate { }; private CallbackEventBridge() {} private void CallbackInternal(IAsyncResult result) { CallbackComplete(result); } public AsyncCallback Callback { get { return new AsyncCallback(CallbackInternal); } } public static CallbackEventBridge Create() { return new CallbackEventBridge(); } } '@ } $bridge = [callbackeventbridge]::create() Register-ObjectEvent -InputObject $bridge -EventName callbackcomplete -Action $Callback -MessageData $args > $null $bridge.Callback } Write-host "Start-WebserverAsync:Start" Write-host "Create Listener on Port $($port)" $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add('http://+:'+$port+'/') $listener.Start() $test="123" Write-host "Start-WebserverAsync:Prepare RequestListener code" $requestListener = { [cmdletbinding()] param($result) [System.Net.HttpListener]$listener = $result.AsyncState; $context = $listener.EndGetContext($result); # waitfor request to complete $request = $context.Request # Collect Request $response = $context.Response # get Response Object write-host "got $($request.RawURL)" switch ($request.RawURL) { # Handle Request based on the URL "/quit" {$listener.Stop(); break} # stop listener and exit handler "/date" {$message = "WebServer Time $((get-date).DateTime)"} Default {$message = "404 Page not found use /quit to end webserver or /date to get system date";} } $response.ContentType = 'text/html'; [byte[]]$buffer = [System.Text.Encoding]::UTF8.GetBytes($message) $response.ContentLength64 = $buffer.length $output = $response.OutputStream $output.Write($buffer, 0, $buffer.length) $output.Close() } # Start first listener to wait for Ballback $context = $listener.BeginGetContext((New-ScriptBlockCallback -Callback $requestListener), $listener) Write-host "Start-WebserverAsync:Start Async listener Processing - Press any key to stop" [datetime]$timestamp = Get-date [long]$count = 60 while ($listener.IsListening -and !([console]::KeyAvailable)) { If ($context.IsCompleted -and $listener.IsListening) { # start new Callback for next request. Do it at the start to handle quit events first $context = $listener.BeginGetContext((New-ScriptBlockCallback -Callback $requestListener), $listener) } if ($count -ge 60) { Write-host ; Write-Host "$(([System.DateTime]::UtcNow).tostring("u")) " -nonewline $count=0 } if ( ((get-date) - $timestamp).totalseconds -gt 1) { write-host "." -NoNewline $count ++ $timestamp = Get-date } } $listener.Stop() $listener.Close() Write-host "Start-WebserverAsync:Stop"