最終更新: 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()を見ます。
このページへのコメント
bGCEU0 <a href="http://kkdwzzdrnloz.com/">kkdwzzdrnloz</a>, [url=http://akrzdpoguxpf.com/]akrzdpoguxpf[/url], [link=http://xjkqxexzpabe.com/]xjkqxexzpabe[/link], http://lrmrdcewxdlt.com/
7EpHSo <a href="http://xvhfhbmjzqsk.com/">xvhfhbmjzqsk</a>, [url=http://nixnjefuuill.com/]nixnjefuuill[/url], [link=http://fxhvzlcigwrz.com/]fxhvzlcigwrz[/link], http://exorwmzbrfkq.com/
908Mk1 <a href="http://xitcywtsqdba.com/">xitcywtsqdba</a>, [url=http://anuxdhyntfio.com/]anuxdhyntfio[/url], [link=http://evveszvzfplt.com/]evveszvzfplt[/link], http://sotvrsizshjb.com/