diff options
-rw-r--r-- | .editorconfig | 8 | ||||
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | CONTRIBUTING.md | 86 | ||||
-rw-r--r-- | README.md | 287 | ||||
-rw-r--r-- | README.rdoc | 298 | ||||
-rw-r--r-- | UPGRADE-GUIDE.md | 260 |
6 files changed, 600 insertions, 341 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ee510310 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 076783e0..7c986955 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/). -## [3.0.0] - Unreleased +## [3.0.0.beta1] - 2022-08-05 ### Security diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95db71b9..bd5a5120 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,25 @@ -Contributing to Rack -===================== +# Contributing to Rack -Rack is work of [hundreds of contributors](https://github.com/rack/rack/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/rack/rack/pulls), [propose features and discuss issues](https://github.com/rack/rack/issues). When in doubt, post to the [rack-devel](http://groups.google.com/group/rack-devel) mailing list. +Rack is work of [hundreds of +contributors](https://github.com/rack/rack/graphs/contributors). You're +encouraged to submit [pull requests](https://github.com/rack/rack/pulls) and +[propose features and discuss issues](https://github.com/rack/rack/issues). -#### Fork the Project +## Fork the Project -Fork the [project on GitHub](https://github.com/rack/rack) and check out your copy. +Fork the [project on GitHub](https://github.com/rack/rack) and check out your +copy. ``` -git clone https://github.com/contributor/rack.git +git clone https://github.com/(your-github-username)/rack.git cd rack git remote add upstream https://github.com/rack/rack.git ``` -#### Create a Topic Branch +## Create a Topic Branch -Make sure your fork is up-to-date and create a topic branch for your feature or bug fix. +Make sure your fork is up-to-date and create a topic branch for your feature or +bug fix. ``` git checkout main @@ -23,7 +27,7 @@ git pull upstream main git checkout -b my-feature-branch ``` -#### Bundle Install and Quick Test +## Bundle Install and Quick Test Ensure that you can build the project and run quick tests. @@ -32,7 +36,7 @@ bundle install --without extra bundle exec rake test ``` -#### Running All Tests +## Running All Tests Install all dependencies. @@ -46,25 +50,15 @@ Run all tests. rake test ``` -The test suite has no dependencies outside of the core Ruby installation and bacon. +## Write Tests -Some tests will be skipped if a dependency is not found. +Try to write a test that reproduces the problem you're trying to fix or +describes a feature that you want to build. -To run the test suite completely, you need: +We definitely appreciate pull requests that highlight or reproduce a problem, +even without a fix. - * fcgi - * dalli - * thin - -To test Memcache sessions, you need memcached (will be run on port 11211) and dalli installed. - -#### Write Tests - -Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. - -We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix. - -#### Write Code +## Write Code Implement your feature or bug fix. @@ -74,15 +68,15 @@ Make sure that all tests pass: bundle exec rake test ``` -#### Write Documentation +## Write Documentation -Document any external behavior in the [README](README.rdoc). +Document any external behavior in the [README](README.md). -#### Update Changelog +## Update Changelog Add a line to [CHANGELOG](CHANGELOG.md). -#### Commit Changes +## Commit Changes Make sure git knows your name and email address: @@ -91,24 +85,27 @@ git config --global user.name "Your Name" git config --global user.email "contributor@example.com" ``` -Writing good commit logs is important. A commit log should describe what changed and why. +Writing good commit logs is important. A commit log should describe what changed +and why. ``` git add ... git commit ``` -#### Push +## Push ``` git push origin my-feature-branch ``` -#### Make a Pull Request +## Make a Pull Request -Go to https://github.com/contributor/rack and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days. +Go to your fork of rack on GitHub and select your feature branch. Click the +'Pull Request' button and fill out the form. Pull requests are usually +reviewed within a few days. -#### Rebase +## Rebase If you've been working on a change for a while, rebase with upstream/main. @@ -118,7 +115,7 @@ git rebase upstream/main git push origin my-feature-branch -f ``` -#### Make Required Changes +## Make Required Changes Amend your previous commit and force push the changes. @@ -127,14 +124,19 @@ git commit --amend git push origin my-feature-branch -f ``` -#### Check on Your Pull Request +## Check on Your Pull Request -Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above. +Go back to your pull request after a few minutes and see whether it passed +tests with GitHub Actions. Everything should look green, otherwise fix issues and +amend your commit as described above. -#### Be Patient +## Be Patient -It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there! +It's likely that your change will not be merged and that the nitpicky +maintainers will ask you to do more, or fix seemingly benign problems. Hang in +there! -#### Thank You +## Thank You -Please do know that we really appreciate and value your time and work. We love you, really. +Please do know that we really appreciate and value your time and work. We love +you, really. diff --git a/README.md b/README.md new file mode 100644 index 00000000..b3d0c975 --- /dev/null +++ b/README.md @@ -0,0 +1,287 @@ +# ![Rack](contrib/logo.webp) + +> **_NOTE:_** Rack v3.0.0beta1 was recently released. Please check the [Upgrade +> Guide](UPGRADE-GUIDE.md) for more details about migrating your existing +> servers, middlewares and applications. For detailed information on specific +> changes, check the [Change Log](CHANGELOG.md). + +Rack provides a minimal, modular, and adaptable interface for developing web +applications in Ruby. By wrapping HTTP requests and responses in the simplest +way possible, it unifies and distills the bridge between web servers, web +frameworks, and web application into a single method call. + +The exact details of this are described in the [Rack Specification], which all +Rack applications should conform to. + +## Installation + +Add the rack gem to your application bundle, or follow the instructions provided +by a [supported web framework](#supported-web-frameworks): + +```bash +# Install it generally: +$ gem install rack + +# or, add it to your current application gemfile: +$ bundle add rack +``` + +## Usage + +Create a file called `config.ru` with the following contents: + +```ruby +run do |env| + [200, {}, ["Hello World"]] +end +``` + +Run this using the rackup gem or another [supported web +server](#supported-web-servers). + +```bash +$ gem install rackup +$ rackup +$ curl http://localhost:9292 +Hello World +``` + +## Supported web servers + +Rack is supported by a wide range of servers, including: + +* [Agoo](https://github.com/ohler55/agoo) +* [Falcon](https://github.com/socketry/falcon) **(Rack 3 Compatible)** +* [Iodine](https://github.com/boazsegev/iodine) +* [NGINX Unit](https://unit.nginx.org/) +* [Phusion Passenger](https://www.phusionpassenger.com/) (which is mod_rack for + Apache and for nginx) +* [Puma](https://puma.io/) +* [Thin](https://github.com/macournoyer/thin) +* [Unicorn](https://yhbt.net/unicorn/) +* [uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/) +* [Lamby](https://lamby.custominktech.com) (for AWS Lambda) + +You will need to consult the server documentation to find out what features and +limitations they may have. In general, any valid Rack app will run the same on +all these servers, without changing anything. + +### Rackup + +Rack provides a separate gem, [rackup](https://github.com/rack/rackup) which is +a generic interface for running a Rack application on supported servers, which +include `WEBRick`, `Puma`, `Falcon` and others. + +## Supported web frameworks + +These frameworks and many others support the [Rack Specification]: + +* [Camping](https://github.com/camping/camping) +* [Hanami](https://hanamirb.org/) +* [Padrino](https://padrinorb.com/) +* [Roda](https://github.com/jeremyevans/roda) **(Rack 3 Compatible)** +* [Ruby on Rails](https://rubyonrails.org/) +* [Sinatra](https://sinatrarb.com/) +* [Utopia](https://github.com/socketry/utopia) **(Rack 3 Compatible)** +* [WABuR](https://github.com/ohler55/wabur) + +### Older (possibly unsupported) web frameworks + +* [Ramaze](http://ramaze.net/) +* [Rum](https://github.com/leahneukirchen/rum) + +## Available middleware shipped with Rack + +Between the server and the framework, Rack can be customized to your +applications needs using middleware. Rack itself ships with the following +middleware: + +* `Rack::CommonLogger` for creating Apache-style logfiles. +* `Rack::ConditionalGet` for returning [Not + Modified](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304) + responses when the response has not changed. +* `Rack::Config` for modifying the environment before processing the request. +* `Rack::ContentLength` for setting a `content-length` header based on body + size. +* `Rack::ContentType` for setting a default `content-type` header for responses. +* `Rack::Deflater` for compressing responses with gzip. +* `Rack::ETag` for setting `etag` header on bodies that can be buffered. +* `Rack::Events` for providing easy hooks when a request is received and when + the response is sent. +* `Rack::Files` for serving static files. +* `Rack::Head` for returning an empty body for HEAD requests. +* `Rack::Lint` for checking conformance to the [Rack Specification]. +* `Rack::Lock` for serializing requests using a mutex. +* `Rack::Logger` for setting a logger to handle logging errors. +* `Rack::MethodOverride` for modifying the request method based on a submitted + parameter. +* `Rack::Recursive` for including data from other paths in the application, and + for performing internal redirects. +* `Rack::Reloader` for reloading files if they have been modified. +* `Rack::Runtime` for including a response header with the time taken to process + the request. +* `Rack::Sendfile` for working with web servers that can use optimized file + serving for file system paths. +* `Rack::ShowException` for catching unhandled exceptions and presenting them in + a nice and helpful way with clickable backtrace. +* `Rack::ShowStatus` for using nice error pages for empty client error + responses. +* `Rack::Static` for more configurable serving of static files. +* `Rack::TempfileReaper` for removing temporary files creating during a request. + +All these components use the same interface, which is described in detail in the +[Rack Specification]. These optional components can be used in any way you wish. + +### Convenience interfaces + +If you want to develop outside of existing frameworks, implement your own ones, +or develop middleware, Rack provides many helpers to create Rack applications +quickly and without doing the same web stuff all over: + +* `Rack::Request` which also provides query string parsing and multipart + handling. +* `Rack::Response` for convenient generation of HTTP replies and cookie + handling. +* `Rack::MockRequest` and `Rack::MockResponse` for efficient and quick testing + of Rack application without real HTTP round-trips. +* `Rack::Cascade` for trying additional Rack applications if an application + returns a not found or method not supported response. +* `Rack::Directory` for serving files under a given directory, with directory + indexes. +* `Rack::MediaType` for parsing content-type headers. +* `Rack::Mime` for determining content-type based on file extension. +* `Rack::RewindableInput` for making any IO object rewindable, using a temporary + file buffer. +* `Rack::URLMap` to route to multiple applications inside the same process. + +## Configuration + +Rack exposes several configuration parameters to control various features of the +implementation. + +### `param_depth_limit` + +```ruby +Rack::Utils.param_depth_limit = 32 # default +``` + +The maximum amount of nesting allowed in parameters. For example, if set to 3, +this query string would be allowed: + +``` +?a[b][c]=d +``` + +but this query string would not be allowed: + +``` +?a[b][c][d]=e +``` + +Limiting the depth prevents a possible stack overflow when parsing parameters. + +### `multipart_part_limit` + +```ruby +Rack::Utils.multipart_part_limit = 128 # default +``` + +The maximum number of parts a request can contain. Accepting too many parts can +lead to the server running out of file handles. + +The default is 128, which means that a single request can't upload more than 128 +files at once. Set to 0 for no limit. + +Can also be set via the `RACK_MULTIPART_PART_LIMIT` environment variable. + +## Changelog + +See [CHANGELOG.md](CHANGELOG.md). + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for specific details about how to make a +contribution to Rack. + +Please post bugs, suggestions and patches to [GitHub +Issues](https://github.com/rack/rack/issues). + +Please check our [Security Policy](https://github.com/rack/rack/security/policy) +for responsible disclosure and security bug reporting process. Due to wide usage +of the library, it is strongly preferred that we manage timing in order to +provide viable patches at the time of disclosure. Your assistance in this matter +is greatly appreciated. + +## See Also + +### `rack-contrib` + +The plethora of useful middleware created the need for a project that collects +fresh Rack middleware. `rack-contrib` includes a variety of add-on components +for Rack and it is easy to contribute new modules. + +* https://github.com/rack/rack-contrib + +### `rack-session` + +Provides convenient session management for Rack. + +* https://github.com/rack/rack-session + +## Thanks + +The Rack Core Team, consisting of + +* Aaron Patterson [tenderlove](https://github.com/tenderlove) +* Samuel Williams [ioquatix](https://github.com/ioquatix) +* Jeremy Evans [jeremyevans](https://github.com/jeremyevans) +* Eileen Uchitelle [eileencodes](https://github.com/eileencodes) +* Matthew Draper [matthewd](https://github.com/matthewd) +* Rafael França [rafaelfranca](https://github.com/rafaelfranca) + +and the Rack Alumni + +* Ryan Tomayko [rtomayko](https://github.com/rtomayko) +* Scytrin dai Kinthra [scytrin](https://github.com/scytrin) +* Leah Neukirchen [leahneukirchen](https://github.com/leahneukirchen) +* James Tucker [raggi](https://github.com/raggi) +* Josh Peek [josh](https://github.com/josh) +* José Valim [josevalim](https://github.com/josevalim) +* Michael Fellinger [manveru](https://github.com/manveru) +* Santiago Pastorino [spastorino](https://github.com/spastorino) +* Konstantin Haase [rkh](https://github.com/rkh) + +would like to thank: + +* Adrian Madrid, for the LiteSpeed handler. +* Christoffer Sawicki, for the first Rails adapter and `Rack::Deflater`. +* Tim Fletcher, for the HTTP authentication code. +* Luc Heinrich for the Cookie sessions, the static file handler and bugfixes. +* Armin Ronacher, for the logo and racktools. +* Alex Beregszaszi, Alexander Kahn, Anil Wadghule, Aredridel, Ben Alpert, Dan + Kubb, Daniel Roethlisberger, Matt Todd, Tom Robinson, Phil Hagelberg, S. Brent + Faulkner, Bosko Milekic, Daniel Rodríguez Troitiño, Genki Takiuchi, Geoffrey + Grosenbach, Julien Sanchez, Kamal Fariz Mahyuddin, Masayoshi Takahashi, + Patrick Aljordm, Mig, Kazuhiro Nishiyama, Jon Bardin, Konstantin Haase, Larry + Siden, Matias Korhonen, Sam Ruby, Simon Chiang, Tim Connor, Timur Batyrshin, + and Zach Brock for bug fixing and other improvements. +* Eric Wong, Hongli Lai, Jeremy Kemper for their continuous support and API + improvements. +* Yehuda Katz and Carl Lerche for refactoring rackup. +* Brian Candler, for `Rack::ContentType`. +* Graham Batty, for improved handler loading. +* Stephen Bannasch, for bug reports and documentation. +* Gary Wright, for proposing a better `Rack::Response` interface. +* Jonathan Buch, for improvements regarding `Rack::Response`. +* Armin Röhrl, for tracking down bugs in the Cookie generator. +* Alexander Kellett for testing the Gem and reviewing the announcement. +* Marcus Rückert, for help with configuring and debugging lighttpd. +* The WSGI team for the well-done and documented work they've done and Rack + builds up on. +* All bug reporters and patch contributors not mentioned above. + +## License + +Rack is released under the [MIT License](MIT-LICENSE). + +[Rack Specification]: SPEC.rdoc diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 7398c892..00000000 --- a/README.rdoc +++ /dev/null @@ -1,298 +0,0 @@ -= rdoc-image:contrib/logo.webp - -{<img src="https://github.com/rack/rack/workflows/Development/badge.svg" alt="GitHub Actions status" />}[https://github.com/rack/rack/actions?query=workflow%3ADevelopment] -{<img src="https://badge.fury.io/rb/rack.svg" alt="Gem Version" />}[http://badge.fury.io/rb/rack] -{<img src="http://inch-ci.org/github/rack/rack.svg?branch=main" alt="Inline docs" />}[http://inch-ci.org/github/rack/rack] - -\Rack provides a minimal, modular, and adaptable interface for developing -web applications in Ruby. By wrapping HTTP requests and responses in -the simplest way possible, it unifies and distills the API for web -servers, web frameworks, and software in between (the so-called -middleware) into a single method call. - -The exact details of this are described in the {\Rack specification}[link:SPEC.rdoc], -which all \Rack applications should conform to. - -== Supported web servers - -The included *handlers* can connect these web servers to \Rack: - -* WEBrick[https://github.com/ruby/webrick] -* CGI - -These web servers include \Rack handlers in their distributions: - -* Agoo[https://github.com/ohler55/agoo] -* Falcon[https://github.com/socketry/falcon] -* Iodine[https://github.com/boazsegev/iodine] -* {NGINX Unit}[https://unit.nginx.org/] -* {Phusion Passenger}[https://www.phusionpassenger.com/] (which is mod_rack for Apache and for nginx) -* Puma[https://puma.io/] -* Thin[https://rubygems.org/gems/thin] -* Unicorn[https://yhbt.net/unicorn/] -* uWSGI[https://uwsgi-docs.readthedocs.io/en/latest/] -* Lamby[https://lamby.custominktech.com] (for AWS Lambda) - -Any valid \Rack app will run the same on all these handlers, without -changing anything. - -== Supported web frameworks - -These frameworks and many others support the \Rack API: - -* Camping[http://www.ruby-camping.com/] -* Coset[http://leahneukirchen.org/repos/coset/] -* Hanami[https://hanamirb.org/] -* Padrino[http://padrinorb.com/] -* Ramaze[http://ramaze.net/] -* Roda[https://github.com/jeremyevans/roda] -* {Ruby on Rails}[https://rubyonrails.org/] -* Rum[https://github.com/leahneukirchen/rum] -* Sinatra[http://sinatrarb.com/] -* Utopia[https://github.com/socketry/utopia] -* WABuR[https://github.com/ohler55/wabur] - -== Available middleware shipped with \Rack - -Between the server and the framework, \Rack can be customized to your -applications needs using middleware. \Rack itself ships with the following -middleware: - -* Rack::Chunked, for streaming responses using chunked encoding. -* Rack::CommonLogger, for creating Apache-style logfiles. -* Rack::ConditionalGet, for returning not modified responses when the response - has not changed. -* Rack::Config, for modifying the environment before processing the request. -* Rack::ContentLength, for setting content-length header based on body size. -* Rack::ContentType, for setting default content-type header for responses. -* Rack::Deflater, for compressing responses with gzip. -* Rack::ETag, for setting ETag header on string bodies. -* Rack::Events, for providing easy hooks when a request is received - and when the response is sent. -* Rack::Files, for serving static files. -* Rack::Head, for returning an empty body for HEAD requests. -* Rack::Lint, for checking conformance to the \Rack API. -* Rack::Lock, for serializing requests using a mutex. -* Rack::Logger, for setting a logger to handle logging errors. -* Rack::MethodOverride, for modifying the request method based on a submitted - parameter. -* Rack::Recursive, for including data from other paths in the application, - and for performing internal redirects. -* Rack::Reloader, for reloading files if they have been modified. -* Rack::Runtime, for including a response header with the time taken to - process the request. -* Rack::Sendfile, for working with web servers that can use optimized - file serving for file system paths. -* Rack::ShowException, for catching unhandled exceptions and - presenting them in a nice and helpful way with clickable backtrace. -* Rack::ShowStatus, for using nice error pages for empty client error - responses. -* Rack::Static, for more configurable serving of static files. -* Rack::TempfileReaper, for removing temporary files creating during a - request. - -All these components use the same interface, which is described in -detail in the \Rack specification. These optional components can be -used in any way you wish. - -== Convenience - -If you want to develop outside of existing frameworks, implement your -own ones, or develop middleware, \Rack provides many helpers to create -\Rack applications quickly and without doing the same web stuff all -over: - -* Rack::Request, which also provides query string parsing and - multipart handling. -* Rack::Response, for convenient generation of HTTP replies and - cookie handling. -* Rack::MockRequest and Rack::MockResponse for efficient and quick - testing of \Rack application without real HTTP round-trips. -* Rack::Cascade, for trying additional \Rack applications if an - application returns a not found or method not supported response. -* Rack::Directory, for serving files under a given directory, with - directory indexes. -* Rack::MediaType, for parsing content-type headers. -* Rack::Mime, for determining content-type based on file extension. -* Rack::RewindableInput, for making any IO object rewindable, using - a temporary file buffer. -* Rack::URLMap, to route to multiple applications inside the same process. - -== rack-contrib - -The plethora of useful middleware created the need for a project that -collects fresh \Rack middleware. rack-contrib includes a variety of -add-on components for \Rack and it is easy to contribute new modules. - -* https://github.com/rack/rack-contrib - -== rackup - -rackup is a useful tool for running \Rack applications, which uses the -Rack::Builder DSL to configure middleware and build up applications -easily. - -rackup automatically figures out the environment it is run in, and -runs your application as FastCGI, CGI, or WEBrick---all from the -same configuration. - -== Quick start - -Try the lobster! - -Either with the embedded WEBrick starter: - - ruby -Ilib lib/rack/lobster.rb - -Or with rackup: - - bin/rackup -Ilib example/lobster.ru - -By default, the lobster is found at http://localhost:9292. - -== Installing with RubyGems - -A Gem of \Rack is available at {rubygems.org}[https://rubygems.org/gems/rack]. You can install it with: - - gem install rack - -== Usage - -You should require the library: - - require 'rack' - -\Rack uses autoload to automatically load other files \Rack ships with on demand, -so you should not need require paths under +rack+. If you require paths under -+rack+ without requiring +rack+ itself, things may not work correctly. - -== Configuration - -Several parameters can be modified on Rack::Utils to configure \Rack behaviour. - -e.g: - - Rack::Utils.param_depth_limit = 3 - - -=== param_depth_limit - -The maximum amount of nesting allowed in parameters. -For example, if set to 3, this query string would be allowed: - - ?a[b][c]=d - -but this query string would not be allowed: - - ?a[b][c][d]=e - -Limiting the depth prevents a possible stack overflow when parsing parameters. - -Defaults to 32. - -=== multipart_part_limit - -The maximum number of parts a request can contain. -Accepting too many part can lead to the server running out of file handles. - -The default is 128, which means that a single request can't upload more than 128 files at once. - -Set to 0 for no limit. - -Can also be set via the +RACK_MULTIPART_PART_LIMIT+ environment variable. - -=== key_space_limit - -No longer has an effect, deprecated. - -== Changelog - -See {CHANGELOG.md}[link:CHANGELOG.md]. - -== Contributing - -See {CONTRIBUTING.md}[link:CONTRIBUTING.md]. - -== Contact - -Please post bugs, suggestions and patches to -the bug tracker at {issues}[https://github.com/rack/rack/issues]. - -Please post security related bugs and suggestions to the core team at -<https://groups.google.com/forum/#!forum/rack-core> or rack-core@googlegroups.com. This -list is not public. Due to wide usage of the library, it is strongly preferred -that we manage timing in order to provide viable patches at the time of -disclosure. Your assistance in this matter is greatly appreciated. - -Mailing list archives are available at -<https://groups.google.com/forum/#!forum/rack-devel>. - -Git repository (send Git patches to the mailing list): - -* https://github.com/rack/rack - -You are also welcome to join the #rack channel on irc.freenode.net. - -== Thanks - -The \Rack Core Team, consisting of - -* Aaron Patterson (tenderlove[https://github.com/tenderlove]) -* Samuel Williams (ioquatix[https://github.com/ioquatix]) -* Jeremy Evans (jeremyevans[https://github.com/jeremyevans]) -* Eileen Uchitelle (eileencodes[https://github.com/eileencodes]) -* Matthew Draper (matthewd[https://github.com/matthewd]) -* Rafael França (rafaelfranca[https://github.com/rafaelfranca]) - -and the \Rack Alumni - -* Ryan Tomayko (rtomayko[https://github.com/rtomayko]) -* Scytrin dai Kinthra (scytrin[https://github.com/scytrin]) -* Leah Neukirchen (leahneukirchen[https://github.com/leahneukirchen]) -* James Tucker (raggi[https://github.com/raggi]) -* Josh Peek (josh[https://github.com/josh]) -* José Valim (josevalim[https://github.com/josevalim]) -* Michael Fellinger (manveru[https://github.com/manveru]) -* Santiago Pastorino (spastorino[https://github.com/spastorino]) -* Konstantin Haase (rkh[https://github.com/rkh]) - -would like to thank: - -* Adrian Madrid, for the LiteSpeed handler. -* Christoffer Sawicki, for the first Rails adapter and Rack::Deflater. -* Tim Fletcher, for the HTTP authentication code. -* Luc Heinrich for the Cookie sessions, the static file handler and bugfixes. -* Armin Ronacher, for the logo and racktools. -* Alex Beregszaszi, Alexander Kahn, Anil Wadghule, Aredridel, Ben - Alpert, Dan Kubb, Daniel Roethlisberger, Matt Todd, Tom Robinson, - Phil Hagelberg, S. Brent Faulkner, Bosko Milekic, Daniel Rodríguez - Troitiño, Genki Takiuchi, Geoffrey Grosenbach, Julien Sanchez, Kamal - Fariz Mahyuddin, Masayoshi Takahashi, Patrick Aljordm, Mig, Kazuhiro - Nishiyama, Jon Bardin, Konstantin Haase, Larry Siden, Matias - Korhonen, Sam Ruby, Simon Chiang, Tim Connor, Timur Batyrshin, and - Zach Brock for bug fixing and other improvements. -* Eric Wong, Hongli Lai, Jeremy Kemper for their continuous support - and API improvements. -* Yehuda Katz and Carl Lerche for refactoring rackup. -* Brian Candler, for Rack::ContentType. -* Graham Batty, for improved handler loading. -* Stephen Bannasch, for bug reports and documentation. -* Gary Wright, for proposing a better Rack::Response interface. -* Jonathan Buch, for improvements regarding Rack::Response. -* Armin Röhrl, for tracking down bugs in the Cookie generator. -* Alexander Kellett for testing the Gem and reviewing the announcement. -* Marcus Rückert, for help with configuring and debugging lighttpd. -* The WSGI team for the well-done and documented work they've done and - \Rack builds up on. -* All bug reporters and patch contributors not mentioned above. - -== Links - -\Rack:: <https://rack.github.io/> -Official \Rack repositories:: <https://github.com/rack> -\Rack Bug Tracking:: <https://github.com/rack/rack/issues> -rack-devel mailing list:: <https://groups.google.com/forum/#!forum/rack-devel> - -== License - -\Rack is released under the {MIT License}[https://opensource.org/licenses/MIT]. diff --git a/UPGRADE-GUIDE.md b/UPGRADE-GUIDE.md new file mode 100644 index 00000000..ca9d428c --- /dev/null +++ b/UPGRADE-GUIDE.md @@ -0,0 +1,260 @@ +# Rack 3 Upgrade Guide + +This document is a work in progress, but outlines some of the key changes in +Rack 3 which you should be aware of in order to update your server, middleware +and/or applications. + +## Interface Changes + +### `config.ru` `Rack::Builder#run` now accepts block + +Previously, `Rack::Builder#run` method would only accept a callable argument: + +```ruby +run lambda{|env| [200, {}, ["Hello World"]]} +``` + +This can be rewritten more simply: + +```ruby +run do |env| + [200, {}, ["Hello World"]] +end +``` + +### Response bodies can be used for bi-directional streaming + +Previously, the `rack.hijack` response header could be used for implementing +bi-directional streaming (e.g. WebSockets). + +```ruby +def call(env) + stream_callback = proc do |stream| + stream.read(...) + stream.write(...) + ensure + stream.close(...) + end + + return [200, {'rack.hijack' => stream_callback}, []] +end +``` + +This feature was optional and tricky to use correctly. You can now achieve the +same thing by giving `stream_callback` as the response body: + +```ruby +def call(env) + stream_callback = proc do |stream| + stream.read(...) + stream.write(...) + ensure + stream.close(...) + end + + return [200, {}, stream_callback] +end +``` + +### `Rack::Session` is now moved to an external gem + +Previously, `Rack::Session` was part of the `rack` gem. Not every application +needs it, and it increases the security surface area of the `rack`, so it was +decided to extract it into its own gem `rack-session` which can be updated +independently. + +Applications that make use of `rack-session` will need to add that gem as a +dependency: + +```ruby +gem 'rack-session' +``` + + +## Request Changes + +### `rack.version` is no longer required + +Previously, the "rack protocol version" was available in `rack.version` but it +was not practically useful, so it has been removed as a requirement. + +### `rack.multithread`/`rack.multiprocess`/`rack.run_once` are no longer required + +Previously, servers tried to provide these keys to reflect the execution +environment. These come too late to be useful, so they have been removed as a +requirement. + +### `rack.hijack?` now only applies to partial hijack + +Previously, both full and partial hijiack were controlled by the presence and +value of `rack.hijack?`. Now, it only applies to partial hijack (which itself +has been effectively replaced by streaming bodies). + +### `rack.hijack` alone indicates that you can execute a full hijack + +Previously, `rack.hijack?` had to be truthy, as well as having `rack.hijack` +present in the request environment. Now, the presence of the `rack.hijack` +callback is enough. + +### `rack.hijack_io` is removed + +Previously, the server would try to set `rack.hijack_io` into the request +environment when `rack.hijack` was invoked for a full hijack. This was often +impossible if a middleware had called `env.dup`, so this requirement has been +dropped entirely. + +### `rack.input` is no longer required to be rewindable + +Previosuly, `rack.input` was required to be rewindable, i.e. `io.seek(0)` but +this was only generally possible with a file based backing, which prevented +efficient streaming of request bodies. Now, `rack.input` is not required to be +rewindable. + +## Response Changes + +### Response must be mutable + +Rack 3 requires the response Array `[status, headers, body]` to be mutable. +Existing code that uses a frozen response will need to be changed: + +```ruby +NOT_FOUND = [404, {}, ["Not Found"]].freeze + +def call(env) + ... + return NOT_FOUND +end +``` + +should be rewritten as: + +```ruby +def not_found + [404, {}, ["Not Found"]] +end + +def call(env) + ... + return not_found +end +``` + +Note there is a subtle bug in the former version: the headers hash is mutable +and can be modified, and these modifications can leak into subsequent requests. + +### Response headers must be a mutable hash + +Rack 3 requires response headers to be a mutable hash. Previously it could be +any object that would respond to `#each` and yield `key`/`value` pairs. +Previously, the following was acceptable: + +```ruby +def call(env) + return [200, [['content-type', 'text/plain']], ["Hello World]] +end +``` + +Now you must use a hash instance: + +```ruby +def call(env) + return [200, {'content-type' => 'text/plain'}, ["Hello World]] +end +``` + +This ensures middleware can predictably update headers as needed. + +### Response Headers must be lower case + +Rack 3 requires all response headers to be lower case. This is to simplify +fetching and updating response headers. Previously you had to use something like +`Rack::HeadersHash` + +```ruby +def call(env) + response = @app.call(env) + # HeaderHash must allocate internal objects and compute lower case keys: + headers = Rack::Utils::HeaderHash[response[1]] + + cache_response(headers['ETag'], response) + + ... +end +``` + +but now you must just use the normal form for HTTP header: + +```ruby +def call(env) + response = @app.call(env) + # A plain hash with lower case keys: + headers = response[1] + + cache_response(headers['etag'], response) + + ... +end +``` + +### Multiple response header values are encoded using an `Array` + +Response header values can be an Array to handle multiple values (and no longer +supports `\n` encoded headers). If you use `Rack::Response`, you don't need to +do anything, but if manually append values to response headers, you will need to +promote them to an Array, e.g. + +```ruby +def set_cookie_header!(headers, key, value) + if header = headers[SET_COOKIE] + if header.is_a?(Array) + header << set_cookie_header(key, value) + else + headers[SET_COOKIE] = [header, set_cookie_header(key, value)] + end + else + headers[SET_COOKIE] = set_cookie_header(key, value) + end +end +``` + +### Response body might not respond to `#each` + +Rack 3 has more strict requirements on response bodies. Previously, response +body would only need to respond to `#each` and optionally `#close`. In addition, +there was no way to determine whether it was safe to call `#each` and buffer the +response. + +### Response bodies can be buffered if they expose `#to_ary` + +If your body responds to `#to_ary` then it must return an `Array` whose contents +are identical to that produced by calling `#each`. If the body responds to both +`#to_ary` and `#close` then its implementation of `#to_ary` must also call +`#close`. + +Previously, it was not possible to determine whether a response body was +immediately available (could be buffered) or was streaming chunks. This case is +now unambiguously exposed by `#to_ary`: + +```ruby +def call(env) + status, headers, body = @app.call(env) + + # Check if we can buffer the body into an Array, so we can compute a digest: + if body.respond_to?(:to_ary) + body = body.to_ary + digest = digest_body(body) + headers[ETAG_STRING] = %(W/"#{digest}") if digest + end + + return [status, headers, body] +end +``` + +### Middleware should not directly modify the response body + +Be aware that the response body might not respond to `#each` and you must now +check if the body responds to `#each` or not to determine if it is an enumerable +or streaming body. + +You must not call `#each` directly on the body and instead you should return a +new body that calls `#each` on the original body. |