まえがき
最近、Apache 2.2.x Webサーバー用に独自の小さなモジュールを開発する必要に直面しました。 適切な情報を探すのに数時間費やした後、私はロシア語でこれについて話す人がほとんどいないという事実に出会いました。 したがって、この記事を書くというアイデアが生まれました。 以下では、可能な限り私の経験を共有し、モジュールを作成する手順をステップごとに説明し、このトピックに関するさまざまな役立つリンクを提供します。
はじめに
Apacheのモジュールの作成に遭遇したことのない初心者開発者が最初に直面するのは、「どこから始めればよいのか」という質問です。
不要なジェスチャーなしでは、++なしで純粋なCを処理する必要があるという事実を認識することから始めます。 そしておそらくこれは良いことです。
次に、APXSがシステムにインストールされているかどうかを確認し、インストールされていない場合はインストールします。 Debian(Ubuntu)Linuxシステムでは、これは非常に簡単に実行できます。コマンドを実行するだけです:
apt-get install apache2-threaded-dev
または
apt-get install apache2-prefork-dev
使用しているApacheの対応するバージョンによって異なります。
これは、モジュールをビルドしてコンパイルするために必要になります。
これですべての準備が整い、モジュール自体の開発に直接進むことができます。 例ですべてを学習するので、Webサーバーが大文字に与えるHTMLページのすべてのタグを変換する小さなモジュールを作成しましょう。
だから、トリックなしでモジュールを呼び出しましょう-アップタグ。 同じ名前のフォルダーを作成し、その中にソースファイルmod_uptags.cとREADMEおよびconfig.m4ファイルを作成します。 これは、ファイル構造がどのようになるかを示しています。
アップタグ/ config.m4 mod_uptags.c Readme
これらのファイルをすぐにApacheソースに配置することも、後からこれを行うこともできます。必要に応じて続行します。
原則として、モジュールを正常に作成するには、Cソースコードのファイルのみが必要ですが、少なくともREADMEが適切な形式であり、config.m4はWebサーバー全体をコンパイルするときにモジュールを自動的に構築するのに役立ちます。
ApacheモジュールのAPIは、以下で説明するいくつかのグループに論理的に割り当てることができるいくつかの関数と構造の実装を意味します。
ただし、関数の分析を進める前に、必要なライブラリを接続し、ソースmod_uppertags.cでモジュール自体を宣言する必要があります。
#include "apr_general.h"
#include "apr_lib.h"
#include "apr_buckets.h"
#include "apr_strings.h"
#include "ap_config.h"
#include "util_filter.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
#include "http_core.h"
#include < string .h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
module AP_MODULE_DECLARE_DATA uptags_module;
* This source code was highlighted with Source Code Highlighter .
構成管理機能
ご存じのとおり、Apacheは高度にカスタマイズ可能なWebサーバーです。
システム管理者の観点からは、グローバルサーバー構成、<VirtualHost>、<Directory>(および<Files>または<Location>)および.htaccessファイルなど、さまざまなスコープで定義できるいくつかのタイプのディレクティブがあります。
さまざまなスコープで定義されたディレクティブの競合は、デフォルトで、次の規則に従ってサーバー自体によって解決されます。
- メイン設定
このファイルのディレクティブ(一部のコンテナで指定されているものを除く)はグローバルに適用されます。 ここではほとんどのディレクティブを使用できます。 - 仮想ホスト<VirtualHost>
各仮想ホストは、<VirtualHost>コンテナー内に独自の(仮想)サーバー構成を持つことができます。 グローバル設定で使用できるディレクティブはここで使用でき、その逆も可能です。 - ディレクトリ
<Directory>、<Files>、および<Location>コンテナーは、グローバル設定の再定義方法をさまざまなレベルで定義します。 この記事では、それらをディレクトリの構成と呼びます。 - .htaccessファイル
ディレクトリ設定にも適用されます。 これらのファイルでは、ユーザーは、サーバー管理者によって設定されたAllowOverrideディレクティブの値に基づいて、ディレクティブ設定自体を決定できます。
モジュールを作成する場合、ディレクティブとコンテナを個別に決定し、スコープを決定し、それらをオーバーライドするルールを変更できます。
これを行うには、まず、構成データをメモリに保存する適切な構造を作成する必要があります。 一般的なケースでは、サーバー構成とディレクトリ構成に別々の構造を定義できることに注意してください。 これは、ディレクティブのセットが異なる場合に実行する必要があります。 それ以外の場合は、同じ構造を使用して設定を保存できます。
作成するモジュールは非常にシンプルで設定が最小限なので、シンプルなパスをたどり、両方のタイプの設定(サーバーおよびディレクトリ用)を1つのデータ構造に結合します。 モジュールの操作を有効または無効にするフラグを定義しましょう:
engine = On / Off-モジュール全体の動作を有効/無効にできます。
つまり、構造は次のようになります。
typedef struct uptags_cfg {
int engine;
} uptags_cfg;
* This source code was highlighted with Source Code Highlighter .
次に、構成の個々のタイプごとに、データ取得関数を作成する必要があります。
/**
*
*
*/
static uptags_cfg *uptags_dconfig( const request_rec *r) {
return (uptags_cfg *) ap_get_module_config( r->per_dir_config, &uptags_module);
}
/**
*
*
*/
static uptags_cfg *uptags_sconfig( const server_rec *s) {
return (uptags_cfg *) ap_get_module_config( s->module_config, &uptags_module);
}
* This source code was highlighted with Source Code Highlighter .
今がデフォルト設定の面倒を見るときです。 これを行うには、次の2つの関数を作成します。
/**
* -
*/
static void *uptags_create_dir_config( apr_pool_t *p, char *dirspec) {
uptags_cfg *cfg;
/**
*
*/
cfg = (uptags_cfg *) apr_pcalloc( p, sizeof ( uptags_cfg));
/**
*
*/
cfg->engine = 1;
return ( void *) cfg;
}
/**
*
*/
static void *uptags_create_server_config( apr_pool_t *p, server_rec *s) {
uptags_cfg *cfg;
cfg = (uptags_cfg *) apr_pcalloc( p, sizeof ( uptags_cfg));
cfg->engine = 1;
return ( void *) cfg;
}
* This source code was highlighted with Source Code Highlighter .
Webサーバーの構造では、異なるブロックに複数の構成が実際に存在する可能性があるため(たとえば、<Directory> </ Directory>、<Files> </ Files>、または<Locationブロックのエントリは、仮想ホスト構造のディレクトリ構成用に作成できます) > </ Location>で、同時に.htaccessファイルを作成できます)、これらの構成を結合するための独自のルールを定義する必要があります。 ありがたいことに、Apache APIを使用すると、サーバー設定とディレクトリ設定をマージするためのルールを定義するだけで、これを簡単に行うことができます。 これを行うには、次の関数を定義するだけで十分です。
/**
*
*
*
*/
static void *uptags_merge_dir_config( apr_pool_t *p, void *parent_conf, void *newloc_conf) {
uptags_cfg *megred_conf = (uptags_cfg *) apr_pcalloc( p, sizeof ( uptags_cfg));
uptags_cfg *pconf = (uptags_cfg *) parent_conf;
uptags_cfg *nconf = (uptags_cfg *) newloc_conf;
/**
* ...
*
*/
return ( void *) merged_conf;
}
/**
*
*/
static void *uptags_merge_server_config( apr_pool_t *p, void *srv1conf, void *srv2conf) {
uptags_cfg *merged_config = (uptags_cfg *) apr_pcalloc( p, sizeof ( uptags_cfg));
uptags_cfg *s1conf = (uptags_cfg *) srv1conf;
uptags_cfg *s2conf = (uptags_cfg *) srv2conf;
/**
* ...
*
*/
return ( void *) merged_config;
}
* This source code was highlighted with Source Code Highlighter .
ただし、フェデレーションルールは、サーバールールがデフォルトであなたに合わない場合にのみ適用されることに注意してください。 あなたはそれらを変更したい。 それ以外の場合は、これらの関数を記述しないでください。すべてが自動的に行われます。
構成ディレクティブ(コマンド)の定義
構成の構造とそれを埋めるための機能を決定し、処理を組み合わせた後、このようにディレクティブ自体を定義する必要はないため、構成で設定する必要があります。
static command_rec uptags_directives[] = {
AP_INIT_FLAG(
"UptagsEngine" ,
ap_set_flag_slot,
( void *) APR_OFFSETOF( uptags_cfg, engine),
OR_OPTIONS,
"uptags module switcher"
),
{NULL}
};
* This source code was highlighted with Source Code Highlighter .
ここでは、Apache WebサーバーAPIが構成ディレクティブの処理に提供するさまざまな可能性を調べるために少し停止します。
ディレクティブ構造の定義は常にNULL構造で終了し、実装マクロはhttp_config.hファイルで定義されます。 AP_INIT_FLAGは、このような多くのマクロの1つであり、一般に、非NULL構造には次の要素が含まれます。
- ディレクティブの名前は、configでディレクティブがどのように設定されるかです(実際、configからの行のマッピングとメモリ内で以前に作成した構造はこの場所です)
- ディレクティブを実装する関数。 ap_set_flag_slotは、スイッチパラメーターまたはフラグを解析する標準のApache API関数です(オン/オフは私たちにぴったりです)
- データ構造体へのポインター(APR_OFFSETOFを使用して、定義した構造体にデータをマップします)
- ディレクティブを許可する場所を定義するマクロ(OR_OPTIONS-AllowOverrideディレクティブの値が管理者によってOptionsタグで設定されている場合、ディレクティブはディレクトリおよび.htaccessファイルに許可されます)
- ヘルプ機能のディレクティブの説明。
多くの場合、Apache APIが提供するコマンドを実装する標準機能は、問題を解決するのに十分です。 これらの関数は、http_config.hファイルにも記載されています。 ここにあります:
/**
*
*/
AP_DECLARE_NONSTD( const char *) ap_set_string_slot( cmd_parms *cmd, void *struct_ptr, const char *arg);
/**
*
*/
AP_DECLARE_NONSTD( const char *) ap_set_int_slot( cmd_parms *cmd, void *struct_ptr, const char *arg);
/**
* ,
*/
AP_DECLARE_NONSTD( const char *) ap_set_string_slot_lower( cmd_parms *cmd, void *struct_ptr, const char *arg);
/**
* ( On/Off)
*/
AP_DECLARE_NONSTD( const char *) ap_set_flag_slot( cmd_parms *cmd, void *struct_ptr, const char *arg);
/**
* , -
*/
AP_DECLARE_NONSTD( const char *) ap_set_file_slot( cmd_parms *cmd, void *struct_ptr, const char *arg);
/**
* , :
* AP_INIT_RAW_ARGS("Foo", ap_set_deprecated, NULL, OR_ALL,
* " Foo , Bar")
*/
AP_DECLARE_NONSTD( const char *) ap_set_deprecated( cmd_parms *cmd, void *struct_ptr, const char *arg);
* This source code was highlighted with Source Code Highlighter .
ディレクティブのデータ型を定義するマクロは次のとおりです。
- AP_INIT_NO_ARGS-引数なしのディレクティブ
- AP_INIT_FLAG-ディレクティブスイッチ(オン/オフ)
- AP_INIT_TAKE1-1つの引数-文字列
- AP_INIT_TAKE2 、 AP_INIT_TAKE3 、 AP_INIT_TAKE12-ディレクティブはいくつかの異なる引数を取ります
- AP_INIT_ITERATE-ディレクティブの各引数に関数が適用されます
- AP_INIT_ITERATE2-関数は2つの引数を渡して呼び出されます
- AP_INIT_RAW_ARGS-関数は、すべてのディレクティブ引数を転送して呼び出されます
ディレクティブのスコープを定義するマクロは次のとおりです。
- OR_NONE-構成内のどこにも使用できません
- OR_LIMIT -AllowOverrideディレクティブにLimitキーワードがある場合、<Directory>または<Location>コンテナー内および.htaccessファイル内のサーバー構成
- OR_OPTIONS-どこでも、AllowOverrideディレクティブにOptionsキーワードがある場合は.htaccessを含む
- OR_FILEINFO-どこでも、AllowOverrideディレクティブにFileInfoキーワードがある場合は.htaccessを含む
- OR_AUTHCFG- <Directory>または<Location>コンテナー内のサーバー構成、およびAllowOverrideディレクティブにAuthConfigキーワードがある場合は.htaccessファイル
- OR_INDEXES-どこでも、AllowOverrideディレクティブにIndexesキーワードがある場合は.htaccessファイルを含む
- OR_UNSET-許可の削除ディレクティブ
- ACCESS_CONF- <Directory>または<Location>コンテナー内のサーバー構成
- RSRC_CONF-コンテナ<Directory>または<Location>の外部のサーバー構成内
- EXEC_ON_READ-構成を変更する外部コマンド(外部ファイルやIFModuleの接続など)をディレクティブに強制的に実行させます
- OR_ALL-どこでも許可されるディレクティブ
モジュールの構成を決定したので、その機能の開発に直接進むことができます。
リクエストハンドラー
SetHandlerおよびAddHandlerディレクティブを使用して、対応するリクエストに適切なドキュメントを発行する独自のApacheリクエストハンドラを作成できます。
これらのハンドラーはデータを出力に直接送信するため、コンテンツが出力される前に応答ヘッダーが送信されることを確認する必要があります。 これは、send_http_header()関数を使用して実行できます。 独自のヘッダーを設定することもできます。
ハンドラー関数は、引数として、要求の構造へのポインターを取ります。 返される値は次のとおりです。
- OK-すべてが正常で、リクエストを正常に処理しました
- 不承認-リクエストは拒否されました
- HTTP_ [error_code]-HTTPプロトコルエラーを報告します。たとえば、HTTP_401-認証が必要です
static int uptags_handler( request_rec *r) {
if (strcmp( r->handler, "uptags-handler" )) {
return DECLINED;
}
r->content_type = "text/html" ;
/**
* -
*/
if (r->header_only) {
return OK;
}
/**
* , :
*/
ap_rputs( DOCTYPE_HTML_4_0_STRICT, r);
ap_rputs( "<HTML>\n" , r);
ap_rputs( " <HEAD>\n" , r);
ap_rputs( " <TITLE>Example Title</TITLE>\n" , r);
ap_rputs( " </HEAD>\n" , r);
ap_rputs( " <BODY>\n" , r);
ap_rputs( " <H1>Example content</H1>\n" , r);
ap_rputs( " <P>Example text</P>\n" , r);
ap_rputs( " </BODY>\n" , r);
ap_rputs( "</HTML>\n" , r);
return OK;
}
* This source code was highlighted with Source Code Highlighter .
これで、Webサーバー構成にハンドラーを追加できます。
<場所/ uptags-handler-example> Sethandler uptags-handler </場所>
実際、これは書いているモジュールとは関係ないので、この機能は必要ありません。 ハンドラーの記述例を見てみました。 このタスクは、たとえば、WebサーバーのモジュールとしてWebサイトコンテンツ管理システムを作成する場合に発生する可能性があります。 または、インタープリター言語を埋め込みます。
私たちが提起する問題を解決するには、フィルターを使用する方が適切です。これについては、次のセクションで検討します。
フィルター
フィルターを使用すると、Apacheがリクエストに応じて提供する現在のコンテンツを操作できます。 問題を解決するのにちょうどいい場所です。
ここでは、ソリューション自体から少し逸脱し、Webサーバーの原理と、Webサーバーがデータを処理および提供する方法について少し話す必要があります。
Apache 2フィルターのアーキテクチャは、このWebサーバーの主要な技術革新であり、その能力と汎用性とともに、「バスケット」と「旅団」の概念の研究と理解の問題に否定的な意味合いを持っています。
Apacheの低レベルAPIを使用すると、「バスケット」と「チーム」を直接操作できます。 以下では、これがどのように行われるかの原則を見ていきます。
「バスケット」と「旅団」とは何ですか?
ごみ箱は、あらゆるタイプのデータを含むことができるデータコンテナーです。 それにもかかわらず、最も一般的なタイプはメモリブロックであり、ディスク上のファイルと、たとえば別のプログラムなど、あるソースから別のソースに転送されるデータストリームの両方を既に含むことができます。
原則として、いくつかの異なるタイプの「バスケット」があります-異なるタイプのデータ、およびデータエンドサイン(EOS)またはデータバッファーを出力ストリームにフラッシュするFLUSH「バスケット」などのメタデータを持つ「バスケット」。
チームは、「バスケット」のサイクルを結合するコンテナであり、フィルターからフィルターに転送されるユニットです。
「バスケット」と「チーム」は、フィルターアプリケーションに典型的なメモリブロックの効率的な操作を提供します。
「バスケット」と「チーム」を操作するために、Apache 2はapr_buckets.hファイルに記述されている一連のデータ型、構造、マクロ、およびAPI関数を提供します。 彼らの説明はここにあります 。
問題を解決するには、入力(in)と出力(out)の2つのフィルターを作成する必要があることに注意してください。
/**
*
*/
static void uptags_in_filter( request_rec *r) {
/**
* ,
*/
ap_add_output_filter( "Uptags" , NULL, r, r->connection);
}
/**
* -
*/
static apr_status_t uptags_out_filter( ap_filter_t *f, apr_bucket_brigade *pbbIn) {
request_rec *r = f->r;
conn_rec *c = r->connection;
apr_bucket *pbktIn;
apr_bucket_brigade *pbbOut;
uptags_cfg *cfg = uptags_dconfig( f->r);
/**
* , . -
* , HTML
*/
if (!cfg->engine || strcmp( r->content_type, "text/html" ) != 0) {
return ap_pass_brigade( f->next, pbbIn);
}
/* */
pbbOut = apr_brigade_create( r->pool, c->bucket_alloc);
/**
*
*/
for (pbktIn = APR_BRIGADE_FIRST( pbbIn); pbktIn != APR_BRIGADE_SENTINEL( pbbIn); pbktIn = APR_BUCKET_NEXT( pbktIn)) {
const char *data;
apr_size_t len;
char *buf;
apr_bucket *pbktOut;
/* "" - - - */
if (APR_BUCKET_IS_EOS( pbktIn)) {
apr_bucket *pbktEOS = apr_bucket_eos_create( c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL( pbbOut,pbktEOS);
continue ;
}
/* */
apr_bucket_read( pbktIn, &data, &len, APR_NONBLOCK_READ);
/**
*
* ! - .
* .
*/
buf = apr_bucket_alloc( len, c->bucket_alloc);
memset( buf, 0, sizeof ( buf));
uptags_tags_to_uppercase( data, buf);
/* */
pbktOut = apr_bucket_heap_create( buf, len, apr_bucket_free, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL( pbbOut, pbktOut);
}
apr_brigade_cleanup( pbbIn);
return ap_pass_brigade( f->next, pbbOut);
}
* This source code was highlighted with Source Code Highlighter .
当然。 また、関数uptags_tags_to_uppercase()を実装する必要があります。実際には、データに対して必要なすべての操作を行い、その宣言をフィルター宣言の前に配置します。
/**
*
*/
void uptags_tags_to_uppercase( const char *data, char *str) {
int i, s = strlen( data), tag_opened = 0;
for (i = 0; i < s; i++) {
str[i] = data[i];
if (str[i] == '<' ) {
tag_opened = 1;
} else if (str[i] == '>' ) {
tag_opened = 0;
}
if (tag_opened && str[i] != '\0' ) {
str[i] = apr_toupper( str[i]);
}
}
}
* This source code was highlighted with Source Code Highlighter .
私たちは、実際の生活ではいつもどおりに動作するとは限らない、かなり単純な関数を書きました。 しかし、私たちのタスクは非常に単純なので、テスト目的には非常に適しています。
これで、モジュール用のフィルターの開発は完了したと見なすことができ、最終段階に進むことができます。
モジュール定義
モジュールのすべての機能を特定したら、Webサーバーにそれらの使用方法を「説明」する必要があります。
これを行うには、Apacheモジュール構造の適切なセルにそれらをリストします。
/**
* Apache
*/
module AP_MODULE_DECLARE_DATA uptags_module = {
STANDARD20_MODULE_STUFF,
uptags_create_dir_config, /* */
NULL, /* */
uptags_create_server_config, /* */
NULL, /* */
uptags_directives, /* */
uptags_register_hooks /* */
};
* This source code was highlighted with Source Code Highlighter .
ご覧のとおり、小さなモジュールuptags_register_hooksのみが欠落しています。この関数は、このモジュールに必要なすべてのハンドラーを登録する役割を果たします。
書きましょう:
/**
*
*/
static void uptags_register_hooks( apr_pool_t *p) {
ap_hook_insert_filter( uptags_in_filter, NULL, NULL, APR_HOOK_MIDDLE);
ap_register_output_filter( "Uptags" , uptags_out_filter, NULL, AP_FTYPE_RESOURCE);
}
* This source code was highlighted with Source Code Highlighter .
ここからモジュールの完全なソースコードをダウンロードできます。
モジュールのコンパイルとアセンブリ
モジュールをコンパイルしてプラグインする最も簡単な方法は、DSO(動的共有オブジェクト)としてビルドすることです。 これを行うには、ソースディレクトリでコマンドを実行します。
apxs2 -c mod_uppertags.c
コンパイルが成功した場合、.libsフォルダーとその中のmod_uptegs.soファイルが表示されます。 このファイルをApacheロード可能モジュールのあるディレクトリにコピーします。 Debian(Ubuntu)Linuxでは、これは通常ディレクトリ/ usr / lib / apache2 / modules /です
次に、メインのApache構成で、ディレクティブを入力する必要があります。
LoadModule uptags_module /usr/lib/apache2/modules/mod_uptags.so <IfModule mod_uptags.c> Upptagsengineオン </ IfModule>
次に、Webサーバーを再起動します。
/etc/init.d/apache2 restart
これで、Webサーバーが表示するページを見て、結果を確認できます。
config.m4ファイルの内容を書き込むことも役立ちます。
APACHE_MODPATH_INIT(アップタグ) APACHE_MODULE(アップタグ、すべてのHTMLタグを大文字に再フォーマット、、、いいえ) APACHE_MODPATH_FINISH
これにより、Apacheの一般的なビルド中にモジュールを自動的にコンパイルできます。 Webサーバー全体を構築するときにモジュールを静的に構築するには、。/ configureコマンドの実行時に--enable-uptagsオプションを指定するだけで十分です。
./configure --enable-uptags
そして、もちろん、READMEファイルで説明することにより、他の人に必要な指示を与えることを忘れないでください。
リンク(英語)
モジュールの構成
バケットと旅団の紹介
Apacheの基本的なリソース管理:APRプール
Apacheでのリクエスト処理
Apache Portable Runtime(APR)ドキュメント
おわりに
これまで見てきたように、Apache Webサーバー用の独自のモジュールの開発はそれほど複雑な問題ではありません。 忍耐力と関連ドキュメントへのリンクを用意すれば十分であり、タスクは完全に解決可能になります。
このガイドが、読者がモジュールを記述するためのApache APIに精通するのを助け、この興味深い事例の出発点になることを願っています。
開発に頑張ってください!
PSこれは私のブログのオリジナル記事のクロス投稿です。