---
title: "ngx_mruby v2.7.1"
language: "ja"
documentation_version: "2.x"
channel: "maintenance"
release_context: {"latest_release_tag":"v2.7.2","unreleased_versions":["v2.7.3"],"latest_release_revision":"d5ae755471c48c6617cb23495e7b65d7c5241b45"}
source_revision: "967b61cc6a232aa1ad27dd452c8b5835f94c2a01"
working_tree_clean: true
source_path: "docs/releases/v2.7.1.ja.md"
source_sha256: "209f82a1c502c5bb1d953e5a46a38bc609a9ea0b491877d3c6c9fdff84953b16"
source_at_revision: true
source_url: "https://github.com/matsumotory/ngx_mruby/blob/967b61cc6a232aa1ad27dd452c8b5835f94c2a01/docs/releases/v2.7.1.ja.md"
html_url: "https://ngx.mruby.org/v2/ja/releases/v2.7.1/"
markdown_url: "https://ngx.mruby.org/v2/markdown/ja/releases/v2.7.1.md"
last_updated: "2026-10-29"
---

# ngx_mruby v2.7.1

ngx_mruby 2.7.1は、2.x系列のメンテナンスリリースで、セキュリティリリースでもあります。セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてある問題を直しました。また、テストスイートを拡充するときやコードをレビューするときに見つけた、複数の不具合も直しました。

修正の一部で、既存の設定やRubyのスクリプトから見える挙動が変わります。たとえば、ステータスコード、レスポンスのボディやその長さ、例外、nginxが次のフェーズへ進むかどうか、ハンドシェイクが受け取る証明書、nginxが起動するかどうかが変わります。前の挙動に合わせて書いたスクリプトは、変更が必要になることがあります。アップグレードの前に最初の節を読んでください。各項目では、2.7.0で見えた挙動を「変更前」に、2.7.1で見える挙動を「変更後」に書きました。挙動を変える必要があった理由は「変更の理由」に書き、「不具合」か「背景」、「前の挙動を残せなかった理由」、「ドキュメントが示していた挙動」の3行に分けました。続けて、影響を受ける設定と必要な対応を書きました。テストスイートが変更を試している項目には、そのテストのポートも書きました。「このリリースで変わらない挙動」には、関係する挙動のうち変わらないものを書きました。「既知の制限」の節には、このリリースで変えなかった挙動を挙げました。

## アップグレードの前に読む挙動の変更

