Apache HttpClient中循环重定向的bug详解

作者:袖梨 2022-06-25

在Apache HttpClient 4.0里有个循环重定向的bug,当服务器试图正常的从域名A跳转到B的时候就会发生(我是在 111com.net 跳 www.111com.net 的时候发现的),换过最新版也发现还是有这个问题。

跟踪了DefaultRequestDirector.java之后发现,它在内部创建了一个HttpRedirect对象来处理重定向,这个对象会重用你之前的HttpGet对象里的HTTP 头,导致这个bug的根源在于,连Host头也用了,这表示重定向到的新域名如果和老域名如果是同一组集群,那么服务器会试图重新发一个重定向请求,这样就造成了HttpClient抛出CircularRedirectException。

我是派生了DefaultRequestDirector类来改掉这个问题,代码:

 代码如下 复制代码

public class RedirectRequestDirector extends DefaultRequestDirector
{
    RedirectRequestDirector(
            final HttpRequestExecutor requestExec,
            final ClientConnectionManager conman,
            final ConnectionReuseStrategy reustrat,
            final ConnectionKeepAliveStrategy kastrat,
            final HttpRoutePlanner rouplan,
            final HttpProcessor httpProcessor,
            final HttpRequestRetryHandler retryHandler,
            final RedirectHandler redirectHandler,
            final AuthenticationHandler targetAuthHandler,
            final AuthenticationHandler proxyAuthHandler,
            final UserTokenHandler userTokenHandler,
            final HttpParams params)
    {
        super(requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler, redirectHandler, targetAuthHandler, proxyAuthHandler, userTokenHandler, params);

    }
    @Override
    protected RoutedRequest handleResponse(RoutedRequest roureq,
            HttpResponse response,
            HttpContext context)
                    throws HttpException, IOException
    {
        RoutedRequest req = super.handleResponse(roureq, response, context);
        if(req != null)
        {
            String redirectTarget = req.getRoute().getTargetHost().getHostName();
            req.getRequest().getOriginal().setHeader("Host", redirectTarget);
        }
        return req;
    }

}

这里修改了Host头为试图重定向的主机名。
还需要修改 DefaultHttpClient:

 代码如下 复制代码

public class RedirectHttpClient extends DefaultHttpClient
{
    @Override
    protected RequestDirector createClientRequestDirector(
            final HttpRequestExecutor requestExec,
            final ClientConnectionManager conman,
            final ConnectionReuseStrategy reustrat,
            final ConnectionKeepAliveStrategy kastrat,
            final HttpRoutePlanner rouplan,
            final HttpProcessor httpProcessor,
            final HttpRequestRetryHandler retryHandler,
            final RedirectHandler redirectHandler,
            final AuthenticationHandler targetAuthHandler,
            final AuthenticationHandler proxyAuthHandler,
            final UserTokenHandler stateHandler,
            final HttpParams params) {
        return new RedirectRequestDirector(
                requestExec,
                conman,
                reustrat,
                kastrat,
                rouplan,
                httpProcessor,
                retryHandler,
                redirectHandler,
                targetAuthHandler,
                proxyAuthHandler,
                stateHandler,
                params);
    }
}

现在就能正常处理跳转了。

相关文章

精彩推荐