Skip to content
Snippets Groups Projects
Commit 4e4b6f41 authored by Apertis CI's avatar Apertis CI
Browse files

Import Upstream version 3.0.8

parent b4dfa912
Branches upstream/buster
Tags upstream/2.1.1
1 merge request!19Update from debian/trixie for apertis/v2026dev2
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v2
name: Development
on: [push, pull_request]
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04]
ruby: [2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{matrix.ruby}}
- uses: actions/cache@v1
with:
path: vendor/bundle
key: bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}-${{hashFiles('**/Gemfile')}}
restore-keys: |
bundle-use-ruby-${{matrix.os}}-${{matrix.ruby}}-
- name: Installing packages
run: sudo apt-get install libfcgi-dev libmemcached-dev
- name: Bundle install...
run: |
bundle config path vendor/bundle
bundle install
- run: bundle exec rake
name: Test External
on: [push, pull_request]
permissions:
contents: read
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
ruby: ['2.7', '3.0', '3.1']
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby-pkgs@v1
with:
ruby-version: ${{matrix.ruby}}
bundler-cache: true
apt-get: _update_ libfcgi-dev libmemcached-dev
brew: fcgi libmemcached
- run: bundle exec bake test:external
name: Test
on: [push, pull_request]
permissions:
contents: read
jobs:
test:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
ruby:
- '2.4'
- '2.5'
- '2.6'
- '2.7'
- '3.0'
- '3.1'
- '3.2'
- jruby
- truffleruby-head
include:
- os: macos-latest
ruby: '3.1'
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{matrix.ruby}}
bundler-cache: true
- run: bundle exec rake
require:
- rubocop-packaging
AllCops:
TargetRubyVersion: 2.3
TargetRubyVersion: 2.4
DisabledByDefault: true
Exclude:
- '**/vendor/**/*'
......@@ -50,8 +53,11 @@ Layout/SpaceBeforeFirstArg:
Layout/SpaceInsideHashLiteralBraces:
Enabled: true
Layout/Tab:
Layout/IndentationStyle:
Enabled: true
Layout/TrailingWhitespace:
Enabled: true
Lint/DeprecatedOpenSSLConstant:
Enabled: true
-
SPEC
SPEC.rdoc
This diff is collapsed.
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 master
git pull upstream master
git checkout main
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,39 +50,33 @@ 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.
Make sure that `bundle exec rake fulltest` completes without errors.
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:
......@@ -87,34 +85,37 @@ 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/master.
If you've been working on a change for a while, rebase with upstream/main.
```
git fetch upstream
git rebase upstream/master
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.
......@@ -123,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.
......@@ -4,31 +4,18 @@ source 'https://rubygems.org'
gemspec
# What we need to do here is just *exclude* JRuby, but bundler has no way to do
# this, because of some argument that I know I had with Yehuda and Carl years
# ago, but I've since forgotten. Anyway, we actually need it here, and it's not
# available, so prepare yourself for a yak shave when this breaks.
c_platforms = Bundler::Dsl::VALID_PLATFORMS.dup.delete_if do |platform|
platform =~ /jruby/
end
gem "rubocop", require: false
gem "webrick"
group :test do
gem "webrick" # gemified in Ruby 3.1+
gem "psych"
end
# Alternative solution that might work, but it has bad interactions with
# Gemfile.lock if that gets committed/reused:
# c_platforms = [:mri] if Gem.platforms.last.os == "java"
group :extra do
gem 'fcgi', platforms: c_platforms
gem 'dalli'
gem 'thin', platforms: c_platforms
group :maintenance, optional: true do
gem "rubocop", require: false
gem "rubocop-packaging", require: false
end
group :doc do
gem 'rdoc'
end
group :test do
gem 'minitest'
gem 'bake-test-external', '~> 0.1.3'
end
The MIT License (MIT)
Copyright (C) 2007-2019 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
Copyright (C) 2007-2021 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
......
README.md 0 → 100644
# ![Rack](contrib/logo.webp)
> **_NOTE:_** Rack v3.0.0 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 --pre
# or, add it to your current application gemfile:
$ bundle add rack --version 3.0.0
```
If you need features from `Rack::Session` or `bin/rackup` please add those gems separately.
```bash
$ gem install rack-session rackup
```
## 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_file_limit`
```ruby
Rack::Utils.multipart_file_limit = 128 # default
```
The maximum number of parts with a filename 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_FILE_LIMIT` environment variable.
(This is also aliased as `multipart_part_limit` and `RACK_MULTIPART_PART_LIMIT` for compatibility)
### `multipart_total_part_limit`
The maximum total number of parts a request can contain of any type, including
both file and non-file form fields.
The default is 4096, which means that a single request can't contain more than
4096 parts.
Set to 0 for no limit.
Can also be set via the `RACK_MULTIPART_TOTAL_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
= \Rack, a modular Ruby webserver interface
{<img src="https://rack.github.io/logo.png" width="400" alt="rack powers web applications" />}[https://rack.github.io/]
{<img src="https://circleci.com/gh/rack/rack.svg?style=svg" alt="CircleCI" />}[https://circleci.com/gh/rack/rack]
{<img src="https://badge.fury.io/rb/rack.svg" alt="Gem Version" />}[http://badge.fury.io/rb/rack]
{<img src="https://api.dependabot.com/badges/compatibility_score?dependency-name=rack&package-manager=bundler&version-scheme=semver" alt="SemVer Stability" />}[https://dependabot.com/compatibility-score.html?dependency-name=rack&package-manager=bundler&version-scheme=semver]
{<img src="http://inch-ci.org/github/rack/rack.svg?branch=master" 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,
which all \Rack applications should conform to.
== Supported web servers
The included *handlers* connect all kinds of web servers to \Rack:
* WEBrick[https://github.com/ruby/webrick]
* FCGI
* CGI
* SCGI
* LiteSpeed[https://www.litespeedtech.com/]
* Thin[https://rubygems.org/gems/thin]
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/]
* Unicorn[https://yhbt.net/unicorn/]
* uWSGI[https://uwsgi-docs.readthedocs.io/en/latest/]
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.key_space_limit = 128
=== key_space_limit
The default number of bytes to allow all parameters keys in a given parameter hash to take up.
Does not affect nested parameter hashes, so doesn't actually prevent an attacker from using
more than this many bytes for parameter keys.
Defaults to 65536 characters.
=== 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 100.
=== multipart_file_limit
The maximum number of parts with a filename 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_FILE_LIMIT+ environment variable.
(This is also aliased as +multipart_part_limit+ and +RACK_MULTIPART_PART_LIMIT+ for compatibility)
=== multipart_total_part_limit
The maximum total number of parts a request can contain of any type, including
both file and non-file form fields.
The default is 4096, which means that a single request can't contain more than
4096 parts.
Set to 0 for no limit.
Can also be set via the +RACK_MULTIPART_TOTAL_PART_LIMIT+ environment variable.
== Changelog
See {CHANGELOG.md}[https://github.com/rack/rack/blob/master/CHANGELOG.md].
== Contributing
See {CONTRIBUTING.md}[https://github.com/rack/rack/blob/master/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].
......@@ -98,8 +98,24 @@ task "test_cov" do
Rake::Task['test:regular'].invoke
end
desc "Run separate tests for each test file, to test directly requiring components"
task "test:separate" do
fails = []
FileList["test/**/spec_*.rb"].each do |file|
puts "#{FileUtils::RUBY} -w #{file}"
fails << file unless system({'SEPARATE'=>'1'}, FileUtils::RUBY, '-w', file)
end
if fails.empty?
puts 'All test files passed'
else
puts "Failures in the following test files:"
puts fails
raise "At least one separate test failed"
end
end
desc "Run all the fast + platform agnostic tests"
task test: %w[spec test:regular]
task test: %w[spec test:regular test:separate]
desc "Run all the tests we run on CI"
task ci: :test
......@@ -118,13 +134,3 @@ task rdoc: %w[changelog spec] do
`git ls-files lib/\*\*/\*.rb`.strip.split)
cp "contrib/rdoc.css", "doc/rdoc.css"
end
task pushdoc: :rdoc do
sh "rsync -avz doc/ rack.rubyforge.org:/var/www/gforge-projects/rack/doc/"
end
task pushsite: :pushdoc do
sh "cd site && git gc"
sh "rsync -avz site/ rack.rubyforge.org:/var/www/gforge-projects/rack/"
sh "cd site && git push"
end
# Rack maintenance
# Security Policy
## Supported versions
### New features
New features will only be added to the master branch and will not be made available in point releases.
New features will only be added to the main branch and will not be made available in point releases.
### Bug fixes
Only the latest release series will receive bug fixes. When enough bugs are fixed and its deemed worthy to release a new gem, this is the branch it happens from.
* Current release series: 2.1.x
* Current release series: 2.2.x
### Security issues
The current release series and the next most recent one will receive patches and new versions in case of a security issue.
* Current release series: 2.1.x
* Next most recent release series: 2.0.x
* Current release series: 2.2.x
* Next most recent release series: 2.1.x
### Severe security issues
For severe security issues we will provide new versions as above, and also the last major release series will receive patches and new versions. The classification of the security issue is judged by the core team.
* Current release series: 2.1.x
* Next most recent release series: 2.0.x
* Current release series: 2.2.x
* Next most recent release series: 2.1.x
* Last major release series: 1.6.x
### Unsupported Release Series
......
This specification aims to formalize the Rack protocol. You
This specification aims to formalize the Rack protocol. You
can (and should) use Rack::Lint to enforce it.
When you develop middleware, be sure to add a Lint before and
after to catch all mistakes.
= Rack applications
A Rack application is a Ruby object (not a class) that
responds to +call+.
It takes exactly one argument, the *environment*
and returns an Array of exactly three values:
and returns a non-frozen Array of exactly three values:
The *status*,
the *headers*,
and the *body*.
== The Environment
The environment must be an unfrozen instance of Hash that includes
CGI-like headers. The application is free to modify the
CGI-like headers. The Rack application is free to modify the
environment.
The environment is required to include these variables
(adopted from PEP333), except when they'd be empty, but see
(adopted from {PEP 333}[https://peps.python.org/pep-0333/]), except when they'd be empty, but see
below.
<tt>REQUEST_METHOD</tt>:: The HTTP request method, such as
"GET" or "POST". This cannot ever
......@@ -42,17 +46,20 @@ below.
<tt>QUERY_STRING</tt>:: The portion of the request URL that
follows the <tt>?</tt>, if any. May be
empty, but is always required!
<tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>::
When combined with <tt>SCRIPT_NAME</tt> and
<tt>SERVER_NAME</tt>:: When combined with <tt>SCRIPT_NAME</tt> and
<tt>PATH_INFO</tt>, these variables can be
used to complete the URL. Note, however,
that <tt>HTTP_HOST</tt>, if present,
should be used in preference to
<tt>SERVER_NAME</tt> for reconstructing
the request URL.
<tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt>
can never be empty strings, and so
are always required.
<tt>SERVER_NAME</tt> can never be an empty
string, and so is always required.
<tt>SERVER_PORT</tt>:: An optional +Integer+ which is the port the
server is running on. Should be specified if
the server is running on a non-standard port.
<tt>SERVER_PROTOCOL</tt>:: A string representing the HTTP version used
for the request.
<tt>HTTP_</tt> Variables:: Variables corresponding to the
client-supplied HTTP request
headers (i.e., variables whose
......@@ -66,40 +73,19 @@ below.
for specific behavior.
In addition to this, the Rack environment must include these
Rack-specific variables:
<tt>rack.version</tt>:: The Array representing this version of Rack
See Rack::VERSION, that corresponds to
the version of this SPEC.
<tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the
request URL.
<tt>rack.input</tt>:: See below, the input stream.
<tt>rack.errors</tt>:: See below, the error stream.
<tt>rack.multithread</tt>:: true if the application object may be
simultaneously invoked by another thread
in the same process, false otherwise.
<tt>rack.multiprocess</tt>:: true if an equivalent application object
may be simultaneously invoked by another
process, false otherwise.
<tt>rack.run_once</tt>:: true if the server expects
(but does not guarantee!) that the
application will only be invoked this one
time during the life of its containing
process. Normally, this will only be true
for a server based on CGI
(or something similar).
<tt>rack.hijack?</tt>:: present and true if the server supports
connection hijacking. See below, hijacking.
<tt>rack.hijack</tt>:: an object responding to #call that must be
called at least once before using
rack.hijack_io.
It is recommended #call return rack.hijack_io
as well as setting it in env if necessary.
<tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack
has received #call, this will contain
an object resembling an IO. See hijacking.
<tt>rack.hijack?</tt>:: See below, if present and true, indicates
that the server supports partial hijacking.
<tt>rack.hijack</tt>:: See below, if present, an object responding
to +call+ that is used to perform a full
hijack.
Additional environment specifications have approved to
standardized middleware APIs. None of these are required to
standardized middleware APIs. None of these are required to
be implemented by the server.
<tt>rack.session</tt>:: A hash like interface for storing
<tt>rack.session</tt>:: A hash-like interface for storing
request session data.
The store must implement:
store(key, value) (aliased as []=);
......@@ -122,6 +108,11 @@ and should be prefixed uniquely. The prefix <tt>rack.</tt>
is reserved for use with the Rack core distribution and other
accepted specifications and must not be used otherwise.
The <tt>SERVER_PORT</tt> must be an Integer if set.
The <tt>SERVER_NAME</tt> must be a valid authority as defined by RFC7540.
The <tt>HTTP_HOST</tt> must be a valid authority as defined by RFC7540.
The <tt>SERVER_PROTOCOL</tt> must match the regexp <tt>HTTP/\d(\.\d)?</tt>.
If the <tt>HTTP_VERSION</tt> is present, it must equal the <tt>SERVER_PROTOCOL</tt>.
The environment must not contain the keys
<tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
(use the versions without <tt>HTTP_</tt>).
......@@ -129,26 +120,32 @@ The CGI keys (named without a period) must have String values.
If the string values for CGI keys contain non-ASCII characters,
they should use ASCII-8BIT encoding.
There are the following restrictions:
* <tt>rack.version</tt> must be an array of Integers.
* <tt>rack.url_scheme</tt> must either be +http+ or +https+.
* There must be a valid input stream in <tt>rack.input</tt>.
* There must be a valid error stream in <tt>rack.errors</tt>.
* There may be a valid hijack stream in <tt>rack.hijack_io</tt>
* There may be a valid hijack callback in <tt>rack.hijack</tt>
* The <tt>REQUEST_METHOD</tt> must be a valid token.
* The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
* The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
* The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
* One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be
set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
<tt>SCRIPT_NAME</tt> is empty.
<tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
<tt>rack.response_finished</tt>:: An array of callables run by the server after the response has been
processed. This would typically be invoked after sending the response to the client, but it could also be
invoked if an error occurs while generating the response or sending the response; in that case, the error
argument will be a subclass of +Exception+.
The callables are invoked with +env, status, headers, error+ arguments and should not raise any
exceptions. They should be invoked in reverse order of registration.
=== The Input Stream
The input stream is an IO-like object which contains the raw HTTP
POST data.
When applicable, its external encoding must be "ASCII-8BIT" and it
must be opened in binary mode, for Ruby 1.9 compatibility.
The input stream must respond to +gets+, +each+, +read+ and +rewind+.
The input stream must respond to +gets+, +each+, and +read+.
* +gets+ must be called without arguments and return a string,
or +nil+ on EOF.
* +read+ behaves like IO#read.
......@@ -169,120 +166,175 @@ The input stream must respond to +gets+, +each+, +read+ and +rewind+.
If +buffer+ is given, then the read data will be placed
into +buffer+ instead of a newly created String object.
* +each+ must be called without arguments and only yield Strings.
* +rewind+ must be called without arguments. It rewinds the input
stream back to the beginning. It must not raise Errno::ESPIPE:
that is, it may not be a pipe or a socket. Therefore, handler
developers must buffer the input data into some rewindable object
if the underlying input stream is not rewindable.
* +close+ must never be called on the input stream.
* +close+ can be called on the input stream to indicate that the
any remaining input is not needed.
=== The Error Stream
The error stream must respond to +puts+, +write+ and +flush+.
* +puts+ must be called with a single argument that responds to +to_s+.
* +write+ must be called with a single argument that is a String.
* +flush+ must be called without arguments and must be called
in order to make the error appear for sure.
* +close+ must never be called on the error stream.
=== Hijacking
==== Request (before status)
If rack.hijack? is true then rack.hijack must respond to #call.
rack.hijack must return the io that will also be assigned (or is
already present, in rack.hijack_io.
rack.hijack_io must respond to:
<tt>read, write, read_nonblock, write_nonblock, flush, close,
close_read, close_write, closed?</tt>
The hijacking interfaces provides a means for an application to take
control of the HTTP connection. There are two distinct hijack
interfaces: full hijacking where the application takes over the raw
connection, and partial hijacking where the application takes over
just the response body stream. In both cases, the application is
responsible for closing the hijacked stream.
The semantics of these IO methods must be a best effort match to
those of a normal ruby IO or Socket object, using standard
arguments and raising standard exceptions. Servers are encouraged
to simply pass on real IO objects, although it is recognized that
this approach is not directly compatible with SPDY and HTTP 2.0.
IO provided in rack.hijack_io should preference the
IO::WaitReadable and IO::WaitWritable APIs wherever supported.
There is a deliberate lack of full specification around
rack.hijack_io, as semantics will change from server to server.
Users are encouraged to utilize this API with a knowledge of their
server choice, and servers may extend the functionality of
hijack_io to provide additional features to users. The purpose of
rack.hijack is for Rack to "get out of the way", as such, Rack only
provides the minimum of specification and support.
If rack.hijack? is false, then rack.hijack should not be set.
If rack.hijack? is false, then rack.hijack_io should not be set.
==== Response (after headers)
It is also possible to hijack a response after the status and headers
have been sent.
In order to do this, an application may set the special header
<tt>rack.hijack</tt> to an object that responds to <tt>call</tt>
accepting an argument that conforms to the <tt>rack.hijack_io</tt>
protocol.
After the headers have been sent, and this hijack callback has been
called, the application is now responsible for the remaining lifecycle
of the IO. The application is also responsible for maintaining HTTP
semantics. Of specific note, in almost all cases in the current SPEC,
applications will have wanted to specify the header Connection:close in
HTTP/1.1, and not Connection:keep-alive, as there is no protocol for
returning hijacked sockets to the web server. For that purpose, use the
body streaming API instead (progressively yielding strings via each).
Servers must ignore the <tt>body</tt> part of the response tuple when
the <tt>rack.hijack</tt> response API is in use.
The special response header <tt>rack.hijack</tt> must only be set
if the request env has <tt>rack.hijack?</tt> <tt>true</tt>.
==== Conventions
* Middleware should not use hijack unless it is handling the whole
response.
* Middleware may wrap the IO object for the response pattern.
* Middleware should not wrap the IO object for the request pattern. The
request pattern is intended to provide the hijacker with "raw tcp".
Full hijacking only works with HTTP/1. Partial hijacking is functionally
equivalent to streaming bodies, and is still optionally supported for
backwards compatibility with older Rack versions.
==== Full Hijack
Full hijack is used to completely take over an HTTP/1 connection. It
occurs before any headers are written and causes the request to
ignores any response generated by the application.
It is intended to be used when applications need access to raw HTTP/1
connection.
If +rack.hijack+ is present in +env+, it must respond to +call+
and return an +IO+ instance which can be used to read and write
to the underlying connection using HTTP/1 semantics and
formatting.
==== Partial Hijack
Partial hijack is used for bi-directional streaming of the request and
response body. It occurs after the status and headers are written by
the server and causes the server to ignore the Body of the response.
It is intended to be used when applications need bi-directional
streaming.
If +rack.hijack?+ is present in +env+ and truthy,
an application may set the special response header +rack.hijack+
to an object that responds to +call+,
accepting a +stream+ argument.
After the response status and headers have been sent, this hijack
callback will be invoked with a +stream+ argument which follows the
same interface as outlined in "Streaming Body". Servers must
ignore the +body+ part of the response tuple when the
+rack.hijack+ response header is present. Using an empty +Array+
instance is recommended.
The special response header +rack.hijack+ must only be set
if the request +env+ has a truthy +rack.hijack?+.
== The Response
=== The Status
This is an HTTP status. When parsed as integer (+to_i+), it must be
greater than or equal to 100.
This is an HTTP status. It must be an Integer greater than or equal to
100.
=== The Headers
The header must respond to +each+, and yield values of key and value.
The headers must be a unfrozen Hash.
The header keys must be Strings.
Special headers starting "rack." are for communicating with the
server, and must not be sent back to the client.
The header must not contain a +Status+ key.
The header must conform to RFC7230 token specification, i.e. cannot
Header keys must conform to RFC7230 token specification, i.e. cannot
contain non-printable ASCII, DQUOTE or "(),/:;<=>?@[\]{}".
The values of the header must be Strings,
consisting of lines (for multiple header values, e.g. multiple
<tt>Set-Cookie</tt> values) separated by "\\n".
The lines must not contain characters below 037.
=== The Content-Type
There must not be a <tt>Content-Type</tt>, when the +Status+ is 1xx,
204 or 304.
=== The Content-Length
There must not be a <tt>Content-Length</tt> header when the
+Status+ is 1xx, 204 or 304.
Header keys must not contain uppercase ASCII characters (A-Z).
Header values must be either a String instance,
or an Array of String instances,
such that each String instance must not contain characters below 037.
=== The content-type
There must not be a <tt>content-type</tt> header key when the +Status+ is 1xx,
204, or 304.
=== The content-length
There must not be a <tt>content-length</tt> header key when the
+Status+ is 1xx, 204, or 304.
=== The Body
The Body must respond to +each+
The Body is typically an +Array+ of +String+ instances, an enumerable
that yields +String+ instances, a +Proc+ instance, or a File-like
object.
The Body must respond to +each+ or +call+. It may optionally respond
to +to_path+ or +to_ary+. A Body that responds to +each+ is considered
to be an Enumerable Body. A Body that responds to +call+ is considered
to be a Streaming Body.
A Body that responds to both +each+ and +call+ must be treated as an
Enumerable Body, not a Streaming Body. If it responds to +each+, you
must call +each+ and not +call+. If the Body doesn't respond to
+each+, then you can assume it responds to +call+.
The Body must either be consumed or returned. The Body is consumed by
optionally calling either +each+ or +call+.
Then, if the Body responds to +close+, it must be called to release
any resources associated with the generation of the body.
In other words, +close+ must always be called at least once; typically
after the web server has sent the response to the client, but also in
cases where the Rack application makes internal/virtual requests and
discards the response.
After calling +close+, the Body is considered closed and should not
be consumed again.
If the original Body is replaced by a new Body, the new Body must
also consume the original Body by calling +close+ if possible.
If the Body responds to +to_path+, it must return a +String+
path for the local file system whose contents are identical
to that produced by calling +each+; this may be used by the
server as an alternative, possibly more efficient way to
transport the response. The +to_path+ method does not consume
the body.
==== Enumerable Body
The Enumerable Body must respond to +each+.
It must only be called once.
It must not be called after being closed.
and must only yield String values.
The Body itself should not be an instance of String, as this will
break in Ruby 1.9.
If the Body responds to +close+, it will be called after iteration. If
the body is replaced by a middleware after action, the original body
must be closed first, if it responds to close.
Middleware must not call +each+ directly on the Body.
Instead, middleware can return a new Body that calls +each+ on the
original Body, yielding at least once per iteration.
If the Body responds to +to_path+, it must return a String
identifying the location of a file whose contents are identical
to that produced by calling +each+; this may be used by the
server as an alternative, possibly more efficient way to
transport the response.
If the Body responds to +to_ary+, it must return an +Array+ whose
contents are identical to that produced by calling +each+.
Middleware may call +to_ary+ directly on the Body and return a new
Body in its place. In other words, middleware can only process the
Body directly if it responds to +to_ary+. If the Body responds to both
+to_ary+ and +close+, its implementation of +to_ary+ must call
+close+.
==== Streaming Body
The Streaming Body must respond to +call+.
It must only be called once.
It must not be called after being closed.
It takes a +stream+ argument.
The +stream+ argument must implement:
<tt>read, write, <<, flush, close, close_read, close_write, closed?</tt>
The semantics of these IO methods must be a best effort match to
those of a normal Ruby IO or Socket object, using standard arguments
and raising standard exceptions. Servers are encouraged to simply
pass on real IO objects, although it is recognized that this approach
is not directly compatible with HTTP/2.
The Body commonly is an Array of Strings, the application
instance itself, or a File-like object.
== Thanks
Some parts of this specification are adopted from PEP333: Python
Web Server Gateway Interface
v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank
everyone involved in that effort.
Some parts of this specification are adopted from {PEP 333 – Python Web Server Gateway Interface v1.0}[https://peps.python.org/pep-0333/]
I'd like to thank everyone involved in that effort.
# 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
### Rack 2 & Rack 3 compatibility
Most applications can be compatible with Rack 2 and 3 by following the strict intersection of the Rack Specifications, notably:
- Response array must now be non-frozen.
- Response `status` must now be an integer greater than or equal to 100.
- Response `headers` must now be an unfrozen hash.
- Response header keys can no longer include uppercase characters.
- `rack.input` is no longer required to be rewindable.
- `rack.multithread`/`rack.multiprocess`/`rack.run_once`/`rack.version` are no longer required environment keys.
- `rack.hijack?` (partial hijack) and `rack.hijack` (full hijack) are now independently optional.
- `rack.hijack_io` has been removed completely.
- `SERVER_PROTOCOL` is now a required key, matching the HTTP protocol used in the request.
- Middleware must no longer call `#each` on the body, but they can call `#to_ary` on the body if it responds to `#to_ary`.
There is one changed feature in Rack 3 which is not backwards compatible:
- Response header values can be an `Array` to handle multiple values (and no longer supports `\n` encoded headers).
You can achieve compatibility by using `Rack::Response#add_header` which provides an interface for adding headers without concern for the underlying format.
There is one new feature in Rack 3 which is not directly backwards compatible:
- Response body can now respond to `#call` (streaming body) instead of `#each` (enumerable body), for the equivalent of response hijacking in previous versions.
If supported by your server, you can use partial rack hijack instead (or wrap this behaviour in a middleware).
### `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` was moved to a separate 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'
```
This provides all the previously available functionality.
### `bin/rackup` was moved to a separate gem.
Previously, the `rackup` executable was included with Rack. Because WEBrick is
no longer a default gem with Ruby, we had to make a decision: either `rack`
should depend on `webrick` or we should move that functionality into a
separate gem. We chose the latter which will hopefully allow us to innovate
more rapidly on the design and implementation of `rackup` separately from
"rack the interface".
In Rack 3, you will need to include:
```ruby
gem 'rackup'
```
This provides all the previously available functionality.
## 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 now can
be 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
Previously, `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
```
If you want your code to work with Rack 3 without having to manually lowercase
each header key used, instead of using a plain hash for headers, you can use
`Rack::Headers` on Rack 3.
```ruby
headers = defined?(Rack::Headers) ? Rack::Headers.new : {}
```
`Rack::Headers` is a subclass of Hash that will automatically lowercase keys:
```ruby
headers = Rack::Headers.new
headers['Foo'] = 'bar'
headers['FOO'] # => 'bar'
headers.keys # => ['foo']
```
### 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.
### Status needs to be an `Integer`
The response status is now required to be an `Integer` with a value greater or equal to 100.
Previously any object that responded to `#to_i` was allowed, so a response like `["200", {}, ""]` will need to be replaced with `[200, {}, ""]` and so on. This can be done by calling `#to_i` on the status object yourself.
#!/usr/bin/env ruby
# frozen_string_literal: true
require "rack"
Rack::Server.start
protocol-rack:
url: https://github.com/socketry/protocol-rack
command: bundle exec bake test
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment