2015年1月26日月曜日

input/output filter: ローカルファイルを返す場合の構成

最小限の機能によるリクエスト処理として、/index.htmlにアクセスした場合にリクエストとレスポンスがたどる入出力フィルタの構成をみる。

(1)フィルタ構成の確認

ここではgdbでリクエスト処理を追って確認した。
(他にいい方法があるだろうか)

入力フィルタの確認のため、ap_get_brigadeにbreakpointを設定する。

(gdb) break ap_get_brigade
Breakpoint 1 at 0x4320b0: file util_filter.c, line 552.

出力フィルタの確認のため、ap_pass_brigadeにもbreakpointを設定する。

(gdb) break ap_pass_brigade
Breakpoint 2 at 0x4320d0: file util_filter.c, line 567.

省力化のため、backtraceと、フィルタ情報を出力して処理を継続するマクロを用意しておいた。

(gdb) define pinfo
Type commands for definition of "pinfo".
End with a line saying just "end".
>bt
>print *next->frec
>conti
>end

そして、curlコマンドで/index.htmlへのリクエストを発行した。

(1.1) read_request_line

  • リクエスト行の読み込み処理
  • 入力フィルタ
    • CORE_INフィルタ(COREモジュール)
      • AP_MODE_GETLINEモード
      • APR_BLOCK_READ読込モード

Breakpoint 1, ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
552         if (next) {
(gdb) pinfo
#0  ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
#1  0x0000000000435ef2 in ap_rgetline_core (s=0x7fa9ac00a9e0, n=8192, read=0x7fa9ba00cd10, r=0x7fa9ac00a9b0, fold=0, bb=0x7fa9ac00baa8) at protocol.c:229
#2  0x0000000000436d1e in read_request_line (conn=0x7fa9b400dbe0) at protocol.c:590
#3  ap_read_request (conn=0x7fa9b400dbe0) at protocol.c:956
#4  0x000000000045cff9 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:135
#5  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#6  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#7  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#8  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#9  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#10 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$37 = {name = 0x1370420 "core_in", filter_func = {out_func = 0x446790 , in_func = 0x446790 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.2) ap_get_mime_headers_core(1)

  • リクエストヘッダ行読み込み処理。
    • 1行ごとの処理なので、ヘッダ行の数だけ処理が行われる。
  • 入力フィルタ:
    • CORE_INフィルタ(COREモジュール)
      • AP_MODE_GETLINE
      • APR_BLOCK_READ

Breakpoint 1, ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
#1  0x0000000000435ef2 in ap_rgetline_core (s=0x7fa9ba00cc98, n=8192, read=0x7fa9ba00cc90, r=0x7fa9ac00a9b0, fold=0, bb=0x7fa9ac00baa8) at protocol.c:229
#2  0x00000000004362e7 in ap_get_mime_headers_core (r=0x7fa9ac00a9b0, bb=0x7fa9ac00baa8) at protocol.c:713
#3  0x000000000043738b in ap_read_request (conn=0x7fa9b400dbe0) at protocol.c:1002
#4  0x000000000045cff9 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:135
#5  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#6  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#7  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#8  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#9  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#10 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$38 = {name = 0x1370420 "core_in", filter_func = {out_func = 0x446790 , in_func = 0x446790 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.3) ap_get_mime_headers_core(2)

  • リクエストヘッダ行読み込み処理。
    • 1行ごとの処理なので、ヘッダ行の数だけ処理が行われる。
  • 入力フィルタ:
    • CORE_INフィルタ(COREモジュール)
      • AP_MODE_GETLINE
      • APR_BLOCK_READ

Breakpoint 1, ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
#1  0x0000000000435ef2 in ap_rgetline_core (s=0x7fa9ba00cc98, n=8192, read=0x7fa9ba00cc90, r=0x7fa9ac00a9b0, fold=0, bb=0x7fa9ac00baa8) at protocol.c:229
#2  0x00000000004362e7 in ap_get_mime_headers_core (r=0x7fa9ac00a9b0, bb=0x7fa9ac00baa8) at protocol.c:713
#3  0x000000000043738b in ap_read_request (conn=0x7fa9b400dbe0) at protocol.c:1002
#4  0x000000000045cff9 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:135
#5  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#6  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#7  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#8  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#9  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#10 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$39 = {name = 0x1370420 "core_in", filter_func = {out_func = 0x446790 , in_func = 0x446790 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.4) ap_get_mime_headers_core(3)


  • リクエストヘッダ行読み込み処理。
    • 1行ごとの処理なので、ヘッダ行の数だけ処理が行われる。
  • 入力フィルタ:
    • CORE_INフィルタ(COREモジュール)
      • AP_MODE_GETLINE
      • APR_BLOCK_READ


Breakpoint 1, ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
#1  0x0000000000435ef2 in ap_rgetline_core (s=0x7fa9ba00cc98, n=8192, read=0x7fa9ba00cc90, r=0x7fa9ac00a9b0, fold=0, bb=0x7fa9ac00baa8) at protocol.c:229
#2  0x00000000004362e7 in ap_get_mime_headers_core (r=0x7fa9ac00a9b0, bb=0x7fa9ac00baa8) at protocol.c:713
#3  0x000000000043738b in ap_read_request (conn=0x7fa9b400dbe0) at protocol.c:1002
#4  0x000000000045cff9 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:135
#5  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#6  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#7  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#8  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#9  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#10 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$40 = {name = 0x1370420 "core_in", filter_func = {out_func = 0x446790 , in_func = 0x446790 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.5) ap_get_mime_headers_core(4)


  • リクエストヘッダ行読み込み処理。
    • 1行ごとの処理なので、ヘッダ行の数だけ処理が行われる。
    • 最後は空行になる。
  • 入力フィルタ:
    • CORE_INフィルタ(COREモジュール)
      • AP_MODE_GETLINE
      • APR_BLOCK_READ


Breakpoint 1, ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac00baa8, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
#1  0x0000000000435ef2 in ap_rgetline_core (s=0x7fa9ba00cc98, n=8192, read=0x7fa9ba00cc90, r=0x7fa9ac00a9b0, fold=0, bb=0x7fa9ac00baa8) at protocol.c:229
#2  0x00000000004362e7 in ap_get_mime_headers_core (r=0x7fa9ac00a9b0, bb=0x7fa9ac00baa8) at protocol.c:713
#3  0x000000000043738b in ap_read_request (conn=0x7fa9b400dbe0) at protocol.c:1002
#4  0x000000000045cff9 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:135
#5  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#6  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#7  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#8  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#9  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#10 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$41 = {name = 0x1370420 "core_in", filter_func = {out_func = 0x446790 , in_func = 0x446790 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.6) default_handler: ap_discard_request_body


  • リクエストボディの読み捨て
  • 入力フィルタ:
    • HTTP_INフィルタ(HTTPモジュール)
      • AP_MODE_READBYTES
      • APR_BLOCK_READ


Breakpoint 1, ap_get_brigade (next=0x7fa9ac00bc48, bb=0x7fa9ac00c378, mode=AP_MODE_READBYTES, block=APR_BLOCK_READ, readbytes=8192) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9ac00bc48, bb=0x7fa9ac00c378, mode=AP_MODE_READBYTES, block=APR_BLOCK_READ, readbytes=8192) at util_filter.c:552
#1  0x00000000004616e0 in ap_discard_request_body (r=0x7fa9ac00a9b0) at http_filters.c:1418
#2  0x000000000043d915 in default_handler (r=0x7fa9ac00a9b0) at core.c:4277
#3  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#4  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#5  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#6  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#7  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#8  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#9  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#10 worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#11 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#12 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$42 = {name = 0x1396b98 "http_in", filter_func = {out_func = 0x463410 , in_func = 0x463410 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_PROTOCOL, debug = 0, proto_flags = 0}

