前へ     目次     索引     DocHome     次へ     
iPlanet Web Server, Enterprise Edition NSAPI プログラマーズガイド



第 6 章   カスタム SAF の例


この章では、要求-応答プロセスの各指令用のカスタムサーバアプリケーション関数 (Sever Application Functions: SAF) の例について説明します。 これらの例を基に、ユーザ独自のカスタム SAF を作成することができます。 ユーザ独自のカスタム SAF の作成方法については、第 4 章「カスタム SAF の作成」を参照してください。


  実際のソースファイル内のコメントはすべて英語ですが、この章では、説明のために日本語にしています。

 



カスタム SAF を作成する前に、要求 - 応答プロセス (第 1 章「サーバの動作の基本」で説明されている) と構成ファイル obj.conf (第 2 章「obj.conf の構文と使用法」で説明されている) の役割を理解しておく必要があります。

ユーザ独自の SAF を作成する前に、既存の SAF で同じ目的が果たせないかを確認します。 事前定義済みの SAF の説明は、第 3 章「事前定義済みの SAF および要求処理プロセス」にあります。

新しい SAF を作成するための NSAPI 関数のリストは、第 5 章「NSAPI 関数のリファレンス」を参照してください。

この章は、次の節から構成されています。



ビルドに含まれている例

サーバインストールディレクトリ内の nsapi/examples/ または plugins/nsapi/examples サブディレクトリに、SAF のソースコードの例があります。

同じディレクトリ内の Makefile を使って、例をコンパイルし、すべての例のファイルに含まれる関数が含まれるライブラリを作成することができます。

例をテストするには、magnus.confInit セクションに次の指令を追加して、examples 共用ライブラリを iPlanet Web Server に読み込みます。


Init fn=load-modules shlib=examples.so/dll funcs=function1,function2,function3

funcs パラメータは、共用ライブラリから読み込む関数を指定します。

例で初期化関数を使っている場合は、load-modules への funcs 引数に初期化関数を指定し、また初期化関数を呼び出すための Init 指令を追加します。

たとえば、PathCheck の例は、acf-init 関数で初期化される restrict-by-acf 関数を実装しています。 次の指令は、この両方の関数を読み込みます。

Init fn=load-modules yourlibrary funcs=acf-init,restrict-by-acf

次の指令は、サーバの初期化時に acf-init 関数を呼び出します。

Init fn=acf-init file=extra-arg

新しい SAF を応答処理プロセスの適切なステップで呼び出すには、たとえば次のような適切な指令を、その指令が適用されるオブジェクトに追加します。

PathCheck fn=restrict-by-acf

新しい Init 指令を magnus.conf に追加した後、Init 指令はサーバの初期化時にだけ適用されるので、変更を読み込むために必ず iPlanet Web Server を再起動する必要があります。



AuthTrans の例



この AuthTrans 関数の簡単な例は、リモートクライアントが提供するユーザ名およびパスワードが正しいものであるかどうかを検証するために、ユーザ独自の方法をどのように使用するかを示します。 このプログラムは、ハードコード化されたユーザ名およびパスワードのテーブルを使い、与えられたユーザのパスワードを静的なデータ配列にあるパスワードと突き合せて確認します。 userdb パラメータは、この関数では使われません。

AuthTrans 指令は、PathCheck 指令と連係します。 一般に、AuthTrans 関数は、要求に関連付けられたユーザ名およびパスワードを受け入れることができるかどうかを確認しますが、要求へのアクセスの許可や拒否は行ないません -- それについては、PathCheck 関数に任せます。

AuthTrans 関数は、要求に関連するヘッダーからユーザ名とパスワードを取得します。 クライアントが初めて要求を発行するときには、ユーザ名とパスワードはわかっていないので、AuthTrans 関数と PathCheck 関数は、ユーザ名およびパスワードの妥当性を検査できず、連係して要求を拒否します。 クライアントは拒否の知らせを受けると、通常、ユーザ名とパスワードをユーザに要求するダイアログボックスを表示し、今度はユーザ名とパスワードをヘッダーに入れて要求を再送します。

この例では、AuthTrans ステップで呼び出されるhardcoded-auth 関数が、ユーザ名とパスワードが、ユーザ名とパスワードのハードコード化されたテーブルのエントリに対応するかどうかを確認します。


例のインストール

関数を iPlanet Web Server にインストールするには、コンパイル済みの関数を読み込むために次の Init 指令を magnus.conf に追加します。

Init fn=load-modules shlib=yourlibrary funcs=hardcoded-auth

obj.conf に含まれるデフォルトのオブジェクト内に、次の AuthTrans 指令を追加します。


AuthTrans fn=basic-auth auth-type="basic" userfn=hardcoded-auth userdb=unused

この関数は、実際に承認要求を実行するのではなく、与えられた情報を受け取り、サーバにその情報が正しいかどうかを知らせるだけであることに注意してください。 PathCheck 関数の require-auth が実際に承認を行なうので、次の PathCheck 指令も追加します。

PathCheck fn=require-auth realm="test realm" auth-type="basic"


ソースコード

