WebサーバとしてのNginxの調査結果を記録する。


ngx_http_process_request_headers関数

名前からして明らかですが、HTTPヘッダを受信する関数でしょう。

    909 static void
    910 ngx_http_process_request_headers(ngx_event_t *rev)
    911 {
    912     u_char                     *p;
    913     size_t                      len;
    914     ssize_t                     n;
    915     ngx_int_t                   rc, rv;
    916     ngx_table_elt_t            *h;
    917     ngx_connection_t           *c;
    918     ngx_http_header_t          *hh;
    919     ngx_http_request_t         *r;
    920     ngx_http_core_srv_conf_t   *cscf;
    921     ngx_http_core_main_conf_t  *cmcf;
    922
    923     c = rev->data;
    924     r = c->data;
    925
    926     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
    927                    "http process request header line");
    928
    929     if (rev->timedout) {
    930         ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
    931         c->timedout = 1;
    932         ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
    933         return;
    934     }
    935
    936     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    937     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
    938
    939     rc = NGX_AGAIN;
    940
    941     for ( ;; ) {
    942
    943         if (rc == NGX_AGAIN) {
    944
    945             if (r->header_in->pos == r->header_in->end) {
    946
    947                 rv = ngx_http_alloc_large_header_buffer(r, 0);
    948
    949                 if (rv == NGX_ERROR) {
    950                     ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    951                     return;
    952                 }
    953
    954                 if (rv == NGX_DECLINED) {
    955                     p = r->header_name_start;
    956
    957                     if (p == NULL) {
    958                         ngx_log_error(NGX_LOG_INFO, c->log, 0,
    959                                       "client sent too large request");
    960                         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
    961                         return;
    962                     }
    963
    964                     len = r->header_in->end - p;
    965
    966                     if (len > NGX_MAX_ERROR_STR - 300) {
    967                         len = NGX_MAX_ERROR_STR - 300;
    968                         p[len++] = '.'; p[len++] = '.'; p[len++] = '.';
    969                     }
    970
    971                     ngx_log_error(NGX_LOG_INFO, c->log, 0,
    972                                   "client sent too long header line: \"%*s\"",
    973                                   len, r->header_name_start);
    974                     ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
    975                     return;
    976                 }
    977             }
    978
    979             n = ngx_http_read_request_header(r);
    980
    981             if (n == NGX_AGAIN || n == NGX_ERROR) {
    982                 return;
    983             }
    984         }
    985
    986         rc = ngx_http_parse_header_line(r, r->header_in,
    987                                         cscf->underscores_in_headers);
    988
    989         if (rc == NGX_OK) {
    990
    991             if (r->invalid_header && cscf->ignore_invalid_headers) {
    992
    993                 /* there was error while a header line parsing */
    994
    995                 ngx_log_error(NGX_LOG_INFO, c->log, 0,
    996                               "client sent invalid header line: \"%*s\"",
    997                               r->header_end - r->header_name_start,
    998                               r->header_name_start);
    999                 continue;
   1000             }
   1001
   1002             /* a header line has been parsed successfully */
   1003
   1004             h = ngx_list_push(&r->headers_in.headers);
   1005             if (h == NULL) {
   1006                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
   1007                 return;
   1008             }
   1009
   1010             h->hash = r->header_hash;
   1011
   1012             h->key.len = r->header_name_end - r->header_name_start;
   1013             h->key.data = r->header_name_start;
   1014             h->key.data[h->key.len] = '\0';
   1015
   1016             h->value.len = r->header_end - r->header_start;
   1017             h->value.data = r->header_start;
   1018             h->value.data[h->value.len] = '\0';
   1019
   1020             h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
   1021             if (h->lowcase_key == NULL) {
   1022                 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
   1023                 return;
   1024             }
   1025
   1026             if (h->key.len == r->lowcase_index) {
   1027                 ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
   1028
   1029             } else {
   1030                 ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
   1031             }
   1032
   1033             hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
   1034                                h->lowcase_key, h->key.len);
   1035
   1036             if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
   1037                 return;
   1038             }
   1039
   1040             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
   1041                            "http header: \"%V: %V\"",
   1042                            &h->key, &h->value);
   1043
   1044             continue;
   1045         }
   1046
   1047         if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
   1048
   1049             /* a whole header has been parsed successfully */
   1050
   1051             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
   1052                            "http header done");
   1053
   1054             r->request_length += r->header_in->pos - r->header_in->start;
   1055
   1056             r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
   1057
   1058             rc = ngx_http_process_request_header(r);
   1059
   1060             if (rc != NGX_OK) {
   1061                 return;
   1062             }
   1063
   1064             ngx_http_process_request(r);
   1065
   1066             return;
   1067         }
   1068
   1069         if (rc == NGX_AGAIN) {
   1070
   1071             /* a header line parsing is still not complete */
   1072
   1073             continue;
   1074         }
   1075
   1076         /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */
   1077
   1078         ngx_log_error(NGX_LOG_INFO, c->log, 0,
   1079                       "client sent invalid header line: \"%*s\\r...\"",
   1080                       r->header_end - r->header_name_start,
   1081                       r->header_name_start);
   1082         ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
   1083         return;
   1084     }
   1085 }