(1.7) default_handler


  • 出力フィルタ:
    • byterange出力フィルタ(HTTPモジュール)


Breakpoint 2, ap_pass_brigade (next=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at util_filter.c:567
#1  0x000000000043de27 in default_handler (r=0x7fa9ac00a9b0) at core.c:4369
#2  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#3  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#4  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#5  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#6  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#7  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#8  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#9  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#10 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#11 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$43 = {name = 0x1397878 "byterange", filter_func = {out_func = 0x464c80 , in_func = 0x464c80 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_PROTOCOL, debug = 0, proto_flags = 0}

(1.8) default_handler: ap_byterange_filter


  • 出力フィルタ:
    • content_length出力フィルタ(COREモジュール)


Breakpoint 2, ap_pass_brigade (next=0x7fa9ac00ba30, bb=0x7fa9ac00c598) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9ac00ba30, bb=0x7fa9ac00c598) at util_filter.c:567
#1  0x0000000000464dc2 in ap_byterange_filter (f=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at byterange_filter.c:496
#2  0x000000000043de27 in default_handler (r=0x7fa9ac00a9b0) at core.c:4369
#3  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#4  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#5  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#6  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#7  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#8  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#9  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#10 worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#11 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#12 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$44 = {name = 0x1370740 "content_length", filter_func = {out_func = 0x437df0 , in_func = 0x437df0 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_PROTOCOL, debug = 0, proto_flags = 0}

(1.9) default_handler: ap_content_length_filter


  • 出力フィルタ:
    • http_header出力フィルタ(HTTPモジュール)


Breakpoint 2, ap_pass_brigade (next=0x7fa9ac00ba58, bb=0x7fa9ac00c598) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9ac00ba58, bb=0x7fa9ac00c598) at util_filter.c:567
#1  0x0000000000437f7a in ap_content_length_filter (f=0x7fa9ac00ba30, b=0x7fa9ac00c598) at protocol.c:1421
#2  0x0000000000464dc2 in ap_byterange_filter (f=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at byterange_filter.c:496
#3  0x000000000043de27 in default_handler (r=0x7fa9ac00a9b0) at core.c:4369
#4  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#5  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#6  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#7  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#8  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#9  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#10 0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#11 worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#12 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#13 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$45 = {name = 0x1396e60 "http_header", filter_func = {out_func = 0x462450 , in_func = 0x462450 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_PROTOCOL, debug = 0, proto_flags = 0}

(1.10) default_handler: ap_http_header_filter


  • レスポンスヘッダ処理
  • 出力フィルタ:
    • http_outerror出力フィルタ(HTTPモジュール)


Breakpoint 2, ap_pass_brigade (next=0x7fa9ac00ba80, bb=0x7fa9ac00c690) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9ac00ba80, bb=0x7fa9ac00c690) at util_filter.c:567
#1  0x000000000046270d in ap_http_header_filter (f=, b=) at http_filters.c:1356
#2  0x0000000000437f7a in ap_content_length_filter (f=0x7fa9ac00ba30, b=0x7fa9ac00c598) at protocol.c:1421
#3  0x0000000000464dc2 in ap_byterange_filter (f=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at byterange_filter.c:496
#4  0x000000000043de27 in default_handler (r=0x7fa9ac00a9b0) at core.c:4369
#5  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#6  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#7  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#8  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#9  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#10 0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#11 0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#12 worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#13 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#14 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$46 = {name = 0x1397550 "http_outerror", filter_func = {out_func = 0x460d10 , in_func = 0x460d10 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_PROTOCOL, debug = 0, proto_flags = 0}

