3 This document is a work in progress, but outlines some of the key changes in
4 Rack 3 which you should be aware of in order to update your server, middleware
9 ### Rack 2 & Rack 3 compatibility
11 Most applications can be compatible with Rack 2 and 3 by following the strict intersection of the Rack Specifications, notably:
13 - Response array must now be non-frozen.
14 - Response `status` must now be an integer greater than or equal to 100.
15 - Response `headers` must now be an unfrozen hash.
16 - Response header keys can no longer include uppercase characters.
17 - `rack.input` is no longer required to be rewindable.
18 - `rack.multithread`/`rack.multiprocess`/`rack.run_once`/`rack.version` are no longer required environment keys.
19 - `rack.hijack?` (partial hijack) and `rack.hijack` (full hijack) are now independently optional.
20 - `rack.hijack_io` has been removed completely.
21 - `SERVER_PROTOCOL` is now a required key, matching the HTTP protocol used in the request.
22 - Middleware must no longer call `#each` on the body, but they can call `#to_ary` on the body if it responds to `#to_ary`.
24 There is one changed feature in Rack 3 which is not backwards compatible:
26 - Response header values can be an `Array` to handle multiple values (and no longer supports `\n` encoded headers).
28 You can achieve compatibility by using `Rack::Response#add_header` which provides an interface for adding headers without concern for the underlying format.
30 There is one new feature in Rack 3 which is not directly backwards compatible:
32 - Response body can now respond to `#call` (streaming body) instead of `#each` (enumerable body), for the equivalent of response hijacking in previous versions.
34 If supported by your server, you can use partial rack hijack instead (or wrap this behaviour in a middleware).
36 ### `config.ru` `Rack::Builder#run` now accepts block
38 Previously, `Rack::Builder#run` method would only accept a callable argument:
41 run lambda{|env| [200, {}, ["Hello World"]]}
44 This can be rewritten more simply:
48 [200, {}, ["Hello World"]]
52 ### Response bodies can be used for bi-directional streaming
54 Previously, the `rack.hijack` response header could be used for implementing
55 bi-directional streaming (e.g. WebSockets).
59 stream_callback = proc do |stream|
66 return [200, {'rack.hijack' => stream_callback}, []]
70 This feature was optional and tricky to use correctly. You can now achieve the
71 same thing by giving `stream_callback` as the response body:
75 stream_callback = proc do |stream|
82 return [200, {}, stream_callback]
86 ### `Rack::Session` was moved to a separate gem.
88 Previously, `Rack::Session` was part of the `rack` gem. Not every application
89 needs it, and it increases the security surface area of the `rack`, so it was
90 decided to extract it into its own gem `rack-session` which can be updated
93 Applications that make use of `rack-session` will need to add that gem as a
100 This provides all the previously available functionality.
102 ### `bin/rackup`, `Rack::Server`, `Rack::Handler`and `Rack::Lobster` were moved to a separate gem.
104 Previously, the `rackup` executable was included with Rack. Because WEBrick is
105 no longer a default gem with Ruby, we had to make a decision: either `rack`
106 should depend on `webrick` or we should move that functionality into a
107 separate gem. We chose the latter which will hopefully allow us to innovate
108 more rapidly on the design and implementation of `rackup` separately from
109 "rack the interface".
111 In Rack 3, you will need to include:
117 This provides all the previously available functionality.
119 The classes `Rack::Server`, `Rack::Handler` and `Rack::Lobster` have been moved to the rackup gem too and renamed to `Rackup::Server`, `Rackup::Handler` and `Rackup::Lobster` respectively.
121 To start an app with `Rackup::Server` with Rack 3 :
125 Rackup::Server.start app: app, Port: 3000
128 #### `config.ru` autoloading is disabled unless `require 'rack'`
130 Previously, rack modules like `rack/directory` were autoloaded because `rackup` did require 'rack'. In Rack 3, you will need to write `require 'rack'` or require specific module explicitly.
134 run Rack::Directory.new '.'
140 +require 'rack/directory'
141 run Rack::Directory.new '.'
146 ### `rack.version` is no longer required
148 Previously, the "rack protocol version" was available in `rack.version` but it
149 was not practically useful, so it has been removed as a requirement.
151 ### `rack.multithread`/`rack.multiprocess`/`rack.run_once` are no longer required
153 Previously, servers tried to provide these keys to reflect the execution
154 environment. These come too late to be useful, so they have been removed as a
157 ### `rack.hijack?` now only applies to partial hijack
159 Previously, both full and partial hijiack were controlled by the presence and
160 value of `rack.hijack?`. Now, it only applies to partial hijack (which now can
161 be replaced by streaming bodies).
163 ### `rack.hijack` alone indicates that you can execute a full hijack
165 Previously, `rack.hijack?` had to be truthy, as well as having `rack.hijack`
166 present in the request environment. Now, the presence of the `rack.hijack`
169 ### `rack.hijack_io` is removed
171 Previously, the server would try to set `rack.hijack_io` into the request
172 environment when `rack.hijack` was invoked for a full hijack. This was often
173 impossible if a middleware had called `env.dup`, so this requirement has been
176 ### `rack.input` is no longer required to be rewindable
178 Previously, `rack.input` was required to be rewindable, i.e. `io.seek(0)` but
179 this was only generally possible with a file based backing, which prevented
180 efficient streaming of request bodies. Now, `rack.input` is not required to be
183 ### `rack.input` is no longer rewound after consuming form and multipart data
185 Previously `.rewind` was called after consuming form and multipart data. Use
186 `Rack::RewindableInput::Middleware` to make the body rewindable, and call
187 `.rewind` explicitly to match this behavior.
189 ### Invalid nested query parsing syntax
191 Previously, Rack 2 was able to parse the query string `a[b[c]]=x` in the same
192 way as `a[b][c]=x`. This invalid syntax was never officially supported. However,
193 some libraries and applications used it anyway. Due to implementation details,
194 Rack 2 ended up parsing it the same as the correct syntax. The implementation
195 was changed in Rack 3, and this invalid syntax is no longer parsed the same way
196 as the correct syntax:
199 Rack::Utils.parse_nested_query("a[b[c]]=x")
200 # Rack 3 => {"a"=>{"b[c"=>{"]"=>"x"}}} ❌
201 # Rack 2 => {"a"=>{"b"=>{"c"=>"x"}}} ✅
204 The correct syntax for nested parameters is `a[b][c]=x` and you'll need
205 to change that in your application code to be compatible with Rack 3:
208 Rack::Utils.parse_nested_query("a[b][c]=x")
209 # Rack 3 => {"a"=>{"b"=>{"c"=>"x"}}} ✅
210 # Rack 2 => {"a"=>{"b"=>{"c"=>"x"}}} ✅
213 See <https://github.com/rack/rack/issues/2128> for more context.
217 ### Response must be mutable
219 Rack 3 requires the response Array `[status, headers, body]` to be mutable.
220 Existing code that uses a frozen response will need to be changed:
223 NOT_FOUND = [404, {}, ["Not Found"]].freeze
231 should be rewritten as:
235 [404, {}, ["Not Found"]]
244 Note there is a subtle bug in the former version: the headers hash is mutable
245 and can be modified, and these modifications can leak into subsequent requests.
247 ### Response headers must be a mutable hash
249 Rack 3 requires response headers to be a mutable hash. Previously it could be
250 any object that would respond to `#each` and yield `key`/`value` pairs.
251 Previously, the following was acceptable:
255 return [200, [['content-type', 'text/plain']], ["Hello World"]]
259 Now you must use a hash instance:
263 return [200, {'content-type' => 'text/plain'}, ["Hello World"]]
267 This ensures middleware can predictably update headers as needed.
269 ### Response Headers must be lower case
271 Rack 3 requires all response headers to be lower case. This is to simplify
272 fetching and updating response headers. Previously you had to use something like
277 response = @app.call(env)
278 # HeaderHash must allocate internal objects and compute lower case keys:
279 headers = Rack::Utils::HeaderHash[response[1]]
281 cache_response(headers['ETag'], response)
287 but now you must just use the normal form for HTTP header:
291 response = @app.call(env)
292 # A plain hash with lower case keys:
293 headers = response[1]
295 cache_response(headers['etag'], response)
301 If you want your code to work with Rack 3 without having to manually lowercase
302 each header key used, instead of using a plain hash for headers, you can use
303 `Rack::Headers` on Rack 3.
306 headers = defined?(Rack::Headers) ? Rack::Headers.new : {}
309 `Rack::Headers` is a subclass of Hash that will automatically lowercase keys:
312 headers = Rack::Headers.new
313 headers['Foo'] = 'bar'
314 headers['FOO'] # => 'bar'
315 headers.keys # => ['foo']
318 ### Multiple response header values are encoded using an `Array`
320 Response header values can be an Array to handle multiple values (and no longer
321 supports `\n` encoded headers). If you use `Rack::Response`, you don't need to
322 do anything, but if manually append values to response headers, you will need to
323 promote them to an Array, e.g.
326 def set_cookie_header!(headers, key, value)
327 if header = headers[SET_COOKIE]
328 if header.is_a?(Array)
329 header << set_cookie_header(key, value)
331 headers[SET_COOKIE] = [header, set_cookie_header(key, value)]
334 headers[SET_COOKIE] = set_cookie_header(key, value)
339 ### Response body might not respond to `#each`
341 Rack 3 has more strict requirements on response bodies. Previously, response
342 body would only need to respond to `#each` and optionally `#close`. In addition,
343 there was no way to determine whether it was safe to call `#each` and buffer the
346 ### Response bodies can be buffered if they expose `#to_ary`
348 If your body responds to `#to_ary` then it must return an `Array` whose contents
349 are identical to that produced by calling `#each`. If the body responds to both
350 `#to_ary` and `#close` then its implementation of `#to_ary` must also call
353 Previously, it was not possible to determine whether a response body was
354 immediately available (could be buffered) or was streaming chunks. This case is
355 now unambiguously exposed by `#to_ary`:
359 status, headers, body = @app.call(env)
361 # Check if we can buffer the body into an Array, so we can compute a digest:
362 if body.respond_to?(:to_ary)
364 digest = digest_body(body)
365 headers[ETAG_STRING] = %(W/"#{digest}") if digest
368 return [status, headers, body]
372 ### Middleware should not directly modify the response body
374 Be aware that the response body might not respond to `#each` and you must now
375 check if the body responds to `#each` or not to determine if it is an enumerable
378 You must not call `#each` directly on the body and instead you should return a
379 new body that calls `#each` on the original body.
381 ### Status needs to be an `Integer`
383 The response status is now required to be an `Integer` with a value greater or equal to 100.
385 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.