Sunday, September 8, 2024
HometomcatOnce Tomcat failed to correctly return the redirection 302 status code and...

Once Tomcat failed to correctly return the redirection 302 status code and always returned a 200 error (1)

[A few long words]

At first, people didn’t care what status code Tomcat returned, because the correct status code must be 200, and occasionally there would be some 404, 500. After walking a lot on the road of development, they got used to it being smooth, until the “forwarded” Demand comes.

**Notice! This article is just a description of a status code problem caused by a configuration error. Because the incident happened strangely, it is not a common solution to the problem. It is recommended that netizens only do it for reference and give priority to searching other professional articles. **

【origin】

In the project, I designed a SpringMVC-like architecture that supports FORWARD forwarding and REDIRECT redirection (yes, that’s what I think). Since there has never been a redirection scenario in the requirements, I have not tried it. Redirection function, until this time I designed a PDF browsing interface. In order to allow the browser to simplify the path and facilitate the display of file names during downloading, I tried to use redirection to solve it.

The meaning of redirection is that after the interface that the browser accesses for the first time is recognized, because the address path needs to change, the browser is notified to re-send a request to another address. This article is aimed at solving specific problems, not a tutorial, and will not be explained in detail for the time being.

【Phenomenon】

The code for Java redirection is really not difficult, just one sentence:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //...omission...
        response.sendRedirect(view.getUrl());  //hereview.getUrlJust a bunch of addresses,can behttpbeginning
    } 

In other words, you only need to return this sentence to the client browser, and the browser will automatically jump to another address. Familiar netizens must know that the essence of this sendRedirect is to reply a 302 status code to the browser, and at the same time add an address of the Header parameter Location. I think so too, but the result is that the browser or PostMan side always displays the 200 status code. Actual measurement As shown in the picture below, the status code that should have been 302 was received instead of 200.

[Solution process]

  1. The first reaction was that the code was wrong, so I searched a lot on the Internet and read some foreign language materials. Occasionally, someone encountered this, but the reply was nothing more than the sendRedirect above.

  2. This is interesting. Is there something wrong with the framework I wrote? My framework uses listener, filter and other configurations. According to the information, the execution path of tomcat should be listener—->Filter- —>Servlet (DoGet, DoPost), and return it to the browser step by step after execution. Since the previous response.sendRedirect is executed in the Servlet, it is possible that after execution, the status code inside the response is changed by the Filter. So I checked the Filter code (omitting irrelevant code):

public class HTMLFilter implements Filter{
    String NO_LOGIN_NO = "User is not logged in";
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException{

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpSession session = httpRequest.getSession(false);//Used herefalse,when does not existsessionwill not be created when
        // Get what the user requestedURI
        String request_uri = httpRequest.getRequestURI();
        // getwebApplication context path
        String ctxPath = httpRequest.getContextPath();
        // Remove context path,Get the rest of the path
        String uri = request_uri.substring(ctxPath.length());
        //System.out.println("accessuri="+uri);
        if (uri.equalsIgnoreCase("/index.jsp") || uri.equalsIgnoreCase("/DownloadServlet"))   
        {
            chain.doFilter(request, response);
            //httpResponse.setStatus(302);
            //httpResponse.sendRedirect("www.taobao.com");


        }



    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {
    }
}

In order to reduce the difficulty of troubleshooting, a lot of business codes have been deleted. In fact, there is only one sentence in essence:

chain.doFilter(request, response);

The original meaning of this sentence should be to execute certain filtering conditions. What I wrote above is equivalent to no filtering. Basically, the original object is passed to the Servlet. So the configuration of Filter in the web.xml file was optimized and deleted, but the problem remained the same.

  1. Admit that I am not good enough, maybe there is something wrong with the framework I wrote. Think about writing the simplest Servlet Helloworld, so I actually wrote one, which is also a sentence response.sendRedirect (“http://www.baidu.com”); Still not working.

  2. Is it a configuration problem of web.xml? I changed it over and over again, even doubting whether it was The version problem was not solved by changing the default 3.1 to 4.

  3. Is there a problem with java? I admitted that I couldn't figure it out, so I asked a young colleague to try whether response.sendRedirect could be redirected normally. After testing it in his environment, he said it was OK. Alas, I couldn't stand it anymore. Pretending to be thoughtful, I asked him to give me his module code…oh no! Package the entire tomcat for me! (Otherwise, if you still don’t solve the problem, can you still have the audacity to ask your young colleagues for the code again?)

  4. After many twists and turns, at least now I have a direction. It is expected that the problem is Tomcat's environment configuration, so I started with server.xml. Very good. Most of the configurations of new colleagues are default parameters, which reduces the difficulty of troubleshooting. I started changing the port, SSL, and project parameters one by one, and finally saw a different sentence on the last node:

The standard configuration is roughly as follows:

<Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
</Host>

And my configuration:

<Host name="localhost"  appBase="webapps"
            unpackWARs="false" autoDeploy="true" errorReportValveClass="com.xxxxx.errorPageFilter.CustomErrorServlet">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
        <!-- Here is the replacementtomcatDefault error class,So as to achieve the effect of replacing the page -->
        <Valve className="com.xxxxx.errorPageFilter.CustomErrorServlet" showReport="false" showServerInfo="false" />
</Host>

The difference is that there are two parameters, one is the value of unpackWARs, and the other is an additional error interception class of CustomErrorServlet, so I modified and deleted these two parameters respectively, and finally saw the browser jump.

【Cause Analysis】

In fact, the problem lies in this error interception class. Once I found that if the browser deliberately accesses a non-existent project, Tomcat will return 404 and other default interfaces, and the version number of Tomcat will appear. It is a small There was a small security risk, so I asked another young colleague to write an identification plug-in, which is the com.xxxxx.errorPageFilter.CustomErrorServlet. This is essentially a custom error prompt page, which is innocuous, so it was ignored:

public class CustomErrorServlet extends ErrorReportValve {
     @Override
     protected void report(Request request, Response response, Throwable t) {
         response.setContentType("text/html");
         response.setCharacterEncoding("utf-8");
         response.setStatus(200);
         PrintWriter writer;
            try {
                writer = response.getReporter();
                  if (writer != null) {
                        writer.write("Page does not exist,what do you want to do?");
                      }
            } catch (IOException e) {
                e.printStackTrace();
            }
    }

}

The problem lies in this sentence of this class:

response.setStatus(200);

In order not to display the default error page, such a violent response status code was used? So, this is where the browser’s 200 status code comes from.

It turns out that Tomcat or Java considers the 302 code corresponding to response.sendRedirect redirection as an error code, and also executes this error class plug-in. It is indeed a knowledge blind spot, so I had to ask my colleagues to change this class again and ignore the code before 400. All status codes (in line with the unspoken rule of “everything goes as it goes”, he developed it and he solved it (◔‸◔)).

At this point, this strange and embarrassing problem was solved. Calculating the total time spent, it was almost more than 2 days. I hope it helps netizens who are also confused, so let’s call it a day.

This blog is original content, please indicate this blog information when reprinting, thank you!

RELATED ARTICLES

Most Popular

Recent Comments