HttpClient的RedirectStrategy重定向處理核心機(jī)制
序
本文主要研究一下HttpClient的RedirectStrategy
RedirectStrategy
org/apache/http/client/RedirectStrategy.java
public interface RedirectStrategy {
/**
* Determines if a request should be redirected to a new location
* given the response from the target server.
*
* @param request the executed request
* @param response the response received from the target server
* @param context the context for the request execution
*
* @return {@code true} if the request should be redirected, {@code false}
* otherwise
*/
boolean isRedirected(
HttpRequest request,
HttpResponse response,
HttpContext context) throws ProtocolException;
/**
* Determines the redirect location given the response from the target
* server and the current request execution context and generates a new
* request to be sent to the location.
*
* @param request the executed request
* @param response the response received from the target server
* @param context the context for the request execution
*
* @return redirected request
*/
HttpUriRequest getRedirect(
HttpRequest request,
HttpResponse response,
HttpContext context) throws ProtocolException;
}RedirectStrategy接口定義了isRedirected方法用于判斷是否需要redirect,還定義了getRedirect方法用于返回redirect的目標(biāo)地址
DefaultRedirectStrategy
org/apache/http/impl/client/DefaultRedirectStrategy.java
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class DefaultRedirectStrategy implements RedirectStrategy {
private final Log log = LogFactory.getLog(getClass());
/**
* @deprecated (4.3) use {@link org.apache.http.client.protocol.HttpClientContext#REDIRECT_LOCATIONS}.
*/
@Deprecated
public static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
public static final DefaultRedirectStrategy INSTANCE = new DefaultRedirectStrategy();
private final String[] redirectMethods;
public DefaultRedirectStrategy() {
this(new String[] {
HttpGet.METHOD_NAME,
HttpHead.METHOD_NAME
});
}
/**
* Constructs a new instance to redirect the given HTTP methods.
*
* @param redirectMethods The methods to redirect.
* @since 4.5.10
*/
public DefaultRedirectStrategy(final String[] redirectMethods) {
super();
final String[] tmp = redirectMethods.clone();
Arrays.sort(tmp);
this.redirectMethods = tmp;
}
@Override
public boolean isRedirected(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
Args.notNull(request, "HTTP request");
Args.notNull(response, "HTTP response");
final int statusCode = response.getStatusLine().getStatusCode();
final String method = request.getRequestLine().getMethod();
final Header locationHeader = response.getFirstHeader("location");
switch (statusCode) {
case HttpStatus.SC_MOVED_TEMPORARILY:
return isRedirectable(method) && locationHeader != null;
case HttpStatus.SC_MOVED_PERMANENTLY:
case HttpStatus.SC_TEMPORARY_REDIRECT:
return isRedirectable(method);
case HttpStatus.SC_SEE_OTHER:
return true;
default:
return false;
} //end of switch
}
/**
* @since 4.2
*/
protected boolean isRedirectable(final String method) {
return Arrays.binarySearch(redirectMethods, method) >= 0;
}
@Override
public HttpUriRequest getRedirect(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws ProtocolException {
final URI uri = getLocationURI(request, response, context);
final String method = request.getRequestLine().getMethod();
if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) {
return new HttpHead(uri);
} else if (method.equalsIgnoreCase(HttpGet.METHOD_NAME)) {
return new HttpGet(uri);
} else {
final int status = response.getStatusLine().getStatusCode();
return status == HttpStatus.SC_TEMPORARY_REDIRECT
? RequestBuilder.copy(request).setUri(uri).build()
: new HttpGet(uri);
}
}
}DefaultRedirectStrategy實現(xiàn)了RedirectStrategy接口,它定義了redirectMethods,默認(rèn)是Get和Head;isRedirected方法先獲取response的statusCode,對于302需要location的header有值且請求method在redirectMethods中(isRedirectable),對于301及307僅僅判斷isRedirectable,對于303返回true,其余的返回false
getRedirect方法先通過getLocationURI獲取目標(biāo)地址,然后針對get或者h(yuǎn)ead分別構(gòu)造HttpHead及HttpGet,剩下的根據(jù)statusCode判斷,是307則拷貝原來的request,否則返回HttpGet
RedirectExec
org/apache/http/impl/execchain/RedirectExec.java
/**
* Request executor in the request execution chain that is responsible
* for handling of request redirects.
* <p>
* Further responsibilities such as communication with the opposite
* endpoint is delegated to the next executor in the request execution
* chain.
* </p>
*
* @since 4.3
*/
@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
public class RedirectExec implements ClientExecChain {
private final Log log = LogFactory.getLog(getClass());
private final ClientExecChain requestExecutor;
private final RedirectStrategy redirectStrategy;
private final HttpRoutePlanner routePlanner;
public RedirectExec(
final ClientExecChain requestExecutor,
final HttpRoutePlanner routePlanner,
final RedirectStrategy redirectStrategy) {
super();
Args.notNull(requestExecutor, "HTTP client request executor");
Args.notNull(routePlanner, "HTTP route planner");
Args.notNull(redirectStrategy, "HTTP redirect strategy");
this.requestExecutor = requestExecutor;
this.routePlanner = routePlanner;
this.redirectStrategy = redirectStrategy;
}
@Override
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
Args.notNull(route, "HTTP route");
Args.notNull(request, "HTTP request");
Args.notNull(context, "HTTP context");
final List<URI> redirectLocations = context.getRedirectLocations();
if (redirectLocations != null) {
redirectLocations.clear();
}
final RequestConfig config = context.getRequestConfig();
final int maxRedirects = config.getMaxRedirects() > 0 ? config.getMaxRedirects() : 50;
HttpRoute currentRoute = route;
HttpRequestWrapper currentRequest = request;
for (int redirectCount = 0;;) {
final CloseableHttpResponse response = requestExecutor.execute(
currentRoute, currentRequest, context, execAware);
try {
if (config.isRedirectsEnabled() &&
this.redirectStrategy.isRedirected(currentRequest.getOriginal(), response, context)) {
if (redirectCount >= maxRedirects) {
throw new RedirectException("Maximum redirects ("+ maxRedirects + ") exceeded");
}
redirectCount++;
final HttpRequest redirect = this.redirectStrategy.getRedirect(
currentRequest.getOriginal(), response, context);
if (!redirect.headerIterator().hasNext()) {
final HttpRequest original = request.getOriginal();
redirect.setHeaders(original.getAllHeaders());
}
currentRequest = HttpRequestWrapper.wrap(redirect);
if (currentRequest instanceof HttpEntityEnclosingRequest) {
RequestEntityProxy.enhance((HttpEntityEnclosingRequest) currentRequest);
}
final URI uri = currentRequest.getURI();
final HttpHost newTarget = URIUtils.extractHost(uri);
if (newTarget == null) {
throw new ProtocolException("Redirect URI does not specify a valid host name: " +
uri);
}
// Reset virtual host and auth states if redirecting to another host
if (!currentRoute.getTargetHost().equals(newTarget)) {
final AuthState targetAuthState = context.getTargetAuthState();
if (targetAuthState != null) {
this.log.debug("Resetting target auth state");
targetAuthState.reset();
}
final AuthState proxyAuthState = context.getProxyAuthState();
if (proxyAuthState != null && proxyAuthState.isConnectionBased()) {
this.log.debug("Resetting proxy auth state");
proxyAuthState.reset();
}
}
currentRoute = this.routePlanner.determineRoute(newTarget, currentRequest, context);
if (this.log.isDebugEnabled()) {
this.log.debug("Redirecting to '" + uri + "' via " + currentRoute);
}
EntityUtils.consume(response.getEntity());
response.close();
} else {
return response;
}
} catch (final RuntimeException ex) {
response.close();
throw ex;
} catch (final IOException ex) {
response.close();
throw ex;
} catch (final HttpException ex) {
// Protocol exception related to a direct.
// The underlying connection may still be salvaged.
try {
EntityUtils.consume(response.getEntity());
} catch (final IOException ioex) {
this.log.debug("I/O error while releasing connection", ioex);
} finally {
response.close();
}
throw ex;
}
}
}
}RedirectExec實現(xiàn)了ClientExecChain接口,其構(gòu)造器要求傳入requestExecutor、redirectStrategy、routePlanner,其execute方法會先獲取maxRedirects參數(shù),然后執(zhí)行requestExecutor.execute,接著在config.isRedirectsEnabled()以及redirectStrategy.isRedirected為true時才進(jìn)入redirect邏輯,它會先判斷是否超出maxRedirects,大于等于則拋出RedirectException,否則通過redirectStrategy.getRedirect獲取HttpRequest,更新currentRoute,然后清理entity關(guān)閉response繼續(xù)下次循環(huán),即執(zhí)行redirect邏輯。
小結(jié)
HttpClient的RedirectStrategy定義了兩個方法,一個是是否需要redirect,一個是獲取redirect的請求,DefaultRedirectStrategy的構(gòu)造器支持傳入redirectMethods,默認(rèn)是Get和Head,isRedirected方法主要是對302,301,307,303進(jìn)行了判斷,getRedirect方法主要是通過location獲取目標(biāo)地址,然后根據(jù)原來的method和statusCode構(gòu)造HttpUriRequest。
以上就是HttpClient的RedirectStrategy源碼解讀的詳細(xì)內(nèi)容,更多關(guān)于HttpClient的RedirectStrategy的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringCloud項目中Feign組件添加請求頭所遇到的坑及解決
這篇文章主要介紹了SpringCloud項目中Feign組件添加請求頭所遇到的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
Java序列化框架Kryo高效轉(zhuǎn)換對象為字節(jié)流面試精講
這篇文章主要為大家介紹了Java序列化框架Kryo高效轉(zhuǎn)換對象為字節(jié)流面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
Java畢業(yè)設(shè)計實戰(zhàn)之養(yǎng)老院管理系統(tǒng)的實現(xiàn)
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+JSP+Easyui+maven+mysql實現(xiàn)一個養(yǎng)老院管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2022-03-03
Java8中對于LocalDateTime的序列化和反序列化問題
這篇文章主要介紹了Java8中對于LocalDateTime的序列化和反序列化問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06

