I am currently trying to determine the cause of a nasty redirect bug in a Rails app. The specifics are documented here, although I'm not looking for a specific solution here on StackOverflow. Rather, while I am working on fixing this, I'd like to develop a generic way to catch, log, and investigate infinite redirect loops in a Rails app.
I have an idea here, but I'd still like to see if there are any tried and true techniques.
My idea:
Override Rails' redirect_to
to "log" redirects in the session:
def redirect_to(destination)
session[:redirects] << {destination: destination, timestamp: Time.now}
if is_inifinite_redirect?(session[:redirects])
render "a_redirect_error_page"
else
super
end
end
Then, have some sort of analysis of the redirects array to determine if there is an infinite loop:
def is_inifinite_redirect?(redirects)
recent = redirects.last(21) # 21 is the max redirects allowed by Chrome
return recent.odds.map(&:destination).uniq.length == 1 && \
recent.evens.map(&:destination).uniq.length == 1 && \
(recent.last.timestamp - recent.first.timestamp < 10.seconds)
end
I agree that tests should in theory prevent you running into infinite redirects loops. But I understand that tests are not the answer to your question.
I think you can consider an infinite redirect loop as soon as you have two redirects in a row with the same arguments. I think there is no good reason to wait for more redirects.
My idea is to store the arguments of the redirect into the flash
. If the arguments in the flash
are still the same when the next redirect happens then you are in a loop. If there was a redirect to another location or a normal page was rendered in between then that location would not match or the flash
would be empty:
def redirect_to(*args)
if flash[:redirected_with_args] == args
raise "Infinited redirect loop detected: #{args.inspect}"
else
flash[:redirected_with_args] = args
super
end
end
The tried and true technique is to write tests to ensure that your app works the way you expect it to. This particular problem would be easily detected by a failing controller test, and/or a failing integration test.
There's nothing wrong with adding the code you've got above in order to help you debug this particular situation (if it does), but the real solution here for a production app is to have tests so that you don't get these redirect loops.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With