<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>~iany/ Session</title><link>https://blog.iany.me/tags/session/</link><description>Recent content in Session «~iany/»</description><language>en-US</language><managingEditor>me@iany.me (Ian Yang)</managingEditor><webMaster>me@iany.me (Ian Yang)</webMaster><copyright>CC-BY-SA 4.0</copyright><lastBuildDate>Sun, 28 Apr 2013 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iany.me/tags/session/index.xml" rel="self" type="application/rss+xml"/><item><title>How Rails Assets Prefix Disables the Session</title><link>https://blog.iany.me/2013/04/how-rails-assets-prefix-disables-the-session/</link><pubDate>Sun, 28 Apr 2013 00:00:00 +0000</pubDate><author>me@iany.me (Ian Yang)</author><guid>https://blog.iany.me/2013/04/how-rails-assets-prefix-disables-the-session/</guid><description>&lt;p&gt;This is original posted on
&lt;a href="https://web.archive.org/web/20151009055627/http://www.intridea.com/blog/2013/3/20/rails-assets-prefix-may-disable-your-session"&gt;intridea blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I recently worked in a Rails project with &lt;a href="https://twitter.com/sporkd"&gt;Peter (@sporkd)&lt;/a&gt;. The
project is intended to be used as a sub-site, and should be served under
sub-URI. After google, we ended up by setting &lt;code&gt;config.assets.prefix&lt;/code&gt; and
wrapped all routes in &lt;code&gt;scope&lt;/code&gt;. The solution is simple and worked well. But
soon, some weird bugs were found, and Peter was successfully isolated the
problem to session (see demo
&lt;a href="https://github.com/sporkd/asset_prefix_test"&gt;sporkd/asset_prefix_test&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;After several hours debugging, I finally found out the cause. To make a long
story short, the routes configured in &lt;code&gt;routes.rb&lt;/code&gt; should not start with
&lt;code&gt;config.assets.prefix&lt;/code&gt;, otherwise session save is skipped. The demo
&lt;code&gt;sporkd/asset_prefix_test&lt;/code&gt; can be fixed by simply setting
&lt;code&gt;config.assets.prefix&lt;/code&gt; to &lt;code&gt;/one/assets&lt;/code&gt;. You also get a bonus by setting a
unique prefix for assets, since it is easy to add expiration header for assets
in web server.&lt;/p&gt;
&lt;h2 id="x-cascade-header-in-rails"&gt;X-Cascade Header in Rails&lt;/h2&gt;
&lt;p&gt;I never knew &lt;code&gt;X-Cascade&lt;/code&gt; header in Rails before. @soulcutter has a
&lt;a href="http://teambandb.typepad.com/soultech/2011/10/x-cascade-header-in-rails.html"&gt;post&lt;/a&gt; that described its usage.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The basic idea is this: if you return a response from a controller with the
X-Cascade header set to &amp;ldquo;pass&amp;rdquo;, it indicates that your controller thinks
something else should handle the request. So rails (or is it rack? in any
case&amp;hellip;) will continue down your routes looking for the next rule that
matches the request.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Indeed, &lt;code&gt;X-Cascade&lt;/code&gt; is not only restricted in controller, if a mounted engine
sets this header, Rails also continues down the routes searching.&lt;/p&gt;
&lt;p&gt;It is a feature of Rails. Since 3.2, Rails has moved the routes logic to
&lt;a href="https://github.com/rails/journey"&gt;journey&lt;/a&gt;. The &lt;code&gt;X-Cascade&lt;/code&gt; trick can be found in
&lt;a href="https://github.com/rails/journey/blob/master/lib/journey/router.rb#L69"&gt;journey/router.rb#L69&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pay attention that, the rack &lt;code&gt;env&lt;/code&gt; object is shared when request is passed
on. So if &lt;code&gt;env&lt;/code&gt; is changed by former route, the latter one is affected. This
is the root cause of the weird session issue, because session is controlled by
&lt;code&gt;env['rack.session.options']&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="sprockets-who-skips-the-session"&gt;Sprockets, who skips the session&lt;/h2&gt;
&lt;p&gt;Sprockets, the gem for rails assets pipeline, mounts itself on
&lt;code&gt;config.assets.prefix&lt;/code&gt; and &lt;a href="https://github.com/rails/rails/blob/3-2-stable/actionpack/lib/sprockets/bootstrap.rb#L27"&gt;prepends&lt;/a&gt;
the route to Rails. So if user accesses a page which path starts with
&lt;code&gt;config.assets.prefix&lt;/code&gt;, sprockets always processes the request first.&lt;/p&gt;
&lt;p&gt;Maybe for performance, sprockets disables session save by changing
&lt;code&gt;env['rack.session.options']&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;env['rack.session.options'] ||= {}
env['rack.session.options'][:defer] = true
env['rack.session.options'][:skip] = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The options are changed even when asset is not found. If so, sprockets
returns 404 and sets the header &lt;code&gt;X-Cascade&lt;/code&gt;. Then Rails passes the request to
controller, and correct page is rendered as expected. However, since the
session is already disabled by sprockets, the changed session in controller is
never saved.&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;env&lt;/code&gt; is a shared resource between routes when &lt;code&gt;X-Cascade&lt;/code&gt; is set, it
should not be changed unless it has to. When asset is not found, sprockets
should just pass though without touching &lt;code&gt;env&lt;/code&gt;, so I submit a
&lt;a href="https://github.com/sstephenson/sprockets/pull/421"&gt;PR&lt;/a&gt; for it.&lt;/p&gt;
&lt;h2 id="how-we-debug"&gt;How we Debug&lt;/h2&gt;
&lt;p&gt;Peter and I worked in different time zones. He first found the session issue
because several features related to session did not work. He made the demo
&lt;code&gt;sporkd/asset_prefix_test&lt;/code&gt; to isolate the issue using minimum code at the end
of the day in his time zone and left me the message.&lt;/p&gt;
&lt;p&gt;When my day started, I got the message and started debugging on session based on
the demo in
&lt;a href="https://github.com/doitian/asset_prefix_test/compare/asset-prefix-one-deep"&gt;doitian/asset_prefix_test&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Because session store class is customizable, I inherited one from default
cookie store and added breakpoints using &lt;a href="https://github.com/pry/pry"&gt;pry&lt;/a&gt;. Soon I found out that
&lt;code&gt;options[:skip]&lt;/code&gt; was &lt;code&gt;true&lt;/code&gt;, but I had no idea where it was set to
&lt;code&gt;true&lt;/code&gt;. Then I did a grep (using &lt;a href="https://github.com/ggreer/the_silver_searcher"&gt;ag&lt;/a&gt;) in all gems, and fortunately, only
sprockets has set this option to &lt;code&gt;true&lt;/code&gt;. The remaining work was just figuring
out why sprockets is invoked before controller action.&lt;/p&gt;</description><category domain="https://blog.iany.me/post/">Posts</category><category domain="https://blog.iany.me/tags/rails/">Rails</category><category domain="https://blog.iany.me/tags/session/">Session</category></item></channel></rss>