Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically detect and catch infinite redirect loops in Rails

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
like image 443
user94154 Avatar asked Mar 19 '15 23:03

user94154


2 Answers

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
like image 179
spickermann Avatar answered Oct 24 '22 07:10

spickermann


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.

like image 43
smathy Avatar answered Oct 24 '22 08:10

smathy