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-04Java序列化框架Kryo高效轉(zhuǎn)換對象為字節(jié)流面試精講
這篇文章主要為大家介紹了Java序列化框架Kryo高效轉(zhuǎn)換對象為字節(jié)流面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Java畢業(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-03Java8中對于LocalDateTime的序列化和反序列化問題
這篇文章主要介紹了Java8中對于LocalDateTime的序列化和反序列化問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06