Skip to content

Memoize header hash#1549

Merged
ioquatix merged 4 commits into
masterfrom
memoize-header-hash
Feb 5, 2020
Merged

Memoize header hash#1549
ioquatix merged 4 commits into
masterfrom
memoize-header-hash

Conversation

@ioquatix

@ioquatix ioquatix commented Feb 3, 2020

Copy link
Copy Markdown
Member

The motivation/measurements/benchmarks are in benchmark/header-hash/README.md.

Fixes the main performance issues outlined by #738

@ioquatix ioquatix force-pushed the memoize-header-hash branch from 84b1227 to c9b2ce3 Compare February 3, 2020 22:31
@ioquatix

ioquatix commented Feb 3, 2020

Copy link
Copy Markdown
Member Author

cc @schneems

Comment thread lib/rack/utils.rb Outdated

@jeremyevans jeremyevans left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK with the idea and implementation.

This benchmarks are nice and an abbreviated version could go in the commit message, but I don't think we need them in the repository. Could you remove them and squash?

Also, please update CHANGELOG when merging, referencing both the performance improvement and the change in Response behavior (headers are now be mutated if passed as a HeaderHash). CHANGELOG should now be updated for all significant changes, so that it is always in a releasable state modulo the version at the top.

@tenderlove

Copy link
Copy Markdown
Member

This benchmarks are nice and an abbreviated version could go in the commit message, but I don't think we need them in the repository. Could you remove them and squash?

I agree with this, but can you put the benchmarks and results in this PR? We can refer back to the PR to see the results, and the PR will be linked to from the merge commit (another reason I like backporting merge commits rather than individual cherry picking).

@ioquatix

ioquatix commented Feb 4, 2020

Copy link
Copy Markdown
Member Author

I am happy to remove the benchmarks directory. However:

  • It doesn't cost anyone anything.
  • It provides a framework for future benchmarks so people don't have to reinvent the wheel (I tried to find the original benchmarks done by @schneems in Utils::HeaderHash is EXTREMELY expensive #738 but it doesn't seem to exist anywhere).
  • Assumptions based on benchmarks can be checked again in the future.

Putting the benchmark data into the GitHub PR ties it to GitHub. I often work offline and if I don't have all the data it's a pain.

So, unless there is a strong practical reason not to include the benchmarks in the repo, I'd prefer to leave it. However, I welcome further discussion. I'm easy either way.

@jeremyevans

Copy link
Copy Markdown
Contributor

It does cost something. Everything added to the repository costs something and requires time/bandwidth to clone and store. Even in 10 years where the data is no longer needed, it'll still be in the .git directory taking up space.

If you do want to keep the benchmark, keep just the benchmark and not the results/gemfiles. That way people can use and learn from the benchmark without taking up much space, and it still allows rechecking assumptions later. I would especially remove the gemfiles as we would want to test with with not just Falcon, but also Puma and Unicorn to judge the real world impact.

Also, I think the benchmark should be made more realistic. You could consider worst case to be every middleware that Rack ships with that uses HeaderHash, and use that along with Rack::Response. 100 Rack::ContentLength middlewares doesn't tell us much about how this affects real world cases. I don't even think Rails uses that many middlewares. :)

@tenderlove

Copy link
Copy Markdown
Member

It would be pretty cool if we had a benchmark suite and could keep track of the speeds over time.

@ioquatix

ioquatix commented Feb 4, 2020

Copy link
Copy Markdown
Member Author

It does cost something. Everything added to the repository costs something and requires time/bandwidth to clone and store. Even in 10 years where the data is no longer needed, it'll still be in the .git directory taking up space.

Understood.

We could create a separate repo for benchmarks. But that is very cumbersome when they are tied to PRs.

I hesitate to say that "even in 10 years when the data is no longer needed". I wish the original benchmarks made by @schneems were committed or in a PR, so that I could actually validate the improvements against his original data. That original issue is ~6 years old.

If you do want to keep the benchmark, keep just the benchmark and not the results/gemfiles. That way people can use and learn from the benchmark without taking up much space, and it still allows rechecking assumptions later. I would especially remove the gemfiles as we would want to test with with not just Falcon, but also Puma and Unicorn to judge the real world impact.

It sounds like you are bothered to include Falcon in a benchmark. I just used it because it's practical for the purpose of the benchmark. This benchmark is not affected by the server so much. We could use webrick or something else by default. But it didn't occur to me that someone would be unhappy about that, that I didn't use some other server or multiple servers. Making benchmarks reproducible is a good idea IMHO.

Also, I think the benchmark should be made more realistic. You could consider worst case to be every middleware that Rack ships with that uses HeaderHash, and use that along with Rack::Response. 100 Rack::ContentLength middlewares doesn't tell us much about how this affects real world cases. I don't even think Rails uses that many middlewares. :)

I wish I could reproduce the original rails benchmark done by @schneems but that is lost to the sands of time because it was not committed or in any PR that I'm aware of. That's kind of the crux of the issue here and why I decided to include it. I believe any time we have a performance related PR, we should have a corresponding test or benchmark which is (reasonably) reproducible. This is pretty typical, e.g. CRuby.

@ioquatix

ioquatix commented Feb 4, 2020

Copy link
Copy Markdown
Member Author

Just a few examples:

Async benchmarks to check assumptions, all fairly reproducible:

Useful for comparing assumptions across releases of Ruby, different implementations of Ruby.

Specs which include benchmarks:

https://github.com/socketry/async/blob/master/spec/async/performance_spec.rb

https://github.com/socketry/async/runs/420813752?check_suite_focus=true#step:5:66

There are others, but this is just one example which helps me catch regressions or identify performance issues over time.

It's also possible to do the specific regression testing on memory allocations in specs:

https://github.com/socketry/rspec-memory

honestly, something like the last one might be better than a benchmark, but these all work together to prevent and identify performance issues.

@schneems

schneems commented Feb 4, 2020

Copy link
Copy Markdown
Contributor

I’m on mobile, but it looks like this used to be the behavior of HeaderHash.new, or at least it was when I made this PR https://github.com/rack/rack/pull/1204/files#diff-7c1a24d5b2fe58a6f925c7cacc6c55e7 I’m not able to see when or why it changed right now.

FWIW I extracted my benchmark code to derailed https://github.com/schneems/derailed_benchmarks#i-made-a-patch-to-to-rails-how-can-i-tell-if-it-made-my-rails-app-faster-and-test-for-statistical-significance you can also set the library to rack just like you can for Rails. It won’t show the change over time but will give an a/b perf comparison with some statistical analysis.

@schneems

schneems commented Feb 4, 2020

Copy link
Copy Markdown
Contributor

it looks like this used to be the behavior of HeaderHash.new, or at least it was when I made this PR

Looks like that logic was reverted by @jodosha in #1325

$ git log -S "HeaderHash ===" lib/rack/utils.rb
commit d671a0c3379bc05f17e3f670d943c6243c333df0
Author: Luca Guidi <email-redacted>
Date:   Wed Jan 23 10:57:16 2019 +0100

    Ensure initialization of Utils::HeaderHash will dup given headers

commit e4c7c58597abaa3c0a8281df5f604da33d1304c3
Author: Luca Guidi <email-redacted>
Date:   Fri Jan 4 16:13:24 2019 +0100

    Don't mutate given headers

