nginx リクエスト ヘッダー データ読み取りプロセスの詳細な説明

nginx リクエスト ヘッダー データ読み取りプロセスの詳細な説明

前回の記事では、nginx がリクエスト ラインのデータを読み取って、リクエスト ラインを解析する方法について説明しました。この記事では、nginx がクライアントから送信されたリクエスト ヘッダー データを読み取って解析する方法について主に説明します。本質的には、リクエスト ラインとリクエスト ヘッダーのデータ読み取りプロセスは基本的に同じです。これは、どちらも断続的なデータ ストリームからデータを読み取る方法と、そのデータを処理する方法という問題に直面しているためです。

1. リクエストヘッダー読み取りメインプロセス

リクエスト ヘッダーの読み取りプロセスを紹介する前に、まず http リクエスト メッセージの例を示します。

POST /web/book/read HTTP/1.1
ホスト: ローカルホスト
接続: キープアライブ
コンテンツの長さ: 365
受け入れる: application/json、text/plain、*/*

例の最初のデータ行はリクエスト行であり、次の行はリクエスト ヘッダーです。各リクエスト ヘッダーは、名前: 値の形式で組み立てられ、各リクエスト ヘッダーは 1 行を占めます。 リクエスト ラインの読み取りプロセスを紹介した前回の記事では、リクエスト ラインが読み取られると、nginx は現在の読み取りイベントのコールバック関数を ngx_http_process_request_headers() メソッドに変更し、このメソッドを直接呼び出してリクエスト ヘッダー データの読み取りを試行することを説明しました。このメソッドは、リクエスト ライン データを読み取るメイン プロセスです。以下はこのメソッドのソース コードです。

/**
 * クライアントから送信されたヘッダーデータを解析する*/
静的 void ngx_http_process_request_headers(ngx_event_t *rev) {
 u_char *p;
 size_t 長さ;
 ssize_t n;
 ngx_int_t rc、rv;
 ngx_table_elt_t *h;
 ngx_connection_t *c;
 ngx_http_header_t *hh;
 ngx_http_request_t *r;
 ngx_http_core_srv_conf_t *cscf;
 ngx_http_core_main_conf_t *cmcf;

 c = rev->データ;
 r = c->データ;

 もし (rev->timedout) {
  ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "クライアントがタイムアウトしました");
  c->タイムアウト = 1;
  ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
  戻る;
 }

 ngx_http_get_module_main_conf を r に設定します。
 rc = NGX_AGAIN;

 のために (;;) {
  rc == NGX_AGAINの場合{
   // 現在のヘッダーバッファに空きスペースがない場合は、新しいスペースを申請します if (r->header_in->pos == r->header_in->end) {
    // 新しいスペースを申請する rv = ngx_http_alloc_large_header_buffer(r, 0);
    rv == NGX_ERRORの場合{
     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
     戻る;
    }

    // クライアントから送信されたヘッダーが長すぎて、large_client_header_buffers で指定された最大サイズを超えています if (rv == NGX_DECLINED) {
     p = r->ヘッダー名の開始;
     r->lingering_close = 1;
     (p == NULL)の場合{
      ngx_log_error(NGX_LOG_INFO, c->log, 0, "クライアントが大きすぎるリクエストを送信しました");
      ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
      戻る;
     }

     len = r->header_in->end - p;
     長さが NGX_MAX_ERROR_STR - 300 以上のとき
      長さ = NGX_MAX_ERROR_STR - 300;
     }

     ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
     戻る;
    }
   }

   // 接続されたクライアントから新しく送信されたデータを読み取ろうとします n = ngx_http_read_request_header(r);
   (n == NGX_AGAIN || n == NGX_ERROR) の場合 {
    戻る;
   }
  }

  cscf = ngx_http_get_module_srv_conf(r、ngx_http_core_module);
  // ここでは主に読み取ったデータを変換します rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers);

  // NGX_OKは、解析とヘッダーデータの取得が成功したことを示します。if (rc == NGX_OK) {
   r->request_length += r->header_in->pos - r->header_name_start;
   // 無効なヘッダーをフィルタリングする
   (r->無効なヘッダー&&cscf->無効なヘッダーを無視){
    続く;
   }

   // ヘッダーを保存する構造体を作成します。h = ngx_list_push(&r->headers_in.headers);
   h == NULLの場合{
    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    戻る;
   }

   h->ハッシュ = r->ヘッダーハッシュ;
   // ヘッダー名をハッシュテーブルのキーとして使用します
   h->key.len = r->header_name_end - r->header_name_start;
   h->key.data = r->header_name_start;
   h->key.data[h->key.len] = '\0';

   // ヘッダー値をハッシュテーブルの値として使用します
   h->value.len = r->header_end - r->header_start;
   h->value.data = r->header_start;
   h->値.data[h->値.len] = '\0';

   h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
   h->lowcase_key == NULLの場合{
    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    戻る;
   }

   h->key.len == r->lowcase_indexの場合{
    ngx_memcpy(h->lowcase_key、r->lowcase_header、h->key.len);
   } それ以外 {
    ngx_strlow(h->lowcase_key、h->key.data、h->key.len);
   }

   // headers_in_hash はすべてのヘッダーを格納します。ここでは、現在のクライアントから送信されたヘッダーが有効なヘッダーであるかどうかを確認します。
   hh = ngx_hash_find(&cmcf->headers_in_hash、h->hash、h->lowcase_key、h->key.len);
   // ここでのハンドラは、ngx_http_headers_in 内の各ヘッダーに対して定義された処理メソッドです。各ヘッダーの // handler() メソッドによって処理された後、クライアントから送信されたヘッダーは、r->headers_in 構造内のさまざまな属性に変換されます if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
    戻る;
   }

   続く;
  }

  // NGX_HTTP_PARSE_HEADER_DONE はすべてのヘッダーが処理されたことを意味します if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
   r->request_length += r->header_in->pos - r->header_name_start;
   r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
   // クライアントから送信されたヘッダーデータの正当性を確認します rc = ngx_http_process_request_header(r);
   rc が NGX_OK の場合 {
    戻る;
   }

   ngx_http_process_request() は、リクエストを処理するプロセスです。
   戻る;
  }

  // NGX_AGAIN は、読み取ったヘッダー行のデータが不完全であり、さらに読み取る必要があることを示します if (rc == NGX_AGAIN) {
   続く;
  }
  
  ngx_log_error(NGX_LOG_INFO, c->log, 0, "クライアントが無効なヘッダー行を送信しました");
  ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
  戻る;
 }
}