(1.11) default_handler: ap_http_header_filter


  • レスポンスヘッダ処理
  • 出力フィルタ:
    • core出力フィルタ


Breakpoint 2, ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9ac00c690) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9ac00c690) at util_filter.c:567
#1  0x000000000046270d in ap_http_header_filter (f=, b=) at http_filters.c:1356
#2  0x0000000000437f7a in ap_content_length_filter (f=0x7fa9ac00ba30, b=0x7fa9ac00c598) at protocol.c:1421
#3  0x0000000000464dc2 in ap_byterange_filter (f=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at byterange_filter.c:496
#4  0x000000000043de27 in default_handler (r=0x7fa9ac00a9b0) at core.c:4369
#5  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#6  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#7  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#8  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#9  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#10 0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#11 0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#12 worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#13 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#14 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$47 = {name = 0x1370c78 "core", filter_func = {out_func = 0x445f90 , in_func = 0x445f90 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.12) default_handler: ap_http_header_filter


  • レスポンスボディ処理
  • 出力フィルタ:
    • http_outerror出力フィルタ(HTTPモジュール)


Breakpoint 2, ap_pass_brigade (next=0x7fa9ac00ba80, bb=0x7fa9ac00c598) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9ac00ba80, bb=0x7fa9ac00c598) at util_filter.c:567
#1  0x00000000004624f3 in ap_http_header_filter (f=, b=) at http_filters.c:1379
#2  0x0000000000437f7a in ap_content_length_filter (f=0x7fa9ac00ba30, b=0x7fa9ac00c598) at protocol.c:1421
#3  0x0000000000464dc2 in ap_byterange_filter (f=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at byterange_filter.c:496
#4  0x000000000043de27 in default_handler (r=0x7fa9ac00a9b0) at core.c:4369
#5  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#6  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#7  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#8  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#9  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#10 0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#11 0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#12 worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#13 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#14 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$48 = {name = 0x1397550 "http_outerror", filter_func = {out_func = 0x460d10 , in_func = 0x460d10 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_PROTOCOL, debug = 0, proto_flags = 0}

(1.13) default_handler: ap_http_header_filter


  • レスポンスボディ処理
  • 出力フィルタ:
    • core出力フィルタ(COREモジュール)


Breakpoint 2, ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9ac00c598) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9ac00c598) at util_filter.c:567
#1  0x00000000004624f3 in ap_http_header_filter (f=, b=) at http_filters.c:1379
#2  0x0000000000437f7a in ap_content_length_filter (f=0x7fa9ac00ba30, b=0x7fa9ac00c598) at protocol.c:1421
#3  0x0000000000464dc2 in ap_byterange_filter (f=0x7fa9ac00ba08, bb=0x7fa9ac00c598) at byterange_filter.c:496
#4  0x000000000043de27 in default_handler (r=0x7fa9ac00a9b0) at core.c:4369
#5  0x000000000044a1b0 in ap_run_handler (r=0x7fa9ac00a9b0) at config.c:170
#6  0x000000000044e33e in ap_invoke_handler (r=0x7fa9ac00a9b0) at config.c:439
#7  0x000000000046099a in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:317
#8  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#9  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#10 0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#11 0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#12 worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#13 0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#14 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$49 = {name = 0x1370c78 "core", filter_func = {out_func = 0x445f90 , in_func = 0x445f90 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.14) ap_finalize_request_protocol


  • リクエストボディ読み捨て
  • 入力フィルタ:
    • http_inフィルタ
      • AP_MODE_READBYTES
      • APR_BLOCK_READ


Breakpoint 1, ap_get_brigade (next=0x7fa9ac00bc48, bb=0x7fa9ac00c838, mode=AP_MODE_READBYTES, block=APR_BLOCK_READ, readbytes=8192) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9ac00bc48, bb=0x7fa9ac00c838, mode=AP_MODE_READBYTES, block=APR_BLOCK_READ, readbytes=8192) at util_filter.c:552
#1  0x00000000004616e0 in ap_discard_request_body (r=0x7fa9ac00a9b0) at http_filters.c:1418
#2  0x0000000000435879 in ap_finalize_request_protocol (r=0x7fa9ac00a9b0) at protocol.c:1235
#3  0x0000000000460808 in ap_process_async_request (r=0x7fa9ac00a9b0) at http_request.c:346
#4  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#5  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#6  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#7  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#8  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#9  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#10 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$50 = {name = 0x1396b98 "http_in", filter_func = {out_func = 0x463410 , in_func = 0x463410 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_PROTOCOL, debug = 0, proto_flags = 0}

(1.15) ap_process_request_after_handler


  • 出力フィルタ:
    • coreフィルタ


Breakpoint 2, ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9b400e0b0) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9b400e0b0) at util_filter.c:567
#1  0x000000000045fa8d in ap_process_request_after_handler (r=0x7fa9ac00a9b0) at http_request.c:256
#2  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#3  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#4  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#5  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#6  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#7  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#8  0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$51 = {name = 0x1370c78 "core", filter_func = {out_func = 0x445f90 , in_func = 0x445f90 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.16) ap_process_request_after_handler


  • 次リクエスト到着チェック
  • 入力フィルタ:
    • core_inフィルタ
      • AP_MODE_SPECULATIVEモード
      • APR_NONBLOCK_READ


