├── HTTPListener-WebServer1.ps1 ├── HTTPListener-WebServer2.ps1 ├── LICENSE ├── Powershell-WebServer.ps1 ├── README.md └── TCPListener-WebServer.ps1 /HTTPListener-WebServer1.ps1: -------------------------------------------------------------------------------- 1 | param([int]$port=8080, [string]$message="OK") 2 | $listener = New-Object System.Net.HttpListener 3 | $listener.Prefixes.Add("http://*:$Port/") 4 | $listener.Start() 5 | while ($listener.IsListening) { 6 | $context = $listener.GetContext() #Blocks 7 | $buffer = [System.Text.Encoding]::UTF8.GetBytes($message ) 8 | $context.response.OutputStream.Write($buffer, 0, $buffer.Length) 9 | $context.response.Close(); 10 | } -------------------------------------------------------------------------------- /HTTPListener-WebServer2.ps1: -------------------------------------------------------------------------------- 1 | param([int]$port=8080, [string]$message="OK") 2 | $listener = New-Object System.Net.HttpListener 3 | $listener.Prefixes.Add("http://*:$Port/") 4 | $listener.Start() 5 | while ($listener.IsListening) { 6 | $context = $listener.GetContext() #Blocks 7 | if ($context.Request.Url.LocalPath -eq '/kill') { $listener.Abort(); break; } 8 | $buffer = [System.Text.Encoding]::UTF8.GetBytes($message) 9 | $context.response.OutputStream.Write($buffer, 0, $buffer.Length) 10 | $context.response.Close(); 11 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Christopher Lewis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Powershell-WebServer.ps1: -------------------------------------------------------------------------------- 1 | [cmdletBinding(SupportsShouldProcess=$false,ConfirmImpact='Low')] 2 | param( 3 | #Listening Endpoint 4 | [Parameter(Mandatory=$false,ValueFromPipeline=$false)] $HTTPEndPoint = 'http://localhost:8080/' 5 | , 6 | # LocalPath 7 | [Parameter(Mandatory=$true,ValueFromPipeline=$false)] $LocalRoot = 'c:\wwwroot\' 8 | ) 9 | 10 | $ErrorActionPreference = 'Stop' 11 | $VerbosePreference = 'Continue' 12 | 13 | function Get-HTTPResponse { 14 | param( 15 | [Parameter(Mandatory=$false,ValueFromPipeline=$false)] $Response, 16 | [Parameter(Mandatory=$false,ValueFromPipeline=$false)] $Path 17 | ) 18 | 19 | try { 20 | Write-Verbose "Determine MimeType of $path ..." 21 | $mimetype = [System.Web.MimeMapping]::GetMimeMapping($path) 22 | Write-Verbose " - $mimetype" 23 | 24 | # Generate Response 25 | $content = ( Get-Content -Path $path -Raw ) 26 | $buffer = [System.Text.Encoding]::UTF8.GetBytes($content) 27 | $response.ContentLength64 = $buffer.Length 28 | $response.OutputStream.Write($buffer, 0, $buffer.Length) 29 | } 30 | catch [System.Exception] { 31 | Write-Verbose "ERROR: $($_)" 32 | return "" 33 | } 34 | } 35 | 36 | $listener = New-Object System.Net.HttpListener 37 | $listener.Prefixes.Add( $HTTPEndPoint ) 38 | $listener.Start() 39 | Write-Verbose "Listening at $HTTPEndPoint..." 40 | 41 | while ($listener.IsListening) { 42 | $context = $listener.GetContext() 43 | $requestUrl = $context.Request.Url 44 | $response = $context.Response 45 | 46 | try { 47 | $localPath = $requestUrl.LocalPath 48 | #Close server 49 | if ($localPath -eq '/kill') { 50 | Write-Verbose "Killing ..."; 51 | $response.StatusCode = 200; 52 | $response.Close(); 53 | $listener.Close(); 54 | break; 55 | } 56 | $FullPath = join-path -Path $LocalRoot -ChildPath $LocalPath 57 | if ( Test-Path $FullPath ) { 58 | Write-Verbose "Querying $requestUrl ..." 59 | Get-HTTPResponse -Response $response -Path $FullPath 60 | } else { 61 | $response.StatusCode = 404 62 | } 63 | } catch { 64 | $response.StatusCode = 500 65 | } 66 | $response.Close() 67 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShellWebServers 2 | Sample code for Powershell acting as a web server 3 | 4 | Some simple powershell web servers. Inspired by Jan Ka's https://github.com/Jan-Ka/coms, which is inspired by 5 | Ben Rady's https://github.com/benrady/shinatra - without the MS vs. Linux bagage :-). 6 | 7 | Most of these are pretty silly, with no error handling. 8 | 9 | ## TCPListener-WebServer.ps1 10 | 10 lines of code. 11 | 12 | The TCP Listener uses System.Net.Sockets.TcpListener and is similar to Jan Ka's, but uses a while loop to surround 13 | the blocking $listener.AcceptTcpClient(). 14 | 15 | Note there's no way to close the connection, and PowerShell seems to hang on a ctrl-break. 16 | I ended up killing the PowerShell.exe process. 17 | 18 | ``` 19 | c:\GitHub\PowerShellWebServers>powershell -f TCPListener-WebServer.ps1 8001 test1 20 | ``` 21 | 22 | ``` 23 | c:\>\cURL\bin\curl.exe http://localhost:8001 -v 24 | * Rebuilt URL to: http://localhost:8001/ 25 | * Trying ::1... 26 | * Trying 127.0.0.1... 27 | * Connected to localhost (127.0.0.1) port 8001 (#0) 28 | > GET / HTTP/1.1 29 | > Host: localhost:8001 30 | > User-Agent: curl/7.49.1 31 | > Accept: */* 32 | > 33 | < HTTP/1.1 200 OK 34 | < Connection: keep-alive 35 | * no chunk, no close, no size. Assume close to signal end 36 | < 37 | test1 38 | * Recv failure: Connection was aborted 39 | * Closing connection 0 40 | curl: (56) Recv failure: Connection was aborted 41 | ``` 42 | 43 | Curl says that the connection closed ugly, but this certainly works. 44 | 45 | ## HTTPListener-WebServer1.ps1 46 | 10 lines of code. 47 | 48 | The HTTP Listener uses System.Net.HttpListener, which wraps the incomming connection in a request object. 49 | 50 | As with the TCP Listener, there's no way to close the process gracefully. 51 | 52 | ``` 53 | c:\GitHub\PowerShellWebServers>powershell -f HTTPListener-WebServer1.ps1 8001 Test2 54 | ``` 55 | 56 | ``` 57 | c:\>\cURL\bin\curl.exe http://localhost:8001 -v 58 | * Rebuilt URL to: http://localhost:8001/ 59 | * Trying ::1... 60 | * Connected to localhost (::1) port 8001 (#0) 61 | > GET / HTTP/1.1 62 | > Host: localhost:8001 63 | > User-Agent: curl/7.49.1 64 | > Accept: */* 65 | > 66 | < HTTP/1.1 200 OK 67 | < Transfer-Encoding: chunked 68 | < Server: Microsoft-HTTPAPI/2.0 69 | < Date: Wed, 07 Dec 2016 15:47:30 GMT 70 | < 71 | Test2* Connection #0 to host localhost left intact 72 | ``` 73 | 74 | Note that the connection was left intact, and Curl likes this. 75 | 76 | ## HTTPListener-WebServer2.ps1 77 | 11 lines of code. 78 | 79 | Same listener, with a single line added to close the server after a `/kill` 80 | 81 | ``` 82 | c:\GitHub\PowerShellWebServers>powershell -f HTTPListener-WebServer2.ps1 8001 Test3 83 | ``` 84 | 85 | ``` 86 | c:\>\cURL\bin\curl.exe http://localhost:8001 -v 87 | * Rebuilt URL to: http://localhost:8001/ 88 | * Trying ::1... 89 | * Connected to localhost (::1) port 8001 (#0) 90 | > GET / HTTP/1.1 91 | > Host: localhost:8001 92 | > User-Agent: curl/7.49.1 93 | > Accept: */* 94 | > 95 | < HTTP/1.1 200 OK 96 | < Transfer-Encoding: chunked 97 | < Server: Microsoft-HTTPAPI/2.0 98 | < Date: Wed, 07 Dec 2016 15:53:26 GMT 99 | < 100 | Test3* Connection #0 to host localhost left intact\ 101 | 102 | c:\>\cURL\bin\curl.exe http://localhost:8001/kill -v 103 | * Trying ::1... 104 | * Connected to localhost (::1) port 8001 (#0) 105 | > GET /kill HTTP/1.1 106 | > Host: localhost:8001 107 | > User-Agent: curl/7.49.1 108 | > Accept: */* 109 | > 110 | * Recv failure: Connection was reset 111 | * Closing connection 0 112 | curl: (56) Recv failure: Connection was reset 113 | ``` 114 | 115 | This closes the connection and shuts down powershell.exe somewhat gracefully. 116 | 117 | 118 | ## Powershell-WebServer.ps1 119 | This takes the HTTPListener to the extreme, and creates a moderately functioning web server. 120 | 121 | You pass a URL to listen on, and a path that's your root (has to end in a '\\'), 122 | and the script servers up http content. 123 | 124 | It's not threaded, and currently doesn't handle mime types other then text, but that could be handled in the `Get-HTTPResponse` function. 125 | -------------------------------------------------------------------------------- /TCPListener-WebServer.ps1: -------------------------------------------------------------------------------- 1 | param([int]$port=8080, [string]$message="OK") 2 | $listener = new-object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Any,$port) 3 | $listener.Start() 4 | do { 5 | $client = $listener.AcceptTcpClient() #Blocks 6 | $stream = $client.GetStream(); 7 | $writer = New-Object System.IO.StreamWriter $stream 8 | $writer.Write("HTTP/1.1 200 OK`r`nConnection: keep-alive`r`n`r`n$message`r`n") 9 | $writer.Close() 10 | } while ($true) --------------------------------------------------------------------------------