Symfoware

Symfowareについての考察blog

nginx モジュール作成 メソッド、URL、クエリパラメーターの取得方法

nginxの拡張モジュールを作ってみました。
nginx 拡張モジュールの作り方

色々やってみたいことがあるのですが、あんまりにもわからないことが多いので
一つづつ調べていこうと思います。

こちらのソースと
HttpAccessKeyModule

こちらのサイトを参考にしています。
Emiller's Guide To Nginx Module Development



confファイルでon,off



基礎から調べていきます。
nginx.confファイルでモジュールのon,off指定。

onならログにメッセージを表示するだけのモジュールを作成します。
また、処理はアクセスが行われた時(NGX_HTTP_ACCESS_PHASE)のタイミングで行います。

アクセス制御を行いたいときは、この場所が良いみたい。
HTTP request processing phases in Nginx


モジュールのconfigは前回同様


ngx_addon_name=ngx_http_sample_module
HTTP_MODULES="$HTTP_MODULES ngx_http_sample_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_sample_module.c"




ngx_http_sample_module.cは以下のようになりました。


  1. #include <ngx_config.h>
  2. #include <ngx_core.h>
  3. #include <ngx_http.h>
  4. // nginx.confの設定値取得用
  5. typedef struct {
  6.     ngx_flag_t         enable;
  7. } ngx_http_sample_loc_conf_t;
  8. static ngx_int_t ngx_http_sample_handler(ngx_http_request_t *r);
  9. static void *ngx_http_sample_create_loc_conf(ngx_conf_t *cf);
  10. static char *ngx_http_sample_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
  11. static ngx_int_t ngx_http_sample_init(ngx_conf_t *cf);
  12. // confファイルの記載内容読み取り指定
  13. static ngx_command_t ngx_http_sample_commands[] = {
  14.     { ngx_string("sample"), // キーの名称
  15.      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, // どのconfを読むか
  16.      ngx_conf_set_flag_slot, // on,off 0,1の読み取り
  17.      NGX_HTTP_LOC_CONF_OFFSET,
  18.      offsetof(ngx_http_sample_loc_conf_t, enable), // ngx_http_sample_loc_conf_t->enableに設定
  19.      NULL },
  20.      ngx_null_command
  21. };
  22. static ngx_http_module_t ngx_http_sample_module_ctx = {
  23.     NULL, /* preconfiguration */
  24.     ngx_http_sample_init, /* postconfiguration */
  25.     NULL, /* create main configuration */
  26.     NULL, /* init main configuration */
  27.     NULL, /* create server configuration */
  28.     NULL, /* merge server configuration */
  29.     ngx_http_sample_create_loc_conf, /* create location configuration */
  30.     ngx_http_sample_merge_loc_conf /* merge location configuration */
  31. };
  32. ngx_module_t ngx_http_sample_module = {
  33.     NGX_MODULE_V1,
  34.     &ngx_http_sample_module_ctx, /* module context */
  35.     ngx_http_sample_commands, /* module directives */
  36.     NGX_HTTP_MODULE,             /* module type */
  37.     NULL,                         /* init master */
  38.     NULL,                         /* init module */
  39.     NULL,                         /* init process */
  40.     NULL,                         /* init thread */
  41.     NULL,                         /* exit thread */
  42.     NULL,                         /* exit process */
  43.     NULL,                         /* exit master */
  44.     NGX_MODULE_V1_PADDING
  45. };
  46. // conf読み取り初期化
  47. static void *
  48. ngx_http_sample_create_loc_conf(ngx_conf_t *cf)
  49. {
  50.     ngx_http_sample_loc_conf_t *conf;
  51.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sample_loc_conf_t));
  52.     if (conf == NULL) {
  53.         return NGX_CONF_ERROR;
  54.     }
  55.     
  56.     // 初期値はoff
  57.     conf->enable = NGX_CONF_UNSET;
  58.     return conf;
  59. }
  60. // 読み取ったconfの値をマージする
  61. static char *
  62. ngx_http_sample_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  63. {
  64.     ngx_http_sample_loc_conf_t *prev = parent;
  65.     ngx_http_sample_loc_conf_t *conf = child;
  66.     ngx_conf_merge_value(conf->enable, prev->enable, 0);
  67.     
  68.     return NGX_CONF_OK;
  69. }
  70. // 初期化処理
  71. static ngx_int_t
  72. ngx_http_sample_init(ngx_conf_t *cf)
  73. {
  74.     ngx_http_handler_pt        *h;
  75.     ngx_http_core_main_conf_t *cmcf;
  76.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
  77.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
  78.     if (h == NULL) {
  79.         return NGX_ERROR;
  80.     }
  81.     *h = ngx_http_sample_handler;
  82.     return NGX_OK;
  83. }
  84. static ngx_int_t
  85. ngx_http_sample_handler(ngx_http_request_t *r)
  86. {
  87.     // ここに処理を記載する
  88.     ngx_http_sample_loc_conf_t *alcf;
  89.     // conf読み込み
  90.     alcf = ngx_http_get_module_loc_conf(r, ngx_http_sample_module);
  91.     
  92.     // onになっていなければ終了
  93.     if (!alcf->enable) {
  94.         return NGX_OK;
  95.     }
  96.     
  97.     // onならログを出力して終了
  98.     ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module");
  99.     
  100.     return NGX_OK;
  101. }