Breakpoint 1, ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9b400e0f0, mode=AP_MODE_SPECULATIVE, block=APR_NONBLOCK_READ, readbytes=1) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9b400e0f0, mode=AP_MODE_SPECULATIVE, block=APR_NONBLOCK_READ, readbytes=1) at util_filter.c:552
#1  0x000000000045fad6 in check_pipeline (r=0x7fa9ac00a9b0) at http_request.c:225
#2  ap_process_request_after_handler (r=0x7fa9ac00a9b0) at http_request.c:265
#3  0x000000000045d070 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:143
#4  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#5  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#6  0x00007fa9baa557e1 in process_socket (thd=0x13c5fe0, dummy=) at event.c:970
#7  worker_thread (thd=0x13c5fe0, dummy=) at event.c:1815
#8  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#9  0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$52 = {name = 0x1370420 "core_in", filter_func = {out_func = 0x446790 , in_func = 0x446790 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.17) read_request_line


  • リクエスト行読み込み処理
    • クライアントの終了を検知する
  • 入力フィルタ:
    • core_inフィルタ
      • AP_MODE_GETLINE
      • APR_BLOCK_READ


Breakpoint 1, ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac003a68, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
552         if (next) {
(gdb)
#0  ap_get_brigade (next=0x7fa9b400df20, bb=0x7fa9ac003a68, mode=AP_MODE_GETLINE, block=APR_BLOCK_READ, readbytes=0) at util_filter.c:552
#1  0x0000000000435ef2 in ap_rgetline_core (s=0x7fa9ac0029a0, n=8192, read=0x7fa9b3ffed10, r=0x7fa9ac002970, fold=0, bb=0x7fa9ac003a68) at protocol.c:229
#2  0x0000000000436d1e in read_request_line (conn=0x7fa9b400dbe0) at protocol.c:590
#3  ap_read_request (conn=0x7fa9b400dbe0) at protocol.c:956
#4  0x000000000045cff9 in ap_process_http_async_connection (c=0x7fa9b400dbe0) at http_core.c:135
#5  ap_process_http_connection (c=0x7fa9b400dbe0) at http_core.c:228
#6  0x0000000000454b60 in ap_run_process_connection (c=0x7fa9b400dbe0) at connection.c:41
#7  0x00007fa9baa557e1 in process_socket (thd=0x13c6070, dummy=) at event.c:970
#8  worker_thread (thd=0x13c6070, dummy=) at event.c:1815
#9  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#10 0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$53 = {name = 0x1370420 "core_in", filter_func = {out_func = 0x446790 , in_func = 0x446790 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}

(1.18) ap_start_lingering_close


  • EOF後の処理
  • 出力フィルタ:
    • coreフィルタ


Breakpoint 2, ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9b400e138) at util_filter.c:567
567         if (next) {
(gdb)
#0  ap_pass_brigade (next=0x7fa9b400df48, bb=0x7fa9b400e138) at util_filter.c:567
#1  0x0000000000454d38 in ap_start_lingering_close (c=0x7fa9b400dbe0) at connection.c:121
#2  0x00007fa9baa55541 in start_lingering_close_blocking (thd=0x13c6070, dummy=) at event.c:823
#3  process_socket (thd=0x13c6070, dummy=) at event.c:1025
#4  worker_thread (thd=0x13c6070, dummy=) at event.c:1815
#5  0x0000003f53e079d1 in start_thread () from /lib64/libpthread.so.0
#6  0x0000003f53ae8b6d in clone () from /lib64/libc.so.6

$54 = {name = 0x1370c78 "core", filter_func = {out_func = 0x445f90 , in_func = 0x445f90 }, filter_init_func = 0, next = 0x0, providers = 0x0, ftype = AP_FTYPE_NETWORK, debug = 0, proto_flags = 0}


(2)フィルタの追加処理の確認

もともとのCORE/CORE_INフィルタの設定箇所はすでに
リクエスト処理の流れ: process_socket に戻る で見たが、下記のpre_connectionフック関数で行われている。

(httpd-2.4.9/server/core.c)

   4637 static int core_pre_connection(conn_rec *c, void *csd)
   4638 {

   4676     ap_set_core_module_config(net->c->conn_config, csd);
   4677     ap_add_input_filter_handle(ap_core_input_filter_handle, net, NULL, net->c);


CORE入力フィルタの追加

   4678     ap_add_output_filter_handle(ap_core_output_filter_handle, net, NULL, net->c);

CORE出力フィルタの追加

   4679     return DONE;
   4680 }

そして、HTTPリクエストボディの処理に使われていたHTTP_IN入力フィルタは
HTTPリクエストヘッダ処理後に、ap_read_request()関数内で追加される。
リクエスト処理の流れ: ap_read_request  参照。

(httpd-2.4.9/server/protocol.c)

    895 request_rec *ap_read_request(conn_rec *conn)
    896 {
     :

   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
   1105     ap_add_input_filter_handle(ap_http_input_filter_handle,
   1106                                NULL, r, r->connection);
   1107

次はHTTPコアモジュールが提供しているcreate_requestフック関数だ。
このフック関数を実行する処理 ap_run_create_request()は
リクエスト処理の流れ: ap_read_request で見た。
read_request_line()でリクエスト行を読み込む前に実行される。

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

    235 static int http_create_request(request_rec *r)
    236 {
    237     if (!r->main && !r->prev) {
    238         ap_add_output_filter_handle(ap_byterange_filter_handle,
    239                                     NULL, r, r->connection);

BYTERANGE出力フィルタの追加

    240         ap_add_output_filter_handle(ap_content_length_filter_handle,
    241                                     NULL, r, r->connection);

CONTENT_LENGTH出力フィルタの追加

    242         ap_add_output_filter_handle(ap_http_header_filter_handle,
    243                                     NULL, r, r->connection);

HTTP_HEADER出力フィルタの追加

    244         ap_add_output_filter_handle(ap_http_outerror_filter_handle,
    245                                     NULL, r, r->connection);

HTTP_OUTERROR出力フィルタの追加

    246     }
    247
    248     return OK;
    249 }


(3)フィルタの階層構成図

まとめると、次のような入出力フィルタがセットされていたことになる。


今回はここまで

2015年1月19日月曜日

CORE出力フィルタ: ソケット送信処理(3) sendfile系

CORE出力フィルタ: ソケット送信処理(1) の続きで今回はsendfile()系の
(1) sendfile_nonblocking()
とそこから呼ばれるAPRライブラリ関数の
(2) apr_socket_sendfile()
を見る

(1)sendfile_nonblocking関数

FILEデータバケットをapr_socket_sendfile()を使って送信するための関数となる。
apr_socket_sendfile()は本関数で1度実行される。
実行前に、ソケットのタイムアウト設定を0にし、実行後に元の値に戻す処理が行われている。

apr_socket_sendfile()実行後、ファイル全体の送信ができたか、送信サイズをファイルサイズを比較し、一致した場合には、送信されたFILEバケットをbucket brigadeから取り外し、削除(destroy)する。
未送信データが残っていた場合には、FILEバケットを送信済みデータ部分と未送信データ部分に
bucket分割し(split)、送信済み分のFILEバケットを取り外して、削除(destroy)する。
送信済みデータサイズは引数 *cumulative_bytes_writtenに加えられる。

    834 static apr_status_t sendfile_nonblocking(apr_socket_t *s,
    835                                          apr_bucket *bucket,
    836                                          apr_size_t *cumulative_bytes_written,
    837                                          conn_rec *c)
    838 {
    839     apr_status_t rv = APR_SUCCESS;
    840     apr_bucket_file *file_bucket;
    841     apr_file_t *fd;
    842     apr_size_t file_length;
    843     apr_off_t file_offset;
    844     apr_size_t bytes_written = 0;
    845
    846     if (!APR_BUCKET_IS_FILE(bucket)) {

FILEデータバケットでなければ、エラーで返る。

    847         ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server, APLOGNO(00006)
    848                      "core_filter: sendfile_nonblocking: "
    849                      "this should never happen");
    850         return APR_EGENERAL;
    851     }
    852     file_bucket = (apr_bucket_file *)(bucket->data);

FILEデータバケットのタイプ依存情報)apr_bucket_file)を取得する

    853     fd = file_bucket->fd;

