Repository: rack/rack (Ruby)

Issue:
MockRequest can't handle an empty cookie anymore
Executing
```ruby
Rack::MockRequest.new(->(env) { [200, { "Set-Cookie" => "" }, [""]] }).get("")
```
will fail with
> NoMethodError: undefined method `split' for nil
from /Users/foobar/.rbenv/versions/3.3.2/lib/ruby/gems/3.3.0/gems/rack-3.1.0/lib/rack/mock_response.rb:101:in `identify_cookie_attributes'

In earlier versions (<= 3.0.11) this used to work.


Discussion:
Are you able to submit a PR? Thanks :)
> Are you able to submit a PR? Thanks :)

Give me a few hours 👍 



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e6e216a1..7b1973844 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. For info on
 ### Fixed
 
 - Fix passing non-strings to `Rack::Utils.escape_html`. ([#2202](https://github.com/rack/rack/pull/2202), [@earlopain])
+- `Rack::MockResponse` gracefully handles empty cookies ([#2203](https://github.com/rack/rack/pull/2203) [@wynksaiddestroy](https://github.com/wynksaiddestroy))
 
 ### Added
 
diff --git a/lib/rack/mock_response.rb b/lib/rack/mock_response.rb
index 817f3cac1..9af8079e1 100644
--- a/lib/rack/mock_response.rb
+++ b/lib/rack/mock_response.rb
@@ -78,8 +78,8 @@ def cookie(name)
 
     def parse_cookies_from_header
       cookies = Hash.new
-      if headers.has_key? 'set-cookie'
-        set_cookie_header = headers.fetch('set-cookie')
+      set_cookie_header = headers['set-cookie']
+      if set_cookie_header && !set_cookie_header.empty?
         Array(set_cookie_header).each do |cookie|
           cookie_name, cookie_filling = cookie.split('=', 2)
           cookie_attributes = identify_cookie_attributes cookie_filling


Test patch:
diff --git a/test/spec_mock_response.rb b/test/spec_mock_response.rb
index d1397d5f7..7d30479ee 100644
--- a/test/spec_mock_response.rb
+++ b/test/spec_mock_response.rb
@@ -49,7 +49,7 @@
     response.body.must_equal body.join
   end
 
-  it "provide access to the HTTP status" do
+  it "provides access to the HTTP status" do
     res = Rack::MockRequest.new(app).get("")
     res.must_be :successful?
     res.must_be :ok?
@@ -70,7 +70,7 @@
     res.must_be :empty?
   end
 
-  it "provide access to the HTTP headers" do
+  it "provides access to the HTTP headers" do
     res = Rack::MockRequest.new(app).get("")
     res.must_include "content-type"
     res.headers["content-type"].must_equal "text/yaml"
@@ -81,7 +81,7 @@
     res.location.must_be_nil
   end
 
-  it "provide access to session cookies" do
+  it "provides access to session cookies" do
     res = Rack::MockRequest.new(app).get("")
     session_cookie = res.cookie("session_test")
     session_cookie.value[0].must_equal "session_test"
@@ -121,7 +121,7 @@
     persistent_cookie.expires.must_be :<, (Time.now + 15552000)
   end
 
-  it "provide access to secure cookies" do
+  it "provides access to secure cookies" do
     res = Rack::MockRequest.new(app).get("")
     secure_cookie = res.cookie("secure_test")
     secure_cookie.value[0].must_equal "secure_test"
@@ -137,11 +137,16 @@
     cookie.value[0].must_equal "_somebase64encodedstringwithequalsatthened="
   end
 
-  it "return nil if a non existent cookie is requested" do
+  it "returns nil if a non existent cookie is requested" do
     res = Rack::MockRequest.new(app).get("")
     res.cookie("i_dont_exist").must_be_nil
   end
 
+  it "handles an empty cookie" do
+    res = Rack::MockRequest.new(->(env) { [200, { "Set-Cookie" => "" }, [""]] }).get("")
+    res.cookie("i_dont_exist").must_be_nil
+  end
+
   it "parses multiple set-cookie headers provided as hash with array value" do
     cookie_headers = { "set-cookie" => ["array=awesome", "multiple=times"]}
     res = Rack::MockRequest.new(->(env) { [200, cookie_headers, [""]] }).get("")
@@ -151,7 +156,7 @@
     second_cookie.value[0].must_equal "times"
   end
 
-  it "provide access to the HTTP body" do
+  it "provides access to the HTTP body" do
     res = Rack::MockRequest.new(app).get("")
     res.body.must_match(/rack/)
     assert_match(res, /rack/)
@@ -160,14 +165,14 @@
     res.match('banana').must_be_nil
   end
 
-  it "provide access to the Rack errors" do
+  it "provides access to the Rack errors" do
     res = Rack::MockRequest.new(app).get("/?error=foo", lint: true)
     res.must_be :ok?
     res.errors.wont_be :empty?
     res.errors.must_include "foo"
   end
 
-  it "allow calling body.close afterwards" do
+  it "allows calling body.close afterwards" do
     # this is exactly what rack-test does
     body = StringIO.new("hi")
     res = Rack::MockResponse.new(200, {}, body)
@@ -179,7 +184,7 @@
     Rack::MockResponse.new(200, {}, [], 'e').errors.must_be_nil
   end
 
-  it "optionally make Rack errors fatal" do
+  it "optionally makes Rack errors fatal" do
     lambda {
       Rack::MockRequest.new(app).get("/?error=foo", fatal: true)
     }.must_raise Rack::MockRequest::FatalWarning