2014年8月28日木曜日

リクエスト処理の流れ: ap_process_request_after_handler

ap_process_async_request()関数の最後に実行されている幾つかの関数を見ておく。
(リクエストの処理の流れ)
書き散らかしているだけで、分かりやすくはできていないのだが、一通り見ておきたい。

(httpd-2.4.9/modules/http/http_request.c)

272 void ap_process_async_request(request_rec *r)

273 {
  :

ここまでのリクエストの処理結果がOKかそうでないかで分岐している。

345     if (access_status == OK) {
346         ap_finalize_request_protocol(r);
347     }
348     else {
349         r->status = HTTP_OK;
350         ap_die(access_status, r);
351     }
352
353     ap_process_request_after_handler(r);
354 }

(6)ap_finalize_request_protocol

レスポンスの送信完了時に呼び出される。
この唯一の目的は、(transfer-encodingのような)レスポンスボディまわりのラッパーに対してプロトコル終了情報を通知することだ、とコメントにある。

(httpd-2.4.9/server/protocol.c)

1228 /* finalize_request_protocol is called at completion of sending the
1229  * response.  Its sole purpose is to send the terminating protocol
1230  * information for any wrappers around the response message body
1231  * (i.e., transfer encodings).  It should have been named finalize_response.
1232  */
1233 AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r)
1234 {
1235     (void) ap_discard_request_body(r);

ap_discard_request_body()はリクエストボディを読み込む(読み捨てる)。
通常、既に読み込んでいるはずだ。

1236
1237     /* tell the filter chain there is no more content coming */
1238     if (!r->eos_sent) {
1239         end_output_stream(r);

end_output_stream()では、bucket brigadeを作成、
EOSメタデータバケットを作成し、bucket brigadeの末尾に追加する。
bucket brigadeをrequest_recのoutput_filtersに引き渡す処理が行われる。
出力フィルタでは、EOSメタデータバケットを受け取ると各種の終了処理が行われる。
CHUNK出力フィルタ(データをchunkedエンコーディングするフィルタ)では確かに、終了を示すchunkを作成している。

1240     }
1241 }

(7)ap_die

ap_die関数は、エラーレスポンスを返す。
ErrorDocumentの指定にも対応する。
ここでは取り上げない。

(8)ap_process_request_after_handler

ここはこれまで何度か名前を出した EOR メタデータバケットの追加処理が行われている。
つまり、リクエスト処理の終了を準備していることになる。

242 AP_DECLARE(void) ap_process_request_after_handler(request_rec *r)

243 {
244     apr_bucket_brigade *bb;
245     apr_bucket *b;
246     conn_rec *c = r->connection;
247
248     /* Send an EOR bucket through the output filter chain.  When
249      * this bucket is destroyed, the request will be logged and
250      * its pool will be freed
251      */

EORメタデータバケットを作成し、出力フィルタチェーンに引き渡す。
EORバケットが破棄されるタイミングで、アクセスログへの出力が実行され、requestメモリプールが解放される。

252     bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);

bucket brigade bbを作成

253     b = ap_bucket_eor_create(r->connection->bucket_alloc, r);

EORメタデータバケットを作成

254     APR_BRIGADE_INSERT_HEAD(bb, b);

EORメタデータバケットを brigadeの先頭に挿入。
この時点ではbucket brigade bbにはEORバケットだけが含まれている。

255
256     ap_pass_brigade(r->connection->output_filters, bb);

その bucket brigade bb を出力フィルタに引き渡す。
この出力フィルタは、r->output_filtersではなく、conn_recに登録されている出力フィルタに直接、引き渡す。
コネクションフィルタによる処理だけ行うということだ。
プロトコルフィルタ以上の階層(request_recに登録されている階層)の処理は終わっているということだろう。
ただし、この処理タイミングでEORバケットが破棄されている(処理されている)とは限らない。

257
258     /* From here onward, it is no longer safe to reference r
259      * or r->pool, because r->pool may have been destroyed
260      * already by the EOR bucket's cleanup function.
261      */

EORバケットが処理済みであれば、requestメモリプールが解放されているため、
request_rec情報は無効になっている。
request_rec情報 *rは使ってはいけないことになる。

262
263     if (c->cs)
264         c->cs->state = CONN_STATE_WRITE_COMPLETION;

ここで、コネクションの状態情報がCONN_STATE_WRITE_COMPLETIONにセットされる。

265     check_pipeline(c);

処理待ちの読み込みデータがないかを確認している。
読込可能なデータがあった場合、conn_rec情報 *cのdata_in_input_filters変数に1がセットされる。

