1 # frozen_string_literal: true
3 require_relative 'helper'
6 require_relative '../lib/rack/response'
9 describe Rack::Response do
10 it 'has standard constructor' do
11 headers = { "header" => "value" }
14 response = Rack::Response[200, headers, body]
16 response.status.must_equal 200
17 response.headers.must_equal headers
18 response.body.must_equal body
21 it 'has cache-control methods' do
22 response = Rack::Response.new
24 response.cache_control = cc
25 assert_equal cc, response.cache_control
26 assert_equal cc, response.to_a[1]['cache-control']
29 it 'has an etag method' do
30 response = Rack::Response.new
33 assert_equal etag, response.etag
34 assert_equal etag, response.to_a[1]['ETag']
37 it 'has a content-type method' do
38 response = Rack::Response.new
40 response.content_type = content_type
41 assert_equal content_type, response.content_type
42 assert_equal content_type, response.to_a[1]['Content-Type']
45 it "have sensible default values" do
46 response = Rack::Response.new
47 status, header, body = response.finish
54 response = Rack::Response.new
55 status, header, body = *response
63 it "can be written to inside finish block, but does not update Content-Length" do
64 response = Rack::Response.new('foo')
67 _, h, body = response.finish do
72 body.each { |part| parts << part }
74 parts.must_equal ["foo", "bar", "baz"]
75 h['Content-Length'].must_equal '6'
78 it "can set and read headers" do
79 response = Rack::Response.new
80 response["Content-Type"].must_be_nil
81 response["Content-Type"] = "text/plain"
82 response["Content-Type"].must_equal "text/plain"
85 it "doesn't mutate given headers" do
88 response = Rack::Response.new([], 200, headers)
89 response.headers["Content-Type"] = "text/plain"
90 response.headers["Content-Type"].must_equal "text/plain"
92 headers.wont_include("Content-Type")
95 it "can override the initial Content-Type with a different case" do
96 response = Rack::Response.new("", 200, "content-type" => "text/plain")
97 response["Content-Type"].must_equal "text/plain"
100 it "can set cookies" do
101 response = Rack::Response.new
103 response.set_cookie "foo", "bar"
104 response["Set-Cookie"].must_equal "foo=bar"
105 response.set_cookie "foo2", "bar2"
106 response["Set-Cookie"].must_equal ["foo=bar", "foo2=bar2"]
107 response.set_cookie "foo3", "bar3"
108 response["Set-Cookie"].must_equal ["foo=bar", "foo2=bar2", "foo3=bar3"]
111 it "can set cookies with the same name for multiple domains" do
112 response = Rack::Response.new
113 response.set_cookie "foo", { value: "bar", domain: "sample.example.com" }
114 response.set_cookie "foo", { value: "bar", domain: ".example.com" }
115 response["Set-Cookie"].must_equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"]
118 it "formats the Cookie expiration date accordingly to RFC 6265" do
119 response = Rack::Response.new
121 response.set_cookie "foo", { value: "bar", expires: Time.now + 10 }
122 response["Set-Cookie"].must_match(
123 /expires=..., \d\d ... \d\d\d\d \d\d:\d\d:\d\d .../)
126 it "can set secure cookies" do
127 response = Rack::Response.new
128 response.set_cookie "foo", { value: "bar", secure: true }
129 response["Set-Cookie"].must_equal "foo=bar; secure"
132 it "can set http only cookies" do
133 response = Rack::Response.new
134 response.set_cookie "foo", { value: "bar", httponly: true }
135 response["Set-Cookie"].must_equal "foo=bar; httponly"
138 it "can set http only cookies with :http_only" do
139 response = Rack::Response.new
140 response.set_cookie "foo", { value: "bar", http_only: true }
141 response["Set-Cookie"].must_equal "foo=bar; httponly"
144 it "can set prefers :httponly for http only cookie setting when :httponly and :http_only provided" do
145 response = Rack::Response.new
146 response.set_cookie "foo", { value: "bar", httponly: false, http_only: true }
147 response["Set-Cookie"].must_equal "foo=bar"
150 it "can set SameSite cookies with symbol value :none" do
151 response = Rack::Response.new
152 response.set_cookie "foo", { value: "bar", same_site: :none }
153 response["Set-Cookie"].must_equal "foo=bar; SameSite=None"
156 it "can set SameSite cookies with symbol value :None" do
157 response = Rack::Response.new
158 response.set_cookie "foo", { value: "bar", same_site: :None }
159 response["Set-Cookie"].must_equal "foo=bar; SameSite=None"
162 it "can set SameSite cookies with string value 'None'" do
163 response = Rack::Response.new
164 response.set_cookie "foo", { value: "bar", same_site: "None" }
165 response["Set-Cookie"].must_equal "foo=bar; SameSite=None"
168 it "can set SameSite cookies with symbol value :lax" do
169 response = Rack::Response.new
170 response.set_cookie "foo", { value: "bar", same_site: :lax }
171 response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
174 it "can set SameSite cookies with symbol value :Lax" do
175 response = Rack::Response.new
176 response.set_cookie "foo", { value: "bar", same_site: :lax }
177 response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
180 it "can set SameSite cookies with string value 'Lax'" do
181 response = Rack::Response.new
182 response.set_cookie "foo", { value: "bar", same_site: "Lax" }
183 response["Set-Cookie"].must_equal "foo=bar; SameSite=Lax"
186 it "can set SameSite cookies with boolean value true" do
187 response = Rack::Response.new
188 response.set_cookie "foo", { value: "bar", same_site: true }
189 response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
192 it "can set SameSite cookies with symbol value :strict" do
193 response = Rack::Response.new
194 response.set_cookie "foo", { value: "bar", same_site: :strict }
195 response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
198 it "can set SameSite cookies with symbol value :Strict" do
199 response = Rack::Response.new
200 response.set_cookie "foo", { value: "bar", same_site: :Strict }
201 response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
204 it "can set SameSite cookies with string value 'Strict'" do
205 response = Rack::Response.new
206 response.set_cookie "foo", { value: "bar", same_site: "Strict" }
207 response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
210 it "validates the SameSite option value" do
211 response = Rack::Response.new
213 response.set_cookie "foo", { value: "bar", same_site: "Foo" }
214 }.must_raise(ArgumentError).
215 message.must_match(/Invalid SameSite value: "Foo"/)
218 it "can set SameSite cookies with symbol value" do
219 response = Rack::Response.new
220 response.set_cookie "foo", { value: "bar", same_site: :Strict }
221 response["Set-Cookie"].must_equal "foo=bar; SameSite=Strict"
224 [ nil, false ].each do |non_truthy|
225 it "omits SameSite attribute given a #{non_truthy.inspect} value" do
226 response = Rack::Response.new
227 response.set_cookie "foo", { value: "bar", same_site: non_truthy }
228 response["Set-Cookie"].must_equal "foo=bar"
232 it "can delete cookies" do
233 response = Rack::Response.new
234 response.set_cookie "foo", "bar"
235 response.set_cookie "foo2", "bar2"
236 response.delete_cookie "foo"
237 response["Set-Cookie"].must_equal [
240 "foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
244 it "can delete cookies with the same name from multiple domains" do
245 response = Rack::Response.new
246 response.set_cookie "foo", { value: "bar", domain: "sample.example.com" }
247 response.set_cookie "foo", { value: "bar", domain: ".example.com" }
248 response["Set-Cookie"].must_equal [
249 "foo=bar; domain=sample.example.com",
250 "foo=bar; domain=.example.com"
253 response.delete_cookie "foo", domain: ".example.com"
254 response["Set-Cookie"].must_equal [
255 "foo=bar; domain=sample.example.com",
256 "foo=bar; domain=.example.com",
257 "foo=; domain=.example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
260 response.delete_cookie "foo", domain: "sample.example.com"
261 response["Set-Cookie"].must_equal [
262 "foo=bar; domain=sample.example.com",
263 "foo=bar; domain=.example.com",
264 "foo=; domain=.example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT",
265 "foo=; domain=sample.example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
269 it "only deletes cookies for the domain specified" do
270 response = Rack::Response.new
271 response.set_cookie "foo", { value: "bar", domain: "example.com.example.com" }
272 response.set_cookie "foo", { value: "bar", domain: "example.com" }
273 response["Set-Cookie"].must_equal [
274 "foo=bar; domain=example.com.example.com",
275 "foo=bar; domain=example.com"
278 response.delete_cookie "foo", { domain: "example.com" }
279 response["Set-Cookie"].must_equal [
280 "foo=bar; domain=example.com.example.com",
281 "foo=bar; domain=example.com",
282 "foo=; domain=example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
285 response.delete_cookie "foo", { domain: "example.com.example.com" }
286 response["Set-Cookie"].must_equal [
287 "foo=bar; domain=example.com.example.com",
288 "foo=bar; domain=example.com",
289 "foo=; domain=example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT",
290 "foo=; domain=example.com.example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
294 it "can delete cookies with the same name with different paths" do
295 response = Rack::Response.new
296 response.set_cookie "foo", { value: "bar", path: "/" }
297 response.set_cookie "foo", { value: "bar", path: "/path" }
299 response["Set-Cookie"].must_equal [
301 "foo=bar; path=/path"
304 response.delete_cookie "foo", path: "/path"
305 response["Set-Cookie"].must_equal [
307 "foo=bar; path=/path",
308 "foo=; path=/path; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
312 it "only delete cookies with the path specified" do
313 response = Rack::Response.new
314 response.set_cookie "foo", value: "bar", path: "/a/b"
315 response["Set-Cookie"].must_equal(
319 response.delete_cookie "foo", path: "/a"
320 response["Set-Cookie"].must_equal [
321 "foo=bar; path=/a/b",
322 "foo=; path=/a; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT"
326 it "only delete cookies with the domain and path specified" do
327 response = Rack::Response.new
328 response.delete_cookie "foo", path: "/a", domain: "example.com"
329 response["Set-Cookie"].must_equal(
330 "foo=; domain=example.com; path=/a; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT",
333 response.delete_cookie "foo", path: "/a/b", domain: "example.com"
334 response["Set-Cookie"].must_equal [
335 "foo=; domain=example.com; path=/a; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT",
336 "foo=; domain=example.com; path=/a/b; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT",
340 it "can do redirects" do
341 response = Rack::Response.new
342 response.redirect "/foo"
343 status, header = response.finish
344 status.must_equal 302
345 header["Location"].must_equal "/foo"
347 response = Rack::Response.new
348 response.redirect "/foo", 307
349 status, = response.finish
351 status.must_equal 307
354 it "has a useful constructor" do
355 r = Rack::Response.new("foo")
357 str = "".dup; body.each { |part| str << part }
360 r = Rack::Response.new(["foo", "bar"])
362 str = "".dup; body.each { |part| str << part }
363 str.must_equal "foobar"
365 object_with_each = Object.new
366 def object_with_each.each
370 r = Rack::Response.new(object_with_each)
373 str = "".dup; body.each { |part| str << part }
374 str.must_equal "foobarfoo"
376 r = Rack::Response.new([], 500)
377 r.status.must_equal 500
379 r = Rack::Response.new([], "200 OK")
380 r.status.must_equal 200
383 it "has a constructor that can take a block" do
384 r = Rack::Response.new { |res|
388 status, _, body = r.finish
389 str = "".dup; body.each { |part| str << part }
391 status.must_equal 404
394 it "correctly updates Content-Type when writing when not initialized with body" do
395 r = Rack::Response.new
399 _, header, body = r.finish
400 str = "".dup; body.each { |part| str << part }
401 str.must_equal "foobarbaz"
402 header['Content-Length'].must_equal '9'
405 it "correctly updates Content-Type when writing when initialized with body" do
411 ["foobar", ["foo", "bar"], obj].each do
412 r = Rack::Response.new(["foo", "bar"])
414 _, header, body = r.finish
415 str = "".dup; body.each { |part| str << part }
416 str.must_equal "foobarbaz"
417 header['Content-Length'].must_equal '9'
421 it "doesn't return invalid responses" do
422 r = Rack::Response.new(["foo", "bar"], 204)
423 _, header, body = r.finish
424 str = "".dup; body.each { |part| str << part }
426 header["Content-Type"].must_be_nil
427 header['Content-Length'].must_be_nil
430 Rack::Response.new(Object.new).each{}
431 }.must_raise(NoMethodError).
432 message.must_match(/undefined method .each. for/)
435 it "knows if it's empty" do
436 r = Rack::Response.new
441 r = Rack::Response.new
446 r = Rack::Response.new
452 it "provide access to the HTTP status" do
453 res = Rack::Response.new
455 res.must_be :successful?
459 res.must_be :successful?
460 res.must_be :created?
463 res.must_be :successful?
464 res.must_be :accepted?
467 res.must_be :successful?
468 res.must_be :no_content?
471 res.must_be :redirect?
472 res.must_be :moved_permanently?
475 res.must_be :redirect?
478 res.must_be :redirect?
481 res.must_be :redirect?
484 res.must_be :redirect?
487 res.wont_be :successful?
488 res.must_be :client_error?
489 res.must_be :bad_request?
492 res.wont_be :successful?
493 res.must_be :client_error?
494 res.must_be :unauthorized?
497 res.wont_be :successful?
498 res.must_be :client_error?
499 res.must_be :not_found?
502 res.wont_be :successful?
503 res.must_be :client_error?
504 res.must_be :method_not_allowed?
507 res.wont_be :successful?
508 res.must_be :client_error?
509 res.must_be :precondition_failed?
512 res.wont_be :successful?
513 res.must_be :client_error?
514 res.must_be :unprocessable?
517 res.wont_be :successful?
518 res.must_be :server_error?
521 it "provide access to the HTTP headers" do
522 res = Rack::Response.new
523 res["Content-Type"] = "text/yaml; charset=UTF-8"
525 res.must_include "Content-Type"
526 res.headers["Content-Type"].must_equal "text/yaml; charset=UTF-8"
527 res["Content-Type"].must_equal "text/yaml; charset=UTF-8"
528 res.content_type.must_equal "text/yaml; charset=UTF-8"
529 res.media_type.must_equal "text/yaml"
530 res.media_type_params.must_equal "charset" => "UTF-8"
531 res.content_length.must_be_nil
532 res.location.must_be_nil
535 it "does not add or change Content-Length when #finish()ing" do
536 res = Rack::Response.new
539 res.headers["Content-Length"].must_be_nil
541 res = Rack::Response.new
543 res.headers["Content-Length"] = "10"
545 res.headers["Content-Length"].must_equal "10"
548 it "updates Content-Length when body appended to using #write" do
549 res = Rack::Response.new
551 res.headers["Content-Length"].must_be_nil
553 res.headers["Content-Length"].must_equal "2"
555 res.headers["Content-Length"].must_equal "8"
558 it "does not wrap body" do
560 res = Rack::Response.new(body)
562 # It was passed through unchanged:
563 res.finish.last.must_equal body
566 it "does wraps body when using #write" do
568 res = Rack::Response.new(body)
570 # Write something using the response object:
573 # The original body was not modified:
574 body.must_equal ["Foo"]
576 # But a new buffered body was created:
577 res.finish.last.must_equal ["Foo", "Bar"]
580 it "calls close on #body" do
581 res = Rack::Response.new
582 res.body = StringIO.new
584 res.body.must_be :closed?
587 it "calls close on #body when 204 or 304" do
588 res = Rack::Response.new
589 res.body = StringIO.new
591 res.body.wont_be :closed?
595 res.body.must_be :closed?
596 b.wont_equal res.body
598 res.body = StringIO.new
601 res.body.must_be :closed?
602 b.wont_equal res.body
605 it "doesn't call close on #body when 205" do
606 res = Rack::Response.new
608 res.body = StringIO.new
611 res.body.wont_be :closed?
614 it "flatten doesn't cause infinite loop" do
615 # https://github.com/rack/rack/issues/419
616 res = Rack::Response.new("Hello World")
618 res.finish.flatten.must_be_kind_of(Array)
621 it "should specify not to cache content" do
622 response = Rack::Response.new
624 response.cache!(1000)
625 response.do_not_cache!
627 expect(response['cache-control']).must_equal "no-cache, must-revalidate"
629 expires_header = Time.parse(response['expires'])
630 expect(expires_header).must_be :<=, Time.now
633 it "should specify to cache content" do
634 response = Rack::Response.new
637 expires = Time.now + 100 # At least this far into the future
638 response.cache!(duration)
640 expect(response['cache-control']).must_equal "public, max-age=120"
642 expires_header = Time.parse(response['expires'])
643 expect(expires_header).must_be :>=, expires
647 describe Rack::Response, 'headers' do
649 @response = Rack::Response.new([], 200, { 'foo' => '1' })
653 lambda { @response.has_header? nil }.must_raise ArgumentError
655 @response.has_header?('Foo').must_equal true
656 @response.has_header?('foo').must_equal true
660 lambda { @response.get_header nil }.must_raise ArgumentError
662 @response.get_header('Foo').must_equal '1'
663 @response.get_header('foo').must_equal '1'
667 lambda { @response.set_header nil, '1' }.must_raise ArgumentError
669 @response.set_header('Foo', '2').must_equal '2'
670 @response.has_header?('Foo').must_equal true
671 @response.get_header('Foo').must_equal('2')
673 @response.set_header('Foo', nil).must_be_nil
674 @response.has_header?('Foo').must_equal true
675 @response.get_header('Foo').must_be_nil
679 lambda { @response.add_header nil, '1' }.must_raise ArgumentError
681 # Add a value to an existing header
682 @response.add_header('Foo', '2').must_equal ["1", "2"]
683 @response.get_header('Foo').must_equal ["1", "2"]
685 # Add nil to an existing header
686 @response.add_header('Foo', nil).must_equal ["1", "2"]
687 @response.get_header('Foo').must_equal ["1", "2"]
689 # Add nil to a nonexistent header
690 @response.add_header('Bar', nil).must_be_nil
691 @response.has_header?('Bar').must_equal false
692 @response.get_header('Bar').must_be_nil
694 # Add a value to a nonexistent header
695 @response.add_header('Bar', '1').must_equal '1'
696 @response.has_header?('Bar').must_equal true
697 @response.get_header('Bar').must_equal '1'
700 it 'delete_header' do
701 lambda { @response.delete_header nil }.must_raise ArgumentError
703 @response.delete_header('Foo').must_equal '1'
704 (!!@response.has_header?('Foo')).must_equal false
706 @response.delete_header('Foo').must_be_nil
707 @response.has_header?('Foo').must_equal false
709 @response.set_header('Foo', 1)
710 @response.delete_header('foo').must_equal 1
711 @response.has_header?('Foo').must_equal false