Skip to content

Instantly share code, notes, and snippets.

@nmoadev
Last active February 18, 2024 23:01
Show Gist options
  • Save nmoadev/d15e0ec5c508088e00a66850a5b3f170 to your computer and use it in GitHub Desktop.
Save nmoadev/d15e0ec5c508088e00a66850a5b3f170 to your computer and use it in GitHub Desktop.
Zero-dependency Powershell WebServer in a one file.
# A zero-dependency tiny, copy-pasteable powershell web server for quick hacks
# Credit to https://woshub.com/simple-http-webserver-powershell/ for inspiration
# NOTE: If you download this script direclty, you will need to unblock it before running
# Unblock-File .\serve.ps1
param (
$webRoot = (Get-Location), # The folder from which to serve all files RECURSIVELY
$listenHostname = "localhost", # Hostname to listen on. Change at your own risk.
$listenPort = "9090" # The port to listen on.
)
# Needed for Content-Type header
Add-Type -AssemblyName "System.Web"
function Serve () {
param (
$webRoot = (Get-Location),
$listenHostname = "localhost",
$listenPort = "9090"
)
$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add("http://${listenHostname}:${listenPort}/")
$listener.Start()
try {
while (1 -eq 1) {
try {
$contextTask = $listener.GetContextAsync()
while (-not $contextTask.AsyncWaitHandle.WaitOne(1000)) {}
$context = $contextTask.GetAwaiter().GetResult()
ServeFile $webRoot $context
} catch {
$context.Response.StatusCode = 500
Log "$_`n$($_.ScriptStackTrace)"
} finally {
$context.Response.Close()
Log "$($context.Request.HttpMethod) $($context.Request.RawUrl) => $($context.Response.StatusCode)"
}
}
} catch {
Log "$_`n$($_.ScriptStackTrace)"
} finally {
$listener.Close()
}
}
function ServeFile() {
param (
[string] $webRoot,
[System.Net.HttpListenerContext] $context
)
$contentPath = WebPathToFilePath $webRoot $context.Request.RawUrl
if (Test-Path $contentPath -PathType Leaf) {
$fileStream = New-Object System.IO.FileStream -ArgumentList $contentPath,3
try {
$fileStream.CopyTo($context.Response.OutputStream)
$context.Response.StatusCode = 200
$context.Response.ContentType = [System.Web.MimeMapping]::GetMimeMapping($contentPath)
} finally {
$fileStream.Close();
}
} else {
$context.Response.StatusCode = 404
}
}
function Log() {
param (
[string] $msg
)
$time = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fffzzz"
Write-Host "${time} | ${msg}"
}
function WebPathToFilePath() {
param (
[string] $webRoot,
[string] $webPath
)
$webPath = $webPath.Replace("/", "\")
return (Join-Path $webRoot $webPath)
}
Serve $webRoot $listenHostname $listenPort
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment