1+ <#
2+ . SYNOPSIS
3+ Event Server
4+ . DESCRIPTION
5+ A simple event driven server.
6+
7+ Each request will generate an event, which will be responded to by a handler.
8+ #>
9+ param (
10+ # The rootUrl of the server. By default, a random loopback address.
11+ [string ]$RootUrl =
12+ " http://127.0.0.1:$ ( Get-Random - Minimum 4200 - Maximum 42000 ) /"
13+ )
14+
15+ $httpListener = [Net.HttpListener ]::new()
16+ $httpListener.Prefixes.Add ($RootUrl )
17+ Write-Warning " Listening on $RootUrl $ ( $httpListener.Start ()) "
18+
19+ $io = [Ordered ]@ { # Pack our job input into an IO dictionary
20+ HttpListener = $httpListener ; ServerRoot = $RootDirectory
21+ MainRunspace = [Runspace ]::DefaultRunspace; SourceIdentifier = $RootUrl
22+ TypeMap = $TypeMap
23+ }
24+
25+ # Our server is a thread job
26+ Start-ThreadJob - ScriptBlock {param ([Collections.IDictionary ]$io )
27+ $psvariable = $ExecutionContext.SessionState.PSVariable
28+ foreach ($key in $io.Keys ) { # First, let's unpack.
29+ if ($io [$key ] -is [PSVariable ]) { $psvariable.set ($io [$key ]) }
30+ else { $psvariable.set ($key , $io [$key ]) }
31+ }
32+
33+ $thisRunspace = [Runspace ]::DefaultRunspace
34+
35+ # Because we are handling the event locally, the main thread can keep chugging.
36+ Register-EngineEvent - SourceIdentifier $SourceIdentifier - Action {
37+ try {
38+ $request = $event.MessageData.Request
39+ $reply = $event.MessageData.Reply
40+
41+ $timeToRespond = [DateTime ]::Now - $event.TimeGenerated
42+ $myReply = " $ ( $request.HttpMethod ) $ ( $request.Url ) $ ( $timeToRespond ) "
43+ $reply.Close ($OutputEncoding.GetBytes ($myReply ), $false )
44+ } catch {
45+ Write-Error $_
46+ }
47+ }
48+
49+ # Listen for the next request
50+ :nextRequest while ($httpListener.IsListening ) {
51+ $getContext = $httpListener.GetContextAsync ()
52+ while (-not $getContext.Wait (17 )) { }
53+ $request , $reply =
54+ $getContext.Result.Request , $getContext.Result.Response
55+
56+ # Generate events for every request
57+ foreach ($runspace in $thisRunspace , $mainRunspace ) {
58+ # by broadcasting to multiple runspaces, we can both reply and have a record.
59+ $runspace.Events.GenerateEvent (
60+ $SourceIdentifier , $httpListener , @ (
61+ $getContext.Result , $request , $reply
62+ ), [Ordered ]@ {
63+ Method = $Request.HttpMethod ; Url = $request.Url
64+ Request = $request ; Reply = $reply ; Response = $reply
65+ ServerRoot = $ServerRoot ; TypeMap = $TypeMap
66+ }
67+ )
68+ }
69+ }
70+ } - ThrottleLimit 100 - ArgumentList $IO - Name " $RootUrl " | # Output our job,
71+ Add-Member - NotePropertyMembers @ { # but attach a few properties first:
72+ HttpListener = $httpListener # * The listener (so we can stop it)
73+ IO = $IO # * The IO (so we can change it)
74+ Url = " $RootUrl " # The URL (so we can easily access it).
75+ } - Force - PassThru # Pass all of that thru and return it to you.
0 commit comments