tomcat源码—redirect的底层实现

  1. sendRedirect(客户端作的重定向)

该方法通过修改HTTP协议的Header部分,对浏览器下达重定向指令,让浏览器对在location中指定的URL提出请求,使浏览器重定向网页的内容。

public void sendRedirect(String location) 
        throws IOException {

        if (isCommitted())
            throw new IllegalStateException
                (sm.getString("coyoteResponse.sendRedirect.ise"));

        // Ignore any call from an included servlet
        if (included)
            return; 

        // Clear any data content that has been buffered
        resetBuffer();

        // Generate a temporary redirect to the specified location
        try {
            String absolute = toAbsolute(location);
            setStatus(SC_FOUND);
            setHeader("Location", absolute);
        } catch (IllegalArgumentException e) {
            setStatus(SC_NOT_FOUND);
        }

        // Cause the response to be finished (from the application perspective)
        setSuspended(true);
}

该方法可以接受绝对的或者相对的URL。

如果传递到该方法的参数是一个相对的URL,那么web container在将它发送到客户端前会把它转换成一个绝对的URL。

    protected String toAbsolute(String location) {
        boolean leadingSlash = location.startsWith("/");
        if (location.startsWith("//")) {

            String scheme = request.getScheme();
            try {
                redirectURLCC.append(scheme, 0, scheme.length());
                redirectURLCC.append(':');
                redirectURLCC.append(location, 0, location.length());
                return redirectURLCC.toString();
            } catch (IOException e) {
。。。。
            }

        } else if (leadingSlash || !hasScheme(location)) {

            redirectURLCC.recycle();

            String scheme = request.getScheme();
            String name = request.getServerName();
            int port = request.getServerPort();

            try {
                redirectURLCC.append(scheme, 0, scheme.length());
                redirectURLCC.append("://", 0, 3);
                redirectURLCC.append(name, 0, name.length());
                if ((scheme.equals("http") && port != 80)
                    || (scheme.equals("https") && port != 443)) {
                    redirectURLCC.append(':');
                    String portS = port + "";
                    redirectURLCC.append(portS, 0, portS.length());
                }
                if (!leadingSlash) {
                    String relativePath = request.getDecodedRequestURI();
                    int pos = relativePath.lastIndexOf('/');
                    CharChunk encodedURI = null;
                    final String frelativePath = relativePath;
                    final int fend = pos;
                    if (SecurityUtil.isPackageProtectionEnabled() ){
                        try{
                            encodedURI = AccessController.doPrivileged(
                                new PrivilegedExceptionAction<CharChunk>(){
                                    @Override
                                    public CharChunk run() throws IOException{
                                        return urlEncoder.encodeURL(frelativePath, 0, fend);
                                    }
                           });
                        } catch (PrivilegedActionException pae){
。。。。。。
                        }
                    } else {
                        encodedURI = urlEncoder.encodeURL(relativePath, 0, pos);
                    }
                    redirectURLCC.append(encodedURI);
                    encodedURI.recycle();
                    redirectURLCC.append('/');
                }
                redirectURLCC.append(location, 0, location.length());
                normalize(redirectURLCC);
            } catch (IOException e) {
。。。。。
            }
            return redirectURLCC.toString();
        } else {
            return (location);
        }
    }

 getServerPort(), getLocalPort(), and getRemotePort()的具体含义

 getLocalPort():获取的是应用服务器的端口,即该应用的实际端口,无论请求经过了多少代理,转发,getLocalPort只取最后的端口,也就是应用的端口
getServerPort():获取的是URL请求的端口,比如你的请求时127.0.0.1:8080,应用服务器的端口是80,那么getServerPort得到的端口是8080。而getLocalPort得到的是80。

getRemotePort():发出请求的客户端的端口号。

端口映射问题

外网90端口,在内网80端口,内网的nginx配置代理端口,后端服务器中request.getServerPort()无法获得正确的端口,返回的仍然是80,在response.sendRedirect()时,客户端可能无法获得正确的重定向url。解决方式:修改nginx配置。

在使用response.sendRedirect时,前面不能有HTML被送到浏览器。事实上现在的server都有cache机制,一般在8k(jsp server),这意味着除非关闭cache,或者使用了out.flush()强制刷新,那么在使用sendRedirect之前,可以允许有少量HTML输出。。在response.sendRedirect()之后应该紧跟一句return;因为转向后的输出可能导致转向失败。

2.forward (服务器端作的重定向)

服务器往client发送数据的过程为:服务器在向客户端发送数据之前,是先将数据输出到缓冲区,然后将缓冲区中数据发送给client端。

猜你喜欢

转载自blog.csdn.net/weixin_41701609/article/details/82981299