ここでのリクエスト ヘッダーの読み取りは、主に次の手順に分かれます。

  • まず、現在の読み取りイベントがタイムアウトしていないかどうかを確認します。タイムアウトしている場合は、現在の接続を直接閉じます。
  • r->header_in->pos == r->header_in->end が真かどうかを判断します。これは主に、現在の読み取りバッファに新しく読み取ったデータを格納できるメモリ領域があるかどうかを確認するためです。ない場合は、メモリ プールから新しいメモリ領域を申請します。
  • ngx_http_read_request_header() メソッドを呼び出して、現在の接続ハンドル上のデータを読み取ります。戻り値が 0 より大きい場合は、読み取られたデータの長さを示します。0 に等しい場合は、クライアントが切断されたことを意味します。NGX_ERROR の場合は、読み取り中に例外が発生したことを意味します。NGX_AGAIN の場合は、今回はデータが読み取られなかったため、新しいデータを読み取る必要があります。ご覧のとおり、ここではまず戻り値が NGX_AGAIN かどうかを判断します。そうである場合は、他の処理を行わずにそのまま戻ります。これは主に、現在の読み取りイベントのコールバック関数がまだ ngx_http_process_request_headers() であるためです。新しい読み取りイベントがトリガーされると、引き続き ngx_http_read_request_header() を呼び出してデータを再度読み取ります。一方、ngx_http_read_request_header() メソッドでは、戻り値が NGX_AGAIN の場合、現在の読み取りイベントがイベント キューに再度追加され、現在の接続の epoll ハンドルに読み取りイベントが登録されます。
  • ngx_http_parse_header_line() メソッドを呼び出して、読み取られたリクエスト ヘッダー データを解析します。このメソッドの各呼び出しでは 1 つのリクエスト ヘッダーのみが解析されますが、無限の for ループと継続的なイベント トリガー メカニズムの後、最終的にすべてのリクエスト ヘッダー データが読み取られることに注意してください。
  • ngx_http_parse_header_line() メソッドの戻り値に従って、それが NGX_OK の場合、新しく読み取られたヘッダーは r->headers_in.headers リストに格納されます。
  • ngx_http_parse_header_line() メソッドの戻り値が NGX_HTTP_PARSE_HEADER_DONE の場合、すべてのヘッダーが正常に読み取られたことを意味します。このとき、まず ngx_http_process_request_header() メソッドが呼び出され、読み取られたヘッダーの正当性が確認され、次に ngx_http_process_request() メソッドが呼び出され、nginx の http モジュールの 11 段階が開始されます。このメソッドの実装原理については、次の記事で説明します。

