Sunday, July 19, 2015

Infinite loop in Filter after sendRedirect()

My intention is to redirect to another site whenever the User-Agent has detected the mobile keyword. I have a solution for this approach where I have the file name for mobile version start with m and reside at the location same as desktop version. I try not to branch out another path for mobile version to keep maintenance easy.

Unfortunately the Filter call was unintentionally looping on itself. This seems to me that for every new redirection Filter call, the mobile keyword validation will get invoked and the new redirection is sent again. And this process will go on endlessly. This is so not good, I was trapped!

This is what happened on my code:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
   
   httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()));
  }
  else {  
   chain.doFilter(request, response);
  }
According to the advice from expert. I'm going to need additional validation to stop the process after I reach to that URI. Thus, what I need to do is to put additional validation as below:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {

   // stop redirect if the site has reach
   if( httpServletRequest.getRequestURI().equalsIgnoreCase(httpServletRequest.getContextPath() + "/" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()))) {
    chain.doFilter(request, response);
   }
   else {
    httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length()));
   }
  }
  else {  
   chain.doFilter(request, response);
  }
Tested and worked as expected. I did also tried to forward the request as shown below:
 @Override
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  HttpServletResponse httpServletResponse = (HttpServletResponse) response;

  if( httpServletRequest.getHeader("User-Agent").indexOf("Mobile") != -1 ) {
   
   request.getRequestDispatcher("m" + httpServletRequest.getRequestURI().substring(httpServletRequest.getRequestURI().lastIndexOf("/")+1, httpServletRequest.getRequestURI().length())).forward(request, response);
  }
  else {  
   chain.doFilter(request, response);
  }
I find this working as well but for subsequent call on doFilter, the httpServletRequest.getRequestURI() will always stuck on the old URI. I think this is due to the reason URL is forward on server site, but not reflected on client site.

No comments: