├── PowerShellHttpServer.psm1 ├── README.md └── images ├── example1.png └── example2.png /PowerShellHttpServer.psm1: -------------------------------------------------------------------------------- 1 | function Invoke-HttpServer { 2 | <# 3 | .SYNOPSIS 4 | Starts an HTTP server that listens for incoming requests and processes them. 5 | 6 | .DESCRIPTION 7 | The `Invoke-HttpServer` function creates and starts an HTTP server on the specified IP and port. 8 | It listens for incoming HTTP requests, processes them in a separate runspace, and outputs request information to the console. 9 | 10 | The server is configurable via parameters for the listening port, timeout duration, and IP address. 11 | The server runs in a loop until the specified timeout is reached or it is stopped manually (Ctrl+C). 12 | It has basic error handling built-in, therefore the server should not survive for example Nmap script scans. 13 | 14 | .PARAMETER Port 15 | The port number on which the HTTP server listens. Default is 13824. 16 | 17 | .PARAMETER HttpTimeout 18 | The duration in seconds for which the HTTP server will run before shutting down automatically. 19 | Set to 0 to run indefinitely (Default). 20 | 21 | .PARAMETER Ip 22 | The IP address on which the HTTP server listens. Default is "localhost". 23 | 24 | .EXAMPLE 25 | Invoke-HttpServer -Port 8080 -HttpTimeout 120 -Ip "*" 26 | Starts an HTTP server on IP `127.0.0.1` and port `8080`. The server runs for 120 seconds. 27 | 28 | .EXAMPLE 29 | Invoke-HttpServer 30 | Starts an HTTP server on IP `localhost` and port `13824`. The server runs for 60 seconds. 31 | 32 | .NOTES 33 | GitHub: https://github.com/zh54321/ 34 | #> 35 | 36 | param ( 37 | [Parameter(Mandatory=$false)][int]$Port = 13824, 38 | [Parameter(Mandatory=$false)][int]$HttpTimeout = 0, 39 | [Parameter(Mandatory=$false)][string]$Ip = "localhost" 40 | ) 41 | 42 | # Create and configure the HTTP listener 43 | $HttpListener = [System.Net.HttpListener]::new() 44 | $HttpListener.Prefixes.Add("http://$($Ip):$Port/") 45 | 46 | Try { 47 | $HttpListener.Start() 48 | } Catch { 49 | $HttpStartError = $_ 50 | if ($HttpStartError -match "because it conflicts with an existing registration on the machine") { 51 | Write-Host "[!] The port $Port is already blocked by another process." 52 | Write-Host "[!] Close the other process or use -port to define another port." 53 | } elseif ($HttpStartError -match "Access is denied") { 54 | Write-Host "[!] Listening on any other interface than localhost requires admin privileges." 55 | } else { 56 | write-host "[!] ERROR: $HttpStartError" 57 | } 58 | break 59 | } 60 | 61 | if ($HttpListener.IsListening) { 62 | Write-Host "[*] HTTP server started on http://$($Ip):$Port/. Press Ctrl+C to stop." 63 | } 64 | 65 | # Variable to control the server loop 66 | $KeepRunning = $true 67 | 68 | # Runspace for the HTTP server 69 | $Runspace = [runspacefactory]::CreateRunspace() 70 | $Runspace.Open() 71 | 72 | # Shared object for communication 73 | $SharedData = [System.Collections.Concurrent.ConcurrentQueue[PSObject]]::new() 74 | 75 | # Script block for the HTTP server loop 76 | $ScriptBlock = { 77 | param($HttpListener, [ref]$KeepRunning, $SharedData) 78 | 79 | #Outer while loop to keep the server running in case of errors 80 | while ($KeepRunning.Value -and $HttpListener.IsListening) { 81 | try { 82 | while ($KeepRunning.Value -and $HttpListener.IsListening) { 83 | $Context = $HttpListener.GetContext() 84 | 85 | # Retrieve request information and share with main script 86 | $Request = $Context.Request 87 | $SharedData.Enqueue($Request) 88 | 89 | # Response handeling 90 | # Use "if ($Request.HttpMethod -eq 'GET' -and $Request.RawUrl -eq '/')" to customize responses 91 | $Response = $Context.Response 92 | $Response.StatusCode = 200 93 | $Response.ContentType = "text/plain" 94 | $ResponseOutput = [System.Text.Encoding]::UTF8.GetBytes("Successful! Time to celebrate with coffee.`nMore PowerShell stuff on: https://github.com/zh54321") 95 | $Response.OutputStream.Write($ResponseOutput, 0, $ResponseOutput.Length) 96 | $Response.OutputStream.Close() 97 | } 98 | } catch { 99 | # Share error data 100 | $SharedData.Enqueue($_) 101 | write-host $HttpListener.IsListening 102 | } 103 | } 104 | } 105 | 106 | # Create a PS instance and assign the script block to it 107 | $PSInstance = [powershell]::Create() 108 | $PSInstance.AddScript($ScriptBlock).AddArgument($HttpListener).AddArgument([ref]$KeepRunning).AddArgument($SharedData) | Out-Null 109 | $PSInstance.Runspace = $Runspace 110 | $PSInstance.BeginInvoke() | Out-Null 111 | 112 | # Main loop to process output from the shared queue 113 | $StartTime = [datetime]::Now 114 | $Proceed = $true 115 | 116 | try { 117 | while ($Proceed) { 118 | Start-Sleep -Milliseconds 500 119 | 120 | # Check if the runtime exceeds the timeout (if set) 121 | if ($HttpTimeout -gt 0 -and ([datetime]::Now - $StartTime).TotalSeconds -ge $HttpTimeout) { 122 | Write-Host "[!] Runtime limit reached. Stopping the server..." 123 | break 124 | } 125 | 126 | # Process output from the shared queue 127 | 128 | $Request = $null 129 | while ($SharedData.TryDequeue([ref]$Request)) { 130 | # Customize this section to do stuff which you want (trigger actions, end the script etc.) 131 | 132 | # Quit if if receives a request on /quit 133 | if ($($Request.RawUrl) -match "quit") { 134 | Write-Host "[+] Got request on /quit" 135 | $Proceed = $false 136 | break 137 | } 138 | 139 | #Null check to avoid the script crashing 140 | if ($null -ne $Request -and $Request -is [System.Net.HttpListenerRequest]) { 141 | Write-Host "[+] Got request $($Request.HttpMethod) $($Request.RawUrl) from $($Request.RemoteEndPoint.ToString())" 142 | } else { 143 | Write-Host "[!] Request caused an error: $Request" 144 | } 145 | 146 | } 147 | } 148 | 149 | } finally { 150 | #Cleaning up 151 | Write-Host "[*] Stopping the server..." 152 | $KeepRunning = $false 153 | Start-Sleep -Milliseconds 500 # Allow the loop in the runspace to complete 154 | $HttpListener.Stop() 155 | $PSInstance.Stop() 156 | $PSInstance.Dispose() 157 | $Runspace.Close() 158 | $Runspace.Dispose() 159 | Write-Host "[*] Server stopped." 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Invoke-HttpServer 2 | 3 | ## Description 4 | The `Invoke-HttpServer` function starts an HTTP server that listens for incoming requests on a specified IP and port. It processes requests in a separate runspace and provides request details in the console. The answer served to the client can be customized. The server can be configured for different IP addresses, ports, and timeout durations. It runs in a loop until the specified timeout is reached or it is manually stopped (Ctrl+C). The server has built-in error handling to handle common issues like conflicting ports and access permissions. 5 | 6 | 7 | ## Features 8 | - Start an HTTP server on a specified IP and port. 9 | - Process incoming HTTP requests in a separate runspace. 10 | - Handle requests and output request information to the console. 11 | - Can be stopped using Ctrl+C 12 | - Configurable timeout and listening IP/port. 13 | - Graceful handling of errors such as port conflicts and access restrictions. 14 | 15 | ## Images 16 | Starting the HTTP Server and listening. After 2 request the server is manually stopped using CTRL+C: 17 | 18 | ![alt text](images/example1.png "Request details in the console") 19 | 20 | Response in the browser: 21 | 22 | ![alt text](images/example2.png "Response in the browser") 23 | 24 | ## Usage 25 | 26 | ### Installation 27 | 28 | 1. Clone the repository: 29 | ```powershell 30 | git clone https://github.com/zh54321/PowerShell_HttpServer.git 31 | ``` 32 | 2. Import the module before usage: 33 | ```powershell 34 | Import-Module ./PowerShell_HttpServer/PowerShellHttpServer.psm1 35 | ``` 36 | 37 | ### Parameters 38 | 39 | - `-Port` (optional): The port number on which the HTTP server listens. Default is `13824`. 40 | - `-Ip` (optional): The IP address on which the HTTP server listens. Default is `localhost`. Wildcard (*) can be used to listen on all interfaces. 41 | - `-HttpTimeout` (optional): The duration in seconds for which the HTTP server will run before shutting down automatically. Set to `0` to run indefinitely (Default). 42 | 43 | ### Examples 44 | 45 | #### Example 1: Start an HTTP server with default settings (localhost, port 13824, and no timeout) 46 | ```powershell 47 | Invoke-HttpServer 48 | ``` 49 | This will start an HTTP server on IP `localhost` and port `13824`. The server will run forever. 50 | 51 | #### Example 2: Start an HTTP server on port 8080 with a timeout of 120 seconds and listening on all interfaces 52 | ```powershell 53 | Invoke-HttpServer -Port 8080 -HttpTimeout 120 -Ip "*" 54 | ``` 55 | This will start an HTTP server on all interfaces and port `8080`. The server will run for 120 seconds before shutting down automatically. 56 | Note: To expose the server in the local network, the command has to be executed as admin. 57 | 58 | ### Notes 59 | 60 | - To stop the server manually, press `Ctrl+C`. 61 | -------------------------------------------------------------------------------- /images/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh54321/PowerShell_HttpServer/a2a7c6d7db9d7cba34e50b1745511e3713b55bed/images/example1.png -------------------------------------------------------------------------------- /images/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zh54321/PowerShell_HttpServer/a2a7c6d7db9d7cba34e50b1745511e3713b55bed/images/example2.png --------------------------------------------------------------------------------