commit cdf13618445fa7065c07d62b115907856d6d1095
Author: Eric Wong <email-redacted>
Date:   Thu Dec 10 21:33:53 2009 -0600

    HeaderHash.new avoids unnecessary object creation

    Creating a new HeaderHash is an O(n) operation in addition to
    the cost of allocating a new object.  When using multiple pieces
    of middleware, this can lead to unnecessary memory allocation
    and iteration overhead.   We now explicitly define the
    HeaderHash.new class method to return its original argument if
    it is already a HeaderHash to avoid repeating work.

    Signed-off-by: Joshua Peek <email-redacted>

@schneems

schneems commented Feb 4, 2020

Copy link
Copy Markdown
Contributor

Here's how I will test with Derailed and https://www.codetriage.com app (https://github.com/codetriage/codetriage)

$ git co schneems/rack-patch-speed-test
$ DERAILED_PATH_TO_LIBRARY="$(bundle show rack)" SHAS_TO_TEST="memoize-header-hash,59985351196ad348dbe53b799333ad24d8d64404" bundle exec derailed exec perf:library

The gist of how you perf test a library with derailed:

  • Get an an app that uses the library (CodeTriage uses rack)
  • Point the dependency to a new commit, I did this by modifying the gemfile in the schneems/rack-patch-speed-test branch of CodeTriage
  • Tell derailed where the library you want to test lives DERAILED_PATH_TO_LIBRARY="$(bundle show rack)"
  • Tell it which git refs you want to test against SHAS_TO_TEST="memoize-header-hash,59985351196ad348dbe53b799333ad24d8d64404" (this PR branch, and the commit of master)
  • Then the actual command bundle exec derailed exec perf:library

Derailed will then boot the app in memory and hit it with 200 requests using rack-mock, this is done headlessly. It then toggles the git ref and hits that with 200 requests. It repeats this process 200 times. It will print out intermediate results along with statistical significance information as it runs. Ideally, I want histograms too, but that's not implemented yet zombocom/derailed_benchmarks#169 (and will require significant work).

I'm going to let this run a few times overnight and let you all know how it turns out.

@ioquatix

ioquatix commented Feb 4, 2020

Copy link
Copy Markdown
Member Author

Thanks that sounds awesome looking forward to the results!

@schneems

schneems commented Feb 4, 2020

Copy link
Copy Markdown
Contributor

Big caveat - I'm going to give the results of my tests, they are not THE results. They are one picture of results based off of how this patch behaves with CodeTriage. I've spent a lot of time trying to measure perf differences between code changes in my Rails patches. However, I do not have all the answers. Basically, use this data not as an answer, but rather as the basis for more questions.

My results I ran derailed against code triage 6 times last night and got statistically insignificant results for all six runs with a 95% confidence interval (derailed will report up to 99% significant if available). Some runs were faster some were slower. But we cannot say, with confidence, that the result was due to the change as opposed random chance on CodeTriage. Here's the results:

  FASTER 🚀🚀🚀 by:
    1.0096x [older/newer]
    0.9490% [(older - newer) / older * 100]

  FASTER 🚀🚀🚀 by:
    1.0093x [older/newer]
    0.9205% [(older - newer) / older * 100]

  SLOWER 🐢🐢🐢 by:
     0.9997x [older/newer]
    -0.0269% [(older - newer) / older * 100]

  FASTER 🚀🚀🚀 by:
    1.0212x [older/newer]
    2.0744% [(older - newer) / older * 100]

  SLOWER 🐢🐢🐢 by:
     0.9976x [older/newer]
    -0.2429% [(older - newer) / older * 100]

  SLOWER 🐢🐢🐢 by:
     0.9949x [older/newer]
    -0.5092% [(older - newer) / older * 100]

Again none of these results were significant, so they don't strictly mean anything conclusive. I tried again this morning but so far over two runs, still no significant results. You cannot use these results to say "it is faster!" or "it is slower!" you cannot average or manipulate these results to mean anything, other than we cannot say with 95% confidence that the results are not the result of random chance.

That could mean that there is a change, but it's smaller than we are capable of measuring, or it could mean that there's effectively no measurable difference between the two branches (for this test). The app we're testing on for sure will make a difference. A "hello world" rack app would likely better be able to highlight a perf difference, but i'm not able to see one in CodeTriage.

I've attached the raw results in a zip file. If you want to chat more about the data, or derailed feel free to ping me. I'm in Nate's performance slack and the Rails basecamp. My email is visible on GitHub if you don't have access to either of those and want more synchronous communication.

derailed-raw-results.zip

@ioquatix

ioquatix commented Feb 4, 2020

Copy link
Copy Markdown
Member Author

Nice work!

kamipo added a commit to kamipo/rack that referenced this pull request Feb 4, 2020
…ethod def in the future

Ref rack#1549 (comment).

If we'd not prefer the no paren method def style and avoid the style by
code review, we have a way to avoid that style by using RuboCop.
ioquatix pushed a commit that referenced this pull request Feb 4, 2020
…ethod def in the future

Ref #1549 (comment).

If we'd not prefer the no paren method def style and avoid the style by
code review, we have a way to avoid that style by using RuboCop.
@ioquatix

ioquatix commented Feb 4, 2020

Copy link
Copy Markdown
Member Author

I was reviewing the HeaderHash code, and realised that it's merging together headers using newlines. Which I subsequently have to revert in Falcon because no actual protocol supports this format:

https://github.com/socketry/falcon/blob/b8e9ca5aeaf748bec550a54a8bcdeb7f7c3d3fe7/lib/falcon/adapters/response.rb#L40-L50

For Rack 3.0, we could consider allowing headers to be an array of values and drop the newline format.

The practical benefits of this implementation seem limited, at least according to the data point provided by @schneems which I fully respect the statistical analysis he did, compared to the supplied micro benchmark.

That being said, in theory this does avoid memory allocations when there are lots of headers, so generally I think there are going to be use cases which benefit from this memoization.

Therefore, I'm going to tidy up this PR with some specs, remove the benchmarks, and merge. Then, with Rack 3.0 push, I will reintroduce benchmarks as part of the tests, and hopefully we can do so in other areas too.

ioquatix and others added 2 commits February 5, 2020 13:20
Co-Authored-By: Rafael França <rafael@franca.dev>
@ioquatix ioquatix force-pushed the memoize-header-hash branch from 11f7324 to 1736973 Compare February 5, 2020 00:20
@ioquatix ioquatix force-pushed the memoize-header-hash branch from 1736973 to 6b1f13b Compare February 5, 2020 00:25
@ioquatix ioquatix force-pushed the memoize-header-hash branch from 6b1f13b to e94dc31 Compare February 5, 2020 00:31
@ioquatix ioquatix merged commit d4bb198 into master Feb 5, 2020
@ioquatix ioquatix deleted the memoize-header-hash branch February 5, 2020 00:35
Comment thread test/spec_utils.rb
assert_same response[1], headers
end

it "duplicates header hash" do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting issue?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll fix it!

kamipo added a commit to kamipo/rack that referenced this pull request Feb 5, 2020
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Mar 20, 2020
Update ruby-rack to 2.2.2.


## [2.2.2] - 2020-02-11

### Fixed