ビルド方法も変更なしです。


# ./configure --prefix=/opt/nginx \
> --add-module=/usr/local/src/sample-module




この後make && make installします。


nginx.confは以下のようになりました。


worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include     mime.types;
    default_type application/octet-stream;
    error_log /opt/nginx/logs/error.log debug;
    sendfile        on;

    server {
        listen     80;
        server_name localhost;

        location / {
            # 作ったモジュールを呼び出す
            sample on;
            root html;
            index index.html index.htm;
        }
    }
}




起動後、サイトにクセスするとlogs/error.logに


2014/12/13 17:22:00 [debug] 3725#0: *3 access sample module



と表示されると思います。
ここまでですら長かった。





メソッド名の取得



GETやPOSTといったメソッド名を取得してみます。

引数ngx_http_request_tの定義を見てみます。

src/http/ngx_http_request.h


struct ngx_http_request_s {

(略)
    ngx_uint_t                        method;
...
    ngx_str_t                         method_name;
(略)

};





methodと、method_nameが使えそうです。

methodはNGX_HTTP_GET,NGX_HTTP_HEAD,NGX_HTTP_POSTと言った定数と
論理和を比較して使用するようです。


  1. if (r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))



と言った感じ。


ソースを一部書き換えます。

ちなみに、ngx_str_tはフォーマットに%Vを指定して、ポインタを渡せば
文字列をログに出力してくれます。
これがわかって、デバッグがだいぶ楽になりました。


  1. static ngx_int_t
  2. ngx_http_sample_handler(ngx_http_request_t *r)
  3. {
  4.     // ここに処理を記載する
  5.     ngx_http_sample_loc_conf_t *alcf;
  6.     // conf読み込み
  7.     alcf = ngx_http_get_module_loc_conf(r, ngx_http_sample_module);
  8.     
  9.     // onになっていなければ終了
  10.     if (!alcf->enable) {
  11.         return NGX_OK;
  12.     }
  13.     
  14.     // アクセスされたメソッドに応じてログを出力
  15.     if (r->method == NGX_HTTP_GET) {
  16.         ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module get:%V", &r->method_name);
  17.     } else if (r->method == NGX_HTTP_POST) {
  18.         ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module post:%V", &r->method_name);
  19.     } else {
  20.         ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module etc:%V", &r->method_name);
  21.     }
  22.     
  23.     return NGX_OK;
  24. }




GET、POSTで適当にアクセスしてやるとこんなログが出力されました。


2014/12/13 17:46:22 [debug] 3900#0: *1 access sample module get:GET
2014/12/13 17:46:25 [debug] 3900#0: *2 access sample module post:POST



狙い通りです。





URL(URI)の取得



アクセスされた時のベースURLを取得してみます。

引数ngx_http_request_tの定義を見てみます。

src/http/ngx_http_request.h


struct ngx_http_request_s {

(略)
    ngx_str_t                         uri;
(略)

};



