Start-HttpRoutes.ps1
<#PSScriptInfo
.VERSION 0.0.2 .AUTHOR Roman Kuzmin .COPYRIGHT (c) Roman Kuzmin .TAGS HTTP Server HttpListener .GUID bebe6fb7-4308-4bf0-8c3a-d521eebba6f5 .PROJECTURI https://github.com/nightroman/PowerShelf .LICENSEURI http://www.apache.org/licenses/LICENSE-2.0 #> <# .Synopsis Starts HTTP server with routing script blocks. .Description Starts HTTP server with routing script blocks. Route handler helper commands and variables: Read-Content Get-Headers Get-Query $Context [System.Net.HttpListenerContext] $Request [System.Net.HttpListenerRequest] $Response [System.Net.HttpListenerResponse] .Parameter Prefix HttpListener prefix. Example: http://localhost:8080/ http://127.0.0.1:8080/ .Parameter Routes Route tags and handlers, hashtable or dictionary. Keys are route tags: GET / POST /user POST /test/* Values are route request handlers, script blocks. .Link https://github.com/nightroman/PowerShelf #> [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true)] [string]$Prefix , [Parameter(Position=1, Mandatory=$true)] [System.Collections.IDictionary]$Routes ) <# .Synopsis Reads HTTP request content as string. #> function Read-Content { [CmdletBinding()] param( [System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8 ) $reader = [System.IO.StreamReader]::new($Request.InputStream, $Encoding) try { $reader.ReadToEnd() } finally { $reader.Close() } } <# .Synopsis Gets $Request.Headers as [hashtable]. #> function Get-Headers { [CmdletBinding()]param() Convert-NameValue $Request.Headers } <# .Synopsis Gets $Request.QueryString as [hashtable]. #> function Get-Query { [CmdletBinding()]param() Convert-NameValue $Request.QueryString } <# .Synopsis Converts [NameValueCollection] to [hashtable]. #> function Convert-NameValue { [CmdletBinding()] param( [System.Collections.Specialized.NameValueCollection]$Source ) $data = [ordered]@{} foreach($k in $Source.AllKeys) { $data.Add($k, $Source.GetValues($k)[0]) } $data } function __write_string([string]$Body) { $data = [System.Text.Encoding]::UTF8.GetBytes($Body) $Response.ContentLength64 = $data.Length $Response.OutputStream.Write($data, 0, $data.Length) $Response.OutputStream.Close() } $ErrorActionPreference = 1 if (!$Prefix.EndsWith('/')) { $Prefix = "$Prefix/" } Write-Host $Prefix $Host.UI.RawUI.WindowTitle = $Prefix $HttpListener = [System.Net.HttpListener]::new() $HttpListener.Prefixes.Add($Prefix) $HttpListener.Start() :loop for(; $HttpListener.IsListening; $Response.Close()) { $Context = $HttpListener.GetContext() $Request = $Context.Request $Response = $Context.Response Write-Host "$($Request.HttpMethod) $($Request.Url)" # routes foreach($_ in $Routes.GetEnumerator()) { $private:method, $private:path = $_.Key.Split(' ', 2, 'RemoveEmptyEntries') if ($Request.HttpMethod -eq $method -and $Request.Url.AbsolutePath -like $path) { try { $data = & $_.Value } catch { $Response.StatusCode = 500 $data = "500 Internal Server Error: $_`n$($_.InvocationInfo.PositionMessage.Trim())" Write-Host $data -ForegroundColor Red } __write_string $data continue loop } } # 404 $Response.StatusCode = 404 __write_string '404 Not Found' } |