---
title: "ngx_mruby v2.7.1"
language: "en"
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.md"
source_sha256: "018b2e3cd2b0669ef9f7f179ab4875bd2018842712ee754e089083cf3027ec4a"
source_at_revision: true
source_url: "https://github.com/matsumotory/ngx_mruby/blob/967b61cc6a232aa1ad27dd452c8b5835f94c2a01/docs/releases/v2.7.1.md"
html_url: "https://ngx.mruby.org/v2/releases/v2.7.1/"
markdown_url: "https://ngx.mruby.org/v2/markdown/releases/v2.7.1.md"
last_updated: "2026-10-19"
---

# ngx_mruby v2.7.1

ngx_mruby 2.7.1 is a maintenance and security release of the 2.x line. It fixes
the issues described in the security advisory
[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
and several bugs found while extending the test suite and reviewing the code.

Some of these fixes change what an existing configuration or Ruby script can
observe, for example a status code, a response body or its length, an
exception, whether nginx goes on with the next phase, which certificate a
handshake gets, or whether nginx starts. If a script was written for the old
behavior, it may need a change. Read the first section before you upgrade.
Each entry says what you could observe in 2.7.0 ("Before"), what you observe in
2.7.1 ("Now"), why the behavior had to change ("Why it changed": "The defect"
or "The reason", "Why the old behavior could not be kept" and "What the
documentation promised"), which configurations are affected, what to do, and,
where the test suite covers the change, the port of its tests. "Not changed by
this release" names a related behavior that stays as it was. "Known
limitations" lists behavior that this release leaves as it was.

## Behavior changes: read before upgrading

| If your build, configuration or scripts use | Read |
|---|---|
| `Redis` and the default `build_config.rb` | [1](#1-redis-is-no-longer-in-the-default-build) |
| `mruby_output_body_filter*`, `mruby_output_header_filter*` or `Nginx::Filter` | [2](#2-output-filters-response-length-and-nginxfilter) |
| `raise` after `Nginx.rputs` or `Nginx.echo` | [3](#3-an-exception-after-nginxrputs-answers-500) |
| `Nginx::Async` or `Fiber.yield` in a log handler or an output filter | [4](#4-nginxasync-raises-in-log-handlers-and-output-filters) |
| `Nginx::Async.sleep` or `Nginx::Async::HTTP.sub_request` in a handler that raises or calls `Nginx.redirect` after it resumes, or `sub_request` to a location that sleeps | [5](#5-an-exception-after-nginxasync-answers-500) |
| `mruby_enable_read_request_body on` | [6](#6-mruby_enable_read_request_body-which-handlers-wait-for-the-whole-body) |
| `Nginx::SSL` or `mruby_ssl_handshake_handler*` | [7](#7-nginxssl-works-only-in-the-handshake-handler) |
| request methods or `Nginx.log` in `mruby_init*`, `mruby_init_worker*`, `mruby_exit_worker*`, `mruby_server_context_handler_code` or the handshake handler; `Nginx::Server#add_listener` or `Nginx::Stream.add_listener`; the stream init, init worker, exit worker or server context code | [8](#8-request-methods-outside-a-request-raise-runtimeerror) |
| `mruby_set*` and `Nginx::Async.sleep` in a later handler of the same request | [9](#9-mruby_set-variables-are-no-longer-overwritten-by-later-handlers) |
| a configuration without an `http {}` block | [10](#10-nginx-starts-without-an-http-block) |
| `mruby_output_header_filter*` and `mruby_output_body_filter*` in one location, with `Nginx.rputs` or `Nginx.echo` in the body filter | [11](#11-a-body-filter-that-calls-nginxrputs-after-an-mruby-header-filter-no-longer-logs-header-already-sent) |
| a handler that answers a request with a body (`POST`, `PUT`) with `Nginx.rputs` or `Nginx.echo` without reading the body | [12](#12-an-unread-request-body-is-discarded-before-the-answer) |
| `mruby_content_handler*` that can end without output, or log monitoring that matches `response body is empty` | [13](#13-a-content-handler-that-ends-without-output-answers-500) |
| `mruby_enable_read_request_body on` with a post-read or access handler, and `mruby_log_handler*` or an mruby output filter for the same request | [14](#14-the-waiting-handler-no-longer-runs-after-nginx-ended-its-body-read) |

### 1. Redis is no longer in the default build

- Before: `build_config.rb` included `matsumotory/mruby-redis`, so a binary
  built with the default configuration had the `Redis` class.
- Now: that line is commented out ([#528](https://github.com/matsumotory/ngx_mruby/pull/528)), because the gem clones its
  hiredis dependency from an unpinned branch, which broke the build. A binary
  built with the default `build_config.rb` has no `Redis` class, and code that
  refers to it raises `NameError` (uninitialized constant Redis).
- Why it changed:
  - The reason: except on macOS, the build of mruby-redis clones hiredis from
    the head of its default branch, so a change there could break every build
    made with the default `build_config.rb`; the comment that
    [#528](https://github.com/matsumotory/ngx_mruby/pull/528) added to `build_config.rb` records such a failure of CI
    under `-Werror`.
  - Why the old behavior could not be kept: keeping the gem in the default
    list would have left every default build open to such a failure. The
    default build has to succeed, and `build_config.rb` cannot pin hiredis,
    because mruby-redis clones it in its own build script, so the gem cannot
    stay in the default list until mruby-redis pins hiredis.
  - What the documentation promised: the 2.7.0 documentation listed
    mruby-redis among "the default mrbgems"
    ([docs/install of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/install/README.md#2-configuring-mrbgems)) and `Redis` among
    the classes that can be used "with default build configuration"
    ([built-in mrbgems of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#built-in-mrbgems)), so it
    described the old behavior. Choosing the mrbgems in `build_config.rb` is
    the documented way to build ngx_mruby with extra gems (docs/install), and
    it is how you keep Redis.
- Affected: scripts that use `Redis`, such as the examples in the README and
  in `docs/use_case`, and users of the bundled `auto-ssl` mrbgem, whose client
  takes a Redis connection. A build with your own `build_config.rb` is not
  affected.
- What to do: if you use Redis, uncomment
  `conf.gem github: 'matsumotory/mruby-redis'` in `build_config.rb` before you
  build. The build of that gem can fail while hiredis has the problem that [#528](https://github.com/matsumotory/ngx_mruby/pull/528)
  describes.

### 2. Output filters: response length and Nginx::Filter

This entry has four parts.

The response length follows the body filter.

- Before: when an mruby handler, such as `mruby_content_handler*`, wrote the
  response body with `Nginx.rputs` or `Nginx.echo` and
  `mruby_output_body_filter*` replaced it with `Nginx::Filter#body=` or
  `#output`, the response kept the length of the `Nginx.rputs` output. A longer
  replacement body was cut to that length, and a shorter one was padded to it
  with bytes that were not part of the response. When the body filter itself
  called `Nginx.rputs` or `Nginx.echo`, which the documentation has never
  allowed there, `Content-Length` was the length of the response before the
  filter plus the length of that output, and the output itself was not sent.
  These responses were corrupted, and nothing should depend on them; the
  repository's own test of this case expected the cut response, and its
  expectation changes with this release. This part is about locations without
  an mruby header filter; for locations with one, see the fourth part.
- Now: the body is exactly the output of the body filter, and
  `Content-Length` is its length. What the body filter writes with
  `Nginx.rputs` or `Nginx.echo` is neither sent nor counted.

```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"
    ';
}
```

| Version | `Content-Length` | Body |
|---|---|---|
| 2.7.0 | `6` | `[origi` |
| 2.7.1 | `17` | `[origin] and more` |

- Why it changed:
  - The defect: ngx_mruby sends as many bytes of the body filter's buffer as
    the length that it keeps for the body, and sets `Content-Length` to the
    same number. In 2.7.0 that length came from the `Content-Length` that
    `Nginx.rputs` had counted, and the buffer from the body filter, so the two
    disagreed whenever the filter changed the length: a longer body was cut,
    and after a shorter one ngx_mruby sent bytes from beyond the end of the
    new body, memory that was not part of the response.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: the length and the buffer must
    agree, and the buffer is the filter's output, so the length has to follow
    the output; keeping the old length would mean keeping the cut or the read
    past the buffer.
  - What the documentation promised: the response is now what
    `Nginx::Filter#body=` is documented to do ("Sets the string to the
    response body"), as it already was for every other origin. The same
    section of the 2.7.0 documentation
    ([Nginx::Filter Class](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)) says "You can *NOT* use
    `Nginx.rputs` and `Nginx.echo` in the output body handlers".
- Affected: locations whose body filter changes the length of a body
  written with `Nginx.rputs` or `Nginx.echo`, and body filters that call
  `Nginx.rputs` or `Nginx.echo` themselves, whatever the origin. A body filter
  that keeps the length and does not call them sees no change. Neither does a
  body filter on a response from a file, `return`, `proxy_pass` or another
  content handler: there the length already followed the filter's output.
- What to do: nothing, unless a client or a test expected the cut response.
  Remove `Nginx.rputs` and `Nginx.echo` from body filters: what they write there
  is not sent.
- Tests: port 18112 (`test/t/cases/filter_connection.rb`).

`Nginx::Filter#body` before the body is read.

- Before: in `mruby_output_header_filter*`, where the body has not been read
  yet, `Nginx::Filter#body` returned a string as long as `Content-Length` whose
  bytes were not the response body. When the length was unknown, or 10485760
  bytes (10 MiB) or more, it raised `ArgumentError` instead.
- Now: it returns an empty string there, as it already did in a content
  handler.
- Why it changed:
  - The defect: in a header filter the body has not been read yet, and 2.7.0
    returned a string as long as `Content-Length` whose bytes were not the
    response body, or raised `ArgumentError`, as "Before" says.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: there is no body to return at
    that point, so the only correct result is an empty string; the length is
    available from `$sent_http_content_length`.
  - What the documentation promised: the documentation has always described
    `Nginx::Filter` as a class for the output body filters ("designed to be
    used in the output body handlers",
    [Nginx::Filter Class of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)).
- Affected: header filters that read `f.body`, for example its size to
  learn the response length.
- What to do: read the length with
  `Nginx::Var.new.sent_http_content_length` (a String, or `nil` when the length
  is unknown), and read the body in `mruby_output_body_filter*`.
- Tests: port 18080 (`test/t/ngx_mruby.rb`).

`Nginx::Filter#body=` and `#output` only in a body filter.

- Before: they could be called anywhere. When a handler other than a body
  filter called them in a location that also had a body filter, the worker
  crashed as soon as that body filter read a response that was not empty.
  Without a body filter in the location, the call had no effect on the
  response.
- Now: outside `mruby_output_body_filter*` they raise `RuntimeError`
  (`Nginx::Filter#body= can only be used in an output body filter`), which
  ends the handler like any other exception.
- Why it changed:
  - The defect: a body set with `Nginx::Filter#body=` or `#output` outside a
    body filter made the worker crash when a body filter of the location read
    a response that was not empty, and had no effect without a body filter,
    as "Before" says.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: the body filter reads the
    response into a buffer that it allocates only when no body has been set
    yet, and sends that buffer afterwards. `Nginx::Filter#body=` set that
    buffer without the position at which the body filter writes into it, so a
    body filter that ran later crashed the worker as soon as it read a
    response that was not empty. 2.x keeps one buffer for both uses, and a
    body set outside a body filter was never supported, so 2.7.1 refuses the
    call instead of adding a second buffer.
  - What the documentation promised: the documentation has always said that
    `Nginx::Filter` does not work in other handlers ("It doesn't work in other
    handlers like `mruby_content_handler_code`",
    [Nginx::Filter Class of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)).
- Affected: header filters and other handlers that call `body=` or `output`.
- What to do: set the body in `mruby_output_body_filter*`.
- Tests: port 18080 (`test/t/ngx_mruby.rb`).

With an mruby header filter, the header waits for the body filter.

- Before: in a location with `mruby_output_header_filter*` and
  `mruby_output_body_filter*`, the header went out as soon as the header
  filter's code had run, with the `Content-Length` of the response before the
  body filter. A body filter that made the body longer then sent more bytes
  than `Content-Length` announced, and one that made it shorter sent fewer.
  With an `Nginx.rputs` handler as the origin, such a location did not answer
  at all (see "Fixes").
- Now: the header waits until the body filter has run, as it already did
  in a location with only a body filter, and goes out with the length of the
  body filter's output and with the changes that the header filter made. The
  header filter's code still runs before the body filter's code. A response
  without a known length is passed on without the body filter, as before.
- Why it changed:
  - The defect: sending the header first committed the response to a
    `Content-Length` that the body filter then changed, and a client that
    reads a response by its `Content-Length` cannot read a body of another
    length correctly.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: the body filter decides the
    length of the body, so the header can carry the right `Content-Length`
    only after the body filter has run. A location with only a body filter
    already held the header back for this reason.
  - What the documentation promised: the documentation did not say when the
    header is sent in a location that has both filters. It says that
    `Nginx::Filter#body=` "Sets the string to the response body"
    ([Nginx::Filter Class of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)).
- Affected: locations with both an mruby header filter and an mruby body
  filter whose body filter changes the length of the body.
- What to do: nothing.
- Tests: port 18150 (`test/t/cases/header_and_body_filter.rb`, with a file
  and with an `Nginx.rputs` handler as the origin).

### 3. An exception after Nginx.rputs answers 500

- Before: a post-read, server rewrite, rewrite, access or content handler
  that wrote output with `Nginx.rputs` or `Nginx.echo` and then raised an
  exception (without waiting in `Nginx::Async` first) answered 200 with the
  output written so far. In an access or post-read handler, nginx then went on
  with the later phases, so the location's content handler ran as well and its
  output followed.
- Now: the request gets nginx's 500 page, the output is dropped, and the
  later phases do not run. This is what a handler that raises without output
  already got, and what a handler that raises after `Nginx::Async` gets (entry
  5).
  - `mruby_set*`: in 2.7.0 the output was sent as the response, and the
    variable got a value that the handler had not set. Now the output is
    dropped, the variable is an empty string, as for a handler that returns
    `nil` (also when an earlier directive of the location gave it a value), the
    request goes on, and the `Content-Length` of the response does not count
    the dropped output. This is for an exception before the handler waits in
    `Nginx::Async.sleep` or `Nginx::Async::HTTP.sub_request`. After
    `Nginx::Async.sleep` the request does not go on: it gets nginx's 500 page
    without the output (2.7.0 sent the output as the response, with 200).
    After `Nginx::Async::HTTP.sub_request` nginx goes on with the content
    phase without waiting for the variable, so the 500 that follows the
    exception cannot be sent and the client gets no response (entry 5).
  - Output header filters: an exception there already gave the response status
    500 with the body of the origin. A header filter that called `Nginx.rputs`
    or `Nginx.echo` crashed the worker in 2.7.0 (see "Fixes"); when it raises
    afterwards, its output is now dropped, and `Content-Length` is the length
    of the origin's body. Only the header filter's own output is dropped: when
    the origin is an mruby content handler that writes with `Nginx.rputs` or
    `Nginx.echo`, the response has that handler's output and its
    `Content-Length`, also when the header filter raises without output,
    which is what 2.7.0 sent in that case.
  - Log handlers and output body filters: the output is no longer sent after an
    exception.
- Why it changed:
  - The defect: after the exception ngx_mruby logged `mrb_run failed: return
    500 HTTP status code to client` and set the status to 500, and then the
    output path reset the status and sent the partial output with 200, so a
    failed handler was reported to the client as a success. In an access or
    post-read handler that path also returned the value that nginx reads as
    "go on", so an access check that failed with an exception let the request
    through.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: an exception has to end the
    handler with an error status, or the access decision is wrong, and the
    output path cannot send 200 and stop the phase at the same time, so the
    output is dropped. The client now gets the 500 that error.log has always
    announced, the same as after an exception without output or after
    `Nginx::Async`. An `mruby_set*` handler does not answer the request, so
    the request goes on after its exception: the dropped output must then not
    count in the length of the response that follows, and the variable needs
    a defined value, where 2.7.0 let nginx store whatever value its script
    engine held at that point.
  - What the documentation promised: the documentation said nothing about a
    handler that raises an exception. It shows a content handler that writes
    output, calls `Nginx.return Nginx::HTTP_OK` and leaves with `return`
    ([Nginx.return of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxreturnnginx_status_code)), and that answers 200 in 2.7.0 and in
    2.7.1, as "What to do" shows.
- Affected: scripts that use `raise` to stop a handler after writing
  output, access checks that write a message and then raise, and `mruby_set*`
  handlers that write output and then raise.
- What to do: keep `raise` for errors. To end a content handler early with
  its output, end it without an exception:

```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
```

`Nginx.return` with a status other than 200 makes nginx send its own page for
that status without the `Nginx.rputs` output, as in 2.7.0. Do not write output
in an access or post-read handler: it is sent as the response when the handler
ends, and with `Nginx::HTTP_OK` or without a status nginx then goes on with the
later phases, whose output follows the response beyond its `Content-Length`, in
2.7.0 as in 2.7.1 (see "Known limitations"). To stop the request there, return
an error status such as `Nginx.return Nginx::HTTP_FORBIDDEN`.

- Not changed by this release: a handler that called `Nginx.return`,
  `Nginx.send_header` or `Nginx.status_code=` after `Nginx.rputs` and then
  raised already got 500. A handler that called `Nginx.redirect` to a local URI
  before it raised still ends with the response of the redirect target.
- Tests: port 18143 (`test/t/cases/sync_exception.rb`).

### 4. Nginx::Async raises in log handlers and output filters

- Before: `Nginx::Async.sleep` in `mruby_log_handler*` left a timer on a
  request that nginx was already freeing, and the worker could crash when it
  expired. `Nginx::Async::HTTP.sub_request` there started a subrequest. In
  `mruby_output_header_filter*` and `mruby_output_body_filter*`,
  `Nginx::Async.sleep` stopped the filter at that point: the response went out
  unfiltered with the status of the origin (200 in the tested configurations),
  and the code after the sleep did not run, because the request ended before
  the timer expired. `sub_request` in a header filter started a subrequest. A
  direct `Fiber.yield` in these handlers stopped the handler without a message.
- Now: both methods raise `RuntimeError` there, for example
  `Nginx::Async.sleep is not available in a log handler` (also `... in a header
  filter` and `... in a body filter`), before they arm a timer or start a
  subrequest. The exception is handled like any other: error.log gets an
  `[error]` line, and in a filter the response gets status 500 (with the
  unfiltered body in the tested configurations). In a log handler the response
  has already been sent. A direct `Fiber.yield` there logs `... yielded its
  fiber, but nginx cannot resume it there` and, in a filter, sets status 500.
- Why it changed:
  - The defect: nginx runs the log phase while it frees the request, after
    the request cleanups and right before the request's memory is released,
    so a timer armed there fired on freed memory and resumed the handler with
    a request that no longer existed. Output filters run inside
    `ngx_http_send_header` and `ngx_http_output_filter`, which return to their
    caller at once, so a handler that yields there just stops and its
    filtering is skipped; if a timer or a subrequest resumes it later, the
    response has already gone out, and a direct `Fiber.yield` is never resumed
    ([#551](https://github.com/matsumotory/ngx_mruby/pull/551)).
  - Why the old behavior could not be kept: nginx has no point in these
    places at which resuming a handler could still change the response, so
    the call has to fail before it arms a timer or starts a subrequest.
  - What the documentation promised: the documentation has always listed
    where `Nginx::Async` works (the set, rewrite and access handlers); log
    handlers and filters were never among them
    ([Nginx::Async Class of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxasync-class)).
- Affected: log handlers and output filters that call `Nginx::Async.sleep`,
  `Nginx::Async::HTTP.sub_request` or `Fiber.yield`.
- What to do: do the waiting in a rewrite or access handler (or
  `mruby_set_code`), and hand the result to the filter or the log handler in an
  nginx variable.
- Not changed by this release: `mruby_set*` and the post-read, server
  rewrite, rewrite, access and content handlers can still use both methods
  (for the content handler, see "Known limitations").
- Tests: ports 18129 (`test/t/cases/async_sleep_in_log.rb`) and 18131
  (`test/t/cases/async_sleep_in_output_filter.rb`).

### 5. An exception after Nginx::Async answers 500

This entry concerns a handler that `Nginx::Async.sleep` or
`Nginx::Async::HTTP.sub_request` has resumed: when it raises, and when it calls
`Nginx.redirect` and then raises or calls `Nginx.return`; and `sub_request` to a
location that sleeps ([#550](https://github.com/matsumotory/ngx_mruby/pull/550)).

- Before:
  - Without output, nginx closed the connection without a response.
  - After `Nginx.rputs`, the client got 200 with the output written so far.
  - After a status had been set, for example with `Nginx.return 503`, the
    client got that status. When a handler of the request had called
    `Nginx.return Nginx::DECLINED` before (for example a server-level handler),
    nginx went on with the next phase, for example served the file with 200.
  - The next request on the same worker that ran any mruby handler failed with
    500, and error.log logged the earlier exception again.
- Now: the request gets nginx's 500 page and the output is dropped. The
  next request is not affected.
  - When the response had already started, the 500 cannot be sent: nginx logs
    `header already sent` and ends the request, and the client keeps what was
    sent before. When `Nginx.redirect` to a local URI followed
    `Nginx::Async.sleep`, that is the redirect target's response. In two
    cases measured for [#550](https://github.com/matsumotory/ngx_mruby/pull/550) the client now gets no response at all, where
    2.7.0 sent one: `Nginx.redirect` after `Nginx::Async::HTTP.sub_request`
    (2.7.0 sent the redirect target's response and then kept the connection
    open until the client closed it), and a `mruby_set_code` that raises after
    `sub_request` (nginx does not wait for the variable before the content
    phase; 2.7.0 sent 200 with the content handler's output).
  - After `Nginx.redirect` followed by an exception or by `Nginx.return` in a
    resumed handler, nginx closes the connection right after the redirect
    target's response. On an HTTP/1.1 keep-alive connection, a request
    pipelined behind it gets no response, and the client has to send it again
    on a new connection. 2.7.0 kept the connection open, but the pipelined
    request did not get its own response either: after an exception it got a
    500 page caused by the exception that the worker kept (see the last point
    of "Before" above), and after `Nginx.return` it got nothing until the
    client or a timeout closed the connection.
  - `Nginx::Async::HTTP.sub_request` to a location whose handler waits in
    `Nginx::Async.sleep` now completes. 2.7.0 sent nothing until the client or
    `send_timeout` ended the request. When that location raises,
    `Nginx::Async::HTTP.last_response.status` is 0.
- Why it changed:
  - The defect: after an exception in a resumed handler, ngx_mruby logged
    `return 500 HTTP status code to client` and then replaced the 500 with the
    status the handler had before the exception: 0 (no response), 200 with the
    partial output, or a value such as `Nginx::DECLINED` that let nginx go on
    with the next phase. The exception also stayed in the mruby state that all
    requests of the worker share, so the next request failed with it. In
    2.7.0, after `Nginx.redirect` and then `Nginx.return` with a status, the
    request and its connection stayed open until the client or a timeout ended
    them ([#550](https://github.com/matsumotory/ngx_mruby/pull/550)).
  - Why the old behavior could not be kept: that state must be cleared after
    an exception, or the next request on the worker fails, and the status must
    stay an error, or nginx treats the failed handler as a success. Running
    the requests that nginx posted while it finished the request is how nginx
    completes a request that is finished from a timer. In 2.7.1 the 500 (or
    the status) reaches nginx after the redirect target's response has been
    sent, so nginx can only end the request by closing the connection, and
    running the posted requests does that at once; that is why a keep-alive
    connection is now closed after the redirect target's response. The client
    now gets the 500 that error.log announces, as after an exception in a
    handler that did not wait.
  - What the documentation promised: the README of 2.7.0 says that the server
    process frees "the exception flag" of the mruby state that the scripts
    share, to "extend safety by preventing interference between each script"
    ([Abstract](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/README.md#abstract)); the next request that failed with the
    exception of an earlier one is such interference. The documentation said
    nothing about an exception in a handler that `Nginx::Async` had resumed,
    or about `Nginx.redirect` in such a handler. It lists the set, rewrite and
    access handlers as the places where `Nginx::Async.sleep` and
    `Nginx::Async::HTTP.sub_request` work
    ([Nginx::Async Class of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxasync-class)).
- Affected: handlers that use `Nginx::Async` and can raise afterwards,
  clients that relied on the empty reply or on the status set before the
  exception, `mruby_set_code` handlers that use `sub_request`, and clients that
  pipeline requests on a keep-alive connection to a location that calls
  `Nginx.redirect` in a resumed handler and then raises or calls
  `Nginx.return`.
- What to do: if a handler should answer something other than 500, rescue
  the exception inside the handler and set the status there.
- Tests: ports 18130 (`test/t/cases/async_exception_state.rb`) and 18124
  (`test/t/cases/async_subrequest_raise.rb`).

### 6. mruby_enable_read_request_body: which handlers wait for the whole body

With `mruby_enable_read_request_body on`, ngx_mruby reads the request body
before it runs the code of a handler. This entry has two parts: the handlers
that can wait for a body that is not complete yet, and those that cannot.

Post-read, server rewrite, rewrite, access and content handlers wait.

- Before: a `mruby_server_rewrite_handler*`, `mruby_rewrite_handler*` or
  `mruby_content_handler*` whose request body had not fully arrived when the
  handler started never ran. The client got no response, and nginx did not end
  the request when the client closed the connection or `client_body_timeout`
  expired. A body that arrives in more than one read, from a large upload or a
  slow client, was enough. Post-read and access handlers waited for the body
  and ran with it, but after such a wait a later phase handler that waits for a
  subrequest, such as `auth_request`, was never resumed: again the client got
  no response, and the connection stayed open after the client had closed it.
- Now: the handler runs once nginx has read the whole body, and the
  request then goes on with the later phases, including a phase handler that
  waits for a subrequest, such as `auth_request`. On a keep-alive connection
  the next request is answered as usual, and an exception in the handler
  answers 500. If the client closes the connection first, nginx logs `client
  prematurely closed connection` and ends the request; when
  `client_body_timeout` expires, nginx closes the connection without a response,
  and the access log records 408. The handler does not run in either case
  (for the log handler and the filters of such a request, see entry 14).
- Why it changed:
  - The defect: while the body was incomplete, ngx_mruby returned
    `NGX_AGAIN`, which nginx's rewrite and content phases read as "finished":
    nginx stopped reading the body, removed `client_body_timeout` and never ran
    the handler again, so the client got no answer, and the request and its
    connection stayed open after the client had left, until the worker exited.
    After a post-read or access handler had waited for the body, ngx_mruby ran
    the phases again with the write event handler that nginx sets while it
    reads a body, one that does nothing, and nginx resumes a phase handler that
    waits for a subrequest, such as `auth_request`, through that event
    handler, so that handler was never resumed.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: these phases leave a request
    waiting only when the handler returns `NGX_DONE`, and no return value both
    ends the request and keeps reading the body, so the handler has to wait;
    ngx_mruby runs the phases again when the body is complete. A phase handler
    that waits for a subrequest is resumed only through the write event
    handler, so ngx_mruby now sets it back before it runs the phases again, as
    nginx's mirror module does.
  - What the documentation promised: the handler now gets the body, which is
    what the directive is documented to do ("reads a HTTP request body before
    calling mruby code"). The 2.7.0 documentation of the directive
    ([mruby_enable_read_request_body](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/directives/README.md#mruby_enable_read_request_body)) also says that "a request
    body is not always read when invoking a handler like
    mruby_content_handler" and that "The directive force to read the request
    body".
- Affected: configurations that use the directive with server rewrite,
  rewrite or content handlers, and with post-read or access handlers that are
  followed by `auth_request` or another phase handler that waits for a
  subrequest.
- What to do: nothing.
- Tests: ports 18144 (the five kinds of handler, keep-alive,
  `client_body_timeout` and an exception after the body) and 18147
  (`auth_request` after a rewrite and an access handler), in
  `test/t/cases/request_body_later.rb`.

`mruby_set*`, log handlers and output filters do not wait.

- Before: these handlers started to read the body before their code ran,
  like the others. When nginx could read the whole body at once, for example
  because all of it had already arrived, the code ran with it. Otherwise the
  code did not run at that point (the variable of `mruby_set*` got a value that
  the handler had not set), and the body read that ngx_mruby had started stayed
  behind and, when it completed, ran the phases of the request again from
  wherever the request was then. With nginx 1.31.6 this happened to every
  HTTP/2 request whose body no earlier handler had read, because nginx reads
  such a body through events. In the tested configurations, with
  `mruby_set_code` and a body that arrived in two parts:
  - an mruby content handler in the same location ran twice and answered with
    the first part of the body only;
  - with `proxy_pass`, the upstream server got only the part that had arrived.
    When the upstream server waited for the rest, the client got no response;
    when it did not, the client got the response, but nginx did not close the
    HTTP/1.0 connection;
  - a connection stayed open after its client had closed it.
- Now: these handlers read the body only when the read completes at once:
  when an earlier handler or a module has read the body, when the request has
  no body, or, for HTTP/1.x with `Content-Length`, when the whole body is in the
  buffer that nginx read the request headers into (`client_header_buffer_size`,
  1k by default, or the buffer of `large_client_header_buffers` that the
  headers needed). Otherwise the code runs at once without the body:
  `Nginx::Request#body` returns `nil`, and error.log gets one line at the
  `info` level that ends with `the request body is not complete and an
  mruby_set handler cannot wait for it, so it runs without the body` (or `a
  log handler`, `a header filter`, `a body filter`). A chunked body, an HTTP/2
  or HTTP/3 body, and a body of which only a part is in that buffer count as
  not complete, even when the rest has already arrived. A later handler that
  can wait, and `proxy_pass`, still read the whole body. A body that a
  post-read, server rewrite, rewrite, access or content handler is still
  waiting for is not complete either; when nginx has ended that read,
  `Nginx::Request#body` returns the part of the body that nginx had read
  (entry 14).
- Why it changed:
  - The defect: a read that did not complete at once kept a reference to the
    request after the handler had returned, and its completion ran the phases
    again from wherever the request was then, with the results above.
  - Why the old behavior could not be kept: these handlers cannot wait:
    `mruby_set*` runs inside the script of nginx's rewrite module, a log
    handler while nginx frees the request, and the filters while nginx sends
    the response, and nginx goes on as soon as they return. So such a handler
    can only run with a body that is already complete, or without one.
    ngx_mruby decides this before the read starts, from `Content-Length` and
    the bytes in nginx's header buffer: a chunked body has no
    `Content-Length`, and an HTTP/2 or HTTP/3 body is read through events, so
    they count as not complete.
  - What the documentation promised: the 2.7.0 documentation of the directive
    said that 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)) and named no handler
    that cannot wait. Neither 2.7.0 nor 2.7.1 makes these handlers wait for the
    rest of a body: 2.7.0 did not run their code when the read that it started
    did not complete at once, and 2.7.1 runs it at once, without the body or,
    after nginx ended the read of a waiting handler, with the part that nginx
    had read (entry 14). The
    [documentation of the directive](/v2/reference/directives/#mruby_enable_read_request_body)
    now names these handlers and says when they run without the body.
- Affected: configurations with `mruby_enable_read_request_body on` whose
  `mruby_set*` handlers, log handlers or output filters read the request body.
  These handlers get no body in 2.7.1 when it is chunked, comes over HTTP/2 or
  HTTP/3, does not fit in nginx's header buffer together with the headers, or
  arrives after the headers, unless an earlier handler or module has read it.
  In 2.7.0 some of these bodies, such as a chunked body that arrived with the
  headers or a larger body that had already arrived, were read at once.
- What to do: to give these handlers the whole body, read it first in a
  handler that can wait and runs before them, for example a server-level
  `mruby_post_read_handler_code` with `mruby_enable_read_request_body on`: once
  it has run, the body is complete for the handlers that run after it.
- Tests: ports 18148 and 18149 (`test/t/cases/request_body_later.rb`).

### 7. Nginx::SSL works only in the handshake handler

- Before: `Nginx::SSL` methods (`Nginx::SSL.new`, `#servername`, the
  certificate setters, `#local_port`, `#tls_version`, `Nginx::SSL.log`) called
  outside `mruby_ssl_handshake_handler*`, which the documentation did not
  support, could crash the worker or return meaningless values ([#339](https://github.com/matsumotory/ngx_mruby/issues/339)). A
  handshake whose code did not call `Nginx::SSL.new` could get the certificate
  that an earlier handshake on the same worker had set, or fail when the memory
  of that earlier handshake's connection had been reused.
- Now: outside the handshake handler they raise `RuntimeError`
  (`Nginx::SSL can only be used in mruby_ssl_handshake_handler`). A handshake
  whose code sets no certificate, also one that does not call
  `Nginx::SSL.new`, uses the server's `ssl_certificate` and
  `ssl_certificate_key`.
- Why it changed:
  - The defect: outside the handshake handler, `Nginx::SSL` could crash the
    worker or return meaningless values
    ([#339](https://github.com/matsumotory/ngx_mruby/issues/339)), and a handshake whose code did not
    call `Nginx::SSL.new` could get the certificate of an earlier handshake, or
    fail, as "Before" says.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: `Nginx::SSL` kept the state of the
    handshake (the connection, the server name and the certificate strings) in
    the server configuration after the handshake had finished, so a call
    outside the handshake read a null pointer when no handshake had run yet
    (the crash of [#339](https://github.com/matsumotory/ngx_mruby/issues/339)) or memory that was no
    longer valid, and a later handshake whose code did not call
    `Nginx::SSL.new` reused the strings of an earlier connection. Outside the
    handshake there is no connection and no server name, so the methods have
    nothing valid to return, and the state has to be cleared when the
    handshake ends.
  - What the documentation promised: the documentation has said since 2.1.4
    that `Nginx::SSL` is for the handshake handlers and does not work in other
    handlers ([Nginx::SSL Class of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxssl-class)), and
    `ssl_certificate` is nginx's certificate for a handshake that does not
    choose one.
- Affected: handlers that call `Nginx::SSL` outside the handshake handler,
  for example a content handler that reads `servername`, and handshake handlers
  that skip `Nginx::SSL.new` on some handshakes, for example code that returns
  early on a condition, or that creates the object once and keeps it. Handshake
  handlers that call `Nginx::SSL.new` on every handshake are not affected by
  the change of the certificate.
- What to do: call `Nginx::SSL` only in the handshake handler (to use a
  value later, store it there, for example with `Userdata` as in the
  documentation). If your handler sets a certificate only on some handshakes
  without calling `Nginx::SSL.new` on the others, those others now get
  `ssl_certificate`.
- Tests: ports 18089 and 18090 (`test/t/ngx_mruby.rb`).

### 8. Request methods outside a request raise RuntimeError

The code of `mruby_init*`, `mruby_init_worker*`, `mruby_exit_worker*`,
`mruby_ssl_handshake_handler*` and `mruby_server_context_handler_code` runs
outside a request: `mruby_init*` and `mruby_server_context_handler_code` while
nginx reads the configuration, the others in a worker. In the stream module,
the init, init worker, exit worker and server context code runs outside a
session. This entry has two parts.

Request and session methods, and `Nginx.log`, outside a request.

- Before: request-scoped methods found the current request through a
  pointer that kept the last request the worker had handled.
  - Where no request had been handled yet, they crashed: in `mruby_init*` and
    `mruby_server_context_handler_code` nginx itself, while it read the
    configuration, and in `mruby_init_worker*` the worker.
  - In `mruby_exit_worker*` and `mruby_ssl_handshake_handler*` they read the
    last request that the worker had handled, whose memory could already be
    freed (meaningless data or a crash), and crashed as in
    `mruby_init_worker*` when the worker had not handled a request yet, for
    example in its first handshake.
  - `Nginx.log` and `Nginx.errlogger` raised `RuntimeError` where no request
    had been handled yet, which in `mruby_init*` and
    `mruby_server_context_handler_code` made the configuration invalid, and
    elsewhere they wrote through the last request that the worker had handled.
  - The stream module had the same problem in its init, init worker and exit
    worker code.
- Now: outside a request, request-scoped methods raise `RuntimeError`
  (`this method can only be used while processing a request`). They are the
  methods of `Nginx::Request`, `Nginx::Var`, `Nginx::Headers_in`,
  `Nginx::Headers_out`, `Nginx::Connection`, `Nginx::Server` (except
  `add_listener`), `Nginx::Filter`, `Nginx::Upstream` and `Nginx::Async`, and
  `Nginx.rputs`, `Nginx.echo`, `Nginx.return`, `Nginx.send_header`,
  `Nginx.status_code=` and `Nginx.redirect`. In code that runs while nginx reads
  the configuration, this exception makes the configuration invalid, as any
  exception there does, unless the code rescues it. `Nginx.log` and
  `Nginx.errlogger` write to nginx's error log outside a request. In the stream
  init, init worker, exit worker and server context code,
  `Nginx::Stream::Connection.new`, its methods that read the session, and
  `Nginx::Stream::Async.sleep` raise (`this method can only be used while
  processing a stream session`), `Nginx::Stream::Connection.stream_status` and
  `stream_status=` do not, and `Nginx::Stream.log` writes to nginx's error
  log.
- Why it changed:
  - The defect: outside a request, request-scoped methods crashed nginx or the
    worker, or used the last request that the worker had handled, and
    `Nginx.log` and `Nginx.errlogger` raised or wrote through that request, as
    "Before" says.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: request methods found their
    request through a process-wide pointer that was set when mruby code
    started for a request and never cleared. Code outside any request saw a
    null pointer, which crashed the process, or the last request the worker
    had handled, which could already be freed, so it read invalid memory;
    `Nginx.log` wrote through that request's connection. Outside a request
    there is no request to return, so the methods have to report an error; the
    old results were a crash or another request's data. `Nginx.log` does not
    need a request, so it writes to nginx's error log there.
  - What the documentation promised: the documentation shows the request
    methods only in request handlers, and the code listed above runs outside
    any request. The 2.7.0 documentation of `Nginx.errlogger`, of which
    `Nginx.log` is an alias, says that it "Sends an error to the log file" and
    names no handler that it is limited to
    ([Nginx.errlogger of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxerrloggerlog_level-string)).
- Affected: init, init worker, exit worker, server context and handshake
  code that uses request data (it could not work reliably before), and code
  there that relied on `Nginx.log` raising.
- What to do: use request data in request handlers only. `Nginx.log` can
  now be used in the code listed above.
- Tests: ports 18080 (`mruby_init_worker_code`) and 18089 (handshake
  handler), in `test/t/ngx_mruby.rb`.

`add_listener` only in the server context code.

- Before: `Nginx::Server#add_listener` called after nginx had read the
  configuration, for example in a request handler, used configuration data that
  was no longer valid. `Nginx::Stream.add_listener` called in a stream session
  (`mruby_stream_code`) or in the stream init, init worker or exit worker code
  took the internal context of the stream module, which holds the current
  session, for a server configuration, and the process crashed. While
  `mruby_stream_server_context_code` ran, `Nginx::Stream.log` and the session
  methods took the server configuration for that internal context.
- Now: `Nginx::Server#add_listener` raises `RuntimeError` (`add_listener
  can only be used in mruby_server_context_handler_code`) outside
  `mruby_server_context_handler_code`, and `Nginx::Stream.add_listener` raises
  `RuntimeError` (`add_listener can only be used in
  mruby_stream_server_context_code`) outside `mruby_stream_server_context_code`.
  Both work as before in that code, while nginx reads the configuration. In
  `mruby_stream_server_context_code`, `Nginx::Stream.log` writes to nginx's
  error log and the session methods raise, as in the stream init and worker
  code.
- Why it changed:
  - The defect: `add_listener` called outside the server context code used
    configuration data that was no longer valid or crashed the process, and
    in `mruby_stream_server_context_code` `Nginx::Stream.log` and the session
    methods took the server configuration for the internal context of the
    stream module, as "Before" says.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: `add_listener` needs the state of
    nginx's configuration parser, which exists only while nginx reads the
    configuration; `Nginx::Server#add_listener` of the HTTP module used that
    state after parsing had ended.
    The stream module put the server configuration in the place of its
    internal context while `mruby_stream_server_context_code` ran, and
    `Nginx::Stream.add_listener` read that place whenever it was called; at
    any other time the place holds the internal context, which is not a server
    configuration. The server configuration is now kept apart, and only while
    that code runs, so `add_listener` can tell where it is called, and the
    other methods always find the internal context.
    `mruby_stream_server_context_code` runs while nginx reads the
    configuration, outside any session, so the session methods raise there and
    `Nginx::Stream.log` writes to nginx's error log, as in the stream init and
    worker code.
  - What the documentation promised: the 2.7.0 documentation shows
    `Nginx::Server#add_listener` in a configuration only in
    `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)), refers to that
    example for `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)), and does not say
    where else they can be called. It does not describe
    `mruby_stream_server_context_code`.
- Affected: code that calls `add_listener` outside the server context code
  (it could not work before), and `mruby_stream_server_context_code` that calls
  `Nginx::Stream.log` or session methods.
- What to do: call `add_listener` only in the server context code. There is
  no session in `mruby_stream_server_context_code`, so do not call session
  methods there.
- Tests: port 18080 (`Nginx::Server#add_listener` in a content handler,
  `test/t/ngx_mruby.rb`) and port 12357 (`Nginx::Stream.add_listener` in a
  session, in a second nginx that `test/t/cases/_second_instance.rb` starts
  and `test/t/cases/stream.rb` calls through `second_instance` of
  `test/t/cases/_prelude.rb`).

### 9. mruby_set variables are no longer overwritten by later handlers

- Before: after `mruby_set` or `mruby_set_code` (also a server-level one) had
  run in a request, a later handler of the same request that resumed after
  `Nginx::Async.sleep` had its last expression assigned to that variable.
- Now: only the `mruby_set` handler's own result is assigned to its
  variable ([#548](https://github.com/matsumotory/ngx_mruby/pull/548)). An `mruby_set_code` that sleeps itself still sets its
  variable when it resumes.
- Why it changed:
  - The defect: `mruby_set` stored the name of its variable in the request's
    context and nothing cleared it, so when a later handler of the request
    resumed after `Nginx::Async.sleep`, ngx_mruby wrote that handler's last
    expression into the variable, for example the `$backend` of a later
    `proxy_pass` ([#548](https://github.com/matsumotory/ngx_mruby/pull/548)).
  - Why the old behavior could not be kept: the configuration never asked for
    that write, and keeping it would mean that a sleep in an unrelated handler
    changes the variable.
  - What the documentation promised: the variable of a `mruby_set` is set by
    that `mruby_set`, as the directive is documented ("called at one of the
    rewrite phases to set a nginx variable",
    [mruby_set and mruby_set_code of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/directives/README.md#mruby_set-and-mruby_set_code)).
- Affected: requests that combine `mruby_set*` with a later handler that
  uses `Nginx::Async.sleep`.
- What to do: nothing. If a later handler should change the variable, assign
  it explicitly, for example `Nginx::Var.new.backend = "..."`.
- Tests: port 18123 (`test/t/cases/async_set_var_target.rb`).

### 10. nginx starts without an http block

- Before: with ngx_mruby built in, a configuration without an `http {}`
  block, such as a stream-only one, made every worker crash at start, and nginx
  kept starting new ones.
- Now: nginx starts ([#536](https://github.com/matsumotory/ngx_mruby/pull/536)).
- Why it changed:
  - The defect: the HTTP module's worker start and exit hooks read its main
    configuration without checking that it exists; without an `http {}` block
    it does not, and the worker crashed ([#536](https://github.com/matsumotory/ngx_mruby/pull/536)).
  - Why the old behavior could not be kept: nginx accepts configurations
    without `http {}`, and the stream module of ngx_mruby already handled this
    case. The old behavior was a crash, so there is nothing to keep.
  - What the documentation promised: the documentation said nothing about a
    configuration without an `http {}` block.
- Affected: configurations without an `http {}` block, such as stream-only
  ones, in an nginx that has the HTTP module of ngx_mruby built in or loaded.
- What to do: nothing.
- Tests: port 12399 (`test/t/ngx_mruby.rb`).

### 11. A body filter that calls Nginx.rputs after an mruby header filter no longer logs "header already sent"

- Before: in a location with an mruby header filter and an mruby body
  filter whose code calls `Nginx.rputs` or `Nginx.echo` (which the
  documentation says is not supported there), nginx logged
  `[alert] header already sent` for every request, and the body filter set
  the status to 200 after the header was sent. The client received the status
  that nginx sent, but the log phase saw 200: a log handler and `$status` in
  `log_format` read 200 for a 404 response.
- Now: nginx does not log that alert, and the log phase sees the status
  that nginx sent. The response is the same as before
  ([#563](https://github.com/matsumotory/ngx_mruby/pull/563)), unless the body filter changes the length of the body, which
  entry 2 (fourth part) describes.
- Why it changed:
  - The defect: at the end of its code the body filter sent the output of
    `Nginx.rputs` through the path for a handler that answers, which sent the
    header a second time and set the status to 200
    ([#563](https://github.com/matsumotory/ngx_mruby/pull/563)).
  - Why the old behavior could not be kept: keeping that path would keep the
    `[alert] header already sent` line for every request and, in the log
    phase, a status other than the one that nginx sent, as "Before" says. The
    body filter no longer takes that path once an mruby header filter is in
    the location, and the output of `Nginx.rputs` in a body filter is dropped,
    as before.
  - What the documentation promised: the documentation says that
    `Nginx.rputs` and `Nginx.echo` cannot be used in the output body filters
    ("You can *NOT* use `Nginx.rputs` and `Nginx.echo` in the output body
    handlers", [Nginx::Filter Class of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxfilter-class)); it said
    nothing about the status that the log phase sees.
- Affected: locations with `mruby_output_header_filter` (or `_code`) and
  `mruby_output_body_filter` (or `_code`) whose body filter calls
  `Nginx.rputs` or `Nginx.echo`; access logs, log handlers and alerts that
  read `$status` or the `header already sent` line for those requests.
- What to do: nothing, unless a log statistic or an alert counts on the
  status 200 or on the `header already sent` line of those requests.
- Tests: port 18138
  (`test/t/cases/output_filters_with_content_handler.rb`).

### 12. An unread request body is discarded before the answer

- Before: when a handler answered a request with `Nginx.rputs` or
  `Nginx.echo` without reading its body, for example a content or rewrite
  handler that answers a `POST` without `mruby_enable_read_request_body`, the
  body stayed on the connection. On a connection that nginx kept open for the
  next request (HTTP/1.1 keep-alive), nginx then read that body as the
  beginning of the next request.
- Now: before ngx_mruby sends the answer of such a handler, it discards a
  request body that no handler has read, as nginx does for its own responses
  (`return`, its error pages, static files), and the next request on the
  connection is the one that the client sent after the body. If the body
  cannot be discarded, nginx closes the connection after the answer. The same
  holds for the answer that a rewrite handler sends after
  `Nginx::Async.sleep`. A body that a handler has read is not affected.
- Why it changed:
  - The defect: nginx expects a handler that answers a request to read or
    discard the request body first, because the next request on a kept
    connection starts after the body; nginx's own handlers do this
    (`ngx_http_send_response`, the error pages, the static module), and the
    answer of an mruby handler did not.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: a body left on the connection is
    read as the beginning of the next request, as "Before" says. Discarding
    the body keeps the connection usable without making the answer wait for a
    body that the handler did not want.
  - What the documentation promised: the documentation said nothing about a
    request body that no handler reads.
- Affected: handlers that answer requests with a body (`POST`, `PUT`) with
  `Nginx.rputs` or `Nginx.echo` without reading the body, on connections that
  nginx keeps open for the next request.
- What to do: nothing. A handler that needs the body reads it as before,
  with `mruby_enable_read_request_body on`.
- Tests: port 18151 (`test/t/cases/unread_request_body.rb`: a content
  handler, a rewrite handler, and a rewrite handler that answers after
  `Nginx::Async.sleep`).

### 13. A content handler that ends without output answers 500

- Before: an `mruby_content_handler*` on the main request that ended without
  output sent no response when it set no status (it wrote nothing, wrote only
  `""` or `nil` with `Nginx.rputs`, or only set response headers) or set a
  status below 300 other than 200, 201 and 204, for example
  `Nginx::HTTP_ACCEPTED`, also when it ended so after
  `Nginx::Async::HTTP.sub_request` had resumed it. nginx closed an HTTP/1.0
  connection without sending a byte and kept an HTTP/1.1 keep-alive connection
  open without a response until `keepalive_timeout` (75 s by default), so the
  client waited for its own timeout.
- Now: such a request gets 500 with nginx's error page (or the
  `error_page` configured for 500) and `Connection: close`, the answer that
  `Nginx.return Nginx::HTTP_OK` without output already got in 2.7.0
  ([#566](https://github.com/matsumotory/ngx_mruby/pull/566)). error.log gets a line at the `error`
  level: `status code is not set, and response body is empty. return
  NGX_HTTP_INTERNAL_SERVER_ERROR`, or `status code is <N>, but response body is
  empty. return NGX_HTTP_INTERNAL_SERVER_ERROR`.
- Why it changed:
  - The defect: nginx sends a response of its own for 201, 204 and the
    statuses from 300 up, but for any other status below 300, and when no
    status is set, it ends the request without sending anything; a handler
    without output handed exactly such a value to nginx. A request without a
    response is a defect
    ([#225](https://github.com/matsumotory/ngx_mruby/issues/225)): the client hangs until its own
    timeout, and a keep-alive connection is held for nothing.
  - Why the old behavior could not be kept: keeping it would leave these
    requests without a response. The 2.x line answers these requests the way
    it already answered `Nginx::HTTP_OK` without output, so no request that got
    a response in 2.7.0 gets a different one.
  - What the documentation promised: the documentation said nothing about a
    content handler that ends without output. It says that `Nginx.return`
    "Returns nginx status code to send back HTTP status to the client"
    ([Nginx.return of 2.7.0](https://github.com/matsumotory/ngx_mruby/blob/v2.7.0/docs/class_and_method/README.md#nginxreturnnginx_status_code)); a status such as 202 without output
    gets 500 in 2.7.1, as 200 without output already did in 2.7.0.
- Affected: `mruby_content_handler*` handlers that can end without output,
  for example a handler that writes only when a condition holds, or one that
  passes a value that can be empty or `nil` to `Nginx.rputs`, and log
  monitoring of these lines: a pattern that matches `status code is 200, but
  response body is empty` does not match the new lines, and one that matches
  `response body is empty` counts them too.
- What to do: a handler that should answer with an empty body returns
  `Nginx.return Nginx::HTTP_NO_CONTENT` (nginx answers 204 itself) or writes a
  body; a handler that means to deny returns the status it means
  (`Nginx::HTTP_FORBIDDEN`, `Nginx::HTTP_UNAUTHORIZED`). Log monitoring that
  matches `status code is 200, but response body is empty` should match
  `response body is empty. return NGX_HTTP_INTERNAL_SERVER_ERROR` to see the new
  lines too.
- Not changed by this release: `Nginx::HTTP_OK` without output (500, as
  before), 201, 204 and the statuses from 300 up (nginx answers them itself),
  handlers with output, `Nginx.redirect`, the other kinds of handler, and
  subrequests: `Nginx::Async::HTTP.sub_request`, SSI includes and
  `auth_request` see the same subrequest status as before. A content handler
  that writes output and then sets a status below 300 other than 200, 201 and
  204, for example `Nginx.rputs "x"` followed by
  `Nginx.return Nginx::HTTP_ACCEPTED`, still sends no response (see "Known
  limitations").
- Tests: port 18136 (`test/conf/conf.d/54-content-empty-body.conf`,
  `test/t/cases/content_empty_body.rb`).

### 14. The waiting handler no longer runs after nginx ended its body read

With `mruby_enable_read_request_body on`, a post-read, server rewrite, rewrite,
access or content handler waits for the rest of the request body (entry 6).
nginx can end that read without the rest of the body: when
`client_body_timeout` expires, when the client closes the connection, or when
nginx answers 400 or 413 to the body, for example to a malformed chunked body.
This entry concerns the mruby log handler and the mruby output filters that run
for such a request afterwards.

- Before: when the handler that waited was a post-read or access handler,
  such a log handler or filter started a body read of its own, and that read
  ran the code of the waiting handler, which had not run yet, after nginx had
  already decided the answer. After `client_body_timeout`, or after the client
  had closed the connection, the code of an access handler ran during the log
  phase. After a malformed chunked body it ran while nginx sent its 400 page,
  and the response had that page twice under a `Content-Length` that counted
  it once (157 bytes counted, 314 sent). When the handler that waited was a
  server rewrite, rewrite or content handler, 2.7.0 did not end the request at
  all (entry 6), so these handlers did not run.
- Now: the log handler or filter does not start a read: it runs at once,
  `Nginx::Request#body` returns the part of the body that nginx had read
  before it ended the read, or `nil` when that part did not fit in nginx's
  body buffer (`client_body_buffer_size`) and nginx had written it to a
  temporary file, and error.log gets the `info` line of entry 6 (`the request
  body is not complete and a log handler cannot wait for it, so it runs
  without the body`, or `a header filter`, `a body filter`). The code of
  the waiting handler does not run. After `client_body_timeout` nginx closes
  the connection without a response and the access log records 408; after the
  client has closed the connection the access log records 400; after a
  malformed chunked body the client gets nginx's 400 page once, and nginx
  closes the connection.
- Why it changed:
  - The defect: when a body read of the request has already started, nginx
    calls the completion handler of a new read at once, and the completion
    handler of ngx_mruby runs the phases of the request again for the handler
    that waits. A log handler or a filter that started a read after nginx had
    ended the first one therefore ran those phases again, from the log phase
    or from inside the response.
    The security advisory
    [GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5)
    describes this defect.
  - Why the old behavior could not be kept: a read that nginx has ended does
    not complete, so the waiting handler has no body to run with, and nginx
    has already answered the request or closed it: running that handler's code
    then did its work, such as an access check or a log line, for a
    request that was over, and sent nginx's error page a second time. A log
    handler or a filter cannot wait for a body (entry 6), so it runs with what
    nginx had read, without starting a read of its own.
  - What the documentation promised: the 2.7.0 documentation of the directive
    said that 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)); it said nothing about a
    read that nginx ends without the whole body.
- Affected: configurations with `mruby_enable_read_request_body on` whose
  post-read or access handler reads the body and whose requests also run an
  mruby log handler or an mruby output filter, for the requests whose body
  nginx stops reading early (`client_body_timeout`, a closed connection, 400 or
  413 for the body).
- What to do: nothing. The code of a handler that waits for the body runs
  once, with the whole body; when nginx ends the read before the body is
  complete, it does not run.
- Tests: port 18152 (`test/t/cases/request_body_later.rb`: a rewrite
  handler that waits, with a log handler after `client_body_timeout`, and with
  a header filter and with a body filter after a malformed chunked body).

## New features

None.

## Fixes

Fixes described in the security advisory
[GHSA-h7fw-wf49-8rv5](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-h7fw-wf49-8rv5):

- The body filter sends exactly its output, also when the body was written with
  `Nginx.rputs` or `Nginx.echo`, and when the location also has an mruby header
  filter (entry 2).
- `Nginx::Filter#body` in a header filter no longer returns bytes that are not
  the response body, and `Nginx::Filter#body=` and `#output` outside a body
  filter no longer crash the worker when a body filter follows (entry 2).
- An access or post-read handler that raises after `Nginx.rputs` stops the
  request with 500 instead of letting nginx go on with the content handler, and
  after an exception in an `mruby_set*` handler or a header filter,
  `Content-Length` no longer counts the dropped output (entry 3).
- Under `mruby_enable_read_request_body`, a server rewrite, rewrite or content
  handler whose request body arrives after it started runs with the body, a
  later phase handler such as `auth_request` is resumed after a body that
  arrived late, and nginx ends the request when the client closes the
  connection or `client_body_timeout` expires, instead of keeping it open
  (entry 6, first part).
- `Nginx::SSL` raises `RuntimeError` outside the handshake handler instead of
  crashing the worker or returning meaningless values (entry 7, issue
  [#339](https://github.com/matsumotory/ngx_mruby/issues/339)).
- A later handshake whose code does not call `Nginx::SSL.new` no longer gets an
  earlier handshake's certificate (entry 7).
- Request-scoped methods and stream session methods raise `RuntimeError`
  outside a request or a session instead of crashing or using the data of
  another request, `Nginx::Server#add_listener` raises outside
  `mruby_server_context_handler_code`, and `Nginx.log` writes to nginx's error
  log outside a request (entry 8).
- `Nginx::Stream.add_listener` raises outside `mruby_stream_server_context_code`
  instead of using the internal context of the stream module as a server
  configuration (entry 8).
- An `Nginx.rputs` answer no longer leaves an unread request body on the
  connection (entry 12).
- A log handler or an output filter that runs after nginx ended a body read
  without the body no longer runs the code of the handler that waited for the
  body, and nginx sends its error page for the body once (entry 14).

Other fixes:

- `Nginx::Async` raises in log handlers and output filters instead of leaving
  a timer or a suspended handler behind ([#551](https://github.com/matsumotory/ngx_mruby/pull/551), entry 4).
- An exception after `Nginx::Async` resumed a handler answers 500, no longer
  makes the next request on the worker fail, and requests that nginx posted
  meanwhile run ([#550](https://github.com/matsumotory/ngx_mruby/pull/550), entry 5).
- Under `mruby_enable_read_request_body`, `mruby_set*`, log handlers and
  filters no longer leave a body read behind (entry 6, second part).
- A resumed handler no longer overwrites the variable of an earlier
  `mruby_set` ([#548](https://github.com/matsumotory/ngx_mruby/pull/548), entry 9).
- nginx starts with a configuration that has no `http {}` block ([#536](https://github.com/matsumotory/ngx_mruby/pull/536), entry
  10).
- A content handler that ends without output answers 500 instead of leaving
  the client without a response ([#566](https://github.com/matsumotory/ngx_mruby/pull/566), fixes
  [#225](https://github.com/matsumotory/ngx_mruby/issues/225), entry 13).
- A location with an mruby handler that writes the response with
  `Nginx.rputs` or `Nginx.echo`, such as a content or rewrite handler, and an
  mruby header filter no longer crashes the worker; the header filter runs once
  ([#563](https://github.com/matsumotory/ngx_mruby/pull/563), fixes [#206](https://github.com/matsumotory/ngx_mruby/issues/206)).
- A header filter that calls `Nginx.rputs` or `Nginx.echo` no longer crashes
  the worker ([#563](https://github.com/matsumotory/ngx_mruby/pull/563)), but the call is still not supported. After an
  origin other than an mruby handler that writes with `Nginx.rputs`, such as
  nginx's `return`, the response does not contain the output, although in a
  location without an mruby body filter `Content-Length` counts it and a log
  handler of the location sends it after the response; after an mruby content
  handler that writes with `Nginx.rputs`, the output is added to that
  handler's output and counted in `Content-Length`. A header filter that
  raises afterwards drops only its own output, and the output of an mruby
  content handler stays in the response with its `Content-Length` (entry 3).
- The `Nginx::SSL` certificate setters copy a value that contains a NUL byte
  completely.
- A failing `Nginx::Async::HTTP.sub_request` no longer keeps its handler's
  fiber registered with the garbage collector until the worker exits ([#547](https://github.com/matsumotory/ngx_mruby/pull/547)).
- A stream directive that a later directive of the same kind replaces no longer
  leaks the mruby compiler context of the replaced code at configuration time
  ([#540](https://github.com/matsumotory/ngx_mruby/pull/540)).
- Setting the `Server` header from Ruby allocates a zeroed header element
  ([#530](https://github.com/matsumotory/ngx_mruby/pull/530)).

## Known limitations

This release does not change the following behavior of 2.7.0.

- **`Nginx::Async.sleep` in a content handler ends the request without a
  response.** `mruby_content_handler*` can call `Nginx::Async.sleep` without an
  exception (entry 4), but nginx does not wait for a content handler that
  sleeps: the request ends at the sleep, the client gets no response, and the
  code after the sleep does not run. The documentation lists the set, rewrite
  and access handlers as the places where `Nginx::Async` works. Sleep in a
  rewrite or access handler instead; a rewrite handler can also write the
  response after the sleep. A later release is planned to let a content handler
  wait.
- **An access or post-read handler that writes output lets the request go
  on.** An access or post-read handler that writes with `Nginx.rputs` or
  `Nginx.echo` and ends without an exception and without an error status sends
  that output as the response when it ends, and nginx goes on with the later
  phases: the location's content handler or nginx's static module runs as well,
  and nginx logs `header already sent`. What a content handler writes follows
  the response beyond its `Content-Length`. Do not write output in these
  handlers: to deny a request, return an error status such as
  `Nginx.return Nginx::HTTP_FORBIDDEN`, and write answers in the content
  handler. The 3.x line plans to redesign how a handler of each phase ends the
  response.
- **Output followed by a status below 300 other than 200, 201 and 204.** A
  content handler that writes output with `Nginx.rputs` or `Nginx.echo` and then
  sets such a status, for example with `Nginx.return Nginx::HTTP_ACCEPTED`,
  sends no response: nginx closes an HTTP/1.0 connection without sending a
  byte, and keeps an HTTP/1.1 keep-alive connection open without a response.
  Answer with `Nginx::HTTP_OK` when the response has a body, and with
  `Nginx::HTTP_NO_CONTENT` when it has none. The 3.x line plans to allow a body
  with any status.

## Build and test changes

- Default nginx: `nginx_version`, which `build.sh` and `test.sh` use, is
  1.31.6 (2.7.0 had 1.27.2).
- CI tests nginx 1.31.6, 1.30.5, 1.28.3 and 1.26.3, each as a static and as a
  dynamic module, with OpenSSL 1.1.1w installed on the runner as a shared
  library and with OpenSSL 1.1.1w compiled into nginx, plus one build of 1.31.6
  with AddressSanitizer and UndefinedBehaviorSanitizer. It runs on pull
  requests and reports one `ci-ok` check. 2.7.0 tested 1.27.2 and 1.26.2.
- CI: `ci-ok`, the status check that the rulesets require, now also needs the
  new job `files` to succeed. The `files` job fails when a pull request adds a
  file that does not belong in the repository
  ([#578](https://github.com/matsumotory/ngx_mruby/pull/578)). Its script,
  `test/check_added_files.sh`, checks whether a file that the change adds, or
  that one of its commits adds, matches a pattern of
  `test/forbidden_paths.txt`. The patterns include the contents of `.claude/`
  except `.claude/settings.json`, `.claude/commands/` and `.claude/skills/`,
  build directories at the top such as `build/` and `build_dynamic/`,
  `mruby/build/`, files that `./configure` or the Makefile writes at the top,
  `.log` files, `.env` and `.env.*`, private keys, editor and OS leftovers, and
  core dumps. An empty file that the change adds also fails, unless it is named
  `.gitkeep` or `.keep`. On a push to `master`, `next` or `v*.x`, the job checks
  the pushed commits. It needs the whole history of the repository;
  `docs/test/README.md` describes the check and how to run it before opening a
  pull request.
- `build_config.rb` no longer builds `mruby-redis` ([#528](https://github.com/matsumotory/ngx_mruby/pull/528), entry 1).
- `build.sh`: with `OPENSSL_SRC_VERSION` set, it passes the OpenSSL source
  directory that it downloads to `configure` as an absolute path, so OpenSSL's
  `./config` gets the absolute `--prefix` that it requires (2.7.0 stopped with
  `Directory given with --prefix MUST be absolute`)
  ([#562](https://github.com/matsumotory/ngx_mruby/pull/562), fixes
  [#519](https://github.com/matsumotory/ngx_mruby/issues/519)). A
  `--with-openssl-src` given on the command line of `build.sh` or `test.sh` is
  still passed to `configure` as it is.
- `test.sh`: the HTTP test servers listen on 18080-18090, 18101-18103,
  18110-18131, 18136, 18138, 18143-18144 and 18147-18152 instead of 58xxx,
  below the ephemeral port range;
  the stream servers use 12345-12359, and one test uses 12399. It also loads
  the configuration fragments in `test/conf/conf.d` and runs the case files in
  `test/t/cases` (see
  [the test documentation](/v2/contributing/test/)), takes extra compiler and linker
  flags for nginx from `NGINX_EXTRA_CC_OPT` and `NGINX_EXTRA_LD_OPT`, and
  compares the nginx version as a whole, so its builds of nginx 1.11.5 and
  later include the stream access module whatever the patch number. It
  configures nginx with `--with-http_auth_request_module`, for the tests of
  entry 6; a build directory made by an earlier `test.sh` keeps its old
  configure options until it is configured again, as the test documentation
  describes.
- The repository has a security policy (`SECURITY.md`), contributor and agent
  instructions (`AGENTS.md`), and a `v2.x` branch that mirrors `master` until
  v3 is promoted to `master`.

## Performance

Measured on 2026-10-14 with the callgrind comparison of the `next` branch
(`test/perf/compare.sh` at `570cdab05`, see "Performance comparison with
callgrind" in its `docs/test/README.md`): release builds of both trees with
nginx 1.31.6 and the same 21 third-party mrbgems at the same commits, Ubuntu
22.04 in a container on aarch64 (gcc 11.4, valgrind 3.18.1), 20000 requests per
scenario after 2000 warmup requests. Base: `master` at `b3df70f`, the 2.x
commit that the fixes of 2.7.1 were merged with (tree `4e88dcc47eca`); head:
`b3df70f` with the fixes of 2.7.1 (tree `3ab6c7888a35`, `src/` tree
`8b6857bd850e`). Instructions that nginx executes per request, without mruby's
garbage collector:

| Scenario | `b3df70f` | 2.7.1 | Change |
|---|---|---|---|
| `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% |

Every scenario stays below the 3% at which the comparison warns. The largest
relative change is in `hello`, +1.23% or about 138 instructions per request;
the largest absolute change is in `ruby_call_10`, 342 instructions per request
for its ten `mruby_set_code` calls, about 34 per call. The fixes add no
per-request allocation of their own and no blocking wait: a handler that needs
the rest of the request body waits with nginx's `NGX_DONE` and runs again when
nginx has read the body, the fixes for an exception after `Nginx.rputs` record
the chain of `Nginx.rputs`, its last link and the response length before a
handler runs, and the length again when `Nginx.rputs` starts a chain, and put
the length back after an exception (for a header filter also the chain), and
the only new call that can allocate is nginx's own
`ngx_http_discard_request_body`, for a chunked request body that no handler
read.

## Upgrading

Supported nginx versions: the nginx mainline and stable lines (see
[README.md](https://github.com/matsumotory/ngx_mruby/blob/master/README.md)); CI builds the versions listed under "Build and
test changes".

- No directive was added, renamed or removed. Rebuild the module with this
  release: build nginx with ngx_mruby 2.7.1 the way you built 2.7.0, and
  replace the binary or the dynamic module as usual. `Nginx.module_version`
  returns `2.7.1`.
- mruby is unchanged at 3.3.0, and so is ngx_devel_kit.
- The third-party mrbgems in `build_config.rb` are still fetched from their
  repositories without a pinned version on 2.x, so a new build can get newer
  versions of them than your 2.7.0 build had. Redis needs the change in entry 1.

Check these before upgrading. Each line is the "What to do" of its entry
above; the entries that are not listed need no change.

- Redis: uncomment `conf.gem github: 'matsumotory/mruby-redis'` in
  `build_config.rb` before you build (entry 1).
- Output filters: remove `Nginx.rputs` and `Nginx.echo` from body filters, read
  the response length in a header filter with
  `Nginx::Var.new.sent_http_content_length`, and set or read the body only in
  `mruby_output_body_filter*` (entry 2).
- `raise` after output: keep `raise` for errors, end a content handler early
  with `Nginx.return Nginx::HTTP_OK` and `return`, and do not write output in an
  access or post-read handler (entry 3).
- `Nginx::Async` or `Fiber.yield` in a log handler or an output filter: wait in
  a rewrite or access handler (or `mruby_set_code`) and hand the result over in
  an nginx variable (entry 4).
- A handler that should answer something other than 500 after an exception
  that follows `Nginx::Async`: rescue the exception in the handler and set the
  status there (entry 5).
- `mruby_set*`, log handlers and output filters that need the request body:
  read it first in a handler that can wait, for example a server-level
  `mruby_post_read_handler_code` (entry 6).
- `Nginx::SSL`: call it only in the handshake handler. A handler that sets a
  certificate on some handshakes without calling `Nginx::SSL.new` on the others
  gives those others `ssl_certificate` now (entry 7).
- Request methods in code that runs outside a request (the list in entry 8):
  use request data in request handlers only, call `add_listener` only in the
  server context code, and do not call session methods in
  `mruby_stream_server_context_code` (entry 8).
- Log statistics or alerts that count on the status 200 or on the `header
  already sent` line of a body filter that calls `Nginx.rputs` after an mruby
  header filter: update them; the log phase now sees the status that nginx
  sent, and nginx no longer logs that line (entry 11).
- A content handler that can end without output: return
  `Nginx::HTTP_NO_CONTENT` or write a body, and update log monitoring that
  matches `status code is 200, but response body is empty` (entry 13).