この例のソースコードは、サーバルートディレクトリの nsapi/examples/ または plugins/nsapi/examples サブディレクトリ内の auth.c ファイルにあります。

#include "nsapi.h"

typedef struct {
   char *name;
   char *pw;
} user_s;

static user_s user_set[] = {
   {"joe", "shmoe"},
   {"suzy", "creamcheese"},
   {NULL, NULL}
};

#include "frame/log.h"

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int hardcoded_auth(pblock *param, Session *sn, Request *rq)
{
   /* auth-basic によって与えられたパラメータ */
   char *pwfile = pblock_findval("userdb", param);
   char *user = pblock_findval("user", param);
   char *pw = pblock_findval("pw", param);

   /* 一時変数 */
   register int x;

   for(x = 0; user_set[x].name != NULL; ++x) {

       /* これが目的のユーザでない場合は、続行する
       if(strcmp(user, user_set[x].name) != 0) continue;

       /* パスワードを検証する_
       if(strcmp(pw, user_set[x].pw)) {
          log_error(LOG_SECURITY, "hardcoded-auth", sn, rq,
             "user %s entered wrong password", user);
          /* これにより、実行関数に、再度ユーザの入力を */
          /* 求めさせる */
          return REQ_NOACTION;
       }

       /* REQ_PROCEED を返した場合は、ユーザ名が受け入れられる */
       return REQ_PROCEED;
   }

   /* 一致しない場合は、再度入力を求めさせる */
   log_error(LOG_SECURITY, "hardcoded-auth", sn, rq,
       "unknown user %s", user);
   return REQ_NOACTION;
}



NameTrans の例



サーバルートディレクトリの nsapi/examples/ または plugins/nsapi/examples サブディレクトリの ntrans.c ファイルに、NameTrans 関数の次の 2 つの例のソースコードがあります。

  • explicit_pathinfo

    この例では、URL 内で明示的な追加のパス情報を使うことを許可しています。

  • https_redirect

    この例では、クライアントが特定のバージョンの Netscape Navigator である場合、URL をリダイレクトします。

この節では、最初の例について説明します。 2 番目の例については、ntrans.c にあるソースコードを参照してください。



  NameTrans 関数が通常主に実行することは、rq->vars 内の ppath の論理 URL を物理パス名に変換することです。 ただし、ここで説明する例の、explicit_pathinfo は、URL を物理パス名に変換するのではなく、要求された URL の値を変更します。 rq->varsppath の値を URL から物理パス名に変換する NameTrans 関数の例は、2 番目の例の ntrans.chttps_redirect を参照してください。

 



explicit_pathinfo の例は、CGI プログラムが使う追加のパス情報を URL に明示的に指定することを許可します。 追加のパス情報は、URL の主な部分から、コンマなどの指定された区切り文字で区切られます。

次に例を示します。

http://server-name/cgi/marketing,/jan/releases/hardware

この場合、要求されたリソース (CGI プログラムになる) の URL は、http://server-name/cgi/marketing であり、CGI プログラムに与えられる追加のパス情報は /jan/releases/hardware です。

区切り文字を選択するときには、必ず実際の URL の一部として使われることのない文字を選択します。

explicit_pathinfo 関数は、URL を読み込み、コンマの後に続くものをすべて切り離し、request オブジェクト (rq->vars) の vars フィールドの path-info フィールドに入れます。 CGI プログラムは、PATH_INFO 環境変数を介してこの情報にアクセスできます。

explicit_pathinfo の副次作用の 1 つは、SCRIPT_NAME CGI 環境変数の終わりに区切り文字が付加されることです。

通常、NameTrans 指令は、パスを変更するときには、REQ_PROCEED を返して、サーバがさらに NameTrans 指令を処理することがないようにします。 ただし、この場合、URL を物理パス名にまだ変換していないので、パス情報を取り出した後も名前の変換を続ける必要があります。


例のインストール

関数を iPlanet Web Server にインストールするには、コンパイル済みの関数を読み込むために次の Init 指令を magnus.conf に追加します。

Init fn=load-modules shlib=yourlibrary funcs=explicit-pathinfo

obj.conf に含まれているデフォルトのオブジェクト内に、次の NameTrans 指令を追加します。

NameTrans fn=explicit-pathinfo separator=","

この NameTrans 指令は、デフォルトのオブジェクト内のほかの NameTrans 指令の前に指定する必要があります。


ソースコード

この例は、サーバルートディレクトリの nsapi/examples/ または plugins/nsapi/examples サブディレクトリ内の ntrans.c ファイルにあります。

#include "nsapi.h"