| ビルド、設定、スクリプトで使っているもの | 読む項目 |
|---|---|
| `Redis`とデフォルトの`build_config.rb` | [1](#1-redisはデフォルトのビルドに含まれなくなりました) |
| `mruby_output_body_filter*`、`mruby_output_header_filter*`、または`Nginx::Filter` | [2](#2-出力フィルタでのレスポンスの長さとnginxfilter) |
| `Nginx.rputs`か`Nginx.echo`の後の`raise` | [3](#3-nginxrputsの後で例外が発生するとレスポンスは500になります) |
| logハンドラか出力フィルタの中の、`Nginx::Async`か`Fiber.yield` | [4](#4-logハンドラと出力フィルタではnginxasyncで例外が発生します) |
| 再開した後に例外を投げるか`Nginx.redirect`を呼ぶハンドラの中の`Nginx::Async.sleep`か`Nginx::Async::HTTP.sub_request`、またはsleepするlocationへの`sub_request` | [5](#5-nginxasyncの後で例外が発生するとレスポンスは500になります) |
| `mruby_enable_read_request_body on` | [6](#6-mruby_enable_read_request_bodyでどのハンドラがボディ全体を待つか) |
| `Nginx::SSL`か`mruby_ssl_handshake_handler*` | [7](#7-nginxsslはハンドシェイクのハンドラの中だけで動きます) |
| `mruby_init*`、`mruby_init_worker*`、`mruby_exit_worker*`、`mruby_server_context_handler_code`、ハンドシェイクのハンドラで呼ぶリクエストのメソッドか`Nginx.log`、または`Nginx::Server#add_listener`か`Nginx::Stream.add_listener`、またはstreamのinit、init worker、exit worker、server contextのコード | [8](#8-リクエストの外でリクエストのメソッドを呼ぶとruntimeerrorが発生します) |
| `mruby_set*`と、同じリクエストで後に動くハンドラが呼ぶ`Nginx::Async.sleep` | [9](#9-後のハンドラがmruby_setの変数を上書きしなくなりました) |
| `http {}`ブロックの無い設定 | [10](#10-httpブロックの無い設定でもnginxが起動します) |
| 1つのlocationに`mruby_output_header_filter*`と`mruby_output_body_filter*`があり、ボディフィルタが`Nginx.rputs`か`Nginx.echo`を呼ぶ設定 | [11](#11-mrubyのヘッダフィルタの後でnginxrputsを呼ぶボディフィルタでheader-already-sentがログに出なくなりました) |
| `POST`や`PUT`のようにボディのあるリクエストに、ボディを読まずに`Nginx.rputs`か`Nginx.echo`で応答するハンドラ | [12](#12-応答の前に読まれていないリクエストボディを破棄します) |
| 出力なしで終わることのある`mruby_content_handler*`、または`response body is empty`に一致させるログの監視 | [13](#13-contentハンドラが出力なしで終わるとレスポンスは500になります) |
| post-readハンドラかaccessハンドラと組み合わせた`mruby_enable_read_request_body on`で、同じリクエストに`mruby_log_handler*`かmrubyの出力フィルタがあるもの | [14](#14-nginxがボディの読み込みを打ち切った後は待っていたハンドラが動かなくなりました) |

### 1. Redisはデフォルトのビルドに含まれなくなりました

- 変更前：`build_config.rb`に`matsumotory/mruby-redis`が入っていたので、デフォルトの設定でビルドしたバイナリには`Redis`クラスがありました。
- 変更後：mruby-redisの行はコメントアウトしました（[#528](https://github.com/matsumotory/ngx_mruby/pull/528)）。コメントアウトしたのは、このgemが依存するhiredisを、バージョンを固定していないブランチからcloneするためです。このcloneが原因で、ビルドが壊れました。デフォルトの`build_config.rb`でビルドしたバイナリには`Redis`クラスが無く、`Redis`を参照するコードでは`NameError`が発生します。そのメッセージは「uninitialized constant Redis」です。
- 変更の理由：
  - 背景：macOS以外では、mruby-redisのビルドがhiredisをデフォルトブランチの先頭からcloneします。そのため、hiredisのデフォルトブランチが変わると、デフォルトの`build_config.rb`で作るすべてのビルドが壊れる可能性がありました。[#528](https://github.com/matsumotory/ngx_mruby/pull/528)で`build_config.rb`に足したコメントには、`-Werror`を付けたCIでこの種の失敗が起きたことが書いてあります。
  - 前の挙動を残せなかった理由：gemをデフォルトの一覧に残していたら、すべてのデフォルトのビルドがこの種の失敗にさらされたままでした。デフォルトのビルドは成功しなければなりません。一方、mruby-redisは自身のビルドスクリプトでhiredisをcloneするので、`build_config.rb`ではhiredisのバージョンを固定できません。したがって、mruby-redisがhiredisを固定するまで、このgemはデフォルトの一覧に残せません。
  - ドキュメントが示していた挙動：2.7.0のドキュメントでは、mruby-redisが「the default mrbgems」の1つに入っていました（[2.7.0のdocs/install](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/install/README.md#2-configuring-mrbgems)）。また、`Redis`は「with default build configuration」で使えるクラスの1つに入っていました（[2.7.0のbuilt-in mrbgems](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#built-in-mrbgems)）。つまり、ドキュメントには前の挙動が書いてありました。docs/installには、追加のgemを入れてngx_mrubyをビルドする方法として、`build_config.rb`でmrbgemsを選ぶ方法が書いてあります。Redisを残すにも、この方法を使います。
- 影響の範囲：`Redis`を使うスクリプトです。たとえば、READMEと`docs/use_case`の例がこれに当たります。同梱の`auto-ssl` mrbgemの利用者も影響を受けます。このmrbgemのクライアントは、Redisの接続を受け取るからです。独自の`build_config.rb`でビルドする場合は影響を受けません。
- 必要な対応：Redisを使う場合は、ビルドの前に、`build_config.rb`の`conf.gem github: 'matsumotory/mruby-redis'`のコメントアウトを外してください。[#528](https://github.com/matsumotory/ngx_mruby/pull/528)に書いたhiredisの問題が残っている間は、このgemのビルドが失敗することがあります。

### 2. 出力フィルタでのレスポンスの長さとNginx::Filter

この項目は4つの部分に分かれます。

レスポンスの長さは、ボディフィルタの出力に従います。

- 変更前：`mruby_content_handler*`のようなmrubyのハンドラが`Nginx.rputs`か`Nginx.echo`でレスポンスのボディを書き、`mruby_output_body_filter*`がそれを`Nginx::Filter#body=`か`#output`で置き換えると、レスポンスの長さは、`Nginx.rputs`が出力した長さのままでした。置き換えたボディが長いとその長さで切れ、短いとレスポンスの一部でないバイトでその長さまで埋まりました。ボディフィルタ自身が`Nginx.rputs`か`Nginx.echo`を呼ぶと、`Content-Length`は、フィルタを通る前のレスポンスの長さに、その出力の長さを足した値になり、その出力自体は送られませんでした。ドキュメントには、ボディフィルタでのこの呼び出しを認める記述が、どのバージョンにもありません。これらのレスポンスは壊れていたので、これらに依存してよいものはありません。このケースを試すリポジトリ自身のテストは切れたレスポンスを期待しており、その期待値はこのリリースで変わります。この部分では、mrubyのヘッダフィルタの無いlocationについて書きます。ヘッダフィルタのあるlocationについては、4つ目の部分を見てください。
- 変更後：ボディはボディフィルタの出力と同じになり、`Content-Length`はその長さになります。ボディフィルタが`Nginx.rputs`か`Nginx.echo`で書いたものは、送られず、`Content-Length`にも入りません。

```nginx
location /filter/output_rputs_origin {
    mruby_content_handler_code 'Nginx.rputs "origin"';
    mruby_output_body_filter_code '
        f = Nginx::Filter.new
        f.output "[#{f.body}] and more"
    ';
}
```

| バージョン | `Content-Length` | ボディ |
|---|---|---|
| 2.7.0 | `6` | `[origi` |
| 2.7.1 | `17` | `[origin] and more` |

- 変更の理由：
  - 不具合：ngx_mrubyは、ボディ用に保持している長さの分だけ、ボディフィルタのバッファからバイトを送り、`Content-Length`にも同じ数を設定します。2.7.0では、ngx_mrubyは、その長さに`Nginx.rputs`が数えた`Content-Length`を使い、バッファにはボディフィルタの出力を使っていました。そのため、フィルタが長さを変えるたびに、長さとバッファが食い違いました。長いボディは切れ、短いボディの後では、ngx_mrubyが新しいボディの終わりより先のバイト、つまりレスポンスの一部でないメモリを送りました。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：長さとバッファは一致しなければならず、バッファはフィルタの出力なので、長さは出力に従う必要があります。前の長さを残すと、ボディが切れるか、バッファの先まで読む挙動が残ります。
  - ドキュメントが示していた挙動：ドキュメントには、`Nginx::Filter#body=`について「Sets the string to the response body」と書いてあり、2.7.1のレスポンスはこの記述のとおりです。ほかのものが作ったレスポンスでは、2.7.0でも既にこの記述のとおりでした。2.7.0のドキュメントの同じ節（[Nginx::Filter Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)）には、「You can *NOT* use `Nginx.rputs` and `Nginx.echo` in the output body handlers」と書いてあります。
- 影響の範囲：`Nginx.rputs`か`Nginx.echo`で書いたボディの長さをボディフィルタが変えるlocationと、レスポンスを何が作ったかに関わらず、自分で`Nginx.rputs`か`Nginx.echo`を呼ぶボディフィルタです。長さを変えず、これらを呼ばないボディフィルタでは、何も変わりません。ファイル、`return`、`proxy_pass`、ほかのcontentハンドラが作ったレスポンスに掛かるボディフィルタも変わりません。これらでは、長さが既にフィルタの出力に従っていたからです。
- 必要な対応：クライアントやテストが切れたレスポンスを期待していた場合を除き、ありません。ボディフィルタからは`Nginx.rputs`と`Nginx.echo`を取り除いてください。ボディフィルタでこれらが書いたものは送られません。
- テスト：`test/t/cases/filter_connection.rb`のポート18112のテストです。

ボディを読む前に呼んだ`Nginx::Filter#body`が返す値が変わります。

- 変更前：`mruby_output_header_filter*`ではボディをまだ読んでいません。そこで呼んだ`Nginx::Filter#body`は、`Content-Length`と同じ長さで、中身がレスポンスのボディでない文字列を返しました。長さが不明なとき、または長さが10485760バイト、すなわち10 MiB以上のときは、代わりに`ArgumentError`が発生しました。
- 変更後：そこでは空の文字列を返します。contentハンドラでは、既に空の文字列を返していました。
- 変更の理由：
  - 不具合：ヘッダフィルタではボディをまだ読んでいません。2.7.0では、「変更前」に書いたとおり、`Content-Length`と同じ長さで中身がレスポンスのボディでない文字列が返るか、`ArgumentError`が発生しました。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：その時点では返すボディが無いので、正しい結果は空の文字列だけです。長さは`$sent_http_content_length`で得られます。
  - ドキュメントが示していた挙動：ドキュメントには一貫して、`Nginx::Filter`は出力のボディフィルタで使うクラスだと書いてあります。2.7.0のドキュメントの記述は「designed to be used in the output body handlers」です（[2.7.0のNginx::Filter Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)）。
- 影響の範囲：`f.body`を読むヘッダフィルタです。たとえば、レスポンスの長さを知るために`f.body`のサイズを読むものが当たります。
- 必要な対応：長さは`Nginx::Var.new.sent_http_content_length`で読んでください。値はStringで、長さが不明なときは`nil`です。ボディは`mruby_output_body_filter*`で読んでください。
- テスト：`test/t/ngx_mruby.rb`のポート18080のテストです。

`Nginx::Filter#body=`と`#output`は、ボディフィルタの中でだけ使えます。

- 変更前：これらはどこでも呼べました。ボディフィルタもあるlocationで、ボディフィルタ以外のハンドラがこれらを呼ぶと、そのボディフィルタが空でないレスポンスを読んだ時点でワーカーがクラッシュしました。locationにボディフィルタが無いときは、この呼び出しはレスポンスに何の影響もありませんでした。
- 変更後：`mruby_output_body_filter*`の外では`RuntimeError`が発生します。メッセージは`Nginx::Filter#body= can only be used in an output body filter`です。ほかの例外と同じく、この例外でハンドラは終わります。
- 変更の理由：
  - 不具合：ボディフィルタの外で`Nginx::Filter#body=`か`#output`でボディを設定すると、「変更前」に書いたとおり、locationのボディフィルタが空でないレスポンスを読んだときにワーカーがクラッシュしました。ボディフィルタが無いときは、何の影響もありませんでした。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：ボディフィルタは、レスポンスをバッファに読み込み、後でそのバッファを送ります。ボディフィルタがこのバッファを確保するのは、ボディがまだ設定されていないときだけです。`Nginx::Filter#body=`は、このバッファを設定するときに、ボディフィルタがバッファへ書き込む位置を設定しませんでした。そのため、後で動いたボディフィルタが空でないレスポンスを読んだ時点で、ワーカーがクラッシュしました。2.xでは、ボディフィルタが読んで送るボディと、`Nginx::Filter#body=`が設定するボディに、1つのバッファを使います。また、ボディフィルタの外でボディを設定する使い方は、一度もサポートしていませんでした。そのため、2.7.1では2つ目のバッファを足さずに、この呼び出しを拒否します。
  - ドキュメントが示していた挙動：ドキュメントには一貫して、`Nginx::Filter`はほかのハンドラでは動かないと書いてあります。2.7.0のドキュメントの記述は「It doesn't work in other handlers like `mruby_content_handler_code`」です（[2.7.0のNginx::Filter Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)）。
- 影響の範囲：ヘッダフィルタやほかのハンドラのうち、`body=`か`output`を呼ぶものです。
- 必要な対応：ボディは`mruby_output_body_filter*`で設定してください。
- テスト：`test/t/ngx_mruby.rb`のポート18080のテストです。

mrubyのヘッダフィルタがあるときは、ヘッダがボディフィルタを待ちます。

- 変更前：`mruby_output_header_filter*`と`mruby_output_body_filter*`のあるlocationでは、ヘッダフィルタのコードが終わるとすぐにヘッダが出ていき、その`Content-Length`は、ボディフィルタを通る前のレスポンスの長さでした。そのため、ボディを長くするボディフィルタは`Content-Length`が示すより多いバイトを送り、短くするボディフィルタは少ないバイトを送りました。`Nginx.rputs`で書くハンドラがレスポンスを作る場合、このようなlocationはまったく応答しませんでした。この件は「不具合の修正」の節を見てください。
- 変更後：ヘッダは、ボディフィルタが終わるまで待ちます。ボディフィルタだけのlocationでは、既にそうなっていました。ヘッダには、ボディフィルタの出力の長さと、ヘッダフィルタが加えた変更が入ります。ヘッダフィルタのコードは、これまでどおりボディフィルタのコードより先に動きます。長さが分からないレスポンスは、前と同じく、ボディフィルタを通らずに次へ渡ります。
- 変更の理由：
  - 不具合：ヘッダを先に送ると、レスポンスの`Content-Length`がその時点で決まり、その後でボディフィルタが長さを変えていました。`Content-Length`でレスポンスを読むクライアントは、違う長さのボディを正しく読めません。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：ボディの長さを決めるのはボディフィルタなので、ヘッダに正しい`Content-Length`を入れられるのは、ボディフィルタが動いた後だけです。ボディフィルタだけのlocationでは、同じ理由で既にヘッダを待たせていました。
  - ドキュメントが示していた挙動：両方のフィルタがあるlocationでヘッダをいつ送るかは、ドキュメントに書いてありませんでした。`Nginx::Filter#body=`については、「Sets the string to the response body」と書いてあります（[2.7.0のNginx::Filter Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)）。
- 影響の範囲：mrubyのヘッダフィルタとmrubyのボディフィルタの両方があり、ボディフィルタがボディの長さを変えるlocationです。
- 必要な対応：ありません。
- テスト：`test/t/cases/header_and_body_filter.rb`のポート18150のテストです。レスポンスをファイルが作る場合と、`Nginx.rputs`で書くハンドラが作る場合を試します。

### 3. Nginx.rputsの後で例外が発生すると、レスポンスは500になります

- 変更前：post-read、server rewrite、rewrite、access、contentのいずれかのハンドラが、`Nginx.rputs`か`Nginx.echo`で出力を書いてから例外を投げると、それまでに書いた出力を付けて200で応答しました。これは、先に`Nginx::Async`で待たなかった場合です。accessハンドラかpost-readハンドラでは、nginxがその後のフェーズへ進んだので、locationのcontentハンドラも動き、その出力が後に続きました。
- 変更後：リクエストにはnginxの500エラーページが返り、出力は破棄されます。その後のフェーズは動きません。出力なしで例外を投げるハンドラは、既にこの結果になっていました。`Nginx::Async`の後で例外を投げるハンドラも、この結果になります（項目5）。
  - `mruby_set*`：2.7.0では、出力がレスポンスとして送られ、変数にはハンドラが設定していない値が入りました。いまは出力が破棄され、変数は空の文字列になります。これは`nil`を返すハンドラと同じです。locationの前のディレクティブが変数に値を与えていた場合も、変数は空の文字列になります。リクエストは先へ進み、レスポンスの`Content-Length`には破棄した出力が入りません。ここまでは、ハンドラが`Nginx::Async.sleep`か`Nginx::Async::HTTP.sub_request`で待つ前に例外を投げた場合です。`Nginx::Async.sleep`の後では、リクエストは先へ進まず、出力の無いnginxの500エラーページが返ります。2.7.0では、出力がレスポンスとして200で送られていました。`Nginx::Async::HTTP.sub_request`の後では、nginxが変数を待たずにcontentフェーズへ進むので、例外に続く500を送れず、クライアントには何のレスポンスも届きません（項目5）。
  - ヘッダフィルタ：ヘッダフィルタで例外が発生したときは、2.7.0でも既にレスポンスのステータスが500になり、ボディには元のレスポンスのボディが残りました。2.7.0では、`Nginx.rputs`か`Nginx.echo`を呼ぶヘッダフィルタでワーカーがクラッシュしました。この件は「不具合の修正」の節を見てください。そのようなヘッダフィルタがその後で例外を投げると、いまはその出力が破棄され、`Content-Length`は、元のレスポンスが持っていたボディの長さになります。破棄するのは、ヘッダフィルタ自身の出力だけです。レスポンスを作るのが`Nginx.rputs`か`Nginx.echo`で書くmrubyのcontentハンドラなら、レスポンスにはそのハンドラの出力とその`Content-Length`が残ります。ヘッダフィルタが出力なしで例外を投げた場合も同じで、この場合は2.7.0も同じレスポンスを送っていました。
  - logハンドラとボディフィルタ：例外の後は、出力を送らなくなりました。
- 変更の理由：
  - 不具合：例外の後、ngx_mrubyは`mrb_run failed: return 500 HTTP status code to client`をログに出してステータスを500にしました。ところが、その後で出力を送るコードパスがステータスをリセットし、途中までの出力を200で送りました。そのため、失敗したハンドラが、クライアントには成功として伝わりました。accessハンドラかpost-readハンドラでは、このコードパスが、nginxが「先へ進む」と読む値も返しました。そのため、例外で失敗したアクセスチェックでも、リクエストが通りました。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：例外が発生したら、ハンドラはエラーのステータスで終わらなければなりません。そうしないと、アクセスの判定を誤ります。出力を送るコードパスでは、200を送りながらフェーズを止められないので、出力を破棄します。クライアントには、error.logの行が前から示していた500が届きます。これは、出力なしの例外の後や`Nginx::Async`の後と同じです。`mruby_set*`のハンドラはリクエストに応答しないので、例外の後もリクエストは先へ進みます。そのため、破棄した出力は、その後に続くレスポンスの長さに入ってはいけません。また、変数には決まった値が要ります。2.7.0では、nginxのスクリプトエンジンがその時点で持っていた値が、そのまま変数に入りました。
  - ドキュメントが示していた挙動：例外を投げるハンドラについては、ドキュメントに何も書いてありませんでした。ドキュメントには、出力を書いてから`Nginx.return Nginx::HTTP_OK`を呼び、`return`で抜けるcontentハンドラの例があります（[2.7.0のNginx.return](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxreturnnginx_status_code)）。このハンドラは、「必要な対応」に示すとおり、2.7.0でも2.7.1でも200で応答します。
- 影響の範囲：出力を書いた後でハンドラを止めるために`raise`を使うスクリプト、メッセージを書いてから例外を投げるアクセスチェック、出力を書いてから例外を投げる`mruby_set*`のハンドラです。
- 必要な対応：`raise`は、エラーのためだけに使ってください。出力を返してcontentハンドラを途中で終えたいときは、例外を使わずに終えてください。

```ruby
# 2.7.0: 200 "under maintenance"   2.7.1: 500
Nginx.rputs "under maintenance"
raise "stop"
```

```ruby
# 2.7.0 and 2.7.1: 200 "under maintenance"
Nginx.rputs "under maintenance"
Nginx.return Nginx::HTTP_OK
return   # leaves the handler; omit it when nothing follows
```

200以外のステータスで`Nginx.return`を呼ぶと、2.7.0と同じく、nginxは`Nginx.rputs`の出力を付けずに、そのステータスのnginx自身のページを送ります。accessハンドラとpost-readハンドラでは、出力を書かないでください。書いた出力は、ハンドラが終わるときにレスポンスとして送られます。`Nginx::HTTP_OK`を返すかステータスを設定しない場合は、nginxがその後のフェーズへ進み、そのフェーズの出力が`Content-Length`を超えてレスポンスの後に続きます。これは2.7.0でも2.7.1でも同じで、この件は「既知の制限」の節に書きました。accessハンドラやpost-readハンドラでリクエストを止めるには、`Nginx.return Nginx::HTTP_FORBIDDEN`のようにエラーのステータスを返してください。

- このリリースで変わらない挙動：`Nginx.rputs`の後で`Nginx.return`、`Nginx.send_header`、`Nginx.status_code=`のどれかを呼んでから例外を投げたハンドラは、既に500になっていました。例外を投げる前にローカルのURIへ`Nginx.redirect`したハンドラは、これまでどおり、リダイレクト先のレスポンスで終わります。
- テスト：`test/t/cases/sync_exception.rb`のポート18143のテストです。

### 4. logハンドラと出力フィルタでは、Nginx::Asyncで例外が発生します

- 変更前：`mruby_log_handler*`で`Nginx::Async.sleep`を呼ぶと、nginxが既に解放している途中のリクエストにタイマーが残り、タイマーが切れたときにワーカーがクラッシュすることがありました。logハンドラで`Nginx::Async::HTTP.sub_request`を呼ぶと、サブリクエストが始まりました。`mruby_output_header_filter*`と`mruby_output_body_filter*`では、`Nginx::Async.sleep`を呼んだ位置でフィルタが止まりました。レスポンスはフィルタを通らずに、元のレスポンスのステータスで出ていきました。テストした設定では、そのステータスは200でした。タイマーが切れる前にリクエストが終わるので、sleepの後のコードは動きませんでした。ヘッダフィルタで`sub_request`を呼ぶと、サブリクエストが始まりました。これらのハンドラで直接`Fiber.yield`を呼ぶと、メッセージなしでハンドラが止まりました。
- 変更後：これらのハンドラでは、どちらのメソッドでも`RuntimeError`が発生します。メッセージは、たとえば`Nginx::Async.sleep is not available in a log handler`で、ヘッダフィルタでは`... in a header filter`、ボディフィルタでは`... in a body filter`です。例外は、タイマーをセットする前、サブリクエストを始める前に発生します。この例外は、ほかの例外と同じように扱われます。error.logに`[error]`の行が出て、フィルタではレスポンスのステータスが500になります。テストした設定では、そのボディはフィルタを通っていないボディでした。logハンドラでは、レスポンスは既に送ってあります。これらのハンドラで直接`Fiber.yield`を呼ぶと、`... yielded its fiber, but nginx cannot resume it there`がログに出て、フィルタではステータスが500になります。
- 変更の理由：
  - 不具合：nginxは、リクエストを解放する途中でlogフェーズを動かします。それは、リクエストのクリーンアップの後で、リクエストのメモリを解放する直前です。そのため、logフェーズでセットしたタイマーは解放済みのメモリの上で発火し、もう存在しないリクエストでハンドラが再開しました。出力フィルタは`ngx_http_send_header`と`ngx_http_output_filter`の中で動き、この2つはすぐに呼び出し元へ戻ります。そのため、出力フィルタの中でyieldしたハンドラはただ止まり、フィルタの処理が飛ばされます。後でタイマーかサブリクエストがハンドラを再開しても、レスポンスは既に出ていっています。直接の`Fiber.yield`は、一度も再開されません（[#551](https://github.com/matsumotory/ngx_mruby/pull/551)）。
  - 前の挙動を残せなかった理由：これらの場所でハンドラを再開しても、nginxには、レスポンスをまだ変えられる時点がありません。そのため、この呼び出しは、タイマーをセットする前、サブリクエストを始める前に失敗しなければなりません。
  - ドキュメントが示していた挙動：ドキュメントには一貫して、`Nginx::Async`が動く場所として、set、rewrite、accessのハンドラが挙げてあります。logハンドラとフィルタは、その中に一度も入っていません（[2.7.0のNginx::Async Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxasync-class)）。
- 影響の範囲：`Nginx::Async.sleep`、`Nginx::Async::HTTP.sub_request`、`Fiber.yield`を呼ぶlogハンドラと出力フィルタです。
- 必要な対応：rewriteハンドラかaccessハンドラ、または`mruby_set_code`の中で待ち、結果はnginxの変数でフィルタやlogハンドラへ渡してください。
- このリリースで変わらない挙動：`mruby_set*`と、post-read、server rewrite、rewrite、access、contentの各ハンドラは、これまでどおり両方のメソッドを使えます。contentハンドラについては、「既知の制限」の節を見てください。
- テスト：`test/t/cases/async_sleep_in_log.rb`のポート18129のテストと、`test/t/cases/async_sleep_in_output_filter.rb`のポート18131のテストです。

### 5. Nginx::Asyncの後で例外が発生すると、レスポンスは500になります

この項目では、`Nginx::Async.sleep`か`Nginx::Async::HTTP.sub_request`が再開したハンドラについて書きます。対象は、そのハンドラが例外を投げる場合と、`Nginx.redirect`を呼んでから例外を投げるか`Nginx.return`を呼ぶ場合です。sleepするlocationへの`sub_request`も対象です（[#550](https://github.com/matsumotory/ngx_mruby/pull/550)）。

- 変更前：
  - 出力が無いときは、nginxがレスポンスなしで接続を閉じました。
  - `Nginx.rputs`の後では、クライアントには、それまでに書いた出力を付けた200が返りました。
  - ステータスを設定した後、たとえば`Nginx.return 503`の後では、クライアントにはそのステータスが返りました。serverレベルのハンドラのように、同じリクエストのハンドラが前に`Nginx.return Nginx::DECLINED`を呼んでいたときは、nginxが次のフェーズへ進みました。たとえば、ファイルを200で返しました。
  - 同じワーカーで次にいずれかのmrubyのハンドラを動かすリクエストは500で失敗し、error.logには前の例外がもう一度出ました。
- 変更後：リクエストにはnginxの500エラーページが返り、出力は破棄されます。次のリクエストは影響を受けません。
  - レスポンスが既に始まっていたときは、500を送れません。nginxは`header already sent`をログに出してリクエストを終え、クライアントには、それまでに送ったものが残ります。`Nginx::Async.sleep`の後でローカルのURIへ`Nginx.redirect`したときは、クライアントに残るのはリダイレクト先のレスポンスです。[#550](https://github.com/matsumotory/ngx_mruby/pull/550)のために測った2つのケースでは、2.7.0ではレスポンスが届いていたのに、いまはクライアントに何のレスポンスも届きません。1つ目は、`Nginx::Async::HTTP.sub_request`の後の`Nginx.redirect`です。2.7.0では、リダイレクト先のレスポンスが送られた後、クライアントが閉じるまで接続が開いたままでした。2つ目は、`sub_request`の後で例外を投げる`mruby_set_code`です。nginxは、contentフェーズの前に変数を待ちません。2.7.0では、contentハンドラの出力を付けた200が送られていました。
  - 再開したハンドラで`Nginx.redirect`の後に例外か`Nginx.return`が続くと、nginxは、リダイレクト先のレスポンスのすぐ後で接続を閉じます。HTTP/1.1のkeep-aliveの接続では、その後ろにパイプラインで送ったリクエストにはレスポンスが返りません。クライアントは、新しい接続でそのリクエストを送り直す必要があります。2.7.0では接続が開いたままでしたが、パイプラインで送ったリクエストにそのリクエストのレスポンスが返らない点は同じでした。例外の後では、ワーカーが持ち続けた例外による500エラーページが返りました。これは、上の「変更前」の最後の点に書いた挙動です。`Nginx.return`の後では、クライアントかタイムアウトが接続を閉じるまで、何も返りませんでした。
  - `Nginx::Async.sleep`で待つハンドラのあるlocationへの`Nginx::Async::HTTP.sub_request`は、いまは完了します。2.7.0では、クライアントか`send_timeout`がリクエストを終えるまで、何も送られませんでした。そのlocationが例外を投げたときは、`Nginx::Async::HTTP.last_response.status`は0です。
- 変更の理由：
  - 不具合：再開したハンドラで例外が発生した後、ngx_mrubyは`return 500 HTTP status code to client`をログに出し、その後で500を、例外の前にハンドラが持っていたステータスに置き換えました。そのステータスは、0、つまりレスポンスなしか、途中までの出力を付けた200か、nginxが次のフェーズへ進む`Nginx::DECLINED`のような値でした。例外は、ワーカーのすべてのリクエストが共有するmrubyの状態にも残り、次のリクエストがその例外で失敗しました。2.7.0では、`Nginx.redirect`の後にステータスを付けて`Nginx.return`を呼ぶと、クライアントかタイムアウトが終えるまで、リクエストとその接続が開いたままでした（[#550](https://github.com/matsumotory/ngx_mruby/pull/550)）。
  - 前の挙動を残せなかった理由：例外の後は、その状態をクリアしなければなりません。そうしないと、ワーカーの次のリクエストが失敗します。ステータスも、エラーのままでなければなりません。そうしないと、nginxは失敗したハンドラを成功として扱います。nginxは、タイマーの中で終えたリクエストを完了させるとき、そのリクエストを終える間にキューへ入れたposted requestsを実行します。2.7.1では、500か、`Nginx.return`で設定したステータスがnginxに届くのは、リダイレクト先のレスポンスを送った後です。そのため、nginxは接続を閉じる以外の方法でリクエストを終えられず、posted requestsを実行すると、すぐに接続が閉じます。keep-aliveの接続がリダイレクト先のレスポンスの後で閉じるようになったのは、このためです。クライアントには、error.logの行が示す500が届きます。これは、待たなかったハンドラで例外が発生した後と同じです。
  - ドキュメントが示していた挙動：2.7.0のREADMEには、スクリプトが共有するmrubyの状態の「the exception flag」を、サーバーのプロセスが解放すると書いてあります。解放する目的は「extend safety by preventing interference between each script」です（[Abstract](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/README.md#abstract)）。前のリクエストの例外で次のリクエストが失敗するのは、この干渉に当たります。`Nginx::Async`が再開したハンドラでの例外と、そのようなハンドラでの`Nginx.redirect`については、ドキュメントに何も書いてありませんでした。ドキュメントには、`Nginx::Async.sleep`と`Nginx::Async::HTTP.sub_request`が動く場所として、set、rewrite、accessのハンドラが挙げてあります（[2.7.0のNginx::Async Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxasync-class)）。
- 影響の範囲：`Nginx::Async`を使い、その後で例外を投げることのあるハンドラ、レスポンスなしで接続が閉じる挙動や例外の前に設定したステータスに頼っていたクライアント、`sub_request`を使う`mruby_set_code`のハンドラです。再開したハンドラで`Nginx.redirect`を呼んでから例外を投げるか`Nginx.return`を呼ぶlocationに、keep-aliveの接続でリクエストをパイプラインで送るクライアントも、影響を受けます。
- 必要な対応：例外の後に500以外を返したいハンドラは、ハンドラの中で例外をrescueし、そこでステータスを設定してください。
- テスト：`test/t/cases/async_exception_state.rb`のポート18130のテストと、`test/t/cases/async_subrequest_raise.rb`のポート18124のテストです。

### 6. mruby_enable_read_request_bodyで、どのハンドラがボディ全体を待つか

`mruby_enable_read_request_body on`のとき、ngx_mrubyは、ハンドラのコードを動かす前にリクエストボディを読みます。この項目は2つの部分に分かれます。1つ目は、まだそろっていないボディを待てるハンドラ、2つ目は待てないハンドラです。

post-read、server rewrite、rewrite、access、contentの各ハンドラは待ちます。

- 変更前：ハンドラが始まったときにリクエストボディが全部届いていないと、`mruby_server_rewrite_handler*`、`mruby_rewrite_handler*`、`mruby_content_handler*`は一度も動きませんでした。クライアントにはレスポンスが返らず、クライアントが接続を閉じても、`client_body_timeout`が切れても、nginxはリクエストを終えませんでした。大きなアップロードや遅いクライアントのように、ボディが2回以上の読み込みに分かれて届くだけで、この状態になりました。post-readハンドラとaccessハンドラはボディを待ち、ボディを受け取って動きました。しかし、そのように待った後では、`auth_request`のようにサブリクエストを待つ後のフェーズのハンドラが、再開しませんでした。この場合もクライアントにはレスポンスが返らず、クライアントが閉じた後も接続が開いたままでした。
- 変更後：ハンドラは、nginxがボディを全部読んだ後に動きます。続いて、リクエストは後のフェーズへ進み、`auth_request`のようにサブリクエストを待つフェーズのハンドラも動きます。keep-aliveの接続では、次のリクエストにも通常どおり応答し、ハンドラで例外が発生した場合は500を返します。クライアントが先に接続を閉じると、nginxは`client prematurely closed connection`をログに出してリクエストを終えます。`client_body_timeout`が切れると、nginxはレスポンスなしで接続を閉じ、アクセスログには408が記録されます。どちらの場合も、ハンドラは動きません。そのようなリクエストのlogハンドラとフィルタについては、項目14を見てください。
- 変更の理由：
  - 不具合：ボディがそろっていない間、ngx_mrubyは`NGX_AGAIN`を返していました。nginxは、rewriteフェーズとcontentフェーズでこの値を「終わった」と読みます。そのため、nginxはボディの読み込みをやめて、`client_body_timeout`を外しました。ハンドラは、二度と動きませんでした。クライアントには応答が無く、クライアントが去った後も、ワーカーが終了するまでリクエストとその接続が開いたままでした。post-readハンドラかaccessハンドラがボディを待った後は、ngx_mrubyがフェーズを動かし直しました。このとき、書き込みイベントのハンドラは、nginxがボディを読む間に設定する何もしないハンドラのままでした。nginxは、`auth_request`のようにサブリクエストを待つフェーズのハンドラを、この書き込みイベントのハンドラから再開します。そのため、そのハンドラは再開しませんでした。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：これらのフェーズでリクエストを待たせておけるのは、ハンドラが`NGX_DONE`を返したときだけです。リクエストを終えつつボディを読み続ける戻り値はありません。そのため、ハンドラは待つ必要があり、ngx_mrubyは、ボディがそろったときにフェーズを動かし直します。サブリクエストを待つフェーズのハンドラは、書き込みイベントのハンドラからしか再開しません。そのため、ngx_mrubyは、nginxのmirrorモジュールと同じく、フェーズを動かし直す前に書き込みイベントのハンドラを元に戻すようになりました。
  - ドキュメントが示していた挙動：いまは、ハンドラがボディを受け取ります。ドキュメントには、このディレクティブの動作として「reads a HTTP request body before calling mruby code」と書いてあり、2.7.1の動作はこの記述のとおりです。2.7.0のこのディレクティブのドキュメント（[mruby_enable_read_request_body](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/directives/README.md#mruby_enable_read_request_body)）には、「a request body is not always read when invoking a handler like mruby_content_handler」と書いてあります。同じドキュメントには、「The directive force to read the request body」ともあります。
- 影響の範囲：このディレクティブをserver rewrite、rewrite、contentのハンドラと使う設定です。このディレクティブをpost-readハンドラかaccessハンドラと使い、その後に`auth_request`か、サブリクエストを待つほかのフェーズのハンドラが続く設定も、影響を受けます。
- 必要な対応：ありません。
- テスト：`test/t/cases/request_body_later.rb`の、ポート18144とポート18147のテストです。ポート18144のテストは、5種類のハンドラ、keep-alive、`client_body_timeout`、ボディの後の例外を試します。ポート18147のテストは、rewriteハンドラとaccessハンドラの後の`auth_request`を試します。

`mruby_set*`、logハンドラ、出力フィルタは待ちません。

- 変更前：これらのハンドラも、ほかのハンドラと同じく、コードを動かす前にボディを読み始めました。ボディが既に全部届いていたときのように、nginxがボディを一度で全部読めたときは、コードはボディを受け取って動きました。そうでないときは、コードはその時点では動かず、`mruby_set*`の変数には、ハンドラが設定していない値が入りました。ngx_mrubyが始めたボディの読み込みは残りました。その読み込みが完了すると、リクエストのフェーズが、そのときnginxが処理していたフェーズからもう一度動きました。nginx 1.31.6では、前のハンドラがボディを読んでいないHTTP/2のリクエストのすべてで、これが起きました。nginxがそのようなボディをイベントで読むからです。テストした設定では、`mruby_set_code`と2つに分かれて届くボディで、次のことが起きました。
  - 同じlocationのmrubyのcontentハンドラが2回動き、ボディの最初の部分だけで応答しました。
  - `proxy_pass`では、upstreamのサーバーが、届いた部分だけを受け取りました。upstreamのサーバーが残りを待つときは、クライアントにレスポンスが返りませんでした。待たないときは、クライアントにレスポンスは返りましたが、nginxはHTTP/1.0の接続を閉じませんでした。
  - クライアントが閉じた後も、接続が開いたままでした。
- 変更後：これらのハンドラがボディを読むのは、読み込みがすぐに完了するときだけです。それは、前のハンドラかモジュールがボディを読んだとき、リクエストにボディが無いとき、`Content-Length`のあるHTTP/1.xのリクエストでボディ全体がヘッダのバッファに入っているときです。ヘッダのバッファは、nginxがリクエストヘッダを読み込んだバッファで、デフォルトで1kの`client_header_buffer_size`のバッファか、ヘッダが必要とした`large_client_header_buffers`のバッファです。それ以外のときは、コードはボディなしですぐに動きます。`Nginx::Request#body`は`nil`を返し、error.logには`info`レベルの行が1行出ます。その行の末尾は`the request body is not complete and an mruby_set handler cannot wait for it, so it runs without the body`で、ハンドラによって、`an mruby_set handler`の部分が`a log handler`、`a header filter`、`a body filter`になります。chunkedのボディ、HTTP/2かHTTP/3のボディ、そのバッファに一部だけが入っているボディは、残りが既に届いていても、そろっていないものとして扱います。待てる後のハンドラと`proxy_pass`は、これまでどおりボディ全体を読みます。post-read、server rewrite、rewrite、access、contentのハンドラがまだ待っているボディも、そろっていないものとして扱います。nginxがその読み込みを打ち切ったときは、`Nginx::Request#body`は、ボディのうちnginxが読んだ部分を返します（項目14）。
- 変更の理由：
  - 不具合：すぐに完了しなかった読み込みは、ハンドラが戻った後もリクエストへの参照を持ち続けました。その読み込みが完了すると、リクエストのフェーズが、そのときnginxが処理していたフェーズからもう一度動き、上に書いた結果になりました。
  - 前の挙動を残せなかった理由：これらのハンドラは待てません。`mruby_set*`は、nginxのrewriteモジュールが実行するスクリプトの中で動きます。logハンドラはnginxがリクエストを解放する途中で動き、フィルタはnginxがレスポンスを送る途中で動きます。nginxは、これらが戻るとすぐに先へ進みます。そのため、このようなハンドラは、既にそろっているボディを受け取って動くか、ボディなしで動くしかありません。ngx_mrubyは、読み込みを始める前に、`Content-Length`とnginxのヘッダのバッファにあるバイトから、これを決めます。chunkedのボディには`Content-Length`が無く、HTTP/2かHTTP/3のボディはイベントで読むので、そろっていないものとして扱います。
  - ドキュメントが示していた挙動：2.7.0のこのディレクティブのドキュメントには、ngx_mrubyが「reads a HTTP request body before calling mruby code」と書いてありました（[mruby_enable_read_request_body](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/directives/README.md#mruby_enable_read_request_body)）。待てないハンドラは、挙げてありませんでした。2.7.0でも2.7.1でも、これらのハンドラはボディの残りを待ちません。2.7.0では、始めた読み込みがすぐに完了しないと、コードが動きませんでした。2.7.1では、コードがすぐに動きます。そのとき、ボディは無いか、待っているハンドラの読み込みをnginxが打ち切った後なら、nginxが読んだ部分になります（項目14）。いまの[ディレクティブのドキュメント](/v2/ja/reference/directives/#mruby_enable_read_request_body)には、これらのハンドラと、ボディなしで動く場合が書いてあります。
- 影響の範囲：`mruby_enable_read_request_body on`の設定で、`mruby_set*`のハンドラ、logハンドラ、出力フィルタがリクエストボディを読むものです。2.7.1では、これらのハンドラは次のボディを受け取りません。それは、chunkedのボディ、HTTP/2かHTTP/3で届くボディ、ヘッダと一緒にnginxのヘッダのバッファに収まらないボディ、ヘッダの後に届くボディです。ただし、前のハンドラかモジュールがボディを読んでいた場合は受け取ります。2.7.0では、これらのボディのうちいくつかを、すぐに読めていました。たとえば、ヘッダと一緒に届いたchunkedのボディや、既に届いていたもっと大きなボディです。
- 必要な対応：これらのハンドラにボディ全体を渡すには、これらより前に動く、待てるハンドラで、先にボディを読んでください。たとえば、`mruby_enable_read_request_body on`を付けたserverレベルの`mruby_post_read_handler_code`です。そのハンドラが動いた後は、後に動くハンドラにとって、ボディはそろっています。
- テスト：`test/t/cases/request_body_later.rb`の、ポート18148とポート18149のテストです。

### 7. Nginx::SSLはハンドシェイクのハンドラの中だけで動きます

- 変更前：`Nginx::SSL`のメソッドを`mruby_ssl_handshake_handler*`の外で呼ぶと、ワーカーがクラッシュするか、意味の無い値が返ることがありました（[#339](https://github.com/matsumotory/ngx_mruby/issues/339)）。対象のメソッドは、`Nginx::SSL.new`、`#servername`、証明書のセッター、`#local_port`、`#tls_version`、`Nginx::SSL.log`です。ドキュメントでは、この使い方はサポートの対象外でした。コードが`Nginx::SSL.new`を呼ばないハンドシェイクは、同じワーカーの前のハンドシェイクが設定した証明書を受け取ることがありました。前のハンドシェイクが使った接続のメモリが再利用されていたときは、ハンドシェイクが失敗することもありました。
- 変更後：ハンドシェイクのハンドラの外では、`RuntimeError`が発生します。メッセージは`Nginx::SSL can only be used in mruby_ssl_handshake_handler`です。コードが証明書を設定しないハンドシェイクは、サーバーの`ssl_certificate`と`ssl_certificate_key`を使います。`Nginx::SSL.new`を呼ばないハンドシェイクも、これに含まれます。
- 変更の理由：
  - 不具合：「変更前」に書いたとおり、ハンドシェイクのハンドラの外では、`Nginx::SSL`でワーカーがクラッシュするか、意味の無い値が返ることがありました（[#339](https://github.com/matsumotory/ngx_mruby/issues/339)）。コードが`Nginx::SSL.new`を呼ばないハンドシェイクは、前のハンドシェイクの証明書を受け取るか、失敗することがありました。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：`Nginx::SSL`は、ハンドシェイクが終わった後も、ハンドシェイクの状態をサーバーの設定に残していました。この状態は、接続、サーバー名、証明書の文字列です。そのため、ハンドシェイクの外で呼んだメソッドは、まだハンドシェイクが動いていないときはNULLポインタを読みました。これが[#339](https://github.com/matsumotory/ngx_mruby/issues/339)のクラッシュです。ハンドシェイクが動いた後に呼ぶと、メソッドはもう有効でないメモリを読みました。また、コードが`Nginx::SSL.new`を呼ばない後のハンドシェイクは、前の接続の文字列を使い回しました。ハンドシェイクの外には接続もサーバー名も無いので、メソッドには返せる正しい値がありません。また、ハンドシェイクが終わるときに、この状態をクリアしなければなりません。
  - ドキュメントが示していた挙動：ドキュメントには2.1.4から、`Nginx::SSL`はハンドシェイクのハンドラで使うもので、ほかのハンドラでは動かないと書いてあります（[2.7.0のNginx::SSL Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxssl-class)）。また、`ssl_certificate`は、証明書を選ばないハンドシェイクにnginxが使う証明書です。
- 影響の範囲：ハンドシェイクのハンドラの外で`Nginx::SSL`を呼ぶハンドラと、一部のハンドシェイクで`Nginx::SSL.new`を呼ばないハンドシェイクのハンドラです。前者の例は、`servername`を読むcontentハンドラです。後者の例は、条件によって早く戻るコードや、オブジェクトを一度だけ作って持ち続けるコードです。毎回のハンドシェイクで`Nginx::SSL.new`を呼ぶハンドシェイクのハンドラは、証明書の変更の影響を受けません。
- 必要な対応：`Nginx::SSL`は、ハンドシェイクのハンドラの中でだけ呼んでください。値を後で使うには、そこで値を保存してください。たとえば、ドキュメントの例のように`Userdata`を使います。ハンドラが一部のハンドシェイクでだけ証明書を設定し、ほかのハンドシェイクで`Nginx::SSL.new`を呼んでいない場合、そのほかのハンドシェイクは、いまは`ssl_certificate`を受け取ります。
- テスト：`test/t/ngx_mruby.rb`の、ポート18089とポート18090のテストです。

### 8. リクエストの外でリクエストのメソッドを呼ぶと、RuntimeErrorが発生します

`mruby_init*`、`mruby_init_worker*`、`mruby_exit_worker*`、`mruby_ssl_handshake_handler*`、`mruby_server_context_handler_code`のコードは、リクエストの外で動きます。`mruby_init*`と`mruby_server_context_handler_code`はnginxが設定を読む間に動き、ほかはワーカーの中で動きます。streamモジュールでは、init、init worker、exit worker、server contextのコードが、セッションの外で動きます。この項目は2つの部分に分かれます。

リクエストの外では、リクエストのメソッド、セッションのメソッド、`Nginx.log`の挙動が変わります。

- 変更前：リクエストスコープのメソッドは、ワーカーが最後に処理したリクエストを持ち続けるポインタから、いまのリクエストを探していました。
  - まだリクエストを処理していない場所では、これらのメソッドを呼ぶとクラッシュしました。`mruby_init*`と`mruby_server_context_handler_code`では、設定を読んでいる最中のnginx自身がクラッシュし、`mruby_init_worker*`ではワーカーがクラッシュしました。
  - `mruby_exit_worker*`と`mruby_ssl_handshake_handler*`では、ワーカーが最後に処理したリクエストを読みました。そのリクエストのメモリは既に解放されていることがあり、その場合は意味の無いデータを読むか、クラッシュしました。ワーカーがまだリクエストを処理していないとき、たとえば最初のハンドシェイクでは、`mruby_init_worker*`と同じくクラッシュしました。
  - `Nginx.log`と`Nginx.errlogger`では、まだリクエストを処理していない場所で`RuntimeError`が発生しました。そのため、`mruby_init*`と`mruby_server_context_handler_code`では、設定が無効になりました。ワーカーが既にリクエストを処理していた場合は、最後に処理したリクエストを通してログを書いていました。
  - streamモジュールの、init、init worker、exit workerのコードにも、同じ問題がありました。
- 変更後：リクエストの外では、リクエストスコープのメソッドで`RuntimeError`が発生します。メッセージは`this method can only be used while processing a request`です。対象は、`Nginx::Request`、`Nginx::Var`、`Nginx::Headers_in`、`Nginx::Headers_out`、`Nginx::Connection`、`Nginx::Server`、`Nginx::Filter`、`Nginx::Upstream`、`Nginx::Async`のメソッドと、`Nginx.rputs`、`Nginx.echo`、`Nginx.return`、`Nginx.send_header`、`Nginx.status_code=`、`Nginx.redirect`です。`Nginx::Server`のうち、`add_listener`は対象から外れます。nginxが設定を読む間に動くコードでは、そこでのほかの例外と同じく、コードがこの例外をrescueしなければ、設定は無効になります。`Nginx.log`と`Nginx.errlogger`は、リクエストの外ではnginxのエラーログに書きます。streamのinit、init worker、exit worker、server contextのコードでは、`Nginx::Stream::Connection.new`、そのメソッドのうちセッションを読むもの、`Nginx::Stream::Async.sleep`で例外が発生します。メッセージは`this method can only be used while processing a stream session`です。`Nginx::Stream::Connection.stream_status`と`stream_status=`では、例外は発生しません。`Nginx::Stream.log`は、nginxのエラーログに書きます。
- 変更の理由：
  - 不具合：「変更前」に書いたとおり、リクエストの外では、リクエストスコープのメソッドでnginxかワーカーがクラッシュするか、ワーカーが最後に処理したリクエストが使われました。`Nginx.log`と`Nginx.errlogger`では、例外が発生するか、そのリクエストを通してログが書かれました。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：リクエストのメソッドは、プロセス全体で共有するポインタからリクエストを探していました。このポインタは、mrubyのコードがリクエストのために動き始めたときに設定され、クリアされませんでした。リクエストの外のコードでは、このポインタはNULLか、ワーカーが最後に処理したリクエストを指していました。NULLのときは、プロセスがクラッシュしました。最後に処理したリクエストのメモリは既に解放されていることがあり、その場合、メソッドは無効なメモリを読みました。`Nginx.log`は、そのリクエストの接続を通してログを書きました。リクエストの外には返すべきリクエストが無いので、メソッドはエラーを伝えなければなりません。前の結果は、クラッシュか、ほかのリクエストのデータでした。`Nginx.log`はリクエストを必要としないので、そこではnginxのエラーログに書きます。
  - ドキュメントが示していた挙動：ドキュメントは、リクエストのメソッドを、リクエストのハンドラの中でだけ示しています。上に挙げたコードは、どのリクエストにも属さずに動きます。2.7.0のドキュメントには、`Nginx.errlogger`について「Sends an error to the log file」と書いてあり、使えるハンドラを限る記述はありません（[2.7.0のNginx.errlogger](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxerrloggerlog_level-string)）。`Nginx.log`は、`Nginx.errlogger`のエイリアスです。
- 影響の範囲：リクエストのデータを使う、init、init worker、exit worker、server context、ハンドシェイクのコードです。このようなコードは、前から確実には動きませんでした。そこで`Nginx.log`の例外に頼っていたコードも、影響を受けます。
- 必要な対応：リクエストのデータは、リクエストのハンドラの中でだけ使ってください。上に挙げたコードでは、いまは`Nginx.log`を使えます。
- テスト：`test/t/ngx_mruby.rb`の、ポート18080で`mruby_init_worker_code`を試すテストと、ポート18089でハンドシェイクのハンドラを試すテストです。

`add_listener`は、server contextのコードの中でだけ使えます。

- 変更前：nginxが設定を読み終えた後、たとえばリクエストのハンドラで`Nginx::Server#add_listener`を呼ぶと、もう有効でない設定のデータが使われました。`Nginx::Stream.add_listener`は、streamのセッション（`mruby_stream_code`）か、streamのinit、init worker、exit workerのコードで呼ぶと、streamモジュールの内部のコンテキストをサーバーの設定と取り違えました。この内部のコンテキストは、いまのセッションを持つものです。その結果、プロセスがクラッシュしました。`mruby_stream_server_context_code`が動く間は、`Nginx::Stream.log`とセッションのメソッドが、サーバーの設定をこの内部のコンテキストと取り違えていました。
- 変更後：`mruby_server_context_handler_code`の外で`Nginx::Server#add_listener`を呼ぶと、`RuntimeError`が発生します。メッセージは`add_listener can only be used in mruby_server_context_handler_code`です。`mruby_stream_server_context_code`の外で`Nginx::Stream.add_listener`を呼ぶと、`RuntimeError`が発生します。メッセージは`add_listener can only be used in mruby_stream_server_context_code`です。どちらのメソッドも、そのコードの中では、nginxが設定を読む間にこれまでどおり動きます。`mruby_stream_server_context_code`では、streamのinitとworkerのコードと同じく、`Nginx::Stream.log`はnginxのエラーログに書き、セッションのメソッドでは例外が発生します。
- 変更の理由：
  - 不具合：「変更前」に書いたとおり、server contextのコードの外で`add_listener`を呼ぶと、もう有効でない設定のデータが使われるか、プロセスがクラッシュしました。`mruby_stream_server_context_code`では、`Nginx::Stream.log`とセッションのメソッドが、サーバーの設定をstreamモジュールの内部のコンテキストと取り違えていました。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：`add_listener`には、nginxの設定のパーサが持つ状態が必要です。この状態は、nginxが設定を読む間だけ存在します。HTTPモジュールの`Nginx::Server#add_listener`は、パースが終わった後にこの状態を使いました。streamモジュールには、内部のコンテキストを置く場所があります。`mruby_stream_server_context_code`が動く間、streamモジュールは、この場所にサーバーの設定を入れていました。`Nginx::Stream.add_listener`は、呼ばれるたびにこの場所を読みました。それ以外のときにこの場所にあるのは内部のコンテキストで、サーバーの設定ではありません。2.7.1では、そのコードが動く間だけ、サーバーの設定を別の場所に置きます。そのため、`add_listener`は、どこで呼ばれたかを判別できます。ほかのメソッドは、常に内部のコンテキストを見つけます。`mruby_stream_server_context_code`は、nginxが設定を読む間に、どのセッションにも属さずに動きます。そのため、streamのinitとworkerのコードと同じく、そこではセッションのメソッドで例外が発生し、`Nginx::Stream.log`はnginxのエラーログに書きます。
  - ドキュメントが示していた挙動：2.7.0のドキュメントは、設定の中の`Nginx::Server#add_listener`を、`mruby_server_context_handler_code`の中でだけ示しています（[Nginx::Server#add_listener](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxserveradd_listenerhash)）。`Nginx::Stream.add_listener`の説明は、その例を参照しています（[Nginx::Stream.add_listener](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxstreamadd_listener)）。ほかのどこで呼べるかは、書いてありません。`mruby_stream_server_context_code`については、何も書いてありません。
- 影響の範囲：server contextのコードの外で`add_listener`を呼ぶコードと、`Nginx::Stream.log`かセッションのメソッドを呼ぶ`mruby_stream_server_context_code`です。前者のコードは、前から動きませんでした。
- 必要な対応：`add_listener`は、server contextのコードの中でだけ呼んでください。`mruby_stream_server_context_code`にはセッションが無いので、そこではセッションのメソッドを呼ばないでください。
- テスト：ポート18080のテストは、`test/t/ngx_mruby.rb`で、contentハンドラの中の`Nginx::Server#add_listener`を試します。ポート12357のテストは、2つ目のnginxで、セッションの中の`Nginx::Stream.add_listener`を試します。2つ目のnginxは`test/t/cases/_second_instance.rb`が起動し、`test/t/cases/stream.rb`は`test/t/cases/_prelude.rb`の`second_instance`でそれを呼びます。

### 9. 後のハンドラがmruby_setの変数を上書きしなくなりました

- 変更前：`mruby_set`か`mruby_set_code`がリクエストの中で動いた後、同じリクエストの後のハンドラが`Nginx::Async.sleep`から再開すると、そのハンドラの最後の式が、その変数に代入されました。serverレベルの`mruby_set`や`mruby_set_code`でも、同じでした。
- 変更後：変数に代入されるのは、その`mruby_set`のハンドラ自身の結果だけです（[#548](https://github.com/matsumotory/ngx_mruby/pull/548)）。自分でsleepする`mruby_set_code`は、これまでどおり、再開したときに変数を設定します。
- 変更の理由：
  - 不具合：`mruby_set`は、変数の名前をリクエストのコンテキストに保存しました。その名前を消す処理はありませんでした。そのため、リクエストの後のハンドラが`Nginx::Async.sleep`から再開すると、ngx_mrubyはそのハンドラの最後の式を変数に書きました。たとえば、後の`proxy_pass`が使う`$backend`に書きました（[#548](https://github.com/matsumotory/ngx_mruby/pull/548)）。
  - 前の挙動を残せなかった理由：設定には、この書き込みを指示する記述がありません。残すと、関係の無いハンドラでsleepしただけで、変数が変わります。
  - ドキュメントが示していた挙動：`mruby_set`の変数は、その`mruby_set`が設定するものです。ディレクティブのドキュメントには、「called at one of the rewrite phases to set a nginx variable」と書いてあります（[2.7.0のmruby_set and mruby_set_code](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/directives/README.md#mruby_set-and-mruby_set_code)）。
- 影響の範囲：`mruby_set*`と、`Nginx::Async.sleep`を使う後のハンドラを組み合わせるリクエストです。
- 必要な対応：ありません。後のハンドラで変数を変えたいときは、`Nginx::Var.new.backend = "..."`のように、明示的に代入してください。
- テスト：`test/t/cases/async_set_var_target.rb`のポート18123のテストです。

### 10. httpブロックの無い設定でもnginxが起動します

- 変更前：ngx_mrubyを組み込んだnginxで、streamだけの設定のように`http {}`ブロックの無い設定を使うと、起動時にすべてのワーカーがクラッシュしました。nginxは、新しいワーカーを起動し続けました。
- 変更後：nginxが起動します（[#536](https://github.com/matsumotory/ngx_mruby/pull/536)）。
- 変更の理由：
  - 不具合：ワーカーの起動と終了のときに動くHTTPモジュールのフックは、HTTPモジュールのメインの設定があるかを確かめずに、その設定を読んでいました。`http {}`ブロックが無いとメインの設定も無く、ワーカーがクラッシュしました（[#536](https://github.com/matsumotory/ngx_mruby/pull/536)）。
  - 前の挙動を残せなかった理由：nginxは、`http {}`の無い設定を受け付けます。ngx_mrubyのstreamモジュールは、既にこの場合を扱っていました。前の挙動はクラッシュなので、残すものはありません。
  - ドキュメントが示していた挙動：`http {}`ブロックの無い設定については、ドキュメントに何も書いてありませんでした。
- 影響の範囲：ngx_mrubyのHTTPモジュールを組み込んだかロードしたnginxで、streamだけの設定のように`http {}`ブロックの無い設定です。
- 必要な対応：ありません。
- テスト：`test/t/ngx_mruby.rb`のポート12399のテストです。

### 11. mrubyのヘッダフィルタの後でNginx.rputsを呼ぶボディフィルタで、「header already sent」がログに出なくなりました

- 変更前：mrubyのヘッダフィルタと、コードが`Nginx.rputs`か`Nginx.echo`を呼ぶmrubyのボディフィルタのあるlocationでは、nginxがリクエストごとに`[alert] header already sent`をログに出しました。また、ボディフィルタは、ヘッダを送った後でステータスを200にしました。ドキュメントには、ボディフィルタでのこの呼び出しをサポートしないと書いてあります。クライアントはnginxが送ったステータスを受け取りましたが、logフェーズには200が見えていました。logハンドラと`log_format`の`$status`は、404のレスポンスでも200を読みました。
- 変更後：nginxはこのアラートをログに出さず、logフェーズには、nginxが送ったステータスが見えます。レスポンスは前と同じです（[#563](https://github.com/matsumotory/ngx_mruby/pull/563)）。ただし、ボディフィルタがボディの長さを変える場合は、レスポンスが変わります。この場合は、項目2の4つ目の部分に書きました。
- 変更の理由：
  - 不具合：ボディフィルタは、コードの最後で`Nginx.rputs`の出力を送るときに、応答するハンドラのためのコードパスを使っていました。このコードパスでヘッダが2回目に送られ、ステータスが200になりました（[#563](https://github.com/matsumotory/ngx_mruby/pull/563)）。
  - 前の挙動を残せなかった理由：このコードパスを残すと、「変更前」に書いたとおり、リクエストごとに`[alert] header already sent`の行が出続けます。また、logフェーズで見えるステータスが、nginxが送ったステータスと違ったままになります。locationにmrubyのヘッダフィルタがあるときは、ボディフィルタはもうこのコードパスを通りません。ボディフィルタでの`Nginx.rputs`の出力は、前と同じく破棄します。
  - ドキュメントが示していた挙動：ドキュメントには、`Nginx.rputs`と`Nginx.echo`は出力のボディフィルタでは使えないと書いてあります。その記述は「You can *NOT* use `Nginx.rputs` and `Nginx.echo` in the output body handlers」です（[2.7.0のNginx::Filter Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)）。logフェーズが見るステータスについては、何も書いてありませんでした。
- 影響の範囲：`mruby_output_header_filter`かその`_code`の形と、`mruby_output_body_filter`かその`_code`の形があり、ボディフィルタが`Nginx.rputs`か`Nginx.echo`を呼ぶlocationです。それらのリクエストの`$status`か`header already sent`の行を読む、アクセスログ、logハンドラ、アラートも影響を受けます。
- 必要な対応：ログの統計かアラートが、それらのリクエストのステータス200か`header already sent`の行に頼っている場合を除き、ありません。
- テスト：`test/t/cases/output_filters_with_content_handler.rb`のポート18138のテストです。

### 12. 応答の前に、読まれていないリクエストボディを破棄します

- 変更前：ハンドラがリクエストボディを読まずに`Nginx.rputs`か`Nginx.echo`で応答すると、ボディが接続に残りました。たとえば、`mruby_enable_read_request_body`なしで`POST`に応答するcontentハンドラやrewriteハンドラが、これに当たります。nginxが次のリクエストのために開いておく接続、つまりHTTP/1.1のkeep-aliveの接続では、nginxはそのボディを次のリクエストの始まりとして読みました。
- 変更後：ngx_mrubyは、そのようなハンドラの応答を送る前に、どのハンドラも読んでいないリクエストボディを破棄します。nginxも、`return`、エラーページ、静的ファイルのような自分のレスポンスの前に、同じようにボディを破棄します。接続の次のリクエストは、クライアントがボディの後に送ったリクエストになります。ボディを破棄できない場合は、nginxは応答の後に接続を閉じます。`Nginx::Async.sleep`の後でrewriteハンドラが送る応答も、同じです。ハンドラが読んだボディは、影響を受けません。
- 変更の理由：
  - 不具合：nginxは、リクエストに応答するハンドラが、先にリクエストボディを読むか破棄すると想定しています。開いたままの接続では、次のリクエストがボディの後から始まるからです。nginx自身のハンドラは、これをしています。`ngx_http_send_response`、エラーページ、staticモジュールがそうです。mrubyのハンドラの応答は、これをしていませんでした。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：「変更前」に書いたとおり、接続に残ったボディは、次のリクエストの始まりとして読まれます。ボディを破棄すれば、ハンドラが必要としないボディのために応答を待たせずに、接続を使える状態に保てます。
  - ドキュメントが示していた挙動：どのハンドラも読まないリクエストボディについては、ドキュメントに何も書いてありませんでした。
- 影響の範囲：`POST`や`PUT`のようにボディのあるリクエストに、ボディを読まずに`Nginx.rputs`か`Nginx.echo`で応答するハンドラのうち、nginxが次のリクエストのために開いておく接続で応答するものです。
- 必要な対応：ありません。ボディが必要なハンドラは、これまでどおり`mruby_enable_read_request_body on`でボディを読みます。
- テスト：`test/t/cases/unread_request_body.rb`のポート18151のテストです。contentハンドラ、rewriteハンドラ、`Nginx::Async.sleep`の後で応答するrewriteハンドラを試します。

### 13. contentハンドラが出力なしで終わると、レスポンスは500になります

- 変更前：メインリクエストの`mruby_content_handler*`が出力なしで終わり、ステータスを設定していないか、200、201、204以外の300未満のステータスを設定していると、レスポンスが送られませんでした。ステータスを設定していない場合とは、何も書かない場合、`Nginx.rputs`で`""`か`nil`だけを書く場合、レスポンスヘッダだけを設定する場合です。300未満のステータスの例は`Nginx::HTTP_ACCEPTED`です。`Nginx::Async::HTTP.sub_request`が再開した後にこのように終わった場合も、同じでした。nginxは、HTTP/1.0の接続を1バイトも送らずに閉じ、HTTP/1.1のkeep-aliveの接続を、`keepalive_timeout`までレスポンスなしで開いたままにしました。`keepalive_timeout`のデフォルトは75秒です。そのため、クライアントは自分のタイムアウトまで待ちました。
- 変更後：そのようなリクエストには500が返ります。そのボディはnginxのエラーページか、500に設定した`error_page`で、ヘッダには`Connection: close`が付きます。これは、出力なしで`Nginx.return Nginx::HTTP_OK`を呼んだ場合に、2.7.0でも既に返っていた応答と同じです（[#566](https://github.com/matsumotory/ngx_mruby/pull/566)）。error.logには、`error`レベルの行が出ます。その行は、`status code is not set, and response body is empty. return NGX_HTTP_INTERNAL_SERVER_ERROR`か、`status code is <N>, but response body is empty. return NGX_HTTP_INTERNAL_SERVER_ERROR`です。
- 変更の理由：
  - 不具合：nginxは、201、204、300以上のステータスには、自分でレスポンスを送ります。しかし、ステータスが300未満で201と204以外のときと、ステータスが設定されていないときは、何も送らずにリクエストを終えます。出力の無いハンドラは、そのような値をそのままnginxに渡していました。レスポンスの無いリクエストは、不具合です（[#225](https://github.com/matsumotory/ngx_mruby/issues/225)）。クライアントは自分のタイムアウトまで待たされ、keep-aliveの接続が無駄に占有されます。
  - 前の挙動を残せなかった理由：残すと、これらのリクエストはレスポンスの無いままになります。2.x系列では、これらのリクエストに、出力なしの`Nginx::HTTP_OK`に既に返していたのと同じ応答を返します。そのため、2.7.0でレスポンスを受け取っていたリクエストが、違うレスポンスを受け取ることはありません。
  - ドキュメントが示していた挙動：出力なしで終わるcontentハンドラについては、ドキュメントに何も書いてありませんでした。`Nginx.return`については、「Returns nginx status code to send back HTTP status to the client」と書いてあります（[2.7.0のNginx.return](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxreturnnginx_status_code)）。2.7.1では、202のようなステータスを出力なしで返すと、500になります。2.7.0でも、出力なしの200は、既に500になっていました。
- 影響の範囲：出力なしで終わることのある`mruby_content_handler*`のハンドラと、これらの行を見るログの監視です。ハンドラの例は、条件が成り立つときだけ書くハンドラや、空か`nil`になりうる値を`Nginx.rputs`に渡すハンドラです。`status code is 200, but response body is empty`に一致させるパターンは新しい行に一致せず、`response body is empty`に一致させるパターンでは、新しい行も数に入ります。
- 必要な対応：空のボディで応答すべきハンドラは、`Nginx.return Nginx::HTTP_NO_CONTENT`を呼ぶか、ボディを書いてください。`Nginx::HTTP_NO_CONTENT`の場合は、nginx自身が204で応答します。拒否するつもりのハンドラは、`Nginx::HTTP_FORBIDDEN`や`Nginx::HTTP_UNAUTHORIZED`のように、意図したステータスを返してください。`status code is 200, but response body is empty`に一致させるログの監視は、新しい行も見えるように、`response body is empty. return NGX_HTTP_INTERNAL_SERVER_ERROR`に一致させてください。
- このリリースで変わらない挙動：出力なしの`Nginx::HTTP_OK`は、前と同じく500です。201、204、300以上のステータスは、nginx自身が応答します。出力のあるハンドラ、`Nginx.redirect`、ほかの種類のハンドラ、サブリクエストも変わりません。`Nginx::Async::HTTP.sub_request`、SSIのinclude、`auth_request`には、前と同じサブリクエストのステータスが見えます。出力を書いてから、200、201、204以外の300未満のステータスを設定するcontentハンドラは、これまでどおりレスポンスを送りません。たとえば、`Nginx.rputs "x"`の後に`Nginx.return Nginx::HTTP_ACCEPTED`を呼ぶ場合です。この件は、「既知の制限」の節に書きました。
- テスト：`test/conf/conf.d/54-content-empty-body.conf`と`test/t/cases/content_empty_body.rb`の、ポート18136のテストです。

### 14. nginxがボディの読み込みを打ち切った後は、待っていたハンドラが動かなくなりました

`mruby_enable_read_request_body on`のとき、post-read、server rewrite、rewrite、access、contentのハンドラは、リクエストボディの残りを待ちます（項目6）。nginxは、ボディの残りを受け取らずに、その読み込みを打ち切ることがあります。それは、`client_body_timeout`が切れたとき、クライアントが接続を閉じたとき、nginxがボディに400か413で応答したときです。400で応答する例は、不正なchunkedのボディです。この項目では、そのようなリクエストでその後に動く、mrubyのlogハンドラとmrubyの出力フィルタについて書きます。

- 変更前：待っていたハンドラがpost-readハンドラかaccessハンドラのとき、そのようなlogハンドラやフィルタは、自分でボディの読み込みを始めました。その読み込みによって、待っていたハンドラのコードが、nginxが既に応答を決めた後に動きました。このコードは、それまで動いていませんでした。`client_body_timeout`の後や、クライアントが接続を閉じた後は、accessハンドラのコードがlogフェーズの間に動きました。不正なchunkedのボディの後は、nginxが400エラーページを送る間にそのコードが動きました。`Content-Length`はページ1回分の長さなのに、レスポンスにはページが2回入りました。`Content-Length`は157バイトで、送ったのは314バイトでした。待っていたハンドラがserver rewrite、rewrite、contentのハンドラのときは、2.7.0ではリクエストがまったく終わりませんでした（項目6）。そのため、このlogハンドラやフィルタは動きませんでした。
- 変更後：logハンドラやフィルタは、読み込みを始めずに、すぐに動きます。`Nginx::Request#body`は、ボディのうち、nginxが読み込みを打ち切る前に読んだ部分を返します。その部分がnginxのボディのバッファ（`client_body_buffer_size`）に収まらず、nginxが一時ファイルに書いていた場合は、`nil`を返します。error.logには、項目6の`info`の行が出ます。その行は`the request body is not complete and a log handler cannot wait for it, so it runs without the body`で、フィルタでは、`a log handler`の部分が`a header filter`か`a body filter`になります。待っていたハンドラのコードは動きません。`client_body_timeout`の後は、nginxはレスポンスなしで接続を閉じ、アクセスログには408が記録されます。クライアントが接続を閉じた後は、アクセスログに400が記録されます。不正なchunkedのボディの後は、クライアントはnginxの400エラーページを1回だけ受け取り、nginxは接続を閉じます。
- 変更の理由：
  - 不具合：リクエストボディの読み込みが既に始まっているとき、nginxは、新しい読み込みの完了ハンドラをすぐに呼びます。ngx_mrubyの完了ハンドラは、待っているハンドラのために、リクエストのフェーズを動かし直します。そのため、nginxが最初の読み込みを打ち切った後に読み込みを始めたlogハンドラやフィルタは、logフェーズから、またはレスポンスの途中から、それらのフェーズを動かし直しました。この不具合は、セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてあります。
  - 前の挙動を残せなかった理由：nginxが打ち切った読み込みは完了しないので、待っているハンドラには、動くためのボディがありません。また、nginxは既にリクエストに応答したか、リクエストを閉じています。そのときにそのハンドラのコードを動かすと、終わったリクエストのために、アクセスチェックやログの行のような処理をし、nginxのエラーページを2回目に送りました。logハンドラやフィルタはボディを待てないので（項目6）、自分で読み込みを始めずに、nginxが読んだもので動きます。
  - ドキュメントが示していた挙動：2.7.0のこのディレクティブのドキュメントには、ngx_mrubyが「reads a HTTP request body before calling mruby code」と書いてありました（[mruby_enable_read_request_body](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/directives/README.md#mruby_enable_read_request_body)）。nginxがボディ全体を読まずに打ち切る読み込みについては、何も書いてありませんでした。
- 影響の範囲：`mruby_enable_read_request_body on`の設定のうち、post-readハンドラかaccessハンドラがボディを読み、同じリクエストでmrubyのlogハンドラかmrubyの出力フィルタも動くものです。対象は、nginxがボディの読み込みを早く打ち切るリクエストで、`client_body_timeout`、閉じた接続、ボディへの400か413の応答がこれに当たります。
- 必要な対応：ありません。ボディを待つハンドラのコードは、ボディ全体を受け取って1回だけ動きます。ボディがそろう前にnginxが読み込みを打ち切ったときは、動きません。
- テスト：`test/t/cases/request_body_later.rb`のポート18152のテストです。待っているrewriteハンドラについて、`client_body_timeout`の後のlogハンドラと、不正なchunkedのボディを受けた後のヘッダフィルタとボディフィルタを試します。

## 新機能

ありません。

## 不具合の修正

セキュリティアドバイザリ[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)に書いてある修正：

- ボディフィルタは、ボディを`Nginx.rputs`か`Nginx.echo`で書いた場合も、locationにmrubyのヘッダフィルタもある場合も、自分の出力のとおりにボディを送ります（項目2）。
- ヘッダフィルタの中の`Nginx::Filter#body`は、レスポンスのボディでないバイトを返さなくなりました。ボディフィルタの外で`Nginx::Filter#body=`と`#output`を呼んでも、後にボディフィルタが続くときに、ワーカーがクラッシュしなくなりました（項目2）。
- accessハンドラかpost-readハンドラが`Nginx.rputs`の後で例外を投げると、nginxはcontentハンドラへ進まず、500でリクエストが止まります。`mruby_set*`のハンドラかヘッダフィルタで例外が発生した後は、破棄した出力が`Content-Length`に入らなくなりました（項目3）。
- `mruby_enable_read_request_body`を使う場合について、次のように直しました。ハンドラが始まった後にリクエストボディが届くserver rewrite、rewrite、contentのハンドラは、ボディを受け取って動きます。遅れて届いたボディの後でも、`auth_request`のような後のフェーズのハンドラが再開します。クライアントが接続を閉じたときや`client_body_timeout`が切れたときは、nginxがリクエストを開いたままにせずに終えます（項目6の1つ目の部分）。
- ハンドシェイクのハンドラの外で`Nginx::SSL`を使うと、ワーカーがクラッシュしたり意味の無い値が返ったりせずに、`RuntimeError`が発生します（項目7、issue [#339](https://github.com/matsumotory/ngx_mruby/issues/339)）。
- コードが`Nginx::SSL.new`を呼ばない後のハンドシェイクは、前のハンドシェイクの証明書を受け取らなくなりました（項目7）。
- リクエストやセッションの外で、リクエストスコープのメソッドとstreamのセッションのメソッドを呼ぶと、クラッシュしたり、ほかのリクエストのデータを使ったりせずに、`RuntimeError`が発生します。`mruby_server_context_handler_code`の外で`Nginx::Server#add_listener`を呼ぶと例外が発生し、リクエストの外では`Nginx.log`がnginxのエラーログに書きます（項目8）。
- `mruby_stream_server_context_code`の外で`Nginx::Stream.add_listener`を呼ぶと、streamモジュールの内部のコンテキストをサーバーの設定として使わずに、例外が発生します（項目8）。
- `Nginx.rputs`で応答しても、読まれていないリクエストボディが接続に残らなくなりました（項目12）。
- nginxがボディ全体を受け取らずに読み込みを打ち切った後に動くlogハンドラや出力フィルタは、ボディを待っていたハンドラのコードを動かさなくなりました。nginxは、ボディに対するエラーページを1回だけ送ります（項目14）。

そのほかの修正：

- logハンドラと出力フィルタでは、`Nginx::Async`でタイマーや中断したハンドラが残らずに、例外が発生します（[#551](https://github.com/matsumotory/ngx_mruby/pull/551)、項目4）。
- `Nginx::Async`がハンドラを再開した後に例外が発生すると、500が返り、ワーカーの次のリクエストは失敗しなくなりました。その間にnginxがキューへ入れたposted requestsも動きます（[#550](https://github.com/matsumotory/ngx_mruby/pull/550)、項目5）。
- `mruby_enable_read_request_body`を使うとき、`mruby_set*`、logハンドラ、フィルタは、ボディの読み込みを残さなくなりました（項目6の2つ目の部分）。
- 再開したハンドラは、前の`mruby_set`の変数を上書きしなくなりました（[#548](https://github.com/matsumotory/ngx_mruby/pull/548)、項目9）。
- `http {}`ブロックの無い設定でも、nginxが起動します（[#536](https://github.com/matsumotory/ngx_mruby/pull/536)、項目10）。
- 出力なしで終わるcontentハンドラは、クライアントをレスポンスの無いままにせず、500を返します（[#566](https://github.com/matsumotory/ngx_mruby/pull/566)、[#225](https://github.com/matsumotory/ngx_mruby/issues/225)の修正、項目13）。
- mrubyのヘッダフィルタと、`Nginx.rputs`か`Nginx.echo`でレスポンスを書くmrubyのハンドラのあるlocationで、ワーカーがクラッシュしなくなりました。そのハンドラの例は、contentハンドラやrewriteハンドラです。ヘッダフィルタは1回だけ動きます（[#563](https://github.com/matsumotory/ngx_mruby/pull/563)、[#206](https://github.com/matsumotory/ngx_mruby/issues/206)の修正）。
- `Nginx.rputs`か`Nginx.echo`を呼ぶヘッダフィルタで、ワーカーがクラッシュしなくなりました（[#563](https://github.com/matsumotory/ngx_mruby/pull/563)）。ただし、この呼び出しは、引き続きサポートしていません。nginxの`return`のように、`Nginx.rputs`で書くmrubyのハンドラ以外がレスポンスを作った後では、レスポンスにその出力は入りません。一方、mrubyのボディフィルタの無いlocationでは、その出力が`Content-Length`に入り、locationのlogハンドラがレスポンスの後にその出力を送ります。`Nginx.rputs`で書くmrubyのcontentハンドラの後では、その出力はそのハンドラの出力に足され、`Content-Length`にも入ります。その後で例外を投げるヘッダフィルタは自分の出力だけを破棄し、mrubyのcontentハンドラの出力は、その`Content-Length`とともにレスポンスに残ります（項目3）。
- `Nginx::SSL`の証明書のセッターは、NULバイトを含む値も、最後までコピーします。
- 失敗した`Nginx::Async::HTTP.sub_request`は、ワーカーが終了するまでハンドラのFiberをガベージコレクタに登録したままにしなくなりました（[#547](https://github.com/matsumotory/ngx_mruby/pull/547)）。
- 同じ種類の後のディレクティブがstreamのディレクティブを置き換えるとき、置き換えられたコードが使っていたmrubyのコンパイラコンテキストが、nginxが設定を読む間にリークしなくなりました（[#540](https://github.com/matsumotory/ngx_mruby/pull/540)）。
- Rubyから`Server`ヘッダを設定するとき、ゼロで初期化したヘッダの要素を確保するようになりました（[#530](https://github.com/matsumotory/ngx_mruby/pull/530)）。

## 既知の制限

このリリースでは、2.7.0の次の挙動を変えていません。

- **contentハンドラで`Nginx::Async.sleep`を呼ぶと、レスポンスなしでリクエストが終わります。**`mruby_content_handler*`は、例外なしで`Nginx::Async.sleep`を呼べます（項目4）。しかし、nginxはsleepするcontentハンドラを待ちません。リクエストはsleepの位置で終わり、クライアントにはレスポンスが返らず、sleepの後のコードは動きません。ドキュメントには、`Nginx::Async`が動く場所として、set、rewrite、accessのハンドラが挙げてあります。代わりに、rewriteハンドラかaccessハンドラでsleepしてください。rewriteハンドラなら、sleepの後でレスポンスを書くこともできます。後のリリースで、contentハンドラが待てるようにする予定です。
- **出力を書くaccessハンドラやpost-readハンドラでは、リクエストが先へ進みます。**`Nginx.rputs`か`Nginx.echo`で書き、例外もエラーのステータスもなしで終わるaccessハンドラやpost-readハンドラでは、ハンドラが終わるときに、その出力がレスポンスとして送られます。続いて、nginxは後のフェーズへ進みます。locationのcontentハンドラかnginxのstaticモジュールも動き、nginxは`header already sent`をログに出します。contentハンドラが書いたものは、`Content-Length`を超えてレスポンスの後に続きます。これらのハンドラでは、出力を書かないでください。リクエストを拒否するには、`Nginx.return Nginx::HTTP_FORBIDDEN`のようにエラーのステータスを返し、応答はcontentハンドラで書いてください。3.x系列では、各フェーズのハンドラがレスポンスを終える方法を、設計し直す予定です。
- **出力の後に、200、201、204以外の300未満のステータスを設定すると、レスポンスが送られません。**`Nginx.rputs`か`Nginx.echo`で出力を書いてから、たとえば`Nginx.return Nginx::HTTP_ACCEPTED`で、このようなステータスを設定するcontentハンドラが、これに当たります。nginxは、HTTP/1.0の接続を1バイトも送らずに閉じ、HTTP/1.1のkeep-aliveの接続を、レスポンスなしで開いたままにします。レスポンスにボディがあるときは`Nginx::HTTP_OK`で、無いときは`Nginx::HTTP_NO_CONTENT`で応答してください。3.x系列では、どのステータスでもボディを返せるようにする予定です。

## ビルドとテストの変更

- デフォルトのnginx：`build.sh`と`test.sh`が使う`nginx_version`は1.31.6です。2.7.0では1.27.2でした。
- CIは、nginx 1.31.6、1.30.5、1.28.3、1.26.3のそれぞれで、ngx_mrubyを静的モジュールと動的モジュールとしてテストします。OpenSSLは、ランナーに共有ライブラリとして入れた1.1.1wと、nginxに組み込んでコンパイルした1.1.1wの両方を使います。さらに、AddressSanitizerとUndefinedBehaviorSanitizerを有効にした1.31.6のビルドを1つ加えます。CIはプルリクエストで動き、`ci-ok`のチェックを1つ報告します。2.7.0では、1.27.2と1.26.2をテストしていました。
- CI：ルールセットが必須にしているステータスチェックの`ci-ok`は、新しいジョブ`files`の成功も必要とするようになりました。`files`のジョブは、リポジトリに置かないファイルをプルリクエストが追加すると失敗します（[#578](https://github.com/matsumotory/ngx_mruby/pull/578)）。このジョブが動かす`test/check_added_files.sh`は、変更が追加するファイルと、変更のコミットのどれかが追加するファイルが、`test/forbidden_paths.txt`のパターンにマッチするかを調べます。パターンには、`.claude/settings.json`、`.claude/commands/`、`.claude/skills/`を除く`.claude/`の中身、`build/`や`build_dynamic/`のようなトップのビルドディレクトリ、`mruby/build/`、`./configure`かMakefileがトップに書くファイル、`.log`のファイル、`.env`と`.env.*`、秘密鍵、エディタとOSが残すファイル、コアダンプなどがあります。変更が追加する空のファイルも、名前が`.gitkeep`か`.keep`でなければ失敗します。`master`、`next`、`v*.x`へのpushでは、このジョブはpushしたコミットを調べます。このジョブはリポジトリの履歴全体を必要とします。チェックの内容と、プルリクエストを開く前に手元で動かす方法は、`docs/test/README.md`に書いてあります。
- `build_config.rb`は、`mruby-redis`をビルドしなくなりました（[#528](https://github.com/matsumotory/ngx_mruby/pull/528)、項目1）。
- `build.sh`：`OPENSSL_SRC_VERSION`を設定すると、ダウンロードしたOpenSSLのソースディレクトリを、絶対パスで`configure`に渡します。そのため、OpenSSLの`./config`は、必要とする絶対パスの`--prefix`を受け取ります。2.7.0では、`Directory given with --prefix MUST be absolute`で止まっていました（[#562](https://github.com/matsumotory/ngx_mruby/pull/562)、[#519](https://github.com/matsumotory/ngx_mruby/issues/519)の修正）。`build.sh`か`test.sh`のコマンドラインで指定した`--with-openssl-src`は、これまでどおり、そのまま`configure`に渡します。
- `test.sh`：HTTPのテストサーバーは、58xxxの代わりに、エフェメラルポートの範囲より下の18080-18090、18101-18103、18110-18131、18136、18138、18143-18144、18147-18152で待ち受けます。streamのサーバーは12345-12359を使い、1つのテストが12399を使います。また、`test.sh`は、`test/conf/conf.d`の設定の断片を読み込み、`test/t/cases`のケースのファイルを実行します。この仕組みは、[テストのドキュメント](/v2/ja/contributing/test/)に書いてあります。nginxをビルドするときに追加するコンパイラとリンカのフラグは、`NGINX_EXTRA_CC_OPT`と`NGINX_EXTRA_LD_OPT`から受け取ります。nginxのバージョンは全体で比べるので、nginx 1.11.5以降のビルドには、パッチ番号に関わらずstream accessモジュールが入ります。項目6のテストのために、nginxを`--with-http_auth_request_module`付きでconfigureします。前の`test.sh`が作ったビルドディレクトリには、もう一度configureするまで、前のconfigureのオプションが残ります。この点は、テストのドキュメントに書いてあります。
- リポジトリに、セキュリティポリシー（`SECURITY.md`）、コントリビューターとエージェント向けの指示（`AGENTS.md`）、`v2.x`ブランチができました。`v2.x`ブランチは、v3が`master`に昇格するまで、`master`をミラーします。

## 性能

2026-10-14に、`next`ブランチにある、callgrindで命令数を比べるスクリプトで測りました。使ったのは`570cdab05`の`test/perf/compare.sh`で、手順は同じブランチの`docs/test/README.md`の「Performance comparison with callgrind」にあります。両方のツリーを、nginx 1.31.6と21個のサードパーティのmrbgemsでリリースビルドしました。mrbgemsは、両方のツリーで同じものを同じコミットで使いました。環境は、aarch64のマシンで動くコンテナのUbuntu 22.04で、gccは11.4、valgrindは3.18.1です。各シナリオで、ウォームアップとして2000回のリクエストを送った後に、20000回のリクエストを送りました。ベースは`b3df70f`の`master`で、2.7.1の修正とマージした2.xのコミットです。そのツリーは`4e88dcc47eca`です。ヘッドは`b3df70f`に2.7.1の修正を加えたもので、ツリーは`3ab6c7888a35`、`src/`のツリーは`8b6857bd850e`です。次の表に、mrubyのガベージコレクタの分を除いて、nginxが1リクエストあたりに実行する命令数を示します。

| シナリオ | `b3df70f` | 2.7.1 | 変化率 |
|---|---|---|---|
| `hello` | 11159 | 11297 | +1.23% |
| `headers` | 34154 | 34286 | +0.39% |
| `var` | 19837 | 19976 | +0.70% |
| `filter` | 13229 | 13308 | +0.60% |
| `sleep` | 14719 | 14852 | +0.90% |
| `sub_request` | 47443 | 47617 | +0.37% |
| `file` | 13196 | 13335 | +1.05% |
| `proxy_plain_2k` | 21952 | 21992 | +0.18% |
| `proxy_plain_64k` | 22262 | 22301 | +0.18% |
| `proxy_stream_plain_50` | 54301 | 54348 | +0.09% |
| `auth` | 46237 | 46299 | +0.13% |
| `route_json_2k` | 106148 | 106224 | +0.07% |
| `ruby_call_1` | 10736 | 10799 | +0.59% |
| `ruby_call_10` | 35215 | 35557 | +0.97% |

どのシナリオの変化率も、比較のスクリプトが警告を出す3%を下回りました。相対の変化が最も大きいのは`hello`で、+1.23%、1リクエストあたり約138命令です。絶対の変化が最も大きいのは`ruby_call_10`で、10回の`mruby_set_code`の呼び出しで1リクエストあたり342命令、1回あたり約34命令です。修正では、リクエストごとにメモリを確保する処理を自前では足しておらず、ブロッキングの待ちも足していません。リクエストボディの残りが必要なハンドラは、nginxの`NGX_DONE`で待ち、nginxがボディを読んだときにもう一度動きます。`Nginx.rputs`の後で発生する例外に対する修正では、ハンドラが動く前に、`Nginx.rputs`のチェーン、その最後のリンク、レスポンスの長さを記録します。`Nginx.rputs`がチェーンを始めるときにも、長さをもう一度記録します。例外の後には長さを元に戻し、ヘッダフィルタではチェーンも元に戻します。メモリを確保しうる新しい呼び出しは、nginx自身の`ngx_http_discard_request_body`だけで、どのハンドラも読まなかったchunkedのリクエストボディのために呼びます。

## アップグレード

サポートするnginxのバージョンは、[README.md](https://github.com/matsumotory/ngx_mruby/blob/master/README.md)に書いたとおり、nginxのmainlineとstableの系列です。CIは、「ビルドとテストの変更」の節に挙げたバージョンをビルドします。

- 追加、改名、削除したディレクティブはありません。このリリースで、モジュールをビルドし直してください。2.7.0をビルドしたときと同じ方法で、ngx_mruby 2.7.1を組み込んだnginxをビルドし、いつもどおりバイナリか動的モジュールを置き換えます。`Nginx.module_version`は`2.7.1`を返します。
- mrubyは3.3.0のままで、ngx_devel_kitも変わりません。
- `build_config.rb`のサードパーティのmrbgemsは、2.xでは、これまでどおりバージョンを固定せずに、それぞれのリポジトリから取得します。そのため、新しいビルドには、2.7.0のビルドより新しいバージョンのmrbgemsが入ることがあります。Redisには、項目1の変更が必要です。

アップグレードの前に、次の点を確かめてください。各行は、上の各項目の「必要な対応」です。ここに無い項目では、変更は要りません。

- Redis：ビルドの前に、`build_config.rb`の`conf.gem github: 'matsumotory/mruby-redis'`のコメントアウトを外してください（項目1）。
- 出力フィルタ：ボディフィルタから`Nginx.rputs`と`Nginx.echo`を取り除いてください。ヘッダフィルタでは、レスポンスの長さを`Nginx::Var.new.sent_http_content_length`で読んでください。ボディの設定と読み取りは、`mruby_output_body_filter*`の中だけにしてください（項目2）。
- 出力の後の`raise`：`raise`は、エラーのためだけに使ってください。contentハンドラを途中で終えるには`Nginx.return Nginx::HTTP_OK`と`return`を使い、accessハンドラとpost-readハンドラでは出力を書かないでください（項目3）。
- logハンドラか出力フィルタの中の`Nginx::Async`か`Fiber.yield`：rewriteハンドラかaccessハンドラ、または`mruby_set_code`の中で待ち、結果をnginxの変数で渡してください（項目4）。
- `Nginx::Async`の後の例外で500以外を返すべきハンドラ：ハンドラの中で例外をrescueし、そこでステータスを設定してください（項目5）。
- リクエストボディが必要な`mruby_set*`、logハンドラ、出力フィルタ：serverレベルの`mruby_post_read_handler_code`のような待てるハンドラで、先にボディを読んでください（項目6）。
- `Nginx::SSL`：ハンドシェイクのハンドラの中でだけ呼んでください。一部のハンドシェイクでだけ証明書を設定し、ほかのハンドシェイクで`Nginx::SSL.new`を呼ばないハンドラでは、いまは、そのほかのハンドシェイクが`ssl_certificate`を受け取ります（項目7）。
- リクエストの外で動くコードで呼ぶリクエストのメソッド：対象のコードは、項目8に挙げたものです。リクエストのデータは、リクエストのハンドラの中でだけ使ってください。`add_listener`は、server contextのコードの中でだけ呼んでください。`mruby_stream_server_context_code`では、セッションのメソッドを呼ばないでください（項目8）。
- mrubyのヘッダフィルタの後で`Nginx.rputs`を呼ぶボディフィルタについて、ステータス200か`header already sent`の行に頼るログの統計やアラート：それらを更新してください。logフェーズには、nginxが送ったステータスが見えるようになり、nginxはその行をもうログに出しません（項目11）。
- 出力なしで終わることのあるcontentハンドラ：`Nginx::HTTP_NO_CONTENT`を返すか、ボディを書いてください。`status code is 200, but response body is empty`に一致させるログの監視も、更新してください（項目13）。
