Apache
コードリーディング

Apacheのmod_proxy_wstunnelのコードを読んでみる

はじめに

mod_proxyのコードについては、下記のようにひと段落したのですが、mod_proxyの後段のモジュールについても読んでみたので記事にします。

mod_proxyの記事:Apacheのmod_proxyのコードを読んでみる(3)

実行環境や実装のバージョンはmod_proxyの時と同様にApache v2.4.23です。

いつものように我流で読み進めているので、理解内容に間違いなどありましたらご指摘ください。

mod_proxy_wstunnelの概要

AP_DECLARE_MODULE(proxy_wstunnel)

apache標準のhook定義関数です。

基本的に前段のmod_proxyで登録されているため、hook関数の登録しかしていません。

AP_DECLARE_MODULE(proxy_wstunnel) = {
    STANDARD20_MODULE_STUFF,
    NULL,                       /* create per-directory config structure */
    NULL,                       /* merge per-directory config structures */
    NULL,                       /* create per-server config structure */
    NULL,                       /* merge per-server config structures */
    NULL,                       /* command apr_table_t */
    ap_proxy_http_register_hook /* register hooks */
};

register_hooks

hook登録関数で、AP_DECLARE_MODULEの最後に指定されている関数です。

Apache標準のhookの登録はありません。
mod_proxyから呼ばれるhookのみ登録しています。

static void ap_proxy_http_register_hook(apr_pool_t *p)
{
    proxy_hook_scheme_handler(proxy_wstunnel_handler, NULL, NULL, APR_HOOK_FIRST);
    proxy_hook_canon_handler(proxy_wstunnel_canon, NULL, NULL, APR_HOOK_FIRST);
}
  • proxy_hook_scheme_handler:mod_proxyのproxy_handlerからフックされます
  • proxy_hook_canon_handler:mod_proxyのproxy_fixupからフックされます

mod_proxy_wstunnelの詳細

proxy_wstunnel_canon

  • フックされた時点でurlにつぎのどちらかを含んでいないとDECLINEDになり終了です
     - "ws:"
     - "wss:"

  • リクエストに問題がなければ、request_rec構造体のfilenameメンバへ整形したurlを設定してOKを返します。

    r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "//", host, sport,
                              "/", path, (search) ? "?" : "",
                              (search) ? search : "", NULL);

proxy_wstunnel_handler

処理対象の判定

  • proxy_wstunnel_canonと同様にurlにつぎのどちらかを含んでいない場合はDECLINEDで処理終了です。
     - "ws:"
     - "wss:"

  • リクエストヘッダにWebSocket関連のものが含まれていない場合はDECLINEDで処理終了です。具体的には次の判定基準です。

    • "Upgrade"という文字列が含まれており、その値が"WebSocket"という文字列である
    upgrade = apr_table_get(r->headers_in, "Upgrade");
    if (!upgrade || strcasecmp(upgrade, "WebSocket") != 0) {
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02900)
                      "declining URL %s  (not WebSocket)", url);
        return DECLINED;
    }

websocket通信処理の準備

  • mod_proxyのコネクション管理用の構造体proxy_conn_recに必要な情報を設定しています。

  • proxy_wstunnel_handlerの中で必要な準備を終えるとproxy_wstunnel_requestを呼び出して実際のwebsocketの通信処理をします。

websocket通信処理(proxy_wstunnel_request)

  • 処理に必要な構造体の値の設定をした後にdo-while文に入ります。
  • do-while文はどちらかが切断するか、エラー発生までループを継続します。
  • do-while文の中身はそれほど読めていませんが、websocketの実際の通信に関する実装になっていると思います。

おわりに

Apacheのmod_proxy関連の実装は、すべてをmod_proxyに詰め込むのではなく、可変点のみ後段に任せるという設計思想なのだろうなと想像しています。

前段のmod_proxyが約3000行というコード行数であり、後段のmod_proxy_wstunnelは377行ということで、可変点(差分)のみ後段に委譲するという実装をうまく実現しているというのが私の感想です。

備忘録も兼ねていますが、参考になる方がいれば幸いです。

時間があればmod_proxy_balancerについても記事に書いてみようと思っています。