- Fix incorrect `Rack::Request#host` value. ([#1591](rack/rack#1591), [@ioquatix](https://github.com/ioquatix))
- Revert `Rack::Handler::Thin` implementation. ([#1583](rack/rack#1583), [@jeremyevans](https://github.com/jeremyevans))
- Double assignment is still needed to prevent an "unused variable" warning. ([#1589](rack/rack#1589), [@kamipo](https://github.com/kamipo))
- Fix to handle same_site option for session pool. ([#1587](rack/rack#1587), [@kamipo](https://github.com/kamipo))

## [2.2.1] - 2020-02-09

### Fixed

- Rework `Rack::Request#ip` to handle empty `forwarded_for`. ([#1577](rack/rack#1577), [@ioquatix](https://github.com/ioquatix))

## [2.2.0] - 2020-02-08

### SPEC Changes

- `rack.session` request environment entry must respond to `to_hash` and return unfrozen Hash. ([@jeremyevans](https://github.com/jeremyevans))
- Request environment cannot be frozen. ([@jeremyevans](https://github.com/jeremyevans))
- CGI values in the request environment with non-ASCII characters must use ASCII-8BIT encoding. ([@jeremyevans](https://github.com/jeremyevans))
- Improve SPEC/lint relating to SERVER_NAME, SERVER_PORT and HTTP_HOST. ([#1561](rack/rack#1561), [@ioquatix](https://github.com/ioquatix))

### Added

- `rackup` supports multiple `-r` options and will require all arguments. ([@jeremyevans](https://github.com/jeremyevans))
- `Server` supports an array of paths to require for the `:require` option. ([@khotta](https://github.com/khotta))
- `Files` supports multipart range requests. ([@fatkodima](https://github.com/fatkodima))
- `Multipart::UploadedFile` supports an IO-like object instead of using the filesystem, using `:filename` and `:io` options. ([@jeremyevans](https://github.com/jeremyevans))
- `Multipart::UploadedFile` supports keyword arguments `:path`, `:content_type`, and `:binary` in addition to positional arguments. ([@jeremyevans](https://github.com/jeremyevans))
- `Static` supports a `:cascade` option for calling the app if there is no matching file. ([@jeremyevans](https://github.com/jeremyevans))
- `Session::Abstract::SessionHash#dig`. ([@jeremyevans](https://github.com/jeremyevans))
- `Response.[]` and `MockResponse.[]` for creating instances using status, headers, and body. ([@ioquatix](https://github.com/ioquatix))
- Convenient cache and content type methods for `Rack::Response`. ([#1555](rack/rack#1555), [@ioquatix](https://github.com/ioquatix))

### Changed

- `Request#params` no longer rescues EOFError. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` uses a streaming approach, significantly improving time to first byte for large directories. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` no longer includes a Parent directory link in the root directory index. ([@jeremyevans](https://github.com/jeremyevans))
- `QueryParser#parse_nested_query` uses original backtrace when reraising exception with new class. ([@jeremyevans](https://github.com/jeremyevans))
- `ConditionalGet` follows RFC 7232 precedence if both If-None-Match and If-Modified-Since headers are provided. ([@jeremyevans](https://github.com/jeremyevans))
- `.ru` files supports the `frozen-string-literal` magic comment. ([@eregon](https://github.com/eregon))
- Rely on autoload to load constants instead of requiring internal files, make sure to require 'rack' and not just 'rack/...'. ([@jeremyevans](https://github.com/jeremyevans))
- `Etag` will continue sending ETag even if the response should not be cached. ([@henm](https://github.com/henm))
- `Request#host_with_port` no longer includes a colon for a missing or empty port. ([@AlexWayfer](https://github.com/AlexWayfer))
- All handlers uses keywords arguments instead of an options hash argument. ([@ioquatix](https://github.com/ioquatix))
- `Files` handling of range requests no longer return a body that supports `to_path`, to ensure range requests are handled correctly. ([@jeremyevans](https://github.com/jeremyevans))
- `Multipart::Generator` only includes `Content-Length` for files with paths, and `Content-Disposition` `filename` if the `UploadedFile` instance has one. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#ssl?` is true for the `wss` scheme (secure websockets). ([@jeremyevans](https://github.com/jeremyevans))
- `Rack::HeaderHash` is memoized by default. ([#1549](rack/rack#1549), [@ioquatix](https://github.com/ioquatix))
- `Rack::Directory` allow directory traversal inside root directory. ([#1417](rack/rack#1417), [@ThomasSevestre](https://github.com/ThomasSevestre))
- Sort encodings by server preference. ([#1184](rack/rack#1184), [@ioquatix](https://github.com/ioquatix), [@wjordan](https://github.com/wjordan))
- Rework host/hostname/authority implementation in `Rack::Request`. `#host` and `#host_with_port` have been changed to correctly return IPv6 addresses formatted with square brackets, as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-3.2.2). ([#1561](rack/rack#1561), [@ioquatix](https://github.com/ioquatix))
- `Rack::Builder` parsing options on first `#\` line is deprecated. ([#1574](rack/rack#1574), [@ioquatix](https://github.com/ioquatix))

### Removed

- `Directory#path` as it was not used and always returned nil. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy#each` as it was only needed to work around a bug in Ruby <1.9.3. ([@jeremyevans](https://github.com/jeremyevans))
- `URLMap::INFINITY` and `URLMap::NEGATIVE_INFINITY`, in favor of `Float::INFINITY`. ([@ch1c0t](https://github.com/ch1c0t))
- Deprecation of `Rack::File`. It will be deprecated again in rack 2.2 or 3.0. ([@rafaelfranca](https://github.com/rafaelfranca))
- Support for Ruby 2.2 as it is well past EOL. ([@ioquatix](https://github.com/ioquatix))
- Remove `Rack::Files#response_body` as the implementation was broken. ([#1153](rack/rack#1153), [@ioquatix](https://github.com/ioquatix))
- Remove `SERVER_ADDR` which was never part of the original SPEC. ([#1573](rack/rack#1573), [@ioquatix](https://github.com/ioquatix))

### Fixed

- `Directory` correctly handles root paths containing glob metacharacters. ([@jeremyevans](https://github.com/jeremyevans))
- `Cascade` uses a new response object for each call if initialized with no apps. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy` correctly delegates keyword arguments to the body object on Ruby 2.7+. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy#method` correctly handles methods delegated to the body object. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#host` and `Request#host_with_port` handle IPv6 addresses correctly. ([@AlexWayfer](https://github.com/AlexWayfer))
- `Lint` checks when response hijacking that `rack.hijack` is called with a valid object. ([@jeremyevans](https://github.com/jeremyevans))
- `Response#write` correctly updates `Content-Length` if initialized with a body. ([@jeremyevans](https://github.com/jeremyevans))
- `CommonLogger` includes `SCRIPT_NAME` when logging. ([@Erol](https://github.com/Erol))
- `Utils.parse_nested_query` correctly handles empty queries, using an empty instance of the params class instead of a hash. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` correctly escapes paths in links. ([@yous](https://github.com/yous))
- `Request#delete_cookie` and related `Utils` methods handle `:domain` and `:path` options in same call. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#delete_cookie` and related `Utils` methods do an exact match on `:domain` and `:path` options. ([@jeremyevans](https://github.com/jeremyevans))
- `Static` no longer adds headers when a gzipped file request has a 304 response. ([@chooh](https://github.com/chooh))
- `ContentLength` sets `Content-Length` response header even for bodies not responding to `to_ary`. ([@jeremyevans](https://github.com/jeremyevans))
- Thin handler supports options passed directly to `Thin::Controllers::Controller`. ([@jeremyevans](https://github.com/jeremyevans))
- WEBrick handler no longer ignores `:BindAddress` option. ([@jeremyevans](https://github.com/jeremyevans))
- `ShowExceptions` handles invalid POST data. ([@jeremyevans](https://github.com/jeremyevans))
- Basic authentication requires a password, even if the password is empty. ([@jeremyevans](https://github.com/jeremyevans))
- `Lint` checks response is array with 3 elements, per SPEC. ([@jeremyevans](https://github.com/jeremyevans))
- Support for using `:SSLEnable` option when using WEBrick handler. (Gregor Melhorn)
- Close response body after buffering it when buffering. ([@ioquatix](https://github.com/ioquatix))
- Only accept `;` as delimiter when parsing cookies. ([@mrageh](https://github.com/mrageh))
- `Utils::HeaderHash#clear` clears the name mapping as well. ([@raxoft](https://github.com/raxoft))
- Support for passing `nil` `Rack::Files.new`, which notably fixes Rails' current `ActiveStorage::FileServer` implementation. ([@ioquatix](https://github.com/ioquatix))

### Documentation

- CHANGELOG updates. ([@Aupajo](https://github.com/aupajo))
- Added [CONTRIBUTING](CONTRIBUTING.md). ([@dblock](https://github.com/dblock))

## [2.1.2] - 2020-01-27

- Fix multipart parser for some files to prevent denial of service ([@aiomaster](https://github.com/aiomaster))
- Fix `Rack::Builder#use` with keyword arguments ([@kamipo](https://github.com/kamipo))
- Skip deflating in Rack::Deflater if Content-Length is 0 ([@jeremyevans](https://github.com/jeremyevans))
- Remove `SessionHash#transform_keys`, no longer needed ([@pavel](https://github.com/pavel))
- Add to_hash to wrap Hash and Session classes ([@oleh-demyanyuk](https://github.com/oleh-demyanyuk))
- Handle case where session id key is requested but missing ([@jeremyevans](https://github.com/jeremyevans))

## [2.1.1] - 2020-01-12

- Remove `Rack::Chunked` from `Rack::Server` default middleware. ([#1475](rack/rack#1475), [@ioquatix](https://github.com/ioquatix))

## 2.1.0

_Note: There are many unreleased changes in Rack (`master` is around 300 commits ahead of `2-0-stable`), and below is not an exhaustive list. If you would like to help out and document some of the unreleased changes, PRs are welcome._

### Added

- Add support for `SameSite=None` cookie value. ([@hennikul](https://github.com/hennikul))
- Add trailer headers. ([@eileencodes](https://github.com/eileencodes))
- Add MIME Types for video streaming. ([@styd](https://github.com/styd))
- Add MIME Type for WASM. ([@buildrtech](https://github.com/buildrtech))
- Add `Early Hints(103)` to status codes. ([@egtra](https://github.com/egtra))
- Add `Too Early(425)` to status codes. ([@y-yagi]((https://github.com/y-yagi)))
- Add `Bandwidth Limit Exceeded(509)` to status codes. ([@CJKinni](https://github.com/CJKinni))
- Add method for custom `ip_filter`. ([@svcastaneda](https://github.com/svcastaneda))
- Add boot-time profiling capabilities to `rackup`. ([@tenderlove](https://github.com/tenderlove))
- Add multi mapping support for `X-Accel-Mappings` header. ([@yoshuki](https://github.com/yoshuki))
- Add `sync: false` option to `Rack::Deflater`. (Eric Wong)
- Add `Builder#freeze_app` to freeze application and all middleware instances. ([@jeremyevans](https://github.com/jeremyevans))
- Add API to extract cookies from `Rack::MockResponse`. ([@petercline](https://github.com/petercline))

### Changed

- Don't propagate nil values from middleware. ([@ioquatix](https://github.com/ioquatix))
- Lazily initialize the response body and only buffer it if required. ([@ioquatix](https://github.com/ioquatix))
- Fix deflater zlib buffer errors on empty body part. ([@felixbuenemann](https://github.com/felixbuenemann))
- Set `X-Accel-Redirect` to percent-encoded path. ([@diskkid](https://github.com/diskkid))
- Remove unnecessary buffer growing when parsing multipart. ([@tainoe](https://github.com/tainoe))
- Expand the root path in `Rack::Static` upon initialization. ([@rosenfeld](https://github.com/rosenfeld))
- Make `ShowExceptions` work with binary data. ([@axyjo](https://github.com/axyjo))
- Use buffer string when parsing multipart requests. ([@janko-m](https://github.com/janko-m))
- Support optional UTF-8 Byte Order Mark (BOM) in config.ru. ([@mikegee](https://github.com/mikegee))
- Handle `X-Forwarded-For` with optional port. ([@dpritchett](https://github.com/dpritchett))
- Use `Time#httpdate` format for Expires, as proposed by RFC 7231. ([@nanaya](https://github.com/nanaya))
- Make `Utils.status_code` raise an error when the status symbol is invalid instead of `500`. ([@adambutler](https://github.com/adambutler))
- Rename `Request::SCHEME_WHITELIST` to `Request::ALLOWED_SCHEMES`.
- Make `Multipart::Parser.get_filename` accept files with `+` in their name. ([@lucaskanashiro](https://github.com/lucaskanashiro))
- Add Falcon to the default handler fallbacks. ([@ioquatix](https://github.com/ioquatix))
- Update codebase to avoid string mutations in preparation for `frozen_string_literals`. ([@pat](https://github.com/pat))
- Change `MockRequest#env_for` to rely on the input optionally responding to `#size` instead of `#length`. ([@janko](https://github.com/janko))
- Rename `Rack::File` -> `Rack::Files` and add deprecation notice. ([@postmodern](https://github.com/postmodern)).

### Removed

- Remove `to_ary` from Response ([@tenderlove](https://github.com/tenderlove))
- Deprecate `Rack::Session::Memcache` in favor of `Rack::Session::Dalli` from dalli gem ([@fatkodima](https://github.com/fatkodima))

### Documentation

- Update broken example in `Session::Abstract::ID` documentation. ([tonytonyjan](https://github.com/tonytonyjan))
- Add Padrino to the list of frameworks implmenting Rack. ([@wikimatze](https://github.com/wikimatze))
- Remove Mongrel from the suggested server options in the help output. ([@tricknotes](https://github.com/tricknotes))
- Replace `HISTORY.md` and `NEWS.md` with `CHANGELOG.md`. ([@twitnithegirl](https://github.com/twitnithegirl))
- Backfill `CHANGELOG.md` from 2.0.1 to 2.0.7 releases. ([@drenmi](https://github.com/Drenmi))

## [2.0.8] - 2019-12-08

- [[CVE-2019-16782](https://nvd.nist.gov/vuln/detail/CVE-2019-16782)] Prevent timing attacks targeted at session ID lookup. BREAKING CHANGE: Session ID is now a SessionId instance instead of a String. ([@tenderlove](https://github.com/tenderlove), [@rafaelfranca](https://github.com/rafaelfranca))
msk pushed a commit to msk/pkgsrc that referenced this pull request May 11, 2026
Update ruby-rack to 2.2.2.


## [2.2.2] - 2020-02-11

### Fixed

- Fix incorrect `Rack::Request#host` value. ([#1591](rack/rack#1591), [@ioquatix](https://github.com/ioquatix))
- Revert `Rack::Handler::Thin` implementation. ([#1583](rack/rack#1583), [@jeremyevans](https://github.com/jeremyevans))
- Double assignment is still needed to prevent an "unused variable" warning. ([#1589](rack/rack#1589), [@kamipo](https://github.com/kamipo))
- Fix to handle same_site option for session pool. ([#1587](rack/rack#1587), [@kamipo](https://github.com/kamipo))

## [2.2.1] - 2020-02-09

### Fixed

- Rework `Rack::Request#ip` to handle empty `forwarded_for`. ([#1577](rack/rack#1577), [@ioquatix](https://github.com/ioquatix))

## [2.2.0] - 2020-02-08

### SPEC Changes

- `rack.session` request environment entry must respond to `to_hash` and return unfrozen Hash. ([@jeremyevans](https://github.com/jeremyevans))
- Request environment cannot be frozen. ([@jeremyevans](https://github.com/jeremyevans))
- CGI values in the request environment with non-ASCII characters must use ASCII-8BIT encoding. ([@jeremyevans](https://github.com/jeremyevans))
- Improve SPEC/lint relating to SERVER_NAME, SERVER_PORT and HTTP_HOST. ([#1561](rack/rack#1561), [@ioquatix](https://github.com/ioquatix))

### Added

- `rackup` supports multiple `-r` options and will require all arguments. ([@jeremyevans](https://github.com/jeremyevans))
- `Server` supports an array of paths to require for the `:require` option. ([@khotta](https://github.com/khotta))
- `Files` supports multipart range requests. ([@fatkodima](https://github.com/fatkodima))
- `Multipart::UploadedFile` supports an IO-like object instead of using the filesystem, using `:filename` and `:io` options. ([@jeremyevans](https://github.com/jeremyevans))
- `Multipart::UploadedFile` supports keyword arguments `:path`, `:content_type`, and `:binary` in addition to positional arguments. ([@jeremyevans](https://github.com/jeremyevans))
- `Static` supports a `:cascade` option for calling the app if there is no matching file. ([@jeremyevans](https://github.com/jeremyevans))
- `Session::Abstract::SessionHash#dig`. ([@jeremyevans](https://github.com/jeremyevans))
- `Response.[]` and `MockResponse.[]` for creating instances using status, headers, and body. ([@ioquatix](https://github.com/ioquatix))
- Convenient cache and content type methods for `Rack::Response`. ([#1555](rack/rack#1555), [@ioquatix](https://github.com/ioquatix))

### Changed

- `Request#params` no longer rescues EOFError. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` uses a streaming approach, significantly improving time to first byte for large directories. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` no longer includes a Parent directory link in the root directory index. ([@jeremyevans](https://github.com/jeremyevans))
- `QueryParser#parse_nested_query` uses original backtrace when reraising exception with new class. ([@jeremyevans](https://github.com/jeremyevans))
- `ConditionalGet` follows RFC 7232 precedence if both If-None-Match and If-Modified-Since headers are provided. ([@jeremyevans](https://github.com/jeremyevans))
- `.ru` files supports the `frozen-string-literal` magic comment. ([@eregon](https://github.com/eregon))
- Rely on autoload to load constants instead of requiring internal files, make sure to require 'rack' and not just 'rack/...'. ([@jeremyevans](https://github.com/jeremyevans))
- `Etag` will continue sending ETag even if the response should not be cached. ([@henm](https://github.com/henm))
- `Request#host_with_port` no longer includes a colon for a missing or empty port. ([@AlexWayfer](https://github.com/AlexWayfer))
- All handlers uses keywords arguments instead of an options hash argument. ([@ioquatix](https://github.com/ioquatix))
- `Files` handling of range requests no longer return a body that supports `to_path`, to ensure range requests are handled correctly. ([@jeremyevans](https://github.com/jeremyevans))
- `Multipart::Generator` only includes `Content-Length` for files with paths, and `Content-Disposition` `filename` if the `UploadedFile` instance has one. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#ssl?` is true for the `wss` scheme (secure websockets). ([@jeremyevans](https://github.com/jeremyevans))
- `Rack::HeaderHash` is memoized by default. ([#1549](rack/rack#1549), [@ioquatix](https://github.com/ioquatix))
- `Rack::Directory` allow directory traversal inside root directory. ([#1417](rack/rack#1417), [@ThomasSevestre](https://github.com/ThomasSevestre))
- Sort encodings by server preference. ([#1184](rack/rack#1184), [@ioquatix](https://github.com/ioquatix), [@wjordan](https://github.com/wjordan))
- Rework host/hostname/authority implementation in `Rack::Request`. `#host` and `#host_with_port` have been changed to correctly return IPv6 addresses formatted with square brackets, as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-3.2.2). ([#1561](rack/rack#1561), [@ioquatix](https://github.com/ioquatix))
- `Rack::Builder` parsing options on first `#\` line is deprecated. ([#1574](rack/rack#1574), [@ioquatix](https://github.com/ioquatix))

### Removed

- `Directory#path` as it was not used and always returned nil. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy#each` as it was only needed to work around a bug in Ruby <1.9.3. ([@jeremyevans](https://github.com/jeremyevans))
- `URLMap::INFINITY` and `URLMap::NEGATIVE_INFINITY`, in favor of `Float::INFINITY`. ([@ch1c0t](https://github.com/ch1c0t))
- Deprecation of `Rack::File`. It will be deprecated again in rack 2.2 or 3.0. ([@rafaelfranca](https://github.com/rafaelfranca))
- Support for Ruby 2.2 as it is well past EOL. ([@ioquatix](https://github.com/ioquatix))
- Remove `Rack::Files#response_body` as the implementation was broken. ([#1153](rack/rack#1153), [@ioquatix](https://github.com/ioquatix))
- Remove `SERVER_ADDR` which was never part of the original SPEC. ([#1573](rack/rack#1573), [@ioquatix](https://github.com/ioquatix))

### Fixed

- `Directory` correctly handles root paths containing glob metacharacters. ([@jeremyevans](https://github.com/jeremyevans))
- `Cascade` uses a new response object for each call if initialized with no apps. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy` correctly delegates keyword arguments to the body object on Ruby 2.7+. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy#method` correctly handles methods delegated to the body object. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#host` and `Request#host_with_port` handle IPv6 addresses correctly. ([@AlexWayfer](https://github.com/AlexWayfer))
- `Lint` checks when response hijacking that `rack.hijack` is called with a valid object. ([@jeremyevans](https://github.com/jeremyevans))
- `Response#write` correctly updates `Content-Length` if initialized with a body. ([@jeremyevans](https://github.com/jeremyevans))
- `CommonLogger` includes `SCRIPT_NAME` when logging. ([@Erol](https://github.com/Erol))
- `Utils.parse_nested_query` correctly handles empty queries, using an empty instance of the params class instead of a hash. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` correctly escapes paths in links. ([@yous](https://github.com/yous))
- `Request#delete_cookie` and related `Utils` methods handle `:domain` and `:path` options in same call. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#delete_cookie` and related `Utils` methods do an exact match on `:domain` and `:path` options. ([@jeremyevans](https://github.com/jeremyevans))
- `Static` no longer adds headers when a gzipped file request has a 304 response. ([@chooh](https://github.com/chooh))
- `ContentLength` sets `Content-Length` response header even for bodies not responding to `to_ary`. ([@jeremyevans](https://github.com/jeremyevans))
- Thin handler supports options passed directly to `Thin::Controllers::Controller`. ([@jeremyevans](https://github.com/jeremyevans))
- WEBrick handler no longer ignores `:BindAddress` option. ([@jeremyevans](https://github.com/jeremyevans))
- `ShowExceptions` handles invalid POST data. ([@jeremyevans](https://github.com/jeremyevans))
- Basic authentication requires a password, even if the password is empty. ([@jeremyevans](https://github.com/jeremyevans))
- `Lint` checks response is array with 3 elements, per SPEC. ([@jeremyevans](https://github.com/jeremyevans))
- Support for using `:SSLEnable` option when using WEBrick handler. (Gregor Melhorn)
- Close response body after buffering it when buffering. ([@ioquatix](https://github.com/ioquatix))
- Only accept `;` as delimiter when parsing cookies. ([@mrageh](https://github.com/mrageh))
- `Utils::HeaderHash#clear` clears the name mapping as well. ([@raxoft](https://github.com/raxoft))
- Support for passing `nil` `Rack::Files.new`, which notably fixes Rails' current `ActiveStorage::FileServer` implementation. ([@ioquatix](https://github.com/ioquatix))

### Documentation

- CHANGELOG updates. ([@Aupajo](https://github.com/aupajo))
- Added [CONTRIBUTING](CONTRIBUTING.md). ([@dblock](https://github.com/dblock))

## [2.1.2] - 2020-01-27

- Fix multipart parser for some files to prevent denial of service ([@aiomaster](https://github.com/aiomaster))
- Fix `Rack::Builder#use` with keyword arguments ([@kamipo](https://github.com/kamipo))
- Skip deflating in Rack::Deflater if Content-Length is 0 ([@jeremyevans](https://github.com/jeremyevans))
- Remove `SessionHash#transform_keys`, no longer needed ([@pavel](https://github.com/pavel))
- Add to_hash to wrap Hash and Session classes ([@oleh-demyanyuk](https://github.com/oleh-demyanyuk))
- Handle case where session id key is requested but missing ([@jeremyevans](https://github.com/jeremyevans))

## [2.1.1] - 2020-01-12

- Remove `Rack::Chunked` from `Rack::Server` default middleware. ([#1475](rack/rack#1475), [@ioquatix](https://github.com/ioquatix))

## 2.1.0

_Note: There are many unreleased changes in Rack (`master` is around 300 commits ahead of `2-0-stable`), and below is not an exhaustive list. If you would like to help out and document some of the unreleased changes, PRs are welcome._

### Added

- Add support for `SameSite=None` cookie value. ([@hennikul](https://github.com/hennikul))
- Add trailer headers. ([@eileencodes](https://github.com/eileencodes))
- Add MIME Types for video streaming. ([@styd](https://github.com/styd))
- Add MIME Type for WASM. ([@buildrtech](https://github.com/buildrtech))
- Add `Early Hints(103)` to status codes. ([@egtra](https://github.com/egtra))
- Add `Too Early(425)` to status codes. ([@y-yagi]((https://github.com/y-yagi)))
- Add `Bandwidth Limit Exceeded(509)` to status codes. ([@CJKinni](https://github.com/CJKinni))
- Add method for custom `ip_filter`. ([@svcastaneda](https://github.com/svcastaneda))
- Add boot-time profiling capabilities to `rackup`. ([@tenderlove](https://github.com/tenderlove))
- Add multi mapping support for `X-Accel-Mappings` header. ([@yoshuki](https://github.com/yoshuki))
- Add `sync: false` option to `Rack::Deflater`. (Eric Wong)
- Add `Builder#freeze_app` to freeze application and all middleware instances. ([@jeremyevans](https://github.com/jeremyevans))
- Add API to extract cookies from `Rack::MockResponse`. ([@petercline](https://github.com/petercline))

### Changed

- Don't propagate nil values from middleware. ([@ioquatix](https://github.com/ioquatix))
- Lazily initialize the response body and only buffer it if required. ([@ioquatix](https://github.com/ioquatix))
- Fix deflater zlib buffer errors on empty body part. ([@felixbuenemann](https://github.com/felixbuenemann))
- Set `X-Accel-Redirect` to percent-encoded path. ([@diskkid](https://github.com/diskkid))
- Remove unnecessary buffer growing when parsing multipart. ([@tainoe](https://github.com/tainoe))
- Expand the root path in `Rack::Static` upon initialization. ([@rosenfeld](https://github.com/rosenfeld))
- Make `ShowExceptions` work with binary data. ([@axyjo](https://github.com/axyjo))
- Use buffer string when parsing multipart requests. ([@janko-m](https://github.com/janko-m))
- Support optional UTF-8 Byte Order Mark (BOM) in config.ru. ([@mikegee](https://github.com/mikegee))
- Handle `X-Forwarded-For` with optional port. ([@dpritchett](https://github.com/dpritchett))
- Use `Time#httpdate` format for Expires, as proposed by RFC 7231. ([@nanaya](https://github.com/nanaya))
- Make `Utils.status_code` raise an error when the status symbol is invalid instead of `500`. ([@adambutler](https://github.com/adambutler))
- Rename `Request::SCHEME_WHITELIST` to `Request::ALLOWED_SCHEMES`.
- Make `Multipart::Parser.get_filename` accept files with `+` in their name. ([@lucaskanashiro](https://github.com/lucaskanashiro))
- Add Falcon to the default handler fallbacks. ([@ioquatix](https://github.com/ioquatix))
- Update codebase to avoid string mutations in preparation for `frozen_string_literals`. ([@pat](https://github.com/pat))
- Change `MockRequest#env_for` to rely on the input optionally responding to `#size` instead of `#length`. ([@janko](https://github.com/janko))
- Rename `Rack::File` -> `Rack::Files` and add deprecation notice. ([@postmodern](https://github.com/postmodern)).

### Removed

- Remove `to_ary` from Response ([@tenderlove](https://github.com/tenderlove))
- Deprecate `Rack::Session::Memcache` in favor of `Rack::Session::Dalli` from dalli gem ([@fatkodima](https://github.com/fatkodima))

### Documentation

- Update broken example in `Session::Abstract::ID` documentation. ([tonytonyjan](https://github.com/tonytonyjan))
- Add Padrino to the list of frameworks implmenting Rack. ([@wikimatze](https://github.com/wikimatze))
- Remove Mongrel from the suggested server options in the help output. ([@tricknotes](https://github.com/tricknotes))
- Replace `HISTORY.md` and `NEWS.md` with `CHANGELOG.md`. ([@twitnithegirl](https://github.com/twitnithegirl))
- Backfill `CHANGELOG.md` from 2.0.1 to 2.0.7 releases. ([@drenmi](https://github.com/Drenmi))

## [2.0.8] - 2019-12-08

- [[CVE-2019-16782](https://nvd.nist.gov/vuln/detail/CVE-2019-16782)] Prevent timing attacks targeted at session ID lookup. BREAKING CHANGE: Session ID is now a SessionId instance instead of a String. ([@tenderlove](https://github.com/tenderlove), [@rafaelfranca](https://github.com/rafaelfranca))
jperkin pushed a commit to TritonDataCenter/pkgsrc that referenced this pull request May 14, 2026
Update ruby-rack to 2.2.2.


## [2.2.2] - 2020-02-11

### Fixed

- Fix incorrect `Rack::Request#host` value. ([#1591](rack/rack#1591), [@ioquatix](https://github.com/ioquatix))
- Revert `Rack::Handler::Thin` implementation. ([#1583](rack/rack#1583), [@jeremyevans](https://github.com/jeremyevans))
- Double assignment is still needed to prevent an "unused variable" warning. ([#1589](rack/rack#1589), [@kamipo](https://github.com/kamipo))
- Fix to handle same_site option for session pool. ([#1587](rack/rack#1587), [@kamipo](https://github.com/kamipo))

## [2.2.1] - 2020-02-09

### Fixed

- Rework `Rack::Request#ip` to handle empty `forwarded_for`. ([#1577](rack/rack#1577), [@ioquatix](https://github.com/ioquatix))

## [2.2.0] - 2020-02-08

### SPEC Changes

- `rack.session` request environment entry must respond to `to_hash` and return unfrozen Hash. ([@jeremyevans](https://github.com/jeremyevans))
- Request environment cannot be frozen. ([@jeremyevans](https://github.com/jeremyevans))
- CGI values in the request environment with non-ASCII characters must use ASCII-8BIT encoding. ([@jeremyevans](https://github.com/jeremyevans))
- Improve SPEC/lint relating to SERVER_NAME, SERVER_PORT and HTTP_HOST. ([#1561](rack/rack#1561), [@ioquatix](https://github.com/ioquatix))

### Added

- `rackup` supports multiple `-r` options and will require all arguments. ([@jeremyevans](https://github.com/jeremyevans))
- `Server` supports an array of paths to require for the `:require` option. ([@khotta](https://github.com/khotta))
- `Files` supports multipart range requests. ([@fatkodima](https://github.com/fatkodima))
- `Multipart::UploadedFile` supports an IO-like object instead of using the filesystem, using `:filename` and `:io` options. ([@jeremyevans](https://github.com/jeremyevans))
- `Multipart::UploadedFile` supports keyword arguments `:path`, `:content_type`, and `:binary` in addition to positional arguments. ([@jeremyevans](https://github.com/jeremyevans))
- `Static` supports a `:cascade` option for calling the app if there is no matching file. ([@jeremyevans](https://github.com/jeremyevans))
- `Session::Abstract::SessionHash#dig`. ([@jeremyevans](https://github.com/jeremyevans))
- `Response.[]` and `MockResponse.[]` for creating instances using status, headers, and body. ([@ioquatix](https://github.com/ioquatix))
- Convenient cache and content type methods for `Rack::Response`. ([#1555](rack/rack#1555), [@ioquatix](https://github.com/ioquatix))

### Changed

- `Request#params` no longer rescues EOFError. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` uses a streaming approach, significantly improving time to first byte for large directories. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` no longer includes a Parent directory link in the root directory index. ([@jeremyevans](https://github.com/jeremyevans))
- `QueryParser#parse_nested_query` uses original backtrace when reraising exception with new class. ([@jeremyevans](https://github.com/jeremyevans))
- `ConditionalGet` follows RFC 7232 precedence if both If-None-Match and If-Modified-Since headers are provided. ([@jeremyevans](https://github.com/jeremyevans))
- `.ru` files supports the `frozen-string-literal` magic comment. ([@eregon](https://github.com/eregon))
- Rely on autoload to load constants instead of requiring internal files, make sure to require 'rack' and not just 'rack/...'. ([@jeremyevans](https://github.com/jeremyevans))
- `Etag` will continue sending ETag even if the response should not be cached. ([@henm](https://github.com/henm))
- `Request#host_with_port` no longer includes a colon for a missing or empty port. ([@AlexWayfer](https://github.com/AlexWayfer))
- All handlers uses keywords arguments instead of an options hash argument. ([@ioquatix](https://github.com/ioquatix))
- `Files` handling of range requests no longer return a body that supports `to_path`, to ensure range requests are handled correctly. ([@jeremyevans](https://github.com/jeremyevans))
- `Multipart::Generator` only includes `Content-Length` for files with paths, and `Content-Disposition` `filename` if the `UploadedFile` instance has one. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#ssl?` is true for the `wss` scheme (secure websockets). ([@jeremyevans](https://github.com/jeremyevans))
- `Rack::HeaderHash` is memoized by default. ([#1549](rack/rack#1549), [@ioquatix](https://github.com/ioquatix))
- `Rack::Directory` allow directory traversal inside root directory. ([#1417](rack/rack#1417), [@ThomasSevestre](https://github.com/ThomasSevestre))
- Sort encodings by server preference. ([#1184](rack/rack#1184), [@ioquatix](https://github.com/ioquatix), [@wjordan](https://github.com/wjordan))
- Rework host/hostname/authority implementation in `Rack::Request`. `#host` and `#host_with_port` have been changed to correctly return IPv6 addresses formatted with square brackets, as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-3.2.2). ([#1561](rack/rack#1561), [@ioquatix](https://github.com/ioquatix))
- `Rack::Builder` parsing options on first `#\` line is deprecated. ([#1574](rack/rack#1574), [@ioquatix](https://github.com/ioquatix))

### Removed

- `Directory#path` as it was not used and always returned nil. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy#each` as it was only needed to work around a bug in Ruby <1.9.3. ([@jeremyevans](https://github.com/jeremyevans))
- `URLMap::INFINITY` and `URLMap::NEGATIVE_INFINITY`, in favor of `Float::INFINITY`. ([@ch1c0t](https://github.com/ch1c0t))
- Deprecation of `Rack::File`. It will be deprecated again in rack 2.2 or 3.0. ([@rafaelfranca](https://github.com/rafaelfranca))
- Support for Ruby 2.2 as it is well past EOL. ([@ioquatix](https://github.com/ioquatix))
- Remove `Rack::Files#response_body` as the implementation was broken. ([#1153](rack/rack#1153), [@ioquatix](https://github.com/ioquatix))
- Remove `SERVER_ADDR` which was never part of the original SPEC. ([#1573](rack/rack#1573), [@ioquatix](https://github.com/ioquatix))

### Fixed

- `Directory` correctly handles root paths containing glob metacharacters. ([@jeremyevans](https://github.com/jeremyevans))
- `Cascade` uses a new response object for each call if initialized with no apps. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy` correctly delegates keyword arguments to the body object on Ruby 2.7+. ([@jeremyevans](https://github.com/jeremyevans))
- `BodyProxy#method` correctly handles methods delegated to the body object. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#host` and `Request#host_with_port` handle IPv6 addresses correctly. ([@AlexWayfer](https://github.com/AlexWayfer))
- `Lint` checks when response hijacking that `rack.hijack` is called with a valid object. ([@jeremyevans](https://github.com/jeremyevans))
- `Response#write` correctly updates `Content-Length` if initialized with a body. ([@jeremyevans](https://github.com/jeremyevans))
- `CommonLogger` includes `SCRIPT_NAME` when logging. ([@Erol](https://github.com/Erol))
- `Utils.parse_nested_query` correctly handles empty queries, using an empty instance of the params class instead of a hash. ([@jeremyevans](https://github.com/jeremyevans))
- `Directory` correctly escapes paths in links. ([@yous](https://github.com/yous))
- `Request#delete_cookie` and related `Utils` methods handle `:domain` and `:path` options in same call. ([@jeremyevans](https://github.com/jeremyevans))
- `Request#delete_cookie` and related `Utils` methods do an exact match on `:domain` and `:path` options. ([@jeremyevans](https://github.com/jeremyevans))
- `Static` no longer adds headers when a gzipped file request has a 304 response. ([@chooh](https://github.com/chooh))
- `ContentLength` sets `Content-Length` response header even for bodies not responding to `to_ary`. ([@jeremyevans](https://github.com/jeremyevans))
- Thin handler supports options passed directly to `Thin::Controllers::Controller`. ([@jeremyevans](https://github.com/jeremyevans))
- WEBrick handler no longer ignores `:BindAddress` option. ([@jeremyevans](https://github.com/jeremyevans))
- `ShowExceptions` handles invalid POST data. ([@jeremyevans](https://github.com/jeremyevans))
- Basic authentication requires a password, even if the password is empty. ([@jeremyevans](https://github.com/jeremyevans))
- `Lint` checks response is array with 3 elements, per SPEC. ([@jeremyevans](https://github.com/jeremyevans))
- Support for using `:SSLEnable` option when using WEBrick handler. (Gregor Melhorn)
- Close response body after buffering it when buffering. ([@ioquatix](https://github.com/ioquatix))
- Only accept `;` as delimiter when parsing cookies. ([@mrageh](https://github.com/mrageh))
- `Utils::HeaderHash#clear` clears the name mapping as well. ([@raxoft](https://github.com/raxoft))
- Support for passing `nil` `Rack::Files.new`, which notably fixes Rails' current `ActiveStorage::FileServer` implementation. ([@ioquatix](https://github.com/ioquatix))

### Documentation

- CHANGELOG updates. ([@Aupajo](https://github.com/aupajo))
- Added [CONTRIBUTING](CONTRIBUTING.md). ([@dblock](https://github.com/dblock))

## [2.1.2] - 2020-01-27

- Fix multipart parser for some files to prevent denial of service ([@aiomaster](https://github.com/aiomaster))
- Fix `Rack::Builder#use` with keyword arguments ([@kamipo](https://github.com/kamipo))
- Skip deflating in Rack::Deflater if Content-Length is 0 ([@jeremyevans](https://github.com/jeremyevans))
- Remove `SessionHash#transform_keys`, no longer needed ([@pavel](https://github.com/pavel))
- Add to_hash to wrap Hash and Session classes ([@oleh-demyanyuk](https://github.com/oleh-demyanyuk))
- Handle case where session id key is requested but missing ([@jeremyevans](https://github.com/jeremyevans))

## [2.1.1] - 2020-01-12

- Remove `Rack::Chunked` from `Rack::Server` default middleware. ([#1475](rack/rack#1475), [@ioquatix](https://github.com/ioquatix))

## 2.1.0

_Note: There are many unreleased changes in Rack (`master` is around 300 commits ahead of `2-0-stable`), and below is not an exhaustive list. If you would like to help out and document some of the unreleased changes, PRs are welcome._

### Added

- Add support for `SameSite=None` cookie value. ([@hennikul](https://github.com/hennikul))
- Add trailer headers. ([@eileencodes](https://github.com/eileencodes))
- Add MIME Types for video streaming. ([@styd](https://github.com/styd))
- Add MIME Type for WASM. ([@buildrtech](https://github.com/buildrtech))
- Add `Early Hints(103)` to status codes. ([@egtra](https://github.com/egtra))
- Add `Too Early(425)` to status codes. ([@y-yagi]((https://github.com/y-yagi)))
- Add `Bandwidth Limit Exceeded(509)` to status codes. ([@CJKinni](https://github.com/CJKinni))
- Add method for custom `ip_filter`. ([@svcastaneda](https://github.com/svcastaneda))
- Add boot-time profiling capabilities to `rackup`. ([@tenderlove](https://github.com/tenderlove))
- Add multi mapping support for `X-Accel-Mappings` header. ([@yoshuki](https://github.com/yoshuki))
- Add `sync: false` option to `Rack::Deflater`. (Eric Wong)
- Add `Builder#freeze_app` to freeze application and all middleware instances. ([@jeremyevans](https://github.com/jeremyevans))
- Add API to extract cookies from `Rack::MockResponse`. ([@petercline](https://github.com/petercline))

### Changed

- Don't propagate nil values from middleware. ([@ioquatix](https://github.com/ioquatix))
- Lazily initialize the response body and only buffer it if required. ([@ioquatix](https://github.com/ioquatix))
- Fix deflater zlib buffer errors on empty body part. ([@felixbuenemann](https://github.com/felixbuenemann))
- Set `X-Accel-Redirect` to percent-encoded path. ([@diskkid](https://github.com/diskkid))
- Remove unnecessary buffer growing when parsing multipart. ([@tainoe](https://github.com/tainoe))
- Expand the root path in `Rack::Static` upon initialization. ([@rosenfeld](https://github.com/rosenfeld))
- Make `ShowExceptions` work with binary data. ([@axyjo](https://github.com/axyjo))
- Use buffer string when parsing multipart requests. ([@janko-m](https://github.com/janko-m))
- Support optional UTF-8 Byte Order Mark (BOM) in config.ru. ([@mikegee](https://github.com/mikegee))
- Handle `X-Forwarded-For` with optional port. ([@dpritchett](https://github.com/dpritchett))
- Use `Time#httpdate` format for Expires, as proposed by RFC 7231. ([@nanaya](https://github.com/nanaya))
- Make `Utils.status_code` raise an error when the status symbol is invalid instead of `500`. ([@adambutler](https://github.com/adambutler))
- Rename `Request::SCHEME_WHITELIST` to `Request::ALLOWED_SCHEMES`.
- Make `Multipart::Parser.get_filename` accept files with `+` in their name. ([@lucaskanashiro](https://github.com/lucaskanashiro))
- Add Falcon to the default handler fallbacks. ([@ioquatix](https://github.com/ioquatix))
- Update codebase to avoid string mutations in preparation for `frozen_string_literals`. ([@pat](https://github.com/pat))
- Change `MockRequest#env_for` to rely on the input optionally responding to `#size` instead of `#length`. ([@janko](https://github.com/janko))
- Rename `Rack::File` -> `Rack::Files` and add deprecation notice. ([@postmodern](https://github.com/postmodern)).

### Removed

- Remove `to_ary` from Response ([@tenderlove](https://github.com/tenderlove))
- Deprecate `Rack::Session::Memcache` in favor of `Rack::Session::Dalli` from dalli gem ([@fatkodima](https://github.com/fatkodima))

### Documentation

- Update broken example in `Session::Abstract::ID` documentation. ([tonytonyjan](https://github.com/tonytonyjan))
- Add Padrino to the list of frameworks implmenting Rack. ([@wikimatze](https://github.com/wikimatze))
- Remove Mongrel from the suggested server options in the help output. ([@tricknotes](https://github.com/tricknotes))
- Replace `HISTORY.md` and `NEWS.md` with `CHANGELOG.md`. ([@twitnithegirl](https://github.com/twitnithegirl))
- Backfill `CHANGELOG.md` from 2.0.1 to 2.0.7 releases. ([@drenmi](https://github.com/Drenmi))

## [2.0.8] - 2019-12-08

- [[CVE-2019-16782](https://nvd.nist.gov/vuln/detail/CVE-2019-16782)] Prevent timing attacks targeted at session ID lookup. BREAKING CHANGE: Session ID is now a SessionId instance instead of a String. ([@tenderlove](https://github.com/tenderlove), [@rafaelfranca](https://github.com/rafaelfranca))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants