1.はじめに

CEHTTPではC++によってDLLを作成することによって、動的ページを作成することができます。それはApache上で動作するPerlやPHPに似ています。

現在のバージョンでは、MOD形式、CHX形式、DLL形式の三種類がネイティブに対応しています。また、ログファイルもDLLを作成することによって、自由にカスタマイズ可能です。

CEHTTPの拡張には2つのクラスライブラリが重要になってきます。こちらの説明もあわせて行っていきます。

  1. Windows CE の制限

環境変数とstdin/stdoutがありません。

環境変数には通常HTTP リクエストヘッダーがセットされます。通常ならば::getenv()にて、これらの値が取得できますが、CEではできません。このため、環境変数を動的にメモリに配置して拡張ファイルを呼び出します。

stdinにはPOSTコマンドで送信されたデータのボディデータが入ります。また、通常のCGIでは出力をstdoutに書き出します。HTTPサーバとCGIプロセスはパイプによって結ばれ、データのやり取りを行います。CEHTTPではこれらも動的にメモリに配置し、受け渡しを行います。

2.概要

  1. CHX/DLL

  2. CHX拡張とDLL拡張の動作はほぼ同じです。CHXの正体は単なるDLLファイルです。
    ユーザーからの要求で、CHX/DLLが呼び出された場合、CEHTTPは環境変数を作成し、CHX/DLLをロードします。
    CHX/DLLはCEHTTPからユーザーリクエストを受けて動的にレスポンスを作成し、CEHTTPに返却します。
    CHX/DLLが返すものは、ビットマップでもGIFでもHTMLでも何でも可能です。

  3. MOD

    リクエストされたファイルの拡張子によって、呼び出されるファイルを変更するCHX形式ファイルです。
    登録された拡張子が呼び出されると、CEHTTPはMIMEタイプリストを走査し、実行させるCHXを決定します。
    環境変数には、呼び出されたファイル名が入ります。
    MODはこれらの情報を元に動的にレスポンスを作成し、CEHTTPに返却します。

3.MyVarMemクラスについて

MyVarMemは動的に伸張するメモリバッファです。バイナリデータやテキストデータを追加していくことができます。

仕様については「MyVarMem仕様書」を参照してください。

4.MyHashクラスについて

MyVarMemから継承されたクラスです。キーとバリューを保有し、検索、値の取得、書き込みが可能です。

仕様については「MyHash仕様書」を参照してください。

5.CHX拡張

Description

拡張子.chxのファイルで、クラスライブラリにより、DLL拡張より簡単に動的ページを作成することが可能です。 WindowsCEでは標準でPIPEをサポートしないために、メモリ経由で必要な情報を手渡します。 eMVC++にて拡張子「CHX」のDLLを作成し、次の関数をエクスポートしてください。

Prototype

int fnChx(MyHash* pEnv, MyVarMem* pBody, MyVarMem* pRes)

Parameters

in MyHash* pEnv 環境変数が入ります。
in MyVarMem* pBody POSTされた場合、リクエストボディが入ります。
out MyVarMem* pRes クライアントに返す内容を追加します。

Return Value

pResの長さを返却してください。通常 pRes->GetLength() にて取得できます。

remark

6.DLL拡張

Description

全ての受信データを受け取ることが可能で、更に返信伝文を完全にコントロールできますが、扱いが少々面倒です。ISAPIのようなものと考えてください。拡張子.dllのファイルで、サーバが受け取ったデータを全てメモリ経由で受領することが出来ます。 DLLを作成し次の関数をエクスポートしてください。

環境変数は下記の形式で入っています。
実際に入力されるものは、CHX形式と変わりません。

|key1|\0|value1|\0|key2|\0|value2|...|keyN|\0|valueN|\0|\0|
・終端は|\0|\0|
・valueが\0もありうる |key1|\0|\0|key2|...
・Keyが\0は許可しない

Prototype

int fnCAPI(const void* pEnv, int nEnvLen,const void* pHead, int nHeadLen,const void* pBody, int nBodyLen,void** ppResponce)

Parameters

in const void* pEnv 環境変数が入ります。
in int nEnvLen 環境変数の長さが入ります。
in int nEnvLen 環境変数の長さが入ります。
in const void* pHead ヘッダー部分が入ります。
in int nHeadLen ヘッダーの長さが入ります。
in const void* pBody ボディ部分が入ります。
in int nBodyLen ボディ部分の長さが入ります。
out void** ppResponce ブラウザに返す情報をmallocして返してください。

version 2.30からは
 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size ) を ::malloc( size )の代わりに使ってください。
 HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, p, size ) を ::realloc( size )の代わりに使ってください。

Return Value

ブラウザに送信するデータ長(BYTE単位)を返答します。 この値を::send()します。 

7.MOD拡張

Description

MIMEタイプ用の拡張モジュールです。関数の形式はCHXと同じです。

