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)

