ハンドラ関数を実行しているap_invoke_handler()はap_process_async_request()から呼ばれていた(
handlerフック関数の実行処理)。
そして、ap_process_async_request()関数は、ap_process_http_async_connection()から呼ばれていた(
リクエストの処理の流れ)。
このap_process_http_async_connection()は、process_connectionフック関数内で、event MPMの場合に実行され、リクエストを1件処理している。
ap_process_async_request()は、読み込んだリクエストをチェック・変換し、レスポンスを返す。
そして、その前段でリクエストを読み込んでいるのが、ap_read_request()関数だ。
これを少し追っておく。
(5) ap_read_request
この関数は、HTTPリクエスト行、HTTPリクエストヘッダ行を読み込んで、request_rec情報に格納し、この情報を返却している。
ヘッダの読み込み完了前にエラーが発生した場合には、エラーレスポンスを返すこともする。
(httpd-2.4.9/server/protocol.c)
895 request_rec *ap_read_request(conn_rec *conn)
896 {
897 request_rec *r;
898 apr_pool_t *p;
899 const char *expect;
900 int access_status = HTTP_OK;
901 apr_bucket_brigade *tmp_bb;
902 apr_socket_t *csd;
903 apr_interval_time_t cur_timeout;
904
905
906 apr_pool_create(&p, conn->pool);
907 apr_pool_tag(p, "request");
requestメモリプールを生成する。
メモリプールの説明はまだしていない書いていないが、apr_pcalloc()やapr_palloc()などは、メモリプールからメモリ領域を確保する。
EORメタデータバケットの破棄(apr_bucket_destroy())処理内で、requestメモリプールに対してapr_pool_destroy()が実行されている。
これよりrequestメモリプールから確保されたメモリ領域は再利用可能なメモリリストに戻される(解放される)。
このEORメタデータの処理は、httpd-2.2と異なっている(httpd-2.2系にはEORメタデータが存在しない)。
EORメタデータバケットの破棄時には、ap_run_log_transaction()も実行される。リクエスト処理の終了時の処理がここに集約されたのかもしれない。
908 r = apr_pcalloc(p, sizeof(request_rec));
909 AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
910 r->pool = p;
requestプールは、request_rec情報のpool変数に保持される。
911 r->connection = conn;
request_recのconnection変数には、渡されてきたconn_rec情報をセットする。
912 r->server = conn->base_server;
conn_recにセットされているbase_server(このタイミングではメインサーバか合致するIPベースのVirtualHostの情報)をセットする。
913
914 r->user = NULL;
915 r->ap_auth_type = NULL;
916
917 r->allowed_methods = ap_make_method_list(p, 2);
918
919 r->headers_in = apr_table_make(r->pool, 25);
920 r->subprocess_env = apr_table_make(r->pool, 25);
921 r->headers_out = apr_table_make(r->pool, 12);
922 r->err_headers_out = apr_table_make(r->pool, 5);
923 r->notes = apr_table_make(r->pool, 5);
924
925 r->request_config = ap_create_request_config(r->pool);
926 /* Must be set before we run create request hook */
927
928 r->proto_output_filters = conn->output_filters;
929 r->output_filters = r->proto_output_filters;
930 r->proto_input_filters = conn->input_filters;
931 r->input_filters = r->proto_input_filters;
conn_recに登録済みのコネクションタイプの入出力フィルタの設定をrequest_recに設定する。
932 ap_run_create_request(r);
そして、create_requestフック関数を実行する。
933 r->per_dir_config = r->server->lookup_defaults;
934
935 r->sent_bodyct = 0; /* bytect isn't for body */
936
937 r->read_length = 0;
938 r->read_body = REQUEST_NO_BODY;
939
940 r->status = HTTP_OK; /* Until further notice */
941 r->the_request = NULL;
942
943 /* Begin by presuming any module can make its own path_info assumptions,
944 * until some module interjects and changes the value.
945 */
946 r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
947
948 r->useragent_addr = conn->client_addr;
949 r->useragent_ip = conn->client_ip;
950
951 tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
入力フィルタを通してデータを吸い上げるためのbucket brigadeを用意する。
このbucket brigadeは、この関数内で利用され、破棄される。
952
953 ap_run_pre_read_request(r, conn);
pre_read_requestフック関数を実行する。
954
955 /* Get the request... */
956 if (!read_request_line(r, tmp_bb)) {
read_request_line()はHTTPリクエスト行を読み込む。
内部では、更に ap_rgetline_core()を呼び出して、入力フィルタからデータ1行分を吸い上げている。
入力フィルタの細かい処理は、別途確認したい。
ここの条件分岐は、何らかのエラー発生時の分岐になる。
ステータス(r->status)にエラー理由に応じた値がセットされている。
957 if (r->status == HTTP_REQUEST_URI_TOO_LARGE
958 || r->status == HTTP_BAD_REQUEST) {
414 Request-URI Too Long または 400 Bad Requestがセットされている場合の条件分岐だ。
959 if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
960 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00565)
961 "request failed: client's request-line exceeds LimitRequestLine (longer than %d)",
962 r->server->limit_req_line);
これはエラーログメッセージ。
APLOG_INFOはinfoレベルのログであることを意味している。
963 }
964 else if (r->method == NULL) {
965 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00566)
966 "request failed: invalid characters in URI");
967 }
968 ap_send_error_response(r, 0);
エラー応答を送信する処理だが、ErrorDocumentの設定は無視され、組み込みのエラーメッセージが返される。
(ErrorDocumentディレクティブでメッセージを直接書いている場合は有効)
以下の記述の関係だろう。
http://httpd.apache.org/docs/2.4/en/mod/core.html#errordocument
Although most error messages can be overridden, there are certain circumstances
where the internal messages are used regardless of the setting of ErrorDocument.
In particular, if a malformed request is detected, normal request processing
will be immediately halted and the internal error message returned. This is
necessary to guard against security problems caused by bad requests.
969 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
970 ap_run_log_transaction(r);
log_transactionフック関数の実行。
アクセスログが出力される。
971 apr_brigade_destroy(tmp_bb);
作成していたbucket brigadeを破棄する。
972 goto traceout;
973 }
974 else if (r->status == HTTP_REQUEST_TIME_OUT) {
ここは、リクエスト行の読み込みでタイムアウトとなった場合の分岐だ。
このケースではレスポンスを返していない。
975 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
976 if (!r->connection->keepalives) {
977 ap_run_log_transaction(r);
978 }
979 apr_brigade_destroy(tmp_bb);
980 goto traceout;
981 }
それ以外のステータスの場合は、何も行っていない。
アクセスログもエラーログも出力されない。
接続は受け付けたものの、何も送られないで切断された場合(空行のみとか)等がこの経路になる。
982
983 apr_brigade_destroy(tmp_bb);
984 r = NULL;
985 goto traceout;
986 }
987
988 /* We may have been in keep_alive_timeout mode, so toggle back
989 * to the normal timeout mode as we fetch the header lines,
990 * as necessary.
991 */
992 csd = ap_get_conn_socket(conn);
993 apr_socket_timeout_get(csd, &cur_timeout);
994 if (cur_timeout != conn->base_server->timeout) {
995 apr_socket_timeout_set(csd, conn->base_server->timeout);
996 cur_timeout = conn->base_server->timeout;
997 }
現在のソケットタイムアウトの値をapr_socket_timeout_get()で取得し、base_serverの設定値を比較し、変わっていたら、更新する。
値はapr_socket_t構造体のtimeout変数に保持されている。
998
999 if (!r->assbackwards) {
リクエスト行を処理して、メソッドとURLしかなかった場合、assbackwards変数に1がセットされている。
HTTP/0.9とみなされている。この場合、HTTPリクエストヘッダを持たない。
この分岐は否定"!"なので、0の場合がここに入ることになる。
HTTP/1.0や1.1などだ。
1000 const char *tenc;
1001
1002 ap_get_mime_headers_core(r, tmp_bb);
HTTPリクエストヘッダの読込処理を実行する。
内部では、更に ap_rgetline_core()を呼び出して、入力フィルタからデータ1行分を吸い上げては処理を繰り返している。
タイムアウトや領域不足、制限超過、形式不備(ヘッダ名の後のコロンが見つからない)といったことでなければ、request_rec構造体のheadres_in配列に読み込んだヘッダを追記していく。
処理としては、複数行に折りたたまれた(fold)ヘッダの整形もここで行われている。
空行に達したら処理を終える。
見ての通り、戻り値はない。
エラーはrequest_rec構造体のstatus変数の値で判断される。
1003 if (r->status != HTTP_OK) {
この分岐はリクエストヘッダ読み込み中のエラーが発生したケースとなる。
タイムアウトであるとか、ヘッダの行数超過や、ヘッダ一行あたりの文字数超過などがエラーとなる。
ステータスはrequest_recのstatusヘッダにセットされている。
1004 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00567)
1005 "request failed: error reading the headers");
1006 ap_send_error_response(r, 0);
1007 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
1008 ap_run_log_transaction(r);
1009 apr_brigade_destroy(tmp_bb);
1010 goto traceout;
1011 }
1012
1013 tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
Transfer-Encidingヘッダのチェックを行う。
このヘッダが存在する場合、エンコーディングはchunkedしか許容されていない。
chunked以外のエンコーディングが指定されていた場合は、エラーとする(BAD REQUEST)。
また、chunkedエンコーディングが指定されていた場合に、COntent-Lengthヘッダがあるとこれを無視するので、ここで、Content-Lengthヘッダを削除している(存在していれば削除される)。
1014 if (tenc) {
1015 /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
1016 * Section 3.3.3.3: "If a Transfer-Encoding header field is
1017 * present in a request and the chunked transfer coding is not
1018 * the final encoding ...; the server MUST respond with the 400
1019 * (Bad Request) status code and then close the connection".
1020 */
1021 if (!(strcasecmp(tenc, "chunked") == 0 /* fast path */
1022 || ap_find_last_token(r->pool, tenc, "chunked"))) {
1023 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02539)
1024 "client sent unknown Transfer-Encoding "
1025 "(%s): %s", tenc, r->uri);
1026 r->status = HTTP_BAD_REQUEST;
1027 conn->keepalive = AP_CONN_CLOSE;
1028 ap_send_error_response(r, 0);
1029 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
1030 ap_run_log_transaction(r);
1031 apr_brigade_destroy(tmp_bb);
1032 goto traceout;
1033 }
1034
1035 /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
1036 * Section 3.3.3.3: "If a message is received with both a
1037 * Transfer-Encoding and a Content-Length header field, the
1038 * Transfer-Encoding overrides the Content-Length. ... A sender
1039 * MUST remove the received Content-Length field".
1040 */
1041 apr_table_unset(r->headers_in, "Content-Length");
1042 }
1043 }
1044 else {
こちらは、HTTP/0.9の分岐になる。
1045 if (r->header_only) {
HEADメソッドによるリクエストの場合にheader_onlyフラグに1がセットされている。
ヘッダを処理しないはずのバージョンで、ヘッダを要求するメソッドというのは矛盾しているということだろう。
エラーが返されている(BAD REQUEST)。
1046 /*
1047 * Client asked for headers only with HTTP/0.9, which doesn't send
1048 * headers! Have to dink things just to make sure the error message
1049 * comes through...
1050 */
1051 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00568)
1052 "client sent invalid HTTP/0.9 request: HEAD %s",
1053 r->uri);
1054 r->header_only = 0;
1055 r->status = HTTP_BAD_REQUEST;
1056 ap_send_error_response(r, 0);
1057 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
1058 ap_run_log_transaction(r);
1059 apr_brigade_destroy(tmp_bb);
1060 goto traceout;
1061 }
1062 }
1063
1064 apr_brigade_destroy(tmp_bb);
リクエスト読込に利用したbucket brigadeを破棄する。
1065
1066 /* update what we think the virtual host is based on the headers we've
1067 * now read. may update status.
1068 */
1069 ap_update_vhost_from_headers(r);
r->server変数(server_rec)をHostヘッダの情報から得た名前ベースのVirtualHostのserver_rec情報に切り替える。
名前ベースのVirtualHostの設定が有効になる。
1070
1071 /* Toggle to the Host:-based vhost's timeout mode to fetch the
1072 * request body and send the response body, if needed.
1073 */
1074 if (cur_timeout != r->server->timeout) {
名前ベースのVirtualHostに切り替えて、Timeoutの設定値が異なっている場合、設定しなおす。
1075 apr_socket_timeout_set(csd, r->server->timeout);
1076 cur_timeout = r->server->timeout;
1077 }
1078
1079 /* we may have switched to another server */
1080 r->per_dir_config = r->server->lookup_defaults;
1081
1082 if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
1083 || ((r->proto_num == HTTP_VERSION(1, 1))
1084 && !apr_table_get(r->headers_in, "Host"))) {
HTTPバージョンがHTTP 1.1以上なのに、Hostヘッダが指定されていない場合、エラーとしている。
1085 /*
1086 * Client sent us an HTTP/1.1 or later request without telling us the
1087 * hostname, either with a full URL or a Host: header. We therefore
1088 * need to (as per the 1.1 spec) send an error. As a special case,
1089 * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
1090 * a Host: header, and the server MUST respond with 400 if it doesn't.
1091 */
1092 access_status = HTTP_BAD_REQUEST;
1093 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00569)
1094 "client sent HTTP/1.1 request without hostname "
1095 "(see RFC2616 section 14.23): %s", r->uri);
1096 }
1097
1098 /*
1099 * Add the HTTP_IN filter here to ensure that ap_discard_request_body
1100 * called by ap_die and by ap_send_error_response works correctly on
1101 * status codes that do not cause the connection to be dropped and
1102 * in situations where the connection should be kept alive.
1103 */
1104
以下でHTTP_IN入力フィルタを追加する。
この実体は、ap_http_filter()関数だ。chunkedエンコードされたか、Content-Lengthの指定されたリクエストボディの読み込みを行っている。
ボディ部を読み込むために必要なフィルタだ。
1105 ap_add_input_filter_handle(ap_http_input_filter_handle,
1106 NULL, r, r->connection);
1107
1108 if (access_status != HTTP_OK
HTTP_INフィルタを追加してから、statusを判定している。
1109 || (access_status = ap_run_post_read_request(r))) {
access_statusがHTTP_OKだった場合に、post_read_requestフック関数を実行する。
1110 ap_die(access_status, r);
1111 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
1112 ap_run_log_transaction(r);
1113 r = NULL;
1114 goto traceout;
1115 }
1116
1117 if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
1118 && (expect[0] != '\0')) {
Expectヘッダを受信していた場合の処理。
"Expect: 100-continue" の場合、expecting_100に1をセットするが、それ以外の場合はエラー応答を返す(417 Expectation Failed)。
1119 /*
1120 * The Expect header field was added to HTTP/1.1 after RFC 2068
1121 * as a means to signal when a 100 response is desired and,
1122 * unfortunately, to signal a poor man's mandatory extension that
1123 * the server must understand or return 417 Expectation Failed.
1124 */
1125 if (strcasecmp(expect, "100-continue") == 0) {
1126 r->expecting_100 = 1;
1127 }
1128 else {
1129 r->status = HTTP_EXPECTATION_FAILED;
1130 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570)
1131 "client sent an unrecognized expectation value of "
1132 "Expect: %s", expect);
1133 ap_send_error_response(r, 0);
1134 ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
1135 ap_run_log_transaction(r);
1136 goto traceout;
1137 }
1138 }
1139
1140 AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status);
DTraceが利用可能な場合に有効化されるマクロのようだが、まだよく分かっていない。。
1141 return r;
作成したrequest_rec情報が返される。
エラーかどうかは statusで判定されることになる。
1142 traceout:
1143 AP_READ_REQUEST_FAILURE((uintptr_t)r);
1144 return r;
エラーステータスの場合も、作成したrequest_rec情報が返される点は同じ。
1145 }
httpd-2.2から細かい点が変更になっているが、大きな流れに違いはないようだ。