リクエストラインと同じくfor文のループですね。

ngx_http_read_request_headerでデータをrecv()して、HTTPヘッダを「name」「value」を
取得しリストに格納という流れですね。

最初のほう、ngx_http_alloc_large_header_buffer()は受信データを格納するバッファ領域の拡張です。
ここで許容バッファサイズを超えていた場合、新しいバッファ領域は確保できずエラー応答します。

ngx_http_read_request_header()はrecv()してます。
recv()できる状態ではない場合タイマーセットをしています。タイムアウト関連みたいです。詳細不明ですが追わず。
recv()失敗した場合はngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST)でエラーとしてました。

次に受信したデータをngx_http_parse_header_line()で「name」「value」に分解・・・パースします。
関数:ngx_http_parse_header_line
戻り値:受信したサイズ
    受信したデータはr->header_in->posのアドレスからr->header_in->lastまでに入っています。

ヘッダの取得に成功したら、リストに値を入れているみたいです。
h = ngx_list_push(&r->headers_in.headers);
リストにプッシュの関数・・・みたいです。ちゃんと追ってみましょう。

r->headers_in.headersは、ngx_list_t構造体です。
ngx_list_tの方はこんな感じ
typedef struct {
    ngx_list_part_t  *last;
    ngx_list_part_t   part;
    size_t            size;
    ngx_uint_t        nalloc;
    ngx_pool_t       *pool;
} ngx_list_t;

typedef struct ngx_list_part_s  ngx_list_part_t;

struct ngx_list_part_s {
    void             *elts;
    ngx_uint_t        nelts;
    ngx_list_part_t  *next;
};

ngx_list_push()はこんな感じ。
void *
ngx_list_push(ngx_list_t *l)
{
    void             *elt;
    ngx_list_part_t  *last;

    last = l->last;

    if (last->nelts == l->nalloc) {

        /* the last part is full, allocate a new list part */

        last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
        if (last == NULL) {
            return NULL;
        }

        last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
        if (last->elts == NULL) {
            return NULL;
        }

        last->nelts = 0;
        last->next = NULL;

        l->last->next = last;
        l->last = last;
    }

    elt = (char *) last->elts + l->size * last->nelts;
    last->nelts++;

    return elt;
}

なるほど。
ngx_list_part_tに値が一つ一つ入って、それが次々つながっていく。
ngx_list_part_t→ngx_list_part_t→ngx_list_part_t・・・と。

ngx_list_tにはpoolやリストの値の最後のアドレスが入っていると。
ngx_list_pushで値を格納開始するアドレスを返す・・・と。

次のngx_hash_find、こいつは何をしているんでしょう。
どういう処理なのか見たけどわからいのですが、結局なぜかngx_http_headers_in配列に
一致するHTTPヘッダだった場合、そのngx_http_headers_in配列のアドレスが返ります。
んで、次のhh->handler(r, h, hh->offset)で、そのヘッダに対応した関数に処理が飛びました。
Hostヘッダであればngx_http_process_host()、User-Agentであればngx_http_process_user_agentというように。
ngx_http_headers_in配列で定義されていないヘッダの場合はhh=NULLとなるため、特に飛びません。
ngx_http_headers_in配列に用意されている関数は、ngx_http_request.cのngx_http_headers_inを御覧ください。
代表的なHTTPヘッダについて処理があるのがわかります。

そんなこんなで、このfor文はHTTPヘッダが続く限りグルグル回ります。
HTTPヘッダ部の終わり、改行コートが二つ来た場合(ngx_http_parse_header_lineの戻り値がNGX_HTTP_PARSE_HEADER_DONE)
ngx_http_process_request_header(r)、ngx_http_process_request(r)と続くわけです。

あれ、ngx_http_process_request_header(r)って自身をまた呼び出すの?と思ったら最後のsがない。
今見ている関数はngx_http_process_request_headers(r)、これから処理するのはngx_http_process_request_header(r)
紛らわしーーー。

ngx_http_process_request_header()はそんなに長くなかったので見てしまいましょう。

static ngx_int_t
ngx_http_process_request_header(ngx_http_request_t *r)
{
    if (ngx_http_find_virtual_server(r, r->headers_in.server.data,
                                     r->headers_in.server.len)
        == NGX_ERROR)
    {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return NGX_ERROR;
    }

    if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                   "client sent HTTP/1.1 request without \"Host\" header");
        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return NGX_ERROR;
    }

    if (r->headers_in.content_length) {
        r->headers_in.content_length_n =
                            ngx_atoof(r->headers_in.content_length->value.data,
                                      r->headers_in.content_length->value.len);

        if (r->headers_in.content_length_n == NGX_ERROR) {
            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                          "client sent invalid \"Content-Length\" header");
            ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
            return NGX_ERROR;
        }
    }

    if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                  "client sent %V method without \"Content-Length\" header",
                  &r->method_name);
        ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
        return NGX_ERROR;
    }

    if (r->method & NGX_HTTP_TRACE) {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                      "client sent TRACE method");
        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
        return NGX_ERROR;
    }

    if (r->headers_in.transfer_encoding
        && ngx_strcasestrn(r->headers_in.transfer_encoding->value.data,
                           "chunked", 7 - 1))
    {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                      "client sent \"Transfer-Encoding: chunked\" header");
        ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED);
        return NGX_ERROR;
    }

    if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {
        if (r->headers_in.keep_alive) {
            r->headers_in.keep_alive_n =
                            ngx_atotm(r->headers_in.keep_alive->value.data,
                                      r->headers_in.keep_alive->value.len);
        }
    }

    return NGX_OK;
}

最初のngx_http_find_virtual_server()は名前からしてバーチャルホスト関連でしょうが
バーチャルホストは特に興味ないのでパス。

此処から先は各HTTPヘッダのチェックですね。

■Hostヘッダ
HostヘッダがないのにHTTPのバージョンが1.0より上であれば規約違反(RFC2616・・・HTTP1.1ではHostヘッダは必須)なので
エラー応答しています。

■Content-Lengthヘッダ
Content-Lengthヘッダがあって、値が数値じゃなければエラーとしています。
ついでに文字列で格納されていた値を数値に変換しています。

■メソッドPUT かつ Content-Lengthの値が-1
なんだこのコード?いらなくないか?
前の処理でContent-Lengthがエラー(-1)だったら終了しているのに。
絶対通らない処理に見えますけど・・・。

■TRACEメソッド
メソッドがTRACEなら拒否。
うぉ、NginxってTRACEメソッド問答無用で拒否ですか。
セキュリティ脆弱性が指摘されてますがねぇ、問答無用とは。
設定で許容もできないとはえげつないと思う反面、TRACEメソッドなんて誰も使わないだろうからこれでいいのかとも思います。

■Transfer-Encodingヘッダ
chunkedであったら拒否。
へぇ、チャンク通信も拒否っすか。RFC違反じゃねぇのか。。。
こんなコードになっているってことはチャンク通信するクライアントっていないでしょうね。

■connection
Keep-Aliveだったら、Keep-Aliveヘッダの値を取得している。
取得しているということはどかで何かに使うんだろうか。

とまぁ、こんな感じでHTTPヘッダ受信が見終わりました。
次からHTTPヘッダの解析でいよいよHTTP処理本番って感じかな。

次はngx_http_process_request()を見ます。

このページへのコメント

bGCEU0 <a href="http://kkdwzzdrnloz.com/">kkdwzzdrnloz</a>, [url=http://akrzdpoguxpf.com/]akrzdpoguxpf[/url], [link=http://xjkqxexzpabe.com/]xjkqxexzpabe[/link], http://lrmrdcewxdlt.com/

0
Posted by tsshqpy 2013年11月20日(水) 16:50:38 返信

7EpHSo <a href="http://xvhfhbmjzqsk.com/">xvhfhbmjzqsk</a>, [url=http://nixnjefuuill.com/]nixnjefuuill[/url], [link=http://fxhvzlcigwrz.com/]fxhvzlcigwrz[/link], http://exorwmzbrfkq.com/

0
Posted by xirpjfntb 2013年11月14日(木) 10:28:08 返信

908Mk1 <a href="http://xitcywtsqdba.com/">xitcywtsqdba</a>, [url=http://anuxdhyntfio.com/]anuxdhyntfio[/url], [link=http://evveszvzfplt.com/]evveszvzfplt[/link], http://sotvrsizshjb.com/

0
Posted by yvhcfrrp 2013年07月07日(日) 19:37:17 返信

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu

メニューサンプル1

メニューサンプル2

開くメニュー

閉じるメニュー

  • アイテム
  • アイテム
  • アイテム
【メニュー編集】

管理人/副管理人のみ編集できます