最終更新:
library_task 2010年11月14日(日) 16:17:54履歴
名前からして明らかですが、HTTPヘッダを受信する関数でしょう。
リクエストラインと同じく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の方はこんな感じ
ngx_list_push()はこんな感じ。
なるほど。
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()はそんなに長くなかったので見てしまいましょう。
最初の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()を見ます。
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()を見ます。

最新コメント