266     AP_PROCESS_REQUEST_RETURN((uintptr_t)r, r->uri, r->status);

DTrace関連のマクロと考えているが、詳細はよく分からない。

267     if (ap_extended_status) {
268         ap_time_process_request(c->sbh, STOP_PREQUEST);
269     }
270 }


ここまで。

2014年8月18日月曜日

リクエスト処理の流れ: ap_read_request

ハンドラ関数を実行している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から細かい点が変更になっているが、大きな流れに違いはないようだ。

2014年8月5日火曜日

リクエスト処理の流れ: ap_process_request_internal

(4) ap_process_request_internal

多数のフック関数が実行されている、この関数の中を覗いてみる。
handlerフック関数の実行処理 に書いたap_process_async_request()で実行されている関数だ。

ちなみに2.4.9と2.4.10の間では、server/request.cに相違点はないようだった。
従って、ap_process_request_internal関数にも違いはない。

 (httpd-2.4.9/server/request.c)
    126 AP_DECLARE(int) ap_process_request_internal(request_rec *r)
    127 {
    128     int file_req = (r->main && r->filename);

request_recのmain変数は、リクエストのサブリクエストから見た親リクエストを指す。
mainに値があるということは処理中のrequest_recはサブリクエストということだ。
サブリクエストというのは、リクエスト処理中に副次的に発生した処理すべきパスに対してリクエスト処理と一定の例外を除いて同様の操作を行う仕掛けだ。
サブリクエストが直接レスポンスを返すことはないが、リソースのファイルシステム上のパスの判定や、あるいは、アクセスチェックなどは行われる必要があったりする。
そうした処理のために用いられる。
(見たところ、ap_sub_req_method_uri()や、ap_sub_req_lookup_dirent()、ap_sub_req_lookup_file()といった 関数から処理が行われている)

リソースのファイルシステム上のパスは、request_recのfilenameに収められる。
URLパスから生成されるサブリクエストの場合はfilenameはnulと考えられる。

    129     int access_status;
    130     core_dir_config *d;
    131
    132     /* Ignore embedded %2F's in path for proxy requests */

コメントによれば、proxyへのリクエストのパスに含まれている "%2F"(エンコードされたスラッシュ)は 無視するとある。
proxyへのリクエストでなければ、無視しないという意味なんだろう。

    133     if (!r->proxyreq && r->parsed_uri.path) {

request_recのproxyreq変数は、このリクエストがproxyサーバへのリクエストの場合に種別が記録される。
proxyの設定が行われていて、proxyモジュールが受け取ったリクエストを処理対象と判断した場合に値が入る。
このif文はその否定なので、proxyへのリクエストではないリクエストが処理対象となる。

かつ、parsed_uri.path にも値が入っている場合だ。

    134         d = ap_get_core_module_config(r->per_dir_config);

これはcoreモジュールの設定情報のアドレスを取得するマクロだ。
r->per_dir_configにモジュールの設定情報が入っている。
この中のcoreモジュールの設定情報の 場所を返してくれる。

    135         if (d->allow_encoded_slashes) {

これは、AllowEncodedSlashes ディレクティブの設定情報だ。
こちらのif文は Onか、NoDecodeの場合だ。

    136             access_status = ap_unescape_url_keep2f(r->parsed_uri.path, d->decode_encoded_slashes);

この場合、%2Fがパスに含まれていてもエラーとはしない。
Onの場合はデコードする。
NoDecodeの場合はそのまま残す。

    137         }
    138         else {

こちらのelseは Offの場合だ。

    139             access_status = ap_unescape_url(r->parsed_uri.path);

その場合、エンコードされているURLパスをデコードし、不正なエンコードがされていたり 禁止文字(通常は"/")がエンコードされていた場合にエラーとしている。
禁止文字がスラッシュ(%2F)だ。
禁止文字があったら 404(not found)を返している。

On/Offいずれであっても、エンコード自体が誤っていればデコードできないので、エラーとなる。
その場合は 400(bad request)が返ってくる。

    140         }
    141         if (access_status) {

ここは、AllowEncodedSlashes Offで%2Fが見つかるか、エンコードそのものが 誤りの場合。

    142             if (access_status == HTTP_NOT_FOUND) {
    143                 if (! d->allow_encoded_slashes) {
    144                     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00026)
    145                                   "found %%2f (encoded '/') in URI "
    146                                   "(decoded='%s'), returning 404",
    147                                   r->parsed_uri.path);
    148                 }
    149             }
    150             return access_status;
    151         }
    152     }
    153
    154     ap_getparents(r->uri);     /* OK --- shrinking transformations... */

