# iex (iwr "https://gist.github.com/anonhostpi/1cc0084b959a9ea9e97dca9dce414e1f/raw/webserver.ps1").Content function Start-Webserver { param( [string] $Binding = "http://localhost:8080/", [string] $BaseDirectory = "$(Get-Location -PSProvider FileSystem)", [string] $ServerName = "PowerShell Webserver", [System.Collections.IDictionary] $Routes = @{ Before = { param( $command, $listener, $context ) return $true } After = { param( $command, $listener, $context ) return $true } # "GET /" = { ... } # "GET /hello" = "./path/to/static/file.html" Default = { param( $command, $request, $response ) $msg = "Hello, World!" # $response.OutputStream.Write( [System.Text.Encoding]::UTF8.GetBytes($msg), 0, $msg.Length ) return $msg } } ) $BaseDirectory = "$(Resolve-Path $BaseDirectory)" $serve_static = { param( [string] $path, $response ) $file = "$BaseDirectory\$($path.TrimStart('/').Replace('/','\'))" $file = "$(Resolve-Path $file)" If( Test-Path $file ){ # Prevent directory traversal attacks If( -not $file.StartsWith($BaseDirectory) ){ $response.StatusCode = 403 return "403 Forbidden" } return (Get-Content $file -Raw) } Else { $response.StatusCode = 404 return "404 Not Found" } } If( [string]::IsNullOrWhitespace( $Routes.Default ) ) { If( [string]::IsNullOrWhitespace( $Routes["GET /"] ) ) { $Routes["GET /"] = { return "Hello, World!" } } # Simple static webfile server $Routes.Default = { param( $command, $request, $response ) & $serve_static $request.Url.AbsolutePath $response } } $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add($Binding) $listener.Start() $Error.Clear() Try { While ( $listener.IsListening ) { $task = $listener.GetContextAsync() $ready = $task.Wait(100) # 100 ms timeout if (-not $ready) { continue } $context = $task.Result $request = $context.Request $command = "{0} {1}" -f $request.HttpMethod, $request.Url.AbsolutePath If( $Routes.Before ) { $continue = & $Routes.Before $command $listener $context If( -not $continue ) { continue } } $response = $context.Response $response.ContentType = "text/plain; charset=utf-8" $result = try { If( $Routes[$command] ) { If ( $Routes[$command] -is [scriptblock] ) { & $Routes[$command] $command $request $response } Else { & $serve_static $Routes[$command] $response } } Else { If( $Routes.Default -is [scriptblock] ) { & $Routes.Default $command $request $response } Else { & $serve_static $Routes.Default $response } } } catch { $response.StatusCode = 500 "500 Internal Server Error`n`n$($_.Exception.Message)" } if( -not [string]::IsNullOrWhiteSpace($result) ) { $buffer = [System.Text.Encoding]::UTF8.GetBytes($result) $response.ContentLength64 = $buffer.Length If( [string]::IsNullOrWhiteSpace($response.Headers["Last-Modified"]) ){ $response.Headers.Add("Last-Modified", (Get-Date).ToString("r")) } If( [string]::IsNullOrWhiteSpace($response.Headers["Server"]) ){ $response.Headers.Add("Server", $ServerName) } $response.OutputStream.Write( $buffer, 0, $buffer.Length ) } $response.Close() If( $Routes.After ) { $null = & $Routes.After $command $listener $context } } } finally { $listener.Stop() $listener.Close() } } Start "http://localhost:8080" Start-WebServer