どのMIMEタイプを拡張するのかは、MOD拡張のファイル名で決定されます。
txt.modというファイルは、'.txt'拡張子のファイルがリクエストされた際に呼び出されます。

環境変数 SCRIPT_NAME に呼び出されたファイル名が入ります。
PATH_INFO/PATH_TRANSLATED が設定されません。
その他はCHX形式と同じです。

strcpy( szFile, pEnv->GetValueI("DOCUMENT_ROOT") );
strcat( szFile, pEnv->GetValueI("SCRIPT_NAME") );
// '/' ->'\'
char* p = szFile;

while( *p != '\0' ) {
  if(*p == '/' ) {
    *p++ = '\\';
}

これにより、呼び出されたファイルを変換し、ブラウザへ返却するなどの拡張が行えます。

CEHTTPは起動時にカレントディレクトリに存在する'*.mod'ファイルを列挙します。ここで'txt.mod'ファイルを発見すると、MIMEタイプを変更します。 例 .txt \program files\cehttp\txt.mod

MIMEタイプ設定ファイル'AddType.txt'を上記のように書き換えれば、同様のことが可能です。このファイルについては'CEHTTP ユーザーズ マニュアル'を参照してください。

8.ログファイル拡張

Description

ログファイルは外部DLL形式により提供されています。これをモディファイすることによって、ログファイルの出力方法を変更することが可能です。

ファイル名は'cehttplog.dll'、関数名は'fnCehttpLog'です。

ログファイル関数には環境変数が与えられますが、ログファイル用にいくつか追加の情報が入っています。

remote_addr リモートホストのIPアドレスです。
request_line ブラウザが送信してきたリクエストラインです。
send_bytes CEHTTPが送信した有効バイト数です。

送信した有効バイト数とは、HTTP ボディ のカウント数です。HTTP ステータスが200以外の場合はセットされません。

Prototype

typedef int (*CEHTTPLOGAPI)(WCHAR* pwszLogFile, int nLogSize, int nHttpStatus, MyHash* hashRequest);

Parameters

in WCHAR* pwszLogFile CEHTTPが指定するログファイル名です。このファイルにログを記述します。
in int nLogSize ログファイルのMAX値です。
in int nHttpStatus CEHTTPが処理したHTTPステータスです。
in MyHash* pHashHeaders 環境変数が入ります。

Return Value

成功した場合は0以外を、失敗した場合は0を返却してください。

9.eMVC3++

コンパイルは全て Microsoft eMbedded Visual Tools 3.0 の eMVC++ で行われています。
eMVC++はマイクロソフト社から無償で提供されている、WindowsCE用コンパイラです。
CEHTTPは eMVC++ を利用して製作されています。ソースコードのコンパイルを行うにはこれらを入手する必要があります。

現在英語版のダウンロード、日本語版のCD購入が可能です。下記のURLを参考にしてください。

http://www.microsoft.com/japan/windows/embedded/ce/techinfo/default.asp

10.開発について

DLLのデバッグ

拡張モジュールはDLLのため、単体では行えません。一番簡単な方法はCEHTTPと拡張モジュールを同時にデバッグすることです。

ひとつのワークスペースにCEHTTPと拡張モジュールのプロジェクトファイルを追加すると、CEHTTPとDLLの両方をステップ実行することが可能です。

  1. メニュー「ファイル」-「新規作成」をクリックし、'ワークスペース'タブをクリックし、'ワークスペース'を新規に作成します。
  2. メニュー「プロジェクト」-「ワークスペースへプロジェクトの挿入...」をクリックし、CEHTTPのプロジェクトを選択します。
  3. メニュー「プロジェクト」-「ワークスペースへプロジェクトの挿入...」をクリックし、拡張モジュールのプロジェクトを選択します。

メニュー「プロジェクト」-「アクティブプロジェクトに設定」から、各プロジェクトをアクティブにできます。それぞれをデバッグモードでコンパイルします。

これで双方のコードをステップ実行することが可能となります。

WindowsCE上でDLLを取り扱う際に注意しなくてはならない点としては、DLLは一度読み込まれたら、開放されないという点にあります。この点がWindows上との違いになります。

マルチスレッドプログラミングの注意点

CEHTTPから呼び出される拡張モジュールは、マルチスレッドセーフである必要があります。スタティック変数やグローバル変数を利用する際には注意が必要です。グローバル変数の書き込みは、他のスレッドで動作している拡張モジュールの動作に影響を与えます。

システムオブジェクトの初期化とリソースの開放はDllMain()で行います。たとえばCriticalSectionを利用する際は、下記のように記述します。

CRITICAL_SECTION cs;
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
    switch( ul_reason_for_call )
    {
        case DLL_PROCESS_ATTACH:
        {
            InitializeCriticalSection(&cs);
            break;
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            break;
        case DLL_PROCESS_DETACH:
        {
            DeleteCriticalSection( &cs );
            break;
        }
    }

    return TRUE;
}

int fnChx(MyHash* pEnv, MyVarMem* pBody, MyVarMem* pRes)
{
    EnterCriticalSection( &cs );
    // critical section code here...

    LeaveCriticalSection( &cs );
}

こうすることによって、他のスレッドがこのDLLを実行中に、不用意にシステムオブジェクトが開放される事を防げます。

UNICODEについて

WindowsCEはUNICODEがデフォルトとなっています。HTTPプロトコル自体はASCII文字列をシングルバイトで扱う事が主体となりますので、注意が必要です。

CEHTTPではTEXTマクロを使用せずに明示的にWSTRを利用するプログラミングスタイルに移行しています。

試験について

実際ブラウザで見るのが一番のテストですが、telnet にて生データのやり取りを閲覧することも有効です。

Windows2000やXPでは、テストに使えるには十分なtelnetコマンドが標準で用意されています。

telnet <接続先> <ポート番号> の書式で利用できます。

>telnet localhost 80
GET / HTTP/1.0

と打ち込むと、HTTPサーバーから返答が帰ってきます。

HTTP/1.0 200 OK
Server: CEHTTP/2.2
Content-Length: 127
Last-Modified: Mon, 10 Feb 2003 04:28:49 GMT
Connection: Keep-Alive
Content-Type: text/html

<HTML><BODY><H1>Can you see me?</H1><BR><HR>CEHTTP2 by<A HREF="http://www001.upp.so-net.ne.jp/ishi/">IshiSoft</A></BODY></HTML>

ツールについて

IProxy

実際にブラウザとWebサーバーのやり取りを見るためのツールがIshiSoft上で提供されています。このツールを利用すると、ブラウザとWebサーバー上のやり取りを閲覧することができます。

MS Web Application Stress Tool

マルチスレッドでの試験や、耐久試験などを自動で行いたい場合はWASが便利です。これはマイクロソフトから無償で提供されているツールです。

マクロが記述可能で、マルチスレッドで動作します。ダウンロードについては下記のURLから行えます。

http://www.microsoft.com/technet/treeview/default.asp?url=/technet/itsolutions/intranet/downloads/webstres.asp

appendix. A サンプル コード

// sample.chx

#include "stdafx.h"
#include "stdio.h"
#include "chx.h"

//*****************************************************************************
// fhChx
//*****************************************************************************
int fnChx(MyHash* pEnv, MyVarMem* pBody, MyVarMem* pRes)
{
    char work[2048];
    if((char*)pBody)
    {
        sprintf(work,
            "Content-Type: text/html\r\n\r\n"
            "<html>"
            "<head><title>CECGI</title></head>"
            "<u><b>BODY</b></u><br>"
            "%s<br>"
            "<u><b>environment</b></u><br>"
            ,
            pBody->GetBuffer()
            );
    }
    else
    {
        pRes->Add("(null)");
    }

    pRes->Add(work);

    pEnv->SeekToFirst();

    do{
        sprintf(work,
            "%s=%s<br>\n"
            ,pEnv->GetCurrentKey()
            ,pEnv->GetCurrentValue()
            );
        pRes->Add(work);
    }while(pEnv->SeekNext());

    pRes->Add("</html>");
    return pRes->GetLength();
}

//*****************************************************************************
// DllMain
//*****************************************************************************
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
    switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

 

Appendix. B 環境変数

CEHTTPは以下の環境変数を設定します。もし得られない情報があれば、セットされません。

変数名 解説
SCRIPT_NAME 実行されている拡張ファイル名
PATH_INFO URIの後ろについたパス情報
PATH_TRANSLATED パス情報のローカルファイルシステム上の位置
CONTENT_LENGTH POST時のボディ長
QUERY_STRING URIより後ろのパラメータ
REMOTE_ADDR クライアントのIPアドレス
REMOTE_HOST DNSによって解決されたホストネーム (dnslookupオプションが必要です)
REMOTE_PORT クライアントのポート
REQUEST_METHOD POST/GET
SERVER_NAME クライアントが指定したWEBサーバのアドレス
SERVER_PORT クライアントが指定したサーバーのポート
SERVER_PROTOCOL クライアントが指定したHTTPプロトコルバージョン
SERVER_SOFTWARE サーバソフト名
HTTP_ACCEPT クライアントが指定した受信可能なMIMEタイプ
HTTP_USER_AGENT クライアントソフト名
HTTP_COOKIE クライアントが送信したクッキー名
HTTP_REFERER クライアントが来たページ
DOCUMENT_ROOT ローカルファイルシステム上でのドキュメントルート位置
GATEWAY_INTERFACE 拡張ゲートウェイ名

全てのクライアントからのリクエストラインは大文字に変換され、HTTP_で始まるヘッダを付与され、'-'を'_'に変換されています。
たとえば「Accept-Encoding」ならば「HTTP_ACCEPT_ENCORDING」となります。


yu-ishi@ya2.so-net.ne.jp
Copyright (C) 2003 IshiSoft Allrights reserved.