2. リクエストヘッダーデータの読み取り

ご覧のとおり、リクエスト ヘッダーを読み取るための主なメソッドは ngx_http_read_request_header() と ngx_http_parse_header_line() の 2 つあります。ここでの 2 番目のメソッドは比較的長いですが、そのロジックは非常に単純です。主に、読み取ったデータを解析して、完全なリクエスト ヘッダー (名前: 値の形式で 1 ​​行を占める) を形成できるかどうかを確認します。形成できる場合は NGX_OK を返し、そうでない場合は、データの読み取りを続行することを期待して NGX_AGAIN を返します。ここではこの方法については説明しません。読者は自分でソース コードを読むことができます。ここでは主に、ngx_http_read_request_header() メソッドがクライアントから送信されたリクエスト ヘッダー データを読み取る方法について説明します。

静的ssize_t ngx_http_read_request_header(ngx_http_request_t *r) {
 ssize_t n;
 ngx_event_t *rev;
 ngx_connection_t *c;
 ngx_http_core_srv_conf_t *cscf;

 c = r->接続;
 rev = c->読み取り;

 // まだ処理されていないデータの量を計算しま n = r->header_in->last - r->header_in->pos;

 // nが0より大きい場合は、まだ処理されていないデータがあることを意味するので、nを直接返します
 (n > 0) の場合 {
  n を返します。
 }

 // ここに行くということは、現在読み込まれているデータが処理されたということなので、ここで判断が行われます。現在のイベントのreadyパラメータが1の場合、
 // これは、現在の接続のハンドルに未読データが格納されているため、c->recv() メソッドが呼び出されてデータが読み取られることを意味します。それ以外の場合は、現在のイベントがイベント キューに追加され、現在の接続ハンドルの読み取りイベントが引き続き監視されます if (rev->ready) {
  // 接続ファイル記述子上のデータを読み取る n = c->recv(c, r->header_in->last, r->header_in->end - r->header_in->last);
 } それ以外 {
  n = NGX_AGAIN;
 }

 // n が NGX_AGAIN の場合、現在のイベントをイベント リスナーに追加し、現在の epoll ハンドルの読み取りイベントをリッスンし続けます if (n == NGX_AGAIN) {
  if (!rev->timer_set) {
   cscf = ngx_http_get_module_srv_conf(r、ngx_http_core_module);
   ngx_add_timer(rev、cscf->client_header_timeout);
  }

  ngx_handle_read_event(rev, 0) != NGX_OK の場合 {
   ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
   NGX_ERROR を返します。
  }

  NGX_AGAIN を返します。
 }

 // nが0の場合、クライアントが接続を閉じたことを意味します if (n == 0) {
  ngx_log_error(NGX_LOG_INFO, c->log, 0, "クライアントが接続を途中で閉じました");
 }

 // クライアントが接続を閉じるか異常な読み取りをした場合は、現在のリクエスト構造をリサイクルします if (n == 0 || n == NGX_ERROR) {
  c->エラー = 1;
  c->log->action = "クライアント要求ヘッダーを読み取っています";
  ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
  NGX_ERROR を返します。
 }

 // 現在読み取られているデータ ポインターを更新します r->header_in->last += n;
 n を返します。
}



