---
title: "ngx_mruby v2.7.4"
language: "en"
documentation_version: "3.x"
channel: "development"
release_context: null
source_revision: "a312ec59ea617c7d7bb127b6bd18e5aa7e196165"
working_tree_clean: true
source_path: "docs/releases/v2.7.4.md"
source_sha256: "af034b2aa8e17f6eaa923669b6e9a9217f7d8b9367ab311bf1e2b84446e29350"
source_at_revision: true
source_url: "https://github.com/matsumotory/ngx_mruby/blob/a312ec59ea617c7d7bb127b6bd18e5aa7e196165/docs/releases/v2.7.4.md"
html_url: "https://ngx.mruby.org/releases/v2.7.4/"
markdown_url: "https://ngx.mruby.org/markdown/releases/v2.7.4.md"
last_updated: "2026-11-02"
---

# ngx_mruby v2.7.4

ngx_mruby 2.7.4 is a maintenance release of the 2.x line.

It fixes a security issue of the stream module, with its advisory
([GHSA-xjh6-6vgm-56wj](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-xjh6-6vgm-56wj), Moderate): entries 1 and 2 under "Behavior
changes: read before upgrading".

## Behavior changes: read before upgrading

### 1. Nginx::Stream::Connection.proxy_protocol_addr and proxy_protocol_ip return nil for a connection without a PROXY source address

- Before: with nginx 1.17.6 and later, `Nginx::Stream::Connection.proxy_protocol_addr`
  and `Nginx::Stream::Connection.proxy_protocol_ip`, as class and as instance
  methods, did not return a value for a connection that had no source address
  from a PROXY protocol header: on a listen without `proxy_protocol`, and
  after the header `PROXY UNKNOWN` (version 1) or a version 2 header with the
  command `LOCAL`.
- Now: the methods return `nil` for such a connection. After a header that
  carries addresses, they return its source address as before.
- Why it changed:
  - The defect: the methods returned no value for such a connection, as
    "Before" describes. The advisory
    [GHSA-xjh6-6vgm-56wj](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-xjh6-6vgm-56wj)
    describes the defect, the affected versions and the conditions.
  - Why the old behavior could not be kept: such a connection has no address
    to return, and `nil` tells it apart from a connection with one. In HTTP
    code, `Nginx::Var#proxy_protocol_addr` returns `nil` in the same case.
  - What the documentation promised: the methods were not documented.
    [docs/class_and_method/README.md](/reference/ruby/#nginxstreamconnectionproxy_protocol_addrproxy_protocol_ip)
    describes them now.
- Affected: `mruby_stream` and `mruby_stream_code` code that calls these
  methods on a connection without a PROXY source address.
- What to do: nothing for code that compares the result with a String. Code
  that calls a String method on the result handles `nil`, for example with
  `to_s`.

### 2. While the code of a TCP stream session sleeps, nginx reads nothing from the client, and worker_shutdown_timeout ends the session

- Before: while the code of a TCP session waited in
  `Nginx::Stream::Async.sleep`, the read events of the connection were
  handled as before the code ran. On a listen with `proxy_protocol`, bytes
  that the client sent during the sleep could end the session (status 400 in
  the access log). When `worker_shutdown_timeout` expired during the sleep,
  the session did not end then.
- Now: while the code sleeps, nginx reads nothing from the client. The bytes
  that the client sends then stay for the phase after the code: `proxy_pass`
  sends them to the upstream. A client that closes the connection during the
  sleep is seen by the phase after the code, as on a listen without
  `proxy_protocol`. When `worker_shutdown_timeout` expires during the sleep,
  the session ends, and the code does not run again.
- Why it changed:
  - The defect: the read events during the sleep were handled as before the
    code ran, as "Before" describes. The advisory
    [GHSA-xjh6-6vgm-56wj](https://github.com/matsumotory/ngx_mruby/security/advisories/GHSA-xjh6-6vgm-56wj)
    describes the defect, the affected versions and the conditions.
  - Why the old behavior could not be kept: the bytes after the header
    belong to the protocol that the later phases carry. A session that does
    not end at `worker_shutdown_timeout` keeps the old worker process after
    the time that the directive sets.
  - What the documentation promised:
    [docs/class_and_method/README.md](/reference/ruby/#nginxstreamasync-class)
    said nothing about reads during the sleep. nginx's documentation of
    [`worker_shutdown_timeout`](https://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout)
    says that nginx tries to close the open connections when the timeout
    expires.
- Affected: stream servers whose `mruby_stream` or `mruby_stream_code` code
  calls `Nginx::Stream::Async.sleep` on a TCP listen: the clients that send
  bytes or close the connection during the sleep on a listen with
  `proxy_protocol`, and a worker that shuts down (a reload, an upgrade, a
  stop) with `worker_shutdown_timeout` while the code sleeps.
- What to do: nothing. The phase after the code gets the bytes of the
  client, as on a session whose code does not sleep.
- Not changed by this release: a UDP session. Each datagram of its client
  during the sleep runs the code again, as in 2.7.3, and
  `worker_shutdown_timeout` does not end a UDP session whose code sleeps.

## New features

None.

## Fixes

None.

## Build and test changes

- Tests: `test/t/cases/stream_proxy_protocol_wait.rb` with
  `test/conf/conf.d/stream/48-proxy-protocol-wait.conf` (port 12384), and the
  mode `stream_master` of `test/t/cases/_second_instance.rb`, a second nginx
  with a master process and one worker on ports 12385 to 12388.
  `test/t/cases/_tcp_client.rb` can write a second payload 0.3 seconds after
  the first.
- CI: a ready pull request that changes the code or the tests runs four cells of the `build` matrix of `test.yml`, not every cell (the 16 combinations of nginx version, link mode and `--with-openssl-src`, and the sanitizer cell). The four cells are nginx 1.31.8 with the static module and `--with-openssl-src`, nginx 1.30.6 with the dynamic module, the sanitizer cell (AddressSanitizer and UndefinedBehaviorSanitizer) and the cell under valgrind; the last two build nginx 1.31.8 with the static module. Every cell runs in the nightly run under valgrind, on the push of a tag, on a pull request with the new label `full-matrix` (the check pull request before a tag), and on "Run workflow" without the input `base` or with the input `valgrind`. A push to a `v*.x` branch runs no build, as a push to `master` or `next` does not. `test/ci/changes.sh`, the same script as on `next` again, takes the run as options (`--label`, `--draft`, `--push`, `--tag`, `--dispatch`, `--valgrind`) and gives `build` `standard`, `full` or `none` ("Which jobs a run starts" in [docs/test/README.md](/contributing/test/)) (#704)

## Upgrading

- No directive was added, renamed or removed. Rebuild the module with this
  release the way you built 2.7.3, and replace the binary or the dynamic
  module as usual.

Check these before upgrading. Each line is the "What to do" of its entry
above.

- `Nginx::Stream::Connection.proxy_protocol_addr` and `proxy_protocol_ip`:
  nothing for code that compares the result with a String. Code that calls a
  String method on the result handles `nil`, for example with `to_s`
  (entry 1).
- The reads of a TCP stream session during `Nginx::Stream::Async.sleep`:
  nothing (entry 2).
