| |||||||||||||||||||||||||||||||||||||||||||||||||||||
サイト内検索
カスタム検索
|
Web サーバの作成 - HTTP API 入門Web サーバーを作るなんて難しくて良くわからない、という方も多いと思いますが、 Windows には 「Web サーバーを簡単に作る」ための便利な API である、HTTP API があります。 これを使えば HTTP に関する基本的な知識さえあれば、Web サーバーを作ることが出来ます。 ここではその使い方をご紹介します。
ちなみに、HTTP API のバージョン 2 が公開されていますので、新しくコードを書くなら、
ぜひそちらもチェックしてください。 はじめにWindows XP SP2 から HTTP API という、Web サーバ作成用の API が使用可能になりました。この資料では単純な Web サーバのサンプルコードを示しながら、HTTP API の利用方法を説明します。 さらに Vista や Windows Server 2008 以降をターゲットにする場合は、 IIS 7.0 の HWC を 利用するのもテです。(結局 IIS 7.0 は HTTP API を利用しているわけですが...) HTTP API 概要Windows Server 2003 から HTTP API という Web サーバー作成用の API が使用可能となりました。当初 HTTP API を利用できるのは Windows Server 2003 のみでしたが、後に Windows XP SP2 でもサポートされるようになったため、開発作業が大変容易となりました。 HTTP API はカーネルモードで実行されるドライバー http.sys として実装されており、httpapi.dll を通して http.sys と通信することによってユーザープログラムから利用します。http.sys で実装されると聞くと何かあったときはブルースクリーンが心配されると思うかもしれませんが、現在のところ HTTP API を利用する代表格ともいえる IIS6 でもそのようなクリティカルな問題は聞いたことが無いので、そこまで心配しなくて良いでしょう。 さて、早速プログラミングの手順について概要を説明します。基本的な手順は以下の通りです。
リクエストキューのイメージとしては、上図のように考えておけばよいでしょう。青い線の下側がカーネルモード (http.sys) で、上側が HTTP API を利用するプロセスを表しています。リクエストキューは http.sys の中に作成されます。キューは複数作成することが可能です。どのリクエストキューでどの URL を処理するかを決定できるようにするために、URL は明示的にキューと関連付けされます。 Web サーバの作成なんと、これまでの説明だけで単純な Web サーバーは作成可能です。サンプルコード全体は以下からダウンロードして下さい。 単純な Web サーバサンプルコード [ssrv1.zip, makefile プロジェクト]
以前アップロードしていたファイルはそのままだとビルドできないことがわかりました。
新しいバージョンのサンプルコードは準備出来次第アップロードします。 サンプルコード中、キモとなるのは ssrv1.cpp ですから ssrv1.cpp についてコメントを追記しながら見ていきましょう。 HTTP API を使うためにヘッダーファイル http.h をインクルードしています。 #include <http.h> HTTP のステータスラインに返す文字を定義します。このサンプルでは OK (200) と File Not Found(404) のみをサポートすることにします。 #define STATUS_MSG_FILE_NOT_FOUND ("File
Not Found") さらに 404 のときに返すボディも定義します。 まともなサーバーを作るならエラーメッセージも差し替え可能にすべきですが、ここでは単純化のために定数にしてあります。 お好みに応じて直してください。 #define STATUS_BODY_FILE_NOT_FOUND ("<html><body><h1>File not found</h1></body></html>") このサンプルは単純なコンソールプログラムとして実装しています。開始は main (_tmain) から始まります。 int _tmain(int argc, _TCHAR* argv[]) { HTTP API の初期化を行います。 ULONG
ulRet; リクエストキューを作成します。 HttpCreateHttpHandle なんて名前ではなく、HttpCreateRequestQueue という名前だったら良かったのに、と思うのは私だけではないでしょう。 HANDLE
hReqQueue = NULL; リクエストキューに URL を登録します。登録する URL の構文に注意して下さい。ここでは、"+" を利用してこのマシンの全ての IP アドレス及びホスト名における TCP ポート 8080番へのリクエストを受け付けています。 ulRet = HttpAddUrl (hReqQueue, L"http://+:8080/", NULL); もしここで L"http://localhost:8080"& nbsp;を指定すると、http://localhost:8080/ は受け付けますが http://127.0.0.1:8080/ は不正なリクエストと認識されます。 この機能は HTTP 1.1 のホストヘッダーをサポートする際に利用できます。 この応答は HTTP.SYS が返しています。 サンプルコードではこの後、無限ループに入り、HTTP リクエストの受け取りと応答を繰り返します。 while (1) { HTTP リクエストの受信。受信するまでここでブロックします。受け取り用のバッファーは 2kb を指定しています。
ulRet = HttpReceiveHttpRequest ( 基本的に HttpReceiveHttpRequest は HTTP リクエストヘッダーのみを受信します。 この点がソケットを操作するときと比べてプログラミングが楽です。 わざわざヘッダとボディの区切りをチェックする必要がありません。 ボディ部があるかどうかチェックします。ボディが存在する場合、HTTP_REQUEST の Flags にて、HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS がセットされます。 if ( HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS & pHttpRequest->Flags ) { ボディが存在するときは、HttpReceiveRequestEntityBody で読み込みます。 ボディ部が読み込み終わると、HttpReceiveRequestEntityBody が ERROR_HANDLE_EOF を返します。
for (bool bContinue = true; bContinue; ) { 実際にサンプルを実行してみれば、ここで何をしているかわかると思います。 試しに WFetch を使って A ~ Z までの 26 文字を POST してみます。 その結果、次のようなトレースが記録されました。サンプルでは 16 バイトの受け取りバッファを指定しているので、A ~ P までの 16 文字が最初の HttpReceiveRequestEntityBody 呼び出しで読み込まれ、二度目の HttpReceiveRequestEntityBody で Z までの残り 10 バイトが読み込まれています。最後まで読みきった場合にも、戻り値は NO_ERROR となります。Content-Length をチェックして cbBytesReceived の積算と比較してデータを読み込むのがまっとうなやり方でしょうが、ここではもう一度 HttpReceiveRequestEntityBody を呼び出して ERROR_HANDLE_EOF を出し、それを読み込みループの終了判定にしています。 次に HTTP レスポンス。 ここでははじめにデフォルトの属性を text/html と決めました。
HTTP_RESPONSE HttpResponse; 要求された URL は pHttpRequest->CookedUrl.pAbsPath に入ります。ここでは、ルートパスと URL パスからファイルを特定してそれを開き、さらにそのファイルハンドルを返す関数 GetFileHandleFromURL を実装しました。戻り値に INVALID_HANDLE_VALUE が返れば、ファイルが開けなかったことを示し、それ以外ならファイルが開けたと判定します。 一般的には、ファイルが開けない理由は、「ファイルが存在しないから」 という理由だけではなく、 アクセス権が原因である場合もあります。HTTP のステータスコードとしては通常この場合には 401 や 403 などを返すべきなのですが、ここでは簡便のため全て 404 としています。(401 は Authentication を要求することを、403 は Authorization の失敗を、それぞれ意味します)
HANDLE hFile = GetFileHandleFromURL
( // API ではないのでご注意。 "File Not Found" というメッセージはメモリブロックから返されるので、チャンクタイプとして HttpDataChunkFromMemory を指定します。その他に後述するように HttpDataChunkFromFileHandle 等があります。
HttpDataChunk.DataChunkType =
HttpDataChunkFromMemory; 200 の場合は、ファイルハンドルから直接応答します。このときのチャンクタイプは HttpDataChunkFromFileHandle です。Byte-Range に対応できるように、指定したファイルのバイト範囲を指定できます。コンテンツ全体を指定するにはサンプルのように HTTP_BYTE_RANGE_TO_EOF を指定します (StartingOffset は 0)。
HttpDataChunk.DataChunkType =
HttpDataChunkFromFileHandle; このサンプルではループを抜けることは無いので、クリーンアップコードは実行されない。が、一応書いておきました。 cleanup:
以上、シングルスレッド+同期呼び出しを使用する単純な Web サーバーの作成を通して、HTTP API の基本的な利用方法について説明しました。 ぜひ、試しに自分で Web サーバーを書いてみてください!
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
© 2008-2010 小山圭介 All Rights Reserved.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||