ap_getparent()は、urlから ... の相対パス指定を除去する。
. の場合は単に除去し、
.. の場合は前のパスセグメントごと除去する("xx/.."のまとまりで除去する)。

    155
    156     /* All file subrequests are a huge pain... they cannot bubble through the
    157      * next several steps.  Only file subrequests are allowed an empty uri,
    158      * otherwise let translate_name kill the request.
    159      */
    160     if (!file_req) {

ここはサブリクエストではないか、サブリクエストでもfilename(リソースのローカルパス)が未詳のリクエストが処理対象となる。

    161         if ((access_status = ap_location_walk(r))) {

ここでは、<Location><LocationMatch>節でこのリクエストの合致する条件をマージする処理が行われる。
マージの結果は request_rec.per_dir_configに格納される(マージされる)。


    162             return access_status;
    163         }
    164         if ((access_status = ap_if_walk(r))) {

ここでは、<If><IfElse><Else>節でこのリクエストの合致する条件をマージする処理が行われる。
マージの結果は、request_rec.per_dir_configに格納される(マージされる)。
<If>関連のセクションは2.2系には存在しなかった。


    165             return access_status;
    166         }
    167
    168         d = ap_get_core_module_config(r->per_dir_config);
    169         if (d->log) {
    170             r->log = d->log;
    171         }

マージされたディレクトリのログレベルの情報をrequest_recのlog変数(struct ap_logconfig *)にコピーする。

 
    172
    173         if ((access_status = ap_run_translate_name(r))) {

translate_nameフック関数を実行する。

 
    174             return decl_die(access_status, "translate", r);
    175         }
    176     }
    177
    178     /* Reset to the server default config prior to running map_to_storage
    179      */
    180     r->per_dir_config = r->server->lookup_defaults;

request_rec.per_dir_configをリセットする。

 
    181
    182     if ((access_status = ap_run_map_to_storage(r))) {

map_to_storageフック関数を実行する。

 
    183         /* This request wasn't in storage (e.g. TRACE) */
    184         return access_status;
    185     }
    186
    187     /* Rerun the location walk, which overrides any map_to_storage config.
    188      */
    189     if ((access_status = ap_location_walk(r))) {

再度、<Location><LocationMatch>節のマージ処理。
2度めはキャッシュが効くと思われる。


    190         return access_status;
    191     }
    192     if ((access_status = ap_if_walk(r))) {

再度、<If><IfElse><Else>節のマージ処理。
こちらも2度めはキャッシュが効くと思われる。


    193         return access_status;
    194     }
    195
    196     d = ap_get_core_module_config(r->per_dir_config);
    197     if (d->log) {
    198         r->log = d->log;
    199     }

同じく、ログレベルの情報をコピー


    200
    201     if ((access_status = ap_run_post_perdir_config(r))) {

post_perdir_configフック関数の実行 このフック関数は2.2系では存在していない。


    202         return access_status;
    203     }
    204
    205     /* Only on the main request! */
    206     if (r->main == NULL) {

サブリクエストでない場合の処理


    207         if ((access_status = ap_run_header_parser(r))) {

header_parserフック関数の実行


    208             return access_status;
    209         }
    210     }
    211
    212     /* Skip authn/authz if the parent or prior request passed the authn/authz,
    213      * and that configuration didn't change (this requires optimized _walk()
    214      * functions in map_to_storage that use the same merge results given
    215      * identical input.)  If the config changes, we must re-auth.
    216      */