#include <string.h> /* strchr */
#include "frame/log.h" /* log_error */

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int explicit_pathinfo(pblock *pb, Session *sn, Request *rq)
{
   /* パラメータ : パスを分割する文字 */
   char *sep = pblock_findval("separator", pb);

   /* サーバ変数 */
   char *ppath = pblock_findval("ppath", rq->vars);

   /* 一時変数 */
   char *t;

   /* 正しい使用法を検証する */
   if(!sep) {
       log_error(LOG_MISCONFIG, "explicit-pathinfo", sn, rq,
          "missing parameter (need root)");
       /* 中止した場合、デフォルトの状態コードは 500 Server
          Error である_*/
       return REQ_ABORTED;
   }

   /* 区切り文字を確認する。 区切り文字がない場合は、何も操作を行なわない */
   t = strchr(ppath, sep[0]);
   if(!t)
       return REQ_NOACTION;

   /* 区切り文字の箇所でパスを切り捨てる */
   *t++ = '\0';
   /* パス情報を割り当てる */
   pblock_nvinsert("path-info", t, rq->vars);

   /* 通常、NameTrans 関数は、パスを変更したら
       REQ_PROCEED を返す。 しかし、本関数終了後もまだ名前の変換を
       続行したい */
   return REQ_NOACTION;
}

#include "base/util.h" /* is_mozilla */
#include "frame/protocol.h" /* protocol_status */
#include "base/shexp.h" /* shexp_cmp */

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int https_redirect(pblock *pb, Session *sn, Request *rq)
{
   /* サーバ変数 */
   char *ppath = pblock_findval("ppath", rq->vars);
   /* パラメータ */
   char *from = pblock_findval("from", pb);
   char *url = pblock_findval("url", pb);
   char *alt = pblock_findval("alt", pb);
   /* 作業用変数 */
   char *ua;

   /* 使用法を確認する */
   if((!from) || (!url)) {
       log_error(LOG_MISCONFIG, "https-redirect", sn, rq,
          "missing parameter (need from, url)");
       return REQ_ABORTED;
   }
   /* ワイルドカードを使った突き合せで、このパスがリダイレクトする必要があるパス であるかどうかを
       確認する */
   if(shexp_cmp(ppath, from) != 0)
       return REQ_NOACTION; /* 一致するものなし */

   /* SSL の機能を確認するための唯一の方法は、UA を確認する
       こと */
   if(request_header("user-agent", &ua, sn, rq) == REQ_ABORTED)
       return REQ_ABORTED;

   /* is_mozilla 関数は、Mozilla バージョン 0.96 以降であるかを
       確認する */
   if(util_is_mozilla(ua, "0", "96")) {
       /* リターンコードを 302 Redirect に設定する */
       protocol_status(sn, rq, PROTOCOL_REDIRECT, NULL);
       /* エラー処理関数は、これを使用して Location: を設定する */
       pblock_nvinsert("url", url, rq->vars);
       return REQ_ABORTED;
   }

   /* 一致するものなし。 古いクライアント */

   /* 指定された代わりのドキュメントがある場合は、それを使用する */
   if(alt) {
       pb_param *pp = pblock_find("ppath", rq->vars);
       /* 古い値を捨てる */
       FREE(pp->value);
       /* ライブラリが後でこの pblock を解放するため、それを複製しておく
            必要がある */
       pp->value = STRDUP(alt);
       return REQ_PROCEED;
   }
   /* そうでなければ、何も実行しない */
   return REQ_NOACTION;
}



PathCheck の例



この節の例は、パスの確認を実行するカスタム SAF の実現方法を示します。 この例は、要求しているホストが、許可されているホストのリストにあるかどうかを確認するだけです。

Init 関数の acf-init は、各行に 1 つ IP アドレスがある、許可されている IP アドレスのリストが含まれているファイルを読み込みます。 PathCheck 関数の restrict-by-acf は、要求を送信してきたホストの IP アドレスを取得し、リストにあるかどうかを確認します。 ホストがリストにある場合は、アクセスが許可され、ない場合は拒否されます。

簡単にするために、ファイルからの IP アドレスの走査に stdio ライブラリを使っています。


例のインストール

ユーザの関数が含まれている共用オブジェクトを読み込むには、magnus.conf ファイルの Init セクションに次の行を追加します。

Init fn=load-modules yourlibrary funcs=acf-init,restrict-by-acf

許可されるホストのリストを読み込むために acf-init を呼び出すには、magnus.confInit セクションに次の行を追加します。 この行は、acf-init が含まれているライブラリを読み込む行の後に、指定する必要があります。

Init fn=acf-init file=fileContainingHostsList

要求-応答プロセスで、あるオブジェクトに対してカスタム SAF を実行するには、obj.conf ファイル内でそのオブジェクトに次の行を追加します。

PathCheck fn=restrict-by-acf


ソースコード

この例のソースコードは、サーバルートディレクトリの nsapi/examples/ または plugins/nsapi/examples サブディレクトリ内の pcheck.c ファイルにあります。

#include "nsapi.h"

/* acf-init を呼び出していないユーザに対して問題が発生するのを避けるために、 NULL に設定する */
static char **hosts = NULL;

#include <stdio.h>
#include "base/daemon.h"
#include "base/util.h" /* util_sprintf */
#include "frame/log.h" /* log_error */
#include "frame/protocol.h" /* protocol_status */

/* アクセス制御ファイルで使用できる最も長い行 */
#define MAX_ACF_LINE 256

