1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

リバースプロキシでheaderを消したくてheaders-more-nginx-moduleについて調べてみた[備忘録]

Last updated at Posted at 2023-11-28

OpenRestyというNginxの拡張モジュールを開発しているOSS団体が公開しているheaders-more-nginx-moduleのソースコードを読んだので、それの備忘録です。

どういうモジュール?

ngx_headers_more - Set and clear input and output headers...more than "add"!という、nginxがhandleするhttp requestのheaderをアレコレできるnginx用moduleです。

ライセンスはBSDです。

なぜ調べた?

ひょんなことから、Nignx製のリバースプロキシで特定のheaderを削除したいな~ということがあり、どのように実現しているのかを知りたかったからです。

で、見てみると結構コンパクトで、勉強に良さそうだったので見てみるか~となりました。

本編

ソースコードの構成

src/
    ├ ddebug.hh
    ├ ngx_http_headers_more_filter_module.c
    ├ ngx_http_headers_more_filter_module.h
    ├ ngx_http_headers_more_headers_in.c
    ├ ngx_http_headers_more_headers_in.h
    ├ ngx_http_headers_more_headers_out.c
    ├ ngx_http_headers_more_headers_out.h
    ├ ngx_http_headers_more_utils.c
    └ ngx_http_headers_more_utils.h

ngx_http_headers_more_headers_inはNginxに入ってくるhttp関連、ngx_http_headers_more_headers_outは出ていくhttp関連です。moduleとして取りまとめているのがngx_http_headers_more_filter_moduleです。

more_clear_input_headers の挙動を調べる

ディレクティブの追加

今回自分が知りたいのは、http requestからheaderを削除する挙動です。
なので、そこに絞ってコードを読んでいきたいと思います。

まず、more_clear_input_headersがどこにあるのかを調べると、ngx_http_headers_more_filter_module.cの中に以下のようにありました。

static ngx_command_t  ngx_http_headers_more_filter_commands[] = {

   ~~中略~~

    { ngx_string("more_clear_input_headers"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                        |NGX_CONF_1MORE,
      ngx_http_headers_more_clear_input_headers,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL},

      ngx_null_command
};

では、詳細に見ていきます。一行目、

static ngx_command_t  ngx_http_headers_more_filter_commands[]

ではngx_command_t構造体の配列を用いて、新しいディレクティブを定義しています。公式のドキュメントにも同様の例があります。

   { ngx_string("more_clear_input_headers"),

少し飛ばしてここでは、more_clear_input_headers というNginxディレクティブを定義しています。

NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                        |NGX_CONF_1MORE

ここではディレクティブがどのコンテキスト(ディレクティブ)で使用できるかを定義しています。公式のドキュメントに書いてありますが、一応抜き出しておきます。

引数 説明
NGX_HTTP_MAIN_CONF HTTP directive で利用可能。
NGX_HTTP_SRV_CONF server directiveで利用可能
NGX_HTTP_LOC_CONF location derectiveで利用可能。
NGX_HTTP_LIF_CONF location block if statements で利用可能。
NGX_CONF_1MORE このディレクティブは一つ以上の引数を取る
ngx_http_headers_more_clear_input_headers,

これがこのディレクティブが呼ばれた時に実行されるcallback関数です。
つまり、この関数がheaderを消す作業をしています。

NGX_HTTP_LOC_CONF_OFFSET,
0

ドキュメントによるとWhich location the directive’s value should be saved toと、この0はそのoffsetだそうですが、あまり理解できませんでした....。

では続いて、このコールバック関数についてみていきます。

ngx_http_headers_more_clear_input_headers関数について

この関数はngx_http_headers_more_headers_in.c内で、以下のように定義されています。

char *
ngx_http_headers_more_clear_input_headers(ngx_conf_t *cf,
    ngx_command_t *cmd, void *conf)
{
    return ngx_http_headers_more_parse_directive(cf, cmd, conf,
                                        ngx_http_headers_more_opcode_clear);
}

この関数の引数のngx_conf?tはNginx configの構造体で、ngx_command_tは先ほども登場したディレクティブの構造体です。
これを見ると、ngx_http_headers_more_parse_directive関数を実行しており、その引数にNginxのconfig、directive、ngx_http_headers_more_opcode_clearという意味ありげな引数を渡しています。

これを調べてみます。

ngx_http_headers_more_parse_directive関数について

この関数は同じファイルの下の方で定義されています。長いのでここに張り付ける事はしませんが、要素だけかいつ間みたいと思います。

ここで大事になってくるのが以下の構造体です。

 ngx_http_headers_more_cmd_t        *cmd;

これまでのコードの中でcmdとはNginxの提供するngx_command_t構造体でしたが、これ以降はこのモジュール特有の構造体に代わり、これまでのcmdngx_cmdになります。この構造体はngx_http_headers_more_filter_module.hで定義されており、

typedef struct {
    ngx_array_t                       *types; /* of ngx_str_t */
    ngx_array_t                       *statuses; /* of ngx_uint_t */
    ngx_array_t                       *headers; /* of ngx_http_header_val_t */
    ngx_flag_t                         is_input;
} ngx_http_headers_more_cmd_t;

と定義されています。この構造体にデータを入れているのは、その下の

cmd->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_http_headers_more_header_val_t));