内部リダイレクトされたリクエストやサブリクエストの処理中で、per_dir_configが元のリクエストのper_dir_configと同じだった場合、認証認可の処理を省略する(元のリクエストで設定されている認証の情報(userとap_auth_type)を引き継ぐ)。
設定が変わっていれば、再度認証認可処理を実行する必要がある。


    217     if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) {

こちらは内部リダイレクトの認証認可が省略される条件。


    218         r->user = r->prev->user;
    219         r->ap_auth_type = r->prev->ap_auth_type;
    220     }
    221     else if (r->main && (r->main->per_dir_config == r->per_dir_config)) {

こちらはサブリクエストの認証認可が省略される条件


    222         r->user = r->main->user;
    223         r->ap_auth_type = r->main->ap_auth_type;
    224     }
    225     else {

認証認可処理を行う。
mod_access_compatを使用している場合は、ap_satisfies()がSatisfyディレクティブの指定を返す。
そうでない場合は、SATISFY_NOSPECが返る。


    226         switch (ap_satisfies(r)) {
    227         case SATISFY_ALL:
    228         case SATISFY_NOSPEC:

SATISFY_NOSPECなので、ここが、2.4系での標準的な処理となる。


    229             if ((access_status = ap_run_access_checker(r)) != OK) {

access_checkerフック関数を実行する。
見たところ、AllowMethodsディレクティブのチェック処理が行われている。
その他、mod_access_compatモジュールによるOrder/Deny/Allow処理がここで行われている。


    230                 return decl_die(access_status,
    231                                 "check access (with Satisfy All)", r);
    232             }
    233
    234             access_status = ap_run_access_checker_ex(r);

access_cheker_exフック関数を実行する。
access_cheker_exフック関数は 2.2系には存在していない。
ap_hook_access_checker_ex()関数を呼び出しているのは、ap_hook_check_access_ex()だけで、 ap_hook_check_access_ex()を経由してフック関数を登録するようになっている。
見たところ、ここでは、Require ipなどのユーザに依存しない認可処理を行っている。


    235             if (access_status == OK) {
    236                 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
    237                               "request authorized without authentication by "
    238                               "access_checker_ex hook: %s", r->uri);
    239             }
    240             else if (access_status != DECLINED) {
    241                 return decl_die(access_status, "check access", r);
    242             }
    243             else {

ユーザ認証が必要な場合の処理が行われる。


    244                 if ((access_status = ap_run_check_user_id(r)) != OK) {

check_user_idフック関数を実行する。
このフック関数も直接ap_hook_check_user_id関数で登録している場合と、
ap_hook_check_authn()関数を経由して登録している場合とがある。
見たところ、ここではAuthTypeに応じたユーザ認証処理が行われているようだ。


    245                     return decl_die(access_status, "check user", r);
    246                 }
    247                 if (r->user == NULL) {
    248                     /* don't let buggy authn module crash us in authz */
    249                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00027)
    250                                   "No authentication done but request not "
    251                                   "allowed without authentication for %s. "
    252                                   "Authentication not configured?",
    253                                   r->uri);
    254                     access_status = HTTP_INTERNAL_SERVER_ERROR;
    255                     return decl_die(access_status, "check user", r);
    256                 }
    257                 if ((access_status = ap_run_auth_checker(r)) != OK) {

auth_checkerフック関数を実行する。
ap_hook_auth_checker()を直接利用している個所は見当たらない(mod_luaだけ)。 ap_hook_check_authz()経由で登録している。
見たところ、ここではRequireに応じた認可処理が行われている。


    258                     return decl_die(access_status, "check authorization", r);
    259                 }
    260             }
    261             break;
    262         case SATISFY_ANY:
    263             if ((access_status = ap_run_access_checker(r)) == OK) {
    264                 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
    265                               "request authorized without authentication by "
    266                               "access_checker hook and 'Satisfy any': %s",
    267                               r->uri);
    268                 break;
    269             }
    270
    271             access_status = ap_run_access_checker_ex(r);
    272             if (access_status == OK) {
    273                 ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
    274                               "request authorized without authentication by "
    275                               "access_checker_ex hook: %s", r->uri);
    276             }
    277             else if (access_status != DECLINED) {
    278                 return decl_die(access_status, "check access", r);
    279             }
    280             else {
    281                 if ((access_status = ap_run_check_user_id(r)) != OK) {
    282                     return decl_die(access_status, "check user", r);
    283                 }
    284                 if (r->user == NULL) {
    285                     /* don't let buggy authn module crash us in authz */
    286                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00028)
    287                                   "No authentication done but request not "
    288                                   "allowed without authentication for %s. "
    289                                   "Authentication not configured?",
    290                                   r->uri);
    291                     access_status = HTTP_INTERNAL_SERVER_ERROR;
    292                     return decl_die(access_status, "check user", r);
    293                 }
    294                 if ((access_status = ap_run_auth_checker(r)) != OK) {
    295                     return decl_die(access_status, "check authorization", r);
    296                 }
    297             }
    298             break;
    299         }
    300     }
    301     /* XXX Must make certain the ap_run_type_checker short circuits mime
    302      * in mod-proxy for r->proxyreq && r->parsed_uri.scheme
    303      *                              && !strcmp(r->parsed_uri.scheme, "http")
    304      */
    305     if ((access_status = ap_run_type_checker(r)) != OK) {

type_chekerフック関数を実行する。


    306         return decl_die(access_status, "find types", r);
    307     }
    308
    309     if ((access_status = ap_run_fixups(r)) != OK) {

fixupsフック関数を実行する。


    310         ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "fixups hook gave %d: %s",
    311                       access_status, r->uri);
    312         return access_status;
    313     }
    314
    315     return OK;
    316 }

これで、ap_process_request_internalは終わる。