ここでのリクエスト ヘッダー データの読み取りは、主に次の手順に分かれています。

  • 現在のバッファに未処理のデータがあるかどうかを判断します。ある場合は、直接戻ります。読み込まれていないデータが存在する主な理由は、以前にリクエスト ライン データを読み取るプロセスで、リクエスト ヘッダー データの一部または全部が読み取られる可能性があるため、ここでチェックが実行されるためです。
  • 現在の読み取りイベントの準備ができているかどうかを判断します。準備ができている場合は、c->recv() メソッドを呼び出して、現在の接続ハンドル上のデータを読み取ります。
  • 現在の読み取りイベントの準備ができていない場合、現在の読み取りイベントをイベント キューに再度追加し、現在の接続の epoll ハンドルに読み取りイベントを登録します。
  • 2 番目のステップの戻り値が判定されます。0 の場合は、クライアントが切断されたことを意味します。NGX_ERROR の場合は、データの読み取りが異常であることを意味します。どちらの場合も、現在の接続は閉じられ、400 ステータス コードがクライアントに返されます。戻り値が NGX_AGAIN の場合は、手順 3 に進み、読み取りイベントのリッスンを続行します。戻り値が 0 より大きい場合、読み取りが成功したことを意味し、この 0 より大きい値は読み取られたデータの長さを示します。
  • 読み取ったデータを格納するバッファのポインタデータを更新します。

3. まとめ

この記事では、主に nginx がリクエスト ヘッダーを読み取って解析する方法について説明し、データを読み取るためのメイン プロセス コードと読み取りの詳細な手順に焦点を当てます。

nginx リクエスト ヘッダー データ読み取りプロセスの詳細な説明に関するこの記事はこれで終わりです。より関連性の高い nginx リクエスト ヘッダー データ読み取りコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Nginx と GeoIP モジュールを使用して IP の地域情報を読み取る方法
  • NodeJS を使用して Nginx エラー ログを読み取り、分析する方法
  • リクエストボディを読み込むためのNginx設定機能の詳細な説明

<<:  React クラスコンポーネントのライフサイクルと実行順序

>>:  mysql8.0.18 で winx64 をインストールするための詳細なチュートリアル (画像とテキスト付き)

推薦する

Webpack5-react スキャフォールディングをゼロから構築するための実装手順 (ソースコード付き)

目次ウェブパック5公式スタート建築ガイド構築を開始する依存する準備が完了したら、プロジェクトの構築を...

Vueのカスタムディレクティブの詳細なガイド

目次1. カスタム指示とは何ですか? 2. 指示をカスタマイズする方法フック機能3. 応用シナリオ入...

Linux でアップロードされたファイルのスケジュールされたバックアップと増分バックアップを実装する方法

導入Alibaba Cloud のような OSS ストレージ サービスを使用している場合は、サービス...

MySQLとRedisキャッシュ間の同期ソリューションについての簡単な説明

目次1. ソリューション 1 (UDF)デモケース2. ソリューション2(binlogの解析)キャナ...

ブログデザイン ウェブデザイン デビュー

私がデザインした最初の Web ページは次のとおりです。 私はこの業界に7年間在籍し、プログラミング...

CSS 標準: vertical-align プロパティ

<br />原文: http://www.mikkolee.com/13私は最近、ver...

MYSQL ログとバックアップおよび復元の問題の詳細な説明

この記事では、参考までにMYSQLログとバックアップとリストアについて紹介します。具体的な内容は以下...

VMware 仮想マシン ubuntu18.04 インストール チュートリアル

インストール手順1. 仮想マシンを作成する 2. [カスタム(詳細)]を選択し、[次へ]をクリックし...

Zabbixで監視する必要があるホストを追加するための詳細な手順

監視ホストの追加ホスト 192.168.179.104 が zabbix 監視項目に追加されます (...

Div CSS 命名標準 CSS クラスの命名規則 (SEO 標準に準拠)

検索エンジン最適化 (SEO) では実行すべきタスクが多数ありますが、その中でもコードの最適化は重要...

MySQL パーティションテーブルのベストプラクティスガイド

序文:パーティショニングはテーブル設計パターンです。一般的に、テーブル パーティショニングとは、条件...

Windows 10 でカスタムドメイン名をバインドするように Hexo と GitHub を構成する方法

Hexo は Windows 10 でカスタムドメイン名を GitHub にバインドしますまずドメイ...

MySQL におけるさまざまな一般的な結合テーブルクエリの例の概要

この記事では、例を使用して、MySQL のさまざまな一般的な結合テーブルクエリについて説明します。ご...

スタートアップ企業が丹念に作り上げた優れたウェブデザイン17選

スタートアップ企業は、型破りな仕事のやり方、ユニークなビジネスモデル、特徴的な製品やサービス、パーソ...

Vue の 2 択タブバー切り替えの新しいアプローチ

問題の説明プロジェクトに取り組んでいるときに、タブ バーの切り替え効果を作成する必要がある場合があり...