user94154
user94154

Reputation: 16564

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

Upvotes: 12

Views: 1823

Answers (2)

spickermann
spickermann

Reputation: 106882

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

Upvotes: 7

smathy
smathy

Reputation: 27961

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.

Upvotes: 3

Related Questions