apr_bucket_fileの持つファイル情報(apt_file_t)を取得する

    854     file_length = bucket->length;
    855     file_offset = bucket->start;

ファイルの参照する領域(開始位置とサイズ)をbucket情報から取得する

    856
    857     if (bytes_written < file_length) {

ここで、apr_socket_sendfile()が実行される。
実行前にタイムアウトが0にされるのは非ブロック処理とするため。

    858         apr_size_t n = file_length - bytes_written;
    859         apr_status_t arv;
    860         apr_interval_time_t old_timeout;
    861
    862         arv = apr_socket_timeout_get(s, &old_timeout);

現在セットされているタイムアウトの設定値をold_timeoutに退避する。

    863         if (arv != APR_SUCCESS) {
    864             return arv;
    865         }
    866         arv = apr_socket_timeout_set(s, 0);

タイムアウトの設定を0にする。

    867         if (arv != APR_SUCCESS) {
    868             return arv;
    869         }
    870         rv = apr_socket_sendfile(s, fd, NULL, &file_offset, &n, 0);

apr_socket_sendfile()を実行する。

    871         if (rv == APR_SUCCESS) {

成功すると、書込み済みバイト数を格納するbytes_writtenにnを加え、
開始位置であるfile_offsetがnバイト進められる。
しかし、この値はこの関数内では使われないので、この変更は意味が分からない。


    872             bytes_written += n;
    873             file_offset += n;
    874         }
    875         arv = apr_socket_timeout_set(s, old_timeout);

タイムアウトの設定をこの処理実行前の値に戻す。

    876         if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
    877             rv = arv;
    878         }
    879     }
    880     if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {

ap__logio_add_bytes_outはlogioモジュールが提供するオプション関数で、
ここでは、ログ書式%Oのための出力バイト数を集計している。

    881         ap__logio_add_bytes_out(c, bytes_written);
    882     }
    883     *cumulative_bytes_written += bytes_written;

呼び出し元に返却用に、出力済みバイト数を*cumulative_bytes_writtenに加える。

    884     if ((bytes_written < file_length) && (bytes_written > 0)) {

出力したバイト数が、送信する予定だったファイルサイズより小さい場合

    885         apr_bucket_split(bucket, bytes_written);

送信済みの範囲と未送信の範囲にFILEデータバケットを分割(split)する。
apr_file_t 情報と FILEデータバケット (2.2)split関数 参照。

    886         APR_BUCKET_REMOVE(bucket);

送信済みのFILEデータバケットをbucket brigadeから外す。

    887         apr_bucket_destroy(bucket);

送信済みのFILEデータバケットを破棄(destroy)する。
apr_file_t 情報と FILEデータバケット  (2.3)destroy関数参照。

    888     }
    889     else if (bytes_written == file_length) {

出力したバイト数と送信する予定だったファイルサイズが一致した場合

    890         APR_BUCKET_REMOVE(bucket);

送信済みのFILEデータバケットをbucket brigadeから外す。

    891         apr_bucket_destroy(bucket);

送信済みのFILEデータバケットを破棄(destroy)する。
apr_file_t 情報と FILEデータバケット (2.3)destroy関数参照。

    892     }
    893     return rv;
    894 }


(2)apr_socket_sendfile

ちなみに、64bit環境で確認している。
ヘッダ・フッターといった独自のデータ構造(apr_hdtr_t)を扱っており、
コード上、sendfile()の実行前後でこのデータが送信されているようだが、
sendfile_nonblocking()から呼ばれている場合には、この情報はNULL固定で、利用されていない。

(apr/network_io/unix/sendrecv.c)

    257 apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
    258                                  apr_hdtr_t *hdtr, apr_off_t *offset,
    259                                  apr_size_t *len, apr_int32_t flags)
    260 {
    261     int rv, nbytes = 0, total_hdrbytes, i;
    262     apr_status_t arv;
    263
    264 #if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64)
     :
    268 #elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4
     :

    279 #else
    280     off_t off = *offset;
    281
    282     /* Multiple reports have shown sendfile failing with EINVAL if
    283      * passed a >=2Gb count value on some 64-bit kernels.  It won't
    284      * noticably hurt performance to limit each call to <2Gb at a
    285      * time, so avoid that issue here: */

一部の64bitカーネルにおいて、 2GB以上の値を渡すと、sendfileが失敗するという報告が複数ある。 一度の呼び出しでの上限を2GB未満に制限しても性能に悪影響は少ないだろうと考えられるので、 ここでその問題を回避している

    286     if (sizeof(off_t) == 8 && *len > INT_MAX) {

INT_MAXは2GB-1(0x7fffffff)だ。 一度に送信するデータ長の上限をINT_MAXに制限している。

    287         *len = INT_MAX;
    288     }
    289 #endif
    290

ヘッダを送信する。
ファイル送信の前に送信するデータバケットが存在しているケースだ。

ただし、sendfile_nonblocking()から呼ばれている場合、hdtr==NULLだ。
2.4.9のソースをgrepする限り、他の箇所で呼ばれている気配はない。
2.2.27のソースをgrepすると、値が指定されているので、2.4で使わなくなったのか。

    291     if (!hdtr) {

この経路に入る。

    292         hdtr = &no_hdtr;


    252 /* Define a structure to pass in when we have a NULL header value */
    253 static apr_hdtr_t no_hdtr;


    293     }
    294
    295     if (hdtr->numheaders > 0) {

この経路には入らない。

    296         apr_size_t hdrbytes;
    297
    298         /* cork before writing headers */
    299         rv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 1);

TCP_CORKオプションをONにする。
オプションの説明を man tcpからコピペする。

セットされると、 partialフレームを送信しない。
このオプションが解除されると、キューイングされた partial
フレームが送られる。これは sendfile(2) を呼ぶ前にヘッダを前置したり、
スループットを最適化したい場合に便利である。現在の実装では、 TCP_CORK
で出力を抑えることができる時間の上限は 200 ミリ秒である。
この上限に達すると、キューイングされたデータは自動的に送信される。 Linux
2.5.71 以降においてのみ、このオプションを TCP_NODELAY
と同時に用いることができる。
移植性の必要なプログラムではこのオプションを用いるべきではない。

つまり、TCP送信が少し遅延するということだ。sendfileで送信されるデータと このヘッダデータが別個に送信されることを回避できる。

    300         if (rv != APR_SUCCESS) {
    301             return rv;
    302         }
    303
    304         /* Now write the headers */
    305         arv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders,
    306                                &hdrbytes);
    307         if (arv != APR_SUCCESS) {
    308             *len = 0;
    309             return errno;
    310         }
    311         nbytes += hdrbytes;
    312
    313         /* If this was a partial write and we aren't doing timeouts,
    314          * return now with the partial byte count; this is a non-blocking
    315          * socket.
    316          */
    317         total_hdrbytes = 0;
    318         for (i = 0; i < hdtr->numheaders; i++) {
    319             total_hdrbytes += hdtr->headers[i].iov_len;
    320         }
    321         if (hdrbytes < total_hdrbytes) {

ヘッダの一部だけ送信して終了した場合には、 sendfileまで行わないで、returnする。

    322             *len = hdrbytes;
    323             return apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);

TCP_CORKをOFFにしている。 ヘッダがキューイングされていた場合には、これで送信される。

    324         }
    325     }
    326

sendfileを実行する ここの処理の流れは、sendfileをwritevに置き換えれば、 apr_socket_sendvと同様


    327     if (sock->options & APR_INCOMPLETE_WRITE) {
    328         sock->options &= ~APR_INCOMPLETE_WRITE;
    329         goto do_select;
    330     }
    331
    332     do {
    333         rv = sendfile(sock->socketdes,    /* socket */
    334                       file->filedes, /* open file descriptor of the file to be sent */
    335                       &off,    /* where in the file to start */
    336                       *len);   /* number of bytes to send */
    337     } while (rv == -1 && errno == EINTR);
    338
    339     while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
    340                       && (sock->timeout > 0)) {
    341 do_select:
    342         arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
    343         if (arv != APR_SUCCESS) {
    344             *len = 0;
    345             return arv;
    346         }
    347         else {
    348             do {
    349                 rv = sendfile(sock->socketdes,    /* socket */
    350                               file->filedes, /* open file descriptor of the file to be sent */
    351                               &off,    /* where in the file to start */
    352                               *len);    /* number of bytes to send */
    353             } while (rv == -1 && errno == EINTR);
    354         }
    355     }
    356
    357     if (rv == -1) {
    358         *len = nbytes;
    359         rv = errno;
    360         apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
    361         return rv;
    362     }
    363
    364     nbytes += rv;
    365
    366     if (rv < *len) {
    367         *len = nbytes;
    368         arv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
    369         if (rv > 0) {
    370
    371             /* If this was a partial write, return now with the
    372              * partial byte count;  this is a non-blocking socket.
    373              */
    374
    375             if (sock->timeout > 0) {
    376                 sock->options |= APR_INCOMPLETE_WRITE;
    377             }
    378             return arv;
    379         }
    380         else {
    381             /* If the file got smaller mid-request, eventually the offset
    382              * becomes equal to the new file size and the kernel returns 0.
    383              * Make this an error so the caller knows to log something and
    384              * exit.
    385              */
    386             return APR_EOF;
    387         }
    388     }
    389
    390     /* Now write the footers */

