I'm having an issue in production that I can only sometimes reproduce locally.
The issue involves Rack-Cache, Redirects and the ssl_requirement plugin running
within the latest Radiant CMS. Let me lay it out for you...
Radiant is using Rack-Cache to do it's caching these days as we all know.
Here's how I have it configured:
config.middleware.use ::Radiant::Cache,
:cache_key =>
Proc.new { |request|
"http://#{request.env["HTTP_X_FORWARDED_HOST"] ||
request.host}#{request.path}".downcase
},
:entitystore => "memcached://#{CACHE_SERVER}:11211",
:metastore => "memcached://#{CACHE_SERVER}:11211",
:verbose => true,
:default_ttl => 31536000 # one year
You'll note that I'm using a custom cache_key function here. There's good
reason for it, but, I won't bore you with that now. Also notice that I've
hardcoded "http" in the cache_key (bad idea - more on that later). The second
thing that you might notice is that I'm telling Rack-Cache to basically NEVER
expire my cached pages. There's also a good reason for that, but, I won't go
into it. Continuing on...
There is a page in my application /signup/form that is rendered by a custom
Radiant extension that we've written. That extension's controller which
handles the signup/form request specifies that the form action should require
ssl like so:
ssl_required :form
What I've observed in production (sometimes) and locally (sometimes) is that
Rack-Cache is caching the 302 redirect from http://HOSTNAME/signup/form (a
dynamic request) to https://HOSTNAME/signup/form. Locally it's cached in the
other direction because the ssl_requirement plugin won't enforce SSL if the
request is deemed to be "local," i.e., local_request? returns true. The effect
of this is an infinite redirect. How come? Well, recall the bad idea of
hardcoding the "http" in the cache_key function above? Essentially what it
seems is happening is this:
1) Browser requests http://HOSTNAME/signup/form
2) ssl_requirement plugin redirects user to https://HOSTNAME/signup/form
3) Rack-Cache caches the 302 response headers in the meta under the key:
http://HOSTNAME/signup/form
4) Browser requests http://HOSTNAME/signup/form
5) Rack-Cache checks the cache using the key: http://HOSTNAME/signup/form and
finds a fresh response (cached in #3 above)
6) Rack-Cache responds with the 302 again; go back to step #1; infinite loop
ensues
So fine, I made a mistake. I should not have hardcoded the protocol in my
custom cache_key, fine. Mystery solved, right? Wrong.
What still puzzles me is why Rack-Cache is caching the 302 response for a
dynamic page. Furthermore why is it only caching it some-of-the-time and not
all-of-the-time. It seems that there is some sequence of requests after a
memcached restart (that clears the cached 302 and everything works again) that
causes it to cache the response. The one time I was able to reproduce it
locally I captured the meta it had cached from memcached. Here's what I found:
[[{"SERVER_NAME"=>"127.0.0.1",
"HTTP_ACCEPT"=>"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"HTTP_HOST"=>"127.0.0.1",
"HTTP_X_FORWARDED_HOST"=>"127.0.0.1",
"HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US;
rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5",
"REQUEST_PATH"=>"/signup/form",
"SERVER_PROTOCOL"=>"HTTP/1.1",
"HTTP_ACCEPT_LANGUAGE"=>"en-us,en;q=0.5",
"REMOTE_ADDR"=>"127.0.0.1",
"PATH_INFO"=>"/signup/form",
"SERVER_SOFTWARE"=>"Mongrel 1.1.5",
"SCRIPT_NAME"=>"",
"HTTP_VERSION"=>"HTTP/1.1",
"HTTP_X_FORWARDED_SERVER"=>"127.0.0.1",
"HTTP_X_FORWARDED_PROTO"=>"https",
"REQUEST_URI"=>"/signup/form",
"SERVER_PORT"=>"80",
"HTTP_X_FORWARDED_FOR"=>"127.0.0.1",
"HTTP_ACCEPT_CHARSET"=>"ISO-8859-1,utf-8;q=0.7,*;q=0.7",
"REQUEST_METHOD"=>"GET",
"QUERY_STRING"=>"",
"GATEWAY_INTERFACE"=>"CGI/1.2",
"HTTP_CONNECTION"=>"Keep-Alive",
"HTTP_ACCEPT_ENCODING"=>"gzip,deflate"},
{"Location"=>"http://127.0.0.1/signup/form",
"X-Content-Digest"=>"531b3a921cf45cc6ed54e6da8f19b0c12dd4900e",
"Date"=>"Wed, 18 Nov 2009 18:38:28 GMT",
"Content-Type"=>"text/html; charset=utf-8",
"X-Runtime"=>"11",
"Content-Length"=>"96",
"Set-Cookie"=>"",
"Cache-Control"=>"no-cache, s-maxage=31536000",
"X-Status"=>"302"}]]
This particular request was local so the redirection was going in the opposite
order, i.e., https => http. Notice that the Cache-Control is set to "no-cache."
It's my understanding that "no-cache" means that the response should not be
cached no matter what. However, also notice that the s-maxage (which was
recently introduced in Rack-Cache) is set to 1 year (from my configuration of
Rack-Cache above). Now, perhaps s-maxage takes precedence over no-cache, but
even if that was the case, why does it only cache the request's response only
some of the time?
Any help or insight into this problem would be much appreciated.
Thanks in advance,
Jason Fox
[email protected]
_______________________________________________
Radiant mailing list
Post: [email protected]
Search: http://radiantcms.org/mailing-list/search/
Site: http://lists.radiantcms.org/mailman/listinfo/radiant