LoginSignup
29

More than 5 years have passed since last update.

PowerShellでHTTPサーバー的なものを作る

Last updated at Posted at 2015-06-08

MDwikiというツールがあります。
http://dynalon.github.io/mdwiki/#!index.md

MarkDownで書かれたmdファイルを、サーバー側ではなくJavaScriptによりクライアントサイドで処理させてWebコンテンツにしてしまおうというようなツールで、日本語の紹介記事などもあるのでぐぐっていただくと良いと思います。

このMDwikiですが、その作りからしてローカルフォルダに展開したmdwiki.htmlとmdファイルだけでブラウザから使えそうなポテンシャルを感じるのですが、実際試してみますとローカルファイルでのJavaScriptが動かず、IE,Chromeではデフォルトで動作しないという状況があります。Firefoxは大丈夫です。

というわけでFirefoxで使いましょうというのが一つの結論となるのですが、とはいえやはり他のブラウザも使いたい。
Webサーバーをlocalhostで動かせば良いのだけれども、何を使うにしても可搬性が今一つ・・・いやPowerShellでどうにかなるんじゃ?
たしか.NET Frameworkに即興HTTPサーバー用クラスがあったはず…等と思い、とりあえず作ってみたという経緯です。

HTTPサーバーのサンプル

下記のテキストを、mdwiki.htmlのあるフォルダにhttpListener.ps1の名前で保存します。

httpListener.ps1
param($Port=18080, $HomePage='mdwiki.html', $DefaultPage=$HomePage)
$urlRoot = "http://localhost:$Port/"
$urlMain = $urlRoot+$HomePage
$parentPath = [IO.Path]::GetDirectoryName($MyInvocation.InvocationName)

$listener = New-Object Net.HttpListener
$listener.Prefixes.add($urlRoot)

try{
    "localhostで簡易httpサーバーを作動させます。"|oh
    try {
        $listener.Start()
    } finally {
        #起動ついでにブラウザで開いてやる
        #Start()に失敗するケースでも既にHttpサーバーが動いているケースを期待してURLは開く
        "$urlMain を開きます。"|oh
        start $urlMain
    }

    $running = $true
    while($running){
        $context = $listener.GetContext()
        $request = $context.Request
        $response = $context.Response

        ($url = $request.RawUrl)|oh
        $path = $url.TrimStart('/').split("?")[0]
        if(!$path) {
            "def:"+$DefaultPage|oh
            $path = $DefaultPage
        }

        $fullPath = [IO.Path]::Combine($parentPath, $path)
        $content = [byte[]]@()
        if( [IO.File]::Exists($fullPath) ){
            $content = [IO.File]::ReadAllBytes($fullPath)
        } else {
            $response.StatusCode = 404
        }

        $response.OutputStream.Write($content, 0, $content.Length)
        $response.Close()
    }
} finally {
    #スクリプトではあまり真面目にリソース管理しようとは思わないけれども一応
    $listener.Dispose()
}
'終了します。'|oh
pause

ファイルが存在すればその内容を返すだけの、最低限レベルのサンプルとなります。
本当はContent-TypeやEncoding指定の処理も入れた方が無難ですが、拘り始めると行数が増えてサンプルとして見通しが悪くなりそうので、やめました。
ps1ファイルを指定したときはスクリプトを実行する等の遊びも面白いかと思います。

起動について

下記のようなコマンドで起動できます。バッチファイルかショートカット等を作れば良いでしょう。

httpListener.ps1起動
powershell -ExecutionPolicy RemoteSigned -File .\httpListener.ps1

実行して、ブラウザが開きMDwikiのindexページが出れば成功です。
ああ、index.mdも予め作成しておかなければ、コンテンツがありません。

index.md
Hello MDwiki

終了処理は入れていませんので、終了する時はコンソールを閉じて強制終了してください。

一応、ポートの変更もできるようにしています。デフォルトは18080で、以下は19999を指定する例です。

httpListener.ps1起動
powershell -ExecutionPolicy RemoteSigned -File .\httpListener.ps1 19999

参考:Content-Typeを指定するコード断片

Content-Type指定
    $response.ContentEncoding = [Text.Encoding]::UTF8
    $response.ContentType = 'text/html; charset='+$response.ContentEncoding.HeaderName

参考:PowerShellスクリプトを処理するコード断片

ps1処理
    if( [IO.Path]::GetExtension($fullPath) -eq ".ps1" ){
        $response.ContentEncoding = [Text.Encoding]::UTF8
        $response.ContentType = 'text/html; charset='+$response.ContentEncoding.HeaderName
        try{
            & ($fullPath)|foreach{
                $resText = if($_ -is [string]){ $_ }
                elseif( $_ -eq $null){ '' }
                else{ $_.ToString() }
                $bytes = $response.ContentEncoding.GetBytes($resText)
                $response.OutputStream.Write($bytes, 0, $bytes.Length)
                $response.OutputStream.Flush()
            }
        }catch{
            $_|oh
        }
    }

※組み込みは適当に工夫する必要があります

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29