/* 再起動時に静的な配列を解放するために使用する */
#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC void acf_free(void *unused)
{
   register int x;

   for(x = 0; hosts[x]; ++x)
       FREE(hosts[x]);
   FREE(hosts);
   hosts = NULL;
}

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int acf_init(pblock *pb, Session *sn, Request *rq)
{
   /* パラメータ */
   char *acf_file = pblock_findval("file", pb);

   /* 作業用変数 */
   int num_hosts;
   FILE *f;
   char err[MAGNUS_ERROR_LEN];
   char buf[MAX_ACF_LINE];

   /* 使用法を確認する。 Init 関数に特別なエラーロギング機能が備えられている
       ことに注意する */
   if(!acf_file) {
       util_sprintf(err, "missing parameter to acf_init
          (need file)");
       pblock_nvinsert("error", err, pb);
       return REQ_ABORTED;
   }

   f = fopen(acf_file, "r");

   /* 開いたか? */
   if(!f) {
       util_sprintf(err, "can't open access control file %s (%s)",
          acf_file, system_errmsg());
       pblock_nvinsert("error", err, pb);
       return REQ_ABORTED;
   }

   /* ホスト配列を初期化する */
   num_hosts = 0;
   hosts = (char **) MALLOC(1 * sizeof(char *));
   hosts[0] = NULL;

   while(fgets(buf, MAX_ACF_LINE, f)) {
       /* stdio が便宜上そこに残しておいた改行を破棄する */
       buf[strlen(buf) - 1] = '\0';
       hosts = (char **) REALLOC(hosts, (num_hosts + 2) *
          sizeof(char *));
       hosts[num_hosts++] = STRDUP(buf);
       hosts[num_hosts] = NULL;
   }

   fclose(f);

   /* 再起動時に、ホスト配列を解放する */
   daemon_atrestart(acf_free, NULL);

   return REQ_PROCEED
}

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int restrict_by_acf(pblock *pb, Session *sn, Request *rq)
{
   /* パラメータなし */

   /* 作業用変数 */
   char *remip = pblock_findval("ip", sn->client);
   register int x;

   if(!hosts) {
       log_error(LOG_MISCONFIG, "restrict-by-acf", sn, rq,
          "restrict-by-acf called without call to acf-init");
       /* 中止した場合、デフォルトの状態コードは 500 Server
          Error */
       return REQ_ABORTED;
   }

   for(x = 0; hosts[x] != NULL; ++x) {
       /* リストにある場合は、許可される */
       if(!strcmp(remip, hosts[x]))
       return REQ_NOACTION;
   }

   /* 応答コードを forbidden に設定し、エラーを返す */
   protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
   return REQ_ABORTED;
}



ObjectType の例



この節の例は、要求されたファイルの .shtml バージョンが存在する場合に、.html ファイルを .shtml ファイルとして扱うようにサーバに指示するカスタム SAF である html2shtml の実装方法を示します。

適切な処理を行なう ObjectType 関数は、内容のタイプがすでに設定されているかどうかを確認し、設定されている場合は何も実行しないで REQ_NOACTION を返します。


if(pblock_findval("content-type", rq->srvhdrs))
    return REQ_NOACTION;

ObjectType 指令の主な役割は、(内容のタイプが設定されていない場合に) 内容のタイプを設定することです。 この例では、次に示す行で内容のタイプを magnus-internal/parsed-html に設定します。


/* content-type を magnus-internal/parsed-html に設定する */
pblock_nvinsert("content-type", "magnus-internal/parsed-html",
    rq->srvhdrs);

html2shtml 関数は、要求されたファイル名を確認します。 ファイル名が .html で終わる場合は、この関数はベース部分の名前が同じで拡張子が .shtml のファイルを検索します。 該当するファイルが見つかった場合、そのパスを使い、サーバにそのファイルが通常の HTML ではなく解析された HTML であることを知らせます。 これには、アクセスされるすべての HTML ファイルに対して、追加の stat 呼び出しが必要であることに注意してください。


例のインストール

ユーザの関数が含まれている共用オブジェクトを読み込むには、magnus.conf ファイルの Init セクションに次の行を追加します。

Init fn=load-modules shlib=yourlibrary funcs=html2shtml

要求-応答プロセスで、あるオブジェクトに対してカスタム SAF を実行するには、obj.conf ファイル内でそのオブジェクトに次の行を追加します。

ObjectType fn=html2shtml


ソースコード

この例のソースコードは、サーバルートディレクトリの nsapi/examples/ または plugins/nsapi/examples サブディレクトリ内の otype.c ファイルにあります。

#include "nsapi.h"

#include <string.h> /* strncpy */
#include "base/util.h"

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int html2shtml(pblock *pb, Session *sn, Request *rq)
{
   /* パラメータなし */

   /* 作業用変数 */
   pb_param *path = pblock_find("path", rq->vars);
   struct stat finfo;
   char *npath;
   int baselen;

   /* タイプがすでに設定されている場合は、何も操作を行なわない */
   if(pblock_findval("content-type", rq->srvhdrs))
       return REQ_NOACTION;

   /* パスが .html で終了していない場合は、通常のオブジェクトタイプに処理を
       行なわせる */
   baselen = strlen(path->value) - 5;
   if(strcasecmp(&path->value[baselen], ".html") != 0)
       return REQ_NOACTION;

   /* 1 = html を shtml に変換するための領域 */
   npath = (char *) MALLOC((baselen + 5) + 1 + 1);
   strncpy(npath, path->value, baselen);
   strcpy(&npath[baselen], ".shtml");

   /* ない場合は何も操作しない */
   if(stat(npath, &finfo) == -1) {
       FREE(npath);
       return REQ_NOACTION;
   }

   /* 取得したら、入れ替えを行なう */
   FREE(path->value);
   path->value = npath;

   /* サーバが現在のパスの stat() をキャッシングする。
       それを更新する */
   (void) request_stat_path(NULL, rq);

   pblock_nvinsert("content-type", "magnus-internal/parsed-html",
       rq->srvhdrs);
   return REQ_PROCEED;
}



Service の例



この節では、simple-service という非常に単純な Service 関数について説明します。 この関数が実行するのは、クライアントの要求に対する応答としてメッセージを送信することだけです。 メッセージは、サーバの初期化時に simple-service-init 関数で初期化されます。

より複雑な例は、examples ディレクトリ内の service.c ファイルを参照してください。これについては、「より複雑な Service の例」に説明があります。


例のインストール

ユーザの関数が含まれている共用オブジェクトを読み込むには、magnus.conf ファイルの Init セクションに次の行を追加します。


Init fn=load-modules shlib=yourlibrary funcs=simple-service-init,simple-service

生成された出力を表すメッセージを初期化する simple-service-init 関数を呼び出すには、次の行を magnus.confInit セクションに追加します。 この行は、simple-service-init が含まれているライブラリを読み込む行の後に、指定する必要があります。


Init fn=simple-service-init generated-output="<H1>Generated output msg</H1>"

要求-応答プロセスで、あるオブジェクトに対してカスタム SAF を実行するには、obj.conf ファイル内でそのオブジェクトに次の行を追加します。

Service type="text/html" fn=simple-service

type="text/html" 引数は、content-typetext/html に設定されている場合にのみ、Service 段階でこの関数を呼び出すことを示します。


ソースコード

#include <nsapi.h>

static char *simple_msg = "default customized content";

/* これは、初期化関数である
 * この関数は、magnus.conf ファイルの Init 指令に指定されている、
 * generated-output パラメータの値を取得する
*/
NSAPI_PUBLIC int init-simple-service(pblock *pb, Session *sn,
Request *rq)
{
   /* magnus.conf ファイルの指令にあるパラメータからメッセージを
    * 取得する
   */
   simple_msg = pblock_findval("generated-output", pb);
   return REQ_PROCEED;

}

/* これは、カスタマイズされた Service SAF である
 * これは、クライアントに「generated-output」メッセージを送信する
*/
NSAPI_PUBLIC int simple-service(pblock *pb, Session *sn, Request *rq)
{
   int return_value;
   char msg_length[8];

   /* protocol_start_response を呼び出す前に、protocol_status 関数を使用
   * して応答の状態を設定する
   */
   protocol_status(sn, rq, PROTOCOL_OK, NULL);

   /* ObjectType 段階で content-type が設定されると期待していても、
   * ここで設定しておけば確実なので、content-type を text/html に
   * 設定する
   */
   param_free(pblock_remove("content-type", rq->srvhdrs));
   pblock_nvinsert("content-type", "text/html", rq->srvhdrs);

   /* keepalive を使用したい場合は、content-length ヘッダーを設定する必要が
   * ある。util_itoa 関数は、指定された整数を文字列に変換し、
   * 文字列の長さを返す。 数のテキスト表現
   * を作成するには、この関数を使用する
   */

   util_itoa(strlen(simple_msg), msg_length);
   pblock_nvinsert("content-length", msg_length, rq->srvhdrs);

   /* クライアントにヘッダーを送信する*/
   return_value = protocol_start_response(sn, rq);
   if (return_value == REQ_NOACTION) {
       /* GET の代わりに HTTP HEAD */
       return REQ_PROCEED;
   }

   /* net_write を使用して出力を書き込む*/
   return_value = net_write(sn->csd, simple_msg,
       strlen(simple_msg));
   if (return_value == IO_ERROR) {
       return REQ_EXIT;
   }

   return REQ_PROCEED;

}


より複雑な Service の例

send-images 関数は、iPlanet ホームページから入手できる doit.cgi デモに代わる、カスタム SAF です。 ファイルが /dir1/dir2/something.picgroup としてアクセスされると、send-images 関数は、そのファイルが Mozilla/1.1 ブラウザによってアクセスされているかどうかを確認します。 そうでない場合は、短いエラーメッセージを送信します。 ファイル something.picgroup には行のリストが含まれ、各行にはファイル名と、その後に content-type が指定されています (たとえば、one.gif image/gif)。

ユーザの関数が含まれている共用オブジェクトを読み込むには、magnus.conf ファイルの先頭に次の行を追加します。

Init fn=load-modules shlib=yourlibrary funcs=send-images

また、次の行を mime.types ファイルに追加します。

type=magnus-internal/picgroup exts=picgroup

要求-応答プロセスで、あるオブジェクトに対してカスタム SAF を実行するには、obj.conf ファイル内のそのオブジェクトに次の行を追加します。send-images は、任意(省略可能) のパラメータである delay を受け入れますが、この例では使われていません。


Service method=(GET|HEAD) type=magnus-internal/picgroup fn=send-images

ソースコードは、サーバルートディレクトリ内の nsapi/examples/ または plugins/nsapi/examples サブディレクトリの service.c ファイルにあります。



AddLog の例



この節の例は、要求についての情報である IP アドレス、メソッド、および URI (たとえば、198.93.95.99 GET /jocelyn/dogs/homesneeded.html) の 3 項目だけをログに記録するカスタム SAF である brief-log を実装する方法を示します。


例のインストール

ユーザの関数が含まれている共用オブジェクトを読み込むには、magnus.conf ファイルの Init セクションに次の行を追加します。

Init fn=load-modules shlib=yourlibrary funcs=brief-init,brief-log

ログファイルを開くために brief-init を呼び出すには、magnus.confInit セクションに次の行を追加します。 この行は、brief-init が含まれているライブラリを読み込む行の後に、指定する必要があります。

Init fn=brief-init file=/tmp/brief.log

AddLog 段階で、あるオブジェクトに対してカスタム SAF を実行するには、obj.conf ファイル内でそのオブジェクトに次の行を追加します。

AddLog fn=brief-log


ソースコード

ソースコードは、サーバルートディレクトリ内の nsapi/examples/ または plugins/nsapi/examples サブディレクトリの addlog.c ファイルにあります。

#include "nsapi.h"

#include "base/daemon.h" /* daemon_atrestart */
#include "base/file.h" /* system_fopenWA, system_fclose */
#include "base/util.h" /* sprintf */

/* プロセス間で共有されるファイル記述子 */
static SYS_FILE logfd = SYS_ERROR_FD;

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC void brief_terminate(void *parameter)
{
   system_fclose(logfd);
   logfd = SYS_ERROR_FD;
}

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int brief_init(pblock *pb, Session *sn, Request *rq)
{
   /* パラメータ */
   char *fn = pblock_findval("file", pb);

   if(!fn) {
       pblock_nvinsert("error", "brief-init: please supply a
          file name", pb);
       return REQ_ABORTED;
   }

   logfd = system_fopenWA(fn);
   if(logfd == SYS_ERROR_FD) {
       pblock_nvinsert("error", "brief-init: please supply a
          file name", pb);
       return REQ_ABORTED;
   }

   /* サーバが再起動したら、ログファイルを閉じる */
   daemon_atrestart(brief_terminate, NULL);
   return REQ_PROCEED;
}_

#ifdef __cplusplus
extern "C"
#endif

NSAPI_PUBLIC int brief_log(pblock *pb, Session *sn, Request *rq)
{
   /* パラメータなし */

   /* サーバデータ */
   char *method = pblock_findval("method", rq->reqpb);
   char *uri = pblock_findval("uri", rq->reqpb);
   char *ip = pblock_findval("ip", sn->client);

   /* 一時変数 */
   char *logmsg;
   int len;

   logmsg = (char *)
   MALLOC(strlen(ip) + 1 + strlen(method) + 1 + strlen(uri) +
       1 + 1);
   len = util_sprintf(logmsg, "%s %s %s\n", ip, method, uri);
   /* 原子バージョンの場合は、干渉を防止するためにロックが使用される */
   system_fwrite_atomic(logfd, logmsg, len);
   FREE(logmsg);

   return REQ_PROCEED;
}



サービス品質の例



qos-handler および qos-error SAF のコードは、サービス品質の処理のためにユーザ独自の SAF を定義したい場合の例として用意されています。

詳細は、『Performance Tuning, Sizing, and Scaling Guide for iPlanet Web Server』を参照してください。


例のインストール

obj.conf に含まれているデフォルトのオブジェクト内に、次の AuthTrans 指令と Error 指令を追加します。


AuthTrans fn=qos-handler
...
Error fn=qos-error code=503


ソースコード

この例のソースコードは、サーバルートディレクトリ内の plugins/nsapi/examples サブディレクトリ内の qos.c ファイル内にあります。

#include "frame/log.h"
#include "frame/http.h"
#include "safs/qos.h"

/*-----------------------------------------------------------------------------
decode : pblock の QOS 値を解析するために使用される内部関数
-----------------------------------------------------------------------------*/
void decode(const char* val, PRInt32* var, pblock* pb)
{
char* pbval;
if ((!var) || (!val) || (!pb))
return;
pbval = pblock_findval(val, pb);
if (!pbval)
return;
*var = atoi(pbval);
}
/*-----------------------------------------------------------------------------
qos_error
この関数は、HTTP 503 エラーコードのエラーハンドラの役割を果たす
このエラーコードは、QOS 制限値を超えていて、その実行が強制された場合に、qos_handler によって返される
このサンプル関数は、どの制限値を超えているのかを示すメッセージを出力する
-----------------------------------------------------------------------------*/
NSAPI_PUBLIC int qos_error(pblock *pb, Session *sn, Request *rq)
{
char error[1024] = "";

PRBool ours = PR_FALSE;

PRInt32 vs_bw = 0, vs_bwlim = 0, vs_bw_ef = 0,
vs_conn = 0, vs_connlim = 0, vs_conn_ef = 0,
vsc_bw = 0, vsc_bwlim = 0, vsc_bw_ef = 0,
vsc_conn = 0, vsc_connlim = 0, vsc_conn_ef = 0,
srv_bw = 0, srv_bwlim = 0, srv_bw_ef = 0,
srv_conn = 0, srv_connlim = 0, srv_conn_ef = 0;

    pblock* apb = rq->vars;

    decode("vs_bandwidth", &vs_bw, apb);
    decode("vs_connections", &vs_conn, apb);

    decode("vs_bandwidth_limit", &vs_bwlim, apb);
    decode("vs_bandwidth_enforced", &vs_bw_ef, apb);

    decode("vs_connections_limit", &vs_connlim, apb);
    decode("vs_connections_enforced", &vs_conn_ef, apb);

    decode("vsclass_bandwidth", &vsc_bw, apb);
    decode("vsclass_connections", &vsc_conn, apb);

    decode("vsclass_bandwidth_limit", &vsc_bwlim, apb);
    decode("vsclass_bandwidth_enforced", &vsc_bw_ef, apb);

    decode("vsclass_connections_limit", &vsc_connlim, apb);
    decode("vsclass_connections_enforced", &vsc_conn_ef, apb);

    decode("server_bandwidth", &srv_bw, apb);
    decode("server_connections", &srv_conn, apb);

    decode("server_bandwidth_limit", &srv_bwlim, apb);
    decode("server_bandwidth_enforced", &srv_bw_ef, apb);

    decode("server_connections_limit", &srv_connlim, apb);
    decode("server_connections_enforced", &srv_conn_ef, apb);

    if ((vs_bwlim) && (vs_bw>vs_bwlim))
{
    /* VS 帯域幅の制限値を超えた。それを表示する */
    ours = PR_TRUE;
    sprintf(error, "<P>Virtual server bandwidth limit of %d .
    Current VS bandwidth : %d . <P>", &vs_bwlim, vs_bw);
};

if ((vs_connlim) && (vs_conn>vs_connlim))
{
    /* VS 接続の制限値を超えた。それを表示する */
    ours = PR_TRUE;
    sprintf(error, "<P>Virtual server connection limit of %d .
    Current VS connections : %d . <P>", &vs_connlim, vs_conn);
};

if ((vsc_bwlim) && (vsc_bw>vsc_bwlim))
{
    /* VSCLASS 帯域幅の制限値を超えた。それを表示する */
    ours = PR_TRUE;
    sprintf(error, "<P>Virtual server class bandwidth limit of %d
    . Current VSCLASS bandwidth : %d . <P>", &vsc_bwlim, vsc_bw);
};

if ((vsc_connlim) && (vsc_conn>vsc_connlim))
{
    /* VSCLASS 接続の制限値を超えた。それを表示する */
    ours = PR_TRUE;
    sprintf(error, "<P>Virtual server class connection limit of
    %d . Current VSCLASS connections : %d . <P>", &vsc_connlim,
    vsc_conn);
};

if ((srv_bwlim) && (srv_bw>srv_bwlim))
{
    /* SERVER 帯域幅の制限値を超えた。それを表示する */
    ours = PR_TRUE;
    sprintf(error, "<P>Global bandwidth limit of %d . Current
    bandwidth : %d . <P>", &srv_bwlim, srv_bw);
};

if ((srv_connlim) && (srv_conn>srv_connlim))
{
    /* SERVER 接続の制限値を超えた。それを表示する */
    ours = PR_TRUE;
    sprintf(error, "<P>Global connection limit of %d . Current
    connections : %d . <P>", &srv_connlim, srv_conn);
};

if (ours)
{
    /* これは実際に、QOS 障害だった。そのため、エラーページを送信する */
pblock_nvreplace("content-type", "text/html", rq->srvhdrs);

protocol_start_response(sn, rq);
net_write(sn->csd, error, strlen(error));
return REQ_PROCEED;
}
else
{
    /* この 503 は QOS SAF 障害が原因ではない。ほかで処理を行なう必要がある */
    return _REQ_PROCEED;
};
}

/*-----------------------------------------------------------------------------
qos_handler

これは、NSAPI AuthTrans 関数である

この関数は、要求内の QOS 値を検査し、それを QOS 制限値と比較する

次のいくつかの処理を行なう :
1) QOS 制限値を超えている場合は、エラーをログに記録する
2) QOS 制限値を超えている場合は、REQ_ABORTED を 503 エラーコードとともに返し、
そして、QOS 制限を強制実行するように設定する。 QOS 制限値を超えていない場合は、REQ_PROCEED を 返す

-----------------------------------------------------------------------------*/

NSAPI_PUBLIC int qos_handler(pblock *pb, Session *sn, Request *rq)
{
PRBool ok = PR_TRUE;

PRInt32 vs_bw = 0, vs_bwlim = 0, vs_bw_ef = 0,
vs_conn = 0, vs_connlim = 0, vs_conn_ef = 0,
vsc_bw = 0, vsc_bwlim = 0, vsc_bw_ef = 0,
vsc_conn = 0, vsc_connlim = 0, vsc_conn_ef = 0,
srv_bw = 0, srv_bwlim = 0, srv_bw_ef = 0,
srv_conn = 0, srv_connlim = 0, srv_conn_ef = 0;

pblock* apb = rq->vars;

decode("vs_bandwidth", &vs_bw, apb);
decode("vs_connections", &vs_conn, apb);

decode("vs_bandwidth_limit", &vs_bwlim, apb);
decode("vs_bandwidth_enforced", &vs_bw_ef, apb);

decode("vs_connections_limit", &vs_connlim, apb);
decode("vs_connections_enforced", &vs_conn_ef, apb);

decode("vsclass_bandwidth", &vsc_bw, apb);
decode("vsclass_connections", &vsc_conn, apb);

decode("vsclass_bandwidth_limit", &vsc_bwlim, apb);
decode("vsclass_bandwidth_enforced", &vsc_bw_ef, apb);

decode("vsclass_connections_limit", &vsc_connlim, apb);
decode("vsclass_connections_enforced", &vsc_conn_ef, apb);

decode("server_bandwidth", &srv_bw, apb);
decode("server_connections", &srv_conn, apb);

decode("server_bandwidth_limit", &srv_bwlim, apb);
decode("server_bandwidth_enforced", &srv_bw_ef, apb);

decode("server_connections_limit", &srv_connlim, apb);
decode("server_connections_enforced", &srv_conn_ef, apb);

if ((vs_bwlim) && (vs_bw>vs_bwlim))
{
    /* 帯域幅の制限値を超えた。それをログに記録する */
    ereport(LOG_FAILURE, "Virtual server bandwidth limit of %d
    exceeded. Current VS bandwidth : %d", &vs_bwlim, vs_bw);

        if (vs_bw_ef)
        {
        /* 強制実行する */
        ok = PR_FALSE;
        };
    };

    if ((vs_connlim) && (vs_conn>vs_connlim))
    {
    /* 接続の制限値を超えた。それをログに記録する */
    ereport(LOG_FAILURE, "Virtual server connection limit of %d
    exceeded. Current VS connections : %d", &vs_connlim,
    vs_conn);

        if (vs_conn_ef)
        {
        /* 強制実行する */
        ok = PR_FALSE;
        };
    };

    if ((vsc_bwlim) && (vsc_bw>vsc_bwlim))
    {
    /* 帯域幅の制限値を超えた。それをログに記録する */
    ereport(LOG_FAILURE, "Virtual server class bandwidth limit of
    %d exceeded. Current VSCLASS bandwidth : %d", &vsc_bwlim,
         vsc_bw);

        if (vsc_bw_ef)
        {
         /* 強制実行する */
         ok = PR_FALSE;
        };
    };

    if ((vsc_connlim) && (vsc_conn>vsc_connlim))
    {
    /* 接続の制限値を超えた。それをログに記録する */
    ereport(LOG_FAILURE, "Virtual server class connection limit
    of %d exceeded. Current VSCLASS connections : %d",
    &vsc_connlim, vsc_conn);

        if (vsc_conn_ef)
        {
         /* 強制実行する */
         ok = PR_FALSE;
        };
    };


    if ((srv_bwlim) && (srv_bw>srv_bwlim))
    {
    /* 帯域幅の制限値を超えた。それをログに記録する */
    ereport(LOG_FAILURE, "Global bandwidth limit of %d exceeded.
    Current global bandwidth : %d", &srv_bwlim, srv_bw);

        if (srv_bw_ef)
        {
         /* 強制実行する */
         ok = PR_FALSE;
        };
    };

    if ((srv_connlim) && (srv_conn>srv_connlim))
    {
    /* 接続の制限値を超えた。それをログに記録する */
    ereport(LOG_FAILURE, "Global connection limit of %d exceeded.
    Current global connections : %d", &srv_connlim, srv_conn);

        if (srv_conn_ef)
        {
         /* 強制実行する */
         ok = PR_FALSE;
        };
    };

    if (ok)
    {
    return REQ_PROCEED;
    }
    else
    {
    /* いずれかの制限値を超えた
    したがって、HTTP エラー 503「server too busy」を設定する */
    protocol_status(sn, rq, PROTOCOL_SERVICE_UNAVAILABLE, NULL);
    return REQ_ABORTED;
    };
}


前へ     目次     索引     DocHome     次へ     
Copyright © 2000 Sun Microsystems, Inc. Some preexisting portions Copyright © 2000 Netscape Communications Corp. All rights reserved.

Last Updated September 24, 2001