Servers/EventServer.ps1
|
<# .SYNOPSIS Event Server .DESCRIPTION A simple event driven server. Each request will generate an event, which will be responded to by a handler. .EXAMPLE ./EventServer.ps1 ($pwd | Split-Path) #> param( # The rootDirectory. [string]$RootDirectory = $PSScriptRoot, # The rootUrl of the server. By default, a random loopback address. [string]$RootUrl= "http://127.0.0.1:$(Get-Random -Minimum 4200 -Maximum 42000)/", # The type map. This determines how each extension will be served. [Collections.IDictionary] $TypeMap = [Ordered]@{ ".html" = "text/html" ; ".css" = "text/css" ; ".svg" = "image/svg+xml" ; ".png" = "image/png" ; ".jpg" = "image/jpeg"; ".gif" = "image/gif" ".mp3" = "audio/mpeg"; ".mp4" = "video/mp4" ".json" = "application/json"; ".xml" = "application/xml" ; ".js" = "text/javascript" ; ".jsm" = "text/javascript" ; } ) $httpListener = [Net.HttpListener]::new() $httpListener.Prefixes.Add($RootUrl) Write-Warning "Listening on $RootUrl $($httpListener.Start())" $io = [Ordered]@{ # Pack our job input into an IO dictionary HttpListener = $httpListener ; ServerRoot = $RootDirectory MainRunspace = [Runspace]::DefaultRunspace; SourceIdentifier = $RootUrl TypeMap = $TypeMap } # Our server is a thread job Start-ThreadJob -ScriptBlock {param([Collections.IDictionary]$io) $psvariable = $ExecutionContext.SessionState.PSVariable foreach ($key in $io.Keys) { # First, let's unpack. if ($io[$key] -is [PSVariable]) { $psvariable.set($io[$key]) } else { $psvariable.set($key, $io[$key]) } } # Listen for the next request :nextRequest while ($httpListener.IsListening) { $getContext = $httpListener.GetContextAsync() while (-not $getContext.Wait(17)) { } $request, $reply = $getContext.Result.Request, $getContext.Result.Response # Generate an event for every request $mainRunspace.Events.GenerateEvent( $SourceIdentifier, $httpListener, @( $getContext.Result, $request, $reply ), [Ordered]@{ Method = $Request.HttpMethod; Url = $request.Url Request = $request; Reply = $reply; Response = $reply ServerRoot = $ServerRoot; TypeMap = $TypeMap } ) } } -ThrottleLimit 100 -ArgumentList $IO -Name "$RootUrl" | # Output our job, Add-Member -NotePropertyMembers @{ # but attach a few properties first: HttpListener=$httpListener # * The listener (so we can stop it) IO=$IO # * The IO (so we can change it) Url="$RootUrl" # The URL (so we can easily access it). } -Force -PassThru # Pass all of that thru and return it to you. # Now register a handler for these events. Register-EngineEvent -SourceIdentifier $RootUrl -Action { $request = $event.MessageData.Request $reply = $event.MessageData.Reply $timeToRespond = [DateTime]::Now - $event.TimeGenerated $myReply = "$($request.HttpMethod) $($request.Url) $($timeToRespond)" $reply.Close($OutputEncoding.GetBytes($myReply), $false) } # Because events are processed on the main runspace thread, this cannot Invoke-RestMethod itself. |