フッターを送信する、とある。 ファイル送信の後に送信するデータバケットが存在しているケースだ。 ただし、上で見たとおり、sendfile_nonblocking()から呼ばれたケースでは、hdtrは空なので、 ここの処理も行われない。

    391     if (hdtr->numtrailers > 0) {

sendfile_nonblocking()からでは、この経路には入らない。 処理内容は、ヘッダの時と同様だ。

    392         apr_size_t trbytes;
    393         arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
    394                                &trbytes);
    395         nbytes += trbytes;
    396         if (arv != APR_SUCCESS) {
    397             *len = nbytes;
    398             rv = errno;
    399             apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
    400             return rv;
    401         }
    402     }
    403
    404     apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
    405
    406     (*len) = nbytes;
    407     return rv < 0 ? errno : APR_SUCCESS;
    408 }

これで今回はおしまい。

2015年1月8日木曜日

CORE出力フィルタ: ソケット送信処理(2) writev系

CORE出力フィルタ: ソケット送信処理(1) の続きでwritev()系の
(1) writev_nonblocking()
とそこから呼ばれるAPRライブラリ関数の
(2) apr_socket_sendv()
を見る

(1)writev_nonblocking関数


    763 static apr_status_t writev_nonblocking(apr_socket_t *s,
    764                                        struct iovec *vec, apr_size_t nvec,
    765                                        apr_bucket_brigade *bb,
    766                                        apr_size_t *cumulative_bytes_written,
    767                                        conn_rec *c)

この関数呼び出し時に、
IOベクトル vec と IOベクトルと
bucket brigade bb内のデータバケットと対応付けられている。
対応付けられている IOベクトルとbucket brigadeの数は、 nvecで指定されている。

    768 {
    769     apr_status_t rv = APR_SUCCESS, arv;
    770     apr_size_t bytes_written = 0, bytes_to_write = 0;
    771     apr_size_t i, offset;
    772     apr_interval_time_t old_timeout;
    773
    774     arv = apr_socket_timeout_get(s, &old_timeout);

設定されているソケットタイムアウトの情報を取得する→old_timeout

    775     if (arv != APR_SUCCESS) {
    776         return arv;
    777     }
    778     arv = apr_socket_timeout_set(s, 0);

ソケットタイムアウトを0にする

    779     if (arv != APR_SUCCESS) {
    780         return arv;
    781     }
    782

bytes_to_write: 送信するデータサイズの計算。

    783     for (i = 0; i < nvec; i++) {
    784         bytes_to_write += vec[i].iov_len;
    785     }

offset: IOベクトルのインデックスをセットする。初期値が0。

    786     offset = 0;

bytes_written: 送信済データサイズ。初期値0。
以下のwhileループでは、
apr_socket_sendv()で送信処理を行い、
送信したデータバケットを削除する処理を
送信済みデータ(bytes_written)が送信データサイズ(bytes_to_write)になるまで繰り返している。

    787     while (bytes_written < bytes_to_write) {
    788         apr_size_t n = 0;
    789         rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
    790         if (n > 0) {

nバイトデータ送信実行時の経路

    791             bytes_written += n;

送信済みデータサイズをカウントアップする。

    792             for (i = offset; i < nvec; ) {

最初のvec[offset]は、789行目で送信されたIOベクトルの先頭。
処理の最初では、これはvec[0]。

    793                 apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
    794                 if (APR_BUCKET_IS_METADATA(bucket)) {

bucket brigadeのうち、メタデータバケットは無視してよい。
ここでbucket brigadeから切り離し、削除する。

    795                     APR_BUCKET_REMOVE(bucket);
    796                     apr_bucket_destroy(bucket);
    797                 }
    798                 else if (n >= vec[i].iov_len) {

IOベクトル vec の先頭要素のデータ長が、今回送信されたデータ長nと同じか小さい場合。
対応するbucketの持つデータはすべて送信済みなので、削除する。

    799                     APR_BUCKET_REMOVE(bucket);
    800                     apr_bucket_destroy(bucket);

次の送信済みデータを持つbucketおよび対応するIOベクトルを処理するため、offsetを+1する。

    801                     offset++;

残るデータ長を計算する。
このIOベクトル要素に格納されていたデータ長を送信済みデータ長から差し引く。
そして、792行目のforループ先頭に戻る。

    802                     n -= vec[i++].iov_len;
    803                 }
    804                 else {

IOベクトル vec の先頭要素のデータ長が、今回送信されたデータ長nより大きい場合。
この場合、処理対象のデータバケットに未送信のデータが残っている。

送信済みのデータ(nバイト)と未送信のデータとにバケットを切り離す。

    805                     apr_bucket_split(bucket, n);

送信済みのデータのバケット(元の変数bucketに残った方)を切り離し、削除する。

    806                     APR_BUCKET_REMOVE(bucket);
    807                     apr_bucket_destroy(bucket);

処理中のIOベクトルの長さと、データの先頭位置を調整する。

    808                     vec[i].iov_len -= n;
    809                     vec[i].iov_base = (char *) vec[i].iov_base + n;

これで、送信済みのnバイト分のデータバケットの削除が完了する。

    810                     break;
    811                 }
    812             }
    813         }
    814         if (rv != APR_SUCCESS) {
    815             break;
    816         }
    817     }
    818     if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
    819         ap__logio_add_bytes_out(c, bytes_written);

ap__logio_add_bytes_outはlogioモジュールが提供するオプション関数だ。
ログ書式%Oのための出力バイト数を集計する。

    820     }
    821     *cumulative_bytes_written += bytes_written;

