PowerShellで簡易Webサーバを立てる

PowerShellのバッチでHTTPサーバを立ててみる。今日日開発環境ならば何かしらのデバッグサーバがあるが、それらを用意するほどでもない場合などに用いる。

HTTPサーバを起動する手順

方法としては.NetのSystem.Net.HttpListenerを利用する。

  1. HttpListenerオブジェクトを作成して、任意のポートを開く。
  2. GetContextメソッドでリクエストの受信を待機する。
  3. リクエストの内容に合わせてレスポンスを設定する。

2~3をループで繰り返す。

レスポンスにはファイルを読み込んで返すほか、コマンドを実行した結果を返したりすると簡易リモートサーバ的なこともできる。なおシングルスレッドのためリクエストは一つずつ順番に処理される。

ソースコード

以下はバッチファイルのパスをルートディレクトリとしてファイルデータを返すバッチ。

通信ポートを開くには管理者権限が必要なため、powershellが管理者権限でない場合は管理者に昇格して再起動する処理が最初に入っている。

HttpServer.ps1

$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

if(-not $isAdmin){
    Start-Process powershell -Verb runAs $MyInvocation.MyCommand.Path
    exit
}

$myDir=(Split-path $MyInvocation.MyCommand.Path -parent)
$root = "$myDir"
$port = "60080"

function Listen{
    try{
        $listener = New-Object System.Net.HttpListener
        $listener.Prefixes.Add("http://*:$port/")
        $listener.Start()
        
        "START SERVER"
        
        while($true){
            $context = $listener.GetContext()
            try{
                Request $context
            }finally{
                $context.Response.Close()
            }
        }
    }finally{
        $listener.Close()
    }
}

function Request([System.Net.HttpListenerContext]$context){
    $req = $context.Request
    $res = $context.Response

    if($req.HttpMethod -eq "GET"){
        $path = "$root/$($req.Url.AbsolutePath)"
        if($req.Url.AbsolutePath -eq "/"){
            $path = $path + "index.html"
        }
        $path

        if([System.IO.File]::Exists($path)){
            $buf = [System.IO.File]::ReadAllBytes($path)

            $res.StatusCode = 200
            $res.ContentType = GetMIME($path)
            $res.AppendHeader("chash-control", "no-store")
            $res.ContentLength64 = $buf.length
            $res.OutputStream.Write($buf, 0, $buf.length)
        }else{
            $res.StatusCode = 404
        }
    }else{
        $res.StatusCode = 404
    }
}

function GetMIME($path){
    $ext = [System.IO.Path]::GetExtension($path)
        if($ext -eq ".txt")  { "text/plain" }
    elseif($ext -eq ".html") { "text/html" }
    elseif($ext -eq ".htm")  { "text/html" }
    elseif($ext -eq ".js")   { "text/javascript" }
    elseif($ext -eq ".css")  { "text/css" }
    elseif($ext -eq ".csv")  { "text/csv" }
    elseif($ext -eq ".xml")  { "text/xml" }
    elseif($ext -eq ".jpg")  { "image/jpeg" }
    elseif($ext -eq ".png")  { "image/png" }
    elseif($ext -eq ".gif")  { "image/gif" }
    elseif($ext -eq ".svg")  { "image/svg+xml" }
    elseif($ext -eq ".ico")  { "image/x-icon" }
    elseif($ext -eq ".ttf")  { "font/ttf" }
    elseif($ext -eq ".woff") { "font/woff" }
    elseif($ext -eq ".woff2"){ "font/woff2" }
    elseif($ext -eq ".json") { "application/json" }
    elseif($ext -eq ".zip")  { "application/zip" }
    elseif($ext -eq ".pdf")  { "application/pdf" }
    else { "application/octet-stream" }
}

######

Listen

コメント