[pkg-uWSGI-devel] Bug#1125369: uwsgi-plugin-ruby: Incompatibility with ruby-rack 3.x causing some http headers to format wrong

Alexandre Rossi niol at zincube.net
Tue Jan 13 13:23:37 GMT 2026


Control: reassign -1 src:uwsgi
Control: affects -1 uwsgi-plugin-ruby
Control: tag -1 patch
Control: forwarded -1 https://github.com/unbit/uwsgi/pull/2749

Hi,

> I've been running Redmine through uwsgi (better socket activation than
> Passenger) and noticed that some of the cookies being sent to the browser
> were wrong.
> I'd expect a header like:
>
>     Set-Cookie: autologin=[secret];secure
>
> Instead I got a header like:
>
>     Set-Cookie: ["autologin=[secret];secure"]
>
> > 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.
>
>
> I've worked around this for my own purposes by adapting the code block
> suggested by 'pcantrell' in the above mentioned Passenger bug report:
> https://github.com/phusion/passenger/issues/2503#issuecomment-2370192659
> But as I understand it, this issue should be solved at the uwsgi/rack
> layer, not in the ruby application.

Thanks for the detailed bug report and analysis.

Here is a possible fix to this, can you confirm? Or do you need guidance
to rebuild the rack plugin?

Thanks,

Alex


diff --git i/plugins/rack/rack_plugin.c w/plugins/rack/rack_plugin.c
index 792cc4dc..c00ad022 100644
--- i/plugins/rack/rack_plugin.c
+++ w/plugins/rack/rack_plugin.c
@@ -739,14 +739,19 @@ VALUE send_header(VALUE obj, VALUE headers, int argc, const VALUE *argv, VALUE b
 
 	struct wsgi_request *wsgi_req = current_wsgi_req();
 
-	VALUE hkey, hval;
+	VALUE hkey, hval, hval_raw;
 	
 	//uwsgi_log("HEADERS %d\n", TYPE(obj));
 	if (TYPE(obj) == T_ARRAY) {
 		if (RARRAY_LEN(obj) >= 2) {
-			hkey = rb_obj_as_string( RARRAY_PTR(obj)[0]);
-			hval = rb_obj_as_string( RARRAY_PTR(obj)[1]);
-
+			hkey = rb_obj_as_string(RARRAY_PTR(obj)[0]);
+			hval_raw = RARRAY_PTR(obj)[1];
+			if (TYPE(hval_raw) == T_ARRAY) {
+				hval = rb_funcall(hval_raw, rb_intern("join"), 1, rb_str_new_static(", ", 2));
+			}
+			else {
+				hval = rb_obj_as_string( hval_raw);
+			}
 		}
 		else {
 			goto clear;
diff --git i/t/rack/app.ru w/t/rack/app.ru
index 4603375e..9344c4bc 100644
--- i/t/rack/app.ru
+++ w/t/rack/app.ru
@@ -1,7 +1,10 @@
 class App
 
-  def call(environ)
-    [200, {"content-type" => "text/plain"}, ['Hello']]
+  def call(env)
+    headers = {"content-type" => "text/plain"}
+    Rack::Utils.set_cookie_header!(headers, "country", { :value => "UK", :path => "/"})
+    Rack::Utils.set_cookie_header!(headers, "autologin", { :value => "yes", :path => "/", :secure => true})
+    [200, headers, ["Hello"]]
   end
 
 end
diff --git i/t/runner w/t/runner
index b59966b5..c9957959 100755
--- i/t/runner
+++ w/t/runner
@@ -251,8 +251,14 @@ class UwsgiTest(unittest.TestCase):
             ]
         )
 
-        self.assert_GET_body("/", "Hello")
+        with requests.get(f"http://{UWSGI_HTTP}/") as r:
+            self.assertEqual(r.text, "Hello")
+            self.assertEqual(r.headers["Content-type"], "text/plain")
+            self.assertEqual(
+                r.headers["Set-Cookie"],
+                "country=UK; path=/, autologin=yes; path=/; secure",
+            )
 
 
 if __name__ == "__main__":
-    unittest.main()
+    unittest.main(verbosity=2)



More information about the pkg-uWSGI-devel mailing list