送信済みデータサイズ(bytes_written)を引数の値に加算する。

    822
    823     arv = apr_socket_timeout_set(s, old_timeout);

ソケットタイムアウトを元に戻す(774行目に取得したold_timeout)。

    824     if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
    825         return arv;
    826     }
    827     else {
    828         return rv;
    829     }
    830 }


(2)apr_socket_sendv関数

この関数は、writev_nonblocking()から使用されている。
その際には、ソケット情報のtimeout(sock->timeout)を0に変更して使用している。
pollでの書込み可否判定と、writeに戻り値のEAGAIN/EWOULDBLOCKの両方をチェックして、全体としては、timeoutの時間は処理内で書き込み可能になるのを待つようになっている。

(apr-1.5.1/network_io/unix/sendrecv.c)

    194 apr_status_t apr_socket_sendv(apr_socket_t * sock, const struct iovec *vec,
    195                               apr_int32_t nvec, apr_size_t *len)
    196 {
    197 #ifdef HAVE_WRITEV
    198     apr_ssize_t rv;
    199     apr_size_t requested_len = 0;
    200     apr_int32_t i;
    201

送信データ長を計算する

    202     for (i = 0; i < nvec; i++) {
    203         requested_len += vec[i].iov_len;
    204     }
    205

ソケット情報のoptionsをチェックしてAPR_INCOMPLETE_WRITEフラグが立っている場合には、
フラグを消してから、poll処理(219行目)にジャンプする。

    206     if (sock->options & APR_INCOMPLETE_WRITE) {

APR_INCOMPLETE_WRITEフラグは、前回のapr_socket_sendv()処理で、クライアントが書き込み
可能にならないままだったケース(timeout時間待っても書き込み可能にならず、送信データが
残ってしまった場合)にONになる。ここでも、最初に書込み可能かどうかをチェックしている。

なお、writev_nonblocking()から呼ばれている場合、ソケット情報のtimeoutは0にセットされているので、前回のapr_socket_sendv()も同じ条件なら、ここには入らない
(timeout>0である場合に使用されるフラグ)

    207         sock->options &= ~APR_INCOMPLETE_WRITE;
    208         goto do_select;
    209     }
    210

writev()を実行する。
この処理は、EINTRエラーの場合(シグナルで割りこまれた場合)は、処理を再実行する。

    211     do {
    212         rv = writev(sock->socketdes, vec, nvec);
    213     } while (rv == -1 && errno == EINTR);
    214

以下のwhile()ループは
 (1)rv==-1(writevがエラー)
かつ
 (2) エラーがEAGAINかEWOULDBLOCKの場合(書込可能でなかった場合)
かつ
 (3)ソケットのtimeout情報が >0 の場合
に処理を繰り返すことになっている。

つまり、timeout>0の場合に、writevが書込み可能でなかったら繰り返されている。
writev_nonblocking()から呼ばれている場合は、timeout=0なので、この条件は満たさない
timeout>0がセットされている場合にも、このループ内だけ見るとまず、219行目の
apr_wait_for_io_or_timeout()内でソケットが書き込み可能になるのを待つので、
errnoがEAGAIN/EWOULDBLOCKとなってループするケースはないと考えられる。

この条件が必要なのは、212行目のwritev()で書込み可能でなかった場合と考えられる。
その場合、timeut>0であれば、書込み可能になるまで219行目で待ち、
書込み可能になれば、226行目で書き込まれる。

    215     while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
    216                       && (sock->timeout > 0)) {
    217         apr_status_t arv;
    218 do_select:

まず、ソケットが書き込み可能かどうかをチェックする。
poll関数で、sock->socketdesをチェックする。
この際timeoutにsock->timeoutをセットするが、上述の通り、apr_socket_sendv()から呼ばれている場合は、このtimeoutは0となっている(書込み可能でなければ直ちにrerurnする)。

    219         arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
    220         if (arv != APR_SUCCESS) {

エラーのほか、
タイムアウトの場合、apr_wait_for_io_or_timeoutはAPR_TIMEUPを返すのでこの経路となる。
タイムアウトが0の場合は、書込み可能でなければ、直ちにこの経路となる。

    221             *len = 0;
    222             return arv;
    223         }
    224         else {

書込み可能だった場合に、writev()を実行する。
ここでも、シグナル割り込みは無視される(writev()を再実行する)。

    225             do {
    226                 rv = writev(sock->socketdes, vec, nvec);
    227             } while (rv == -1 && errno == EINTR);
    228         }
    229     }

writevエラーの場合、エラーコードを返す。

    230     if (rv == -1) {
    231         *len = 0;
    232         return errno;
    233     }

timeout>0が指定されており、実際の送信済データ長が、リクエストされた送信データ長を満たさなかった場合に、ソケット情報のoptionsにAPR_INCOMPLETE_WRITEフラグを立てている。


    234     if ((sock->timeout > 0) && (rv < requested_len)) {
    235         sock->options |= APR_INCOMPLETE_WRITE;
    236     }

送信したデータ長を *lenにセットし、成功でreturnする。

    237     (*len) = rv;
    238     return APR_SUCCESS;
    239 #else
    240     *len = vec[0].iov_len;
    241     return apr_socket_send(sock, vec[0].iov_base, len);
    242 #endif
    243 }

以上から、apr_socket_sendv()では、writev()を1回実行してreturnする。