uriがそれっぽいですね。
ソースを修正してみます。


  1. static ngx_int_t
  2. ngx_http_sample_handler(ngx_http_request_t *r)
  3. {
  4.     // ここに処理を記載する
  5.     ngx_http_sample_loc_conf_t *alcf;
  6.     // conf読み込み
  7.     alcf = ngx_http_get_module_loc_conf(r, ngx_http_sample_module);
  8.     
  9.     // onになっていなければ終了
  10.     if (!alcf->enable) {
  11.         return NGX_OK;
  12.     }
  13.     
  14.     // uriをログに出力
  15.     if (r->method == NGX_HTTP_GET) {
  16.         ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module get:%V", &r->method_name);
  17.     } else if (r->method == NGX_HTTP_POST) {
  18.         ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module post:%V", &r->method_name);
  19.     } else {
  20.         ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module etc:%V", &r->method_name);
  21.     }
  22.     
  23.     return NGX_OK;
  24. }




適当にアクセスしてみます。


2014/12/13 17:51:10 [debug] 3943#0: *1 access sample module uri:/
2014/12/13 17:51:10 [debug] 3943#0: *1 access sample module uri:/index.html
2014/12/13 17:51:21 [debug] 3943#0: *1 access sample module uri:/test.html




ちゃんとuriが取得できていますね。






クエリパラメーターの取得方法



GETのクエリパラメーターを取得してみます。

また引数ngx_http_request_tの定義を見てみます。

src/http/ngx_http_request.h


struct ngx_http_request_s {

(略)
    ngx_str_t                         args;
(略)

};




argsがそれっぽいです。
早速テスト。


  1. static ngx_int_t
  2. ngx_http_sample_handler(ngx_http_request_t *r)
  3. {
  4.     // ここに処理を記載する
  5.     ngx_http_sample_loc_conf_t *alcf;
  6.     // conf読み込み
  7.     alcf = ngx_http_get_module_loc_conf(r, ngx_http_sample_module);
  8.     
  9.     // onになっていなければ終了
  10.     if (!alcf->enable) {
  11.         return NGX_OK;
  12.     }
  13.     
  14.     // argsをログに出力
  15.     ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module args:%V", &r->args);
  16.     
  17.     return NGX_OK;
  18. }



適当に「/test.html?key=value%key2=value2」というようなパラメーターをつけてアクセス。

出力されたログはこんなかんじでした。


2014/12/13 18:15:43 [debug] 3985#0: *1 access sample module args:key=value%key2=value2




引数の部分がそのまま取得できています。





クエリパラメーターの値を取得



普通はクエリーパラメーターの名前を指定して値がほしいです。

「symfo」というパラメーターの値を取得してみます。
ngx_http_parse.cにある


ngx_int_t
ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)



この関数を使ってみます。

nameはキーの名前。
lenはキーに指定した文字列の長さ。
valueが取得した値になります。

symfoというキーの値を取得したい場合は
nameに「symfo」、lenに「5」を指定してやります

変更した箇所の抜粋です。


  1. static ngx_int_t
  2. ngx_http_sample_handler(ngx_http_request_t *r)
  3. {
  4.     // ここに処理を記載する
  5.     ngx_http_sample_loc_conf_t *alcf;
  6.     ngx_str_t value;
  7.     
  8.     // conf読み込み
  9.     alcf = ngx_http_get_module_loc_conf(r, ngx_http_sample_module);
  10.     
  11.     // onになっていなければ終了
  12.     if (!alcf->enable) {
  13.         return NGX_OK;
  14.     }
  15.     
  16.     
  17.     // symfoというkeyの値を取得
  18.     if (r->args.len) {
  19.         if (ngx_http_arg(r, (u_char *) "symfo", 5, &value) == NGX_OK) {
  20.             ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module args:symfo value:%V", &value);
  21.         } else {
  22.             ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module no value symfo");
  23.         }
  24.     } else {
  25.         ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "access sample module no args");
  26.     }
  27.     
  28.     return NGX_OK;
  29. }




http://192.168.1.102/test.html?symfo=hello
というURLにブラウザでアクセスしてみます。


2014/12/14 11:01:17 [debug] 2047#0: *5 access sample module args:symfo value:hello




狙い通り、値が取得出来ました。
関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2014/12/14(日) 11:06:38|
  2. 備忘録
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<nginx 拡張モジュール サーバー時間を取得 | ホーム | nginx 拡張モジュールの作り方>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1565-b9023b5b
この記事にトラックバックする(FC2ブログユーザー)