です。ngx_array_create関数は、Nginxのメモリプール内でメモリのアロケーションを行います。

引数 説明
cf->pool 使用するメモリプール
1 配列の要素数
sizeof(ngx_http_headers_more_header_val_t) 要素のbytes数

その下に行くと、opcode(おそらくoperation codeのこと)を使用している箇所があります。

     if (arg[i].data[0] != '-') {
            rc = ngx_http_headers_more_parse_header(cf, cmd_name,
                                                    &arg[i], cmd->headers,
                                                    opcode, ngx_http_headers_more_set_handlers);

ここで、また別のngx_http_headers_more_parse_headerという関数を呼び、opcodeやheaderなど関係ありそうな引数を渡しています。

ngx_http_headers_more_parse_header関数について

こちらも長いので全てを記載することはしませんが、その中でngx_http_headers_more_opcode_clearに関連しているのは以下のコードです。

if (opcode == ngx_http_headers_more_opcode_clear) {
        value.len = 0;
    }

    if (value.len == 0) {
        ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
        return NGX_OK;

    }

気になるのはこのvalueという変数ですが、これは関数の先頭で以下のように定義されて、この関数を通して利用されています。おそらくheaderの数なのかなぁと思いますが、正直あんまりわかりません。

ngx_str_t                            value = ngx_null_string;

実際にヘッダーを消しているのは以下の行です。

ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));

この関数は第一引数(&hv->value)がデータを消す予定のメモリのポインタ、第二引数がsizeof(ngx_http_complex_value_t)データを消す(メモリを0にする)長さを取ります。
ここでポインターを保持しているhvは、この関数の先頭で

ngx_http_headers_more_parse_header(ngx_conf_t *cf, ngx_str_t *cmd_name,
    ngx_str_t *raw_header, ngx_array_t *headers,
    ngx_http_headers_more_opcode_t opcode,
    ngx_http_headers_more_set_header_t *handlers)
{
    ngx_http_headers_more_header_val_t             *hv;

    ngx_uint_t                           i;
    ngx_str_t                            key = ngx_null_string;
    ngx_str_t                            value = ngx_null_string;
    ngx_flag_t                           seen_end_of_key;
    ngx_http_compile_complex_value_t     ccv;
    u_char                              *p;

    hv = ngx_array_push(headers);
    if (hv == NULL) {
        return NGX_ERROR;
    }

として定義されており、ngx_http_headers_more_cmd_tからヘッダーの情報だけを抜き出しているように見えます。

このようにしてheaderの情報を消すようです。見ている限り、すべてのheaderを強制的に消してそうです。特定のheaderだけを消すことはできないような気がしますが、どうなんでしょう....。

http requestからheaderを消すのって、結構大変なんすね....。

以上です。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?