当前位置: 首页 > news >正文

4、Httpclient源码解析之HTTP协议

初始化CloseableHttpClient过程中涉及ExecChainHandler & DefaultHttpProcessor,即典型客户端责任链中的请求执行处理器。

责任链中各节点涉及请求处理器【ExecChainHandler】顺序如下:RedirectExec、ContentCompressionExec、HttpRequestRetryExec、ProtocolExec、ConnectExec、MainClientExec。

DefaultHttpProcessor中HttpRequestInterceptor类型的数组【requestInterceptors[]】包含RequestDefaultHeaders、RequestContent、RequestTargetHost、RequestClientConnControl、RequestUserAgent、RequestExpectContinue、RequestAddCookies、RequestAuthCache、ResponseProcessCookies。

1、InternalHttpClient

protected CloseableHttpResponse doExecute(HttpHost target,ClassicHttpRequest request,HttpContext context) {...final HttpClientContext localcontext = HttpClientContext.adapt(context != null ? context : new BasicHttpContext());RequestConfig config = null;if (request instanceof Configurable) {config = ((Configurable) request).getConfig();}if (config != null) {localcontext.setRequestConfig(config);}final HttpRoute route = determineRoute(target, request, localcontext);...final ExecRuntime execRuntime = new InternalExecRuntime(log, connManager, requestExecutor,request instanceof CancellableDependency ? (CancellableDependency) request : null);final ExecChain.Scope scope = new ExecChain.Scope(exchangeId, route, request, execRuntime, localcontext);final ClassicHttpResponse response = this.execChain.execute(ClassicRequestCopier.INSTANCE.copy(request), scope);return CloseableHttpResponse.adapt(response);
}

HttpClientContext:生命周期为单次请求。
execChain:ExecChainElement。按照开头责任链中节点顺序执行。

2、ExecChainElement

class ExecChainElement {
private final ExecChainHandler handler;
private final ExecChainElement next;ExecChainElement(final ExecChainHandler handler, final ExecChainElement next) {this.handler = handler;this.next = next;
}public ClassicHttpResponse execute(final ClassicHttpRequest request,final ExecChain.Scope scope) throws IOException, HttpException {return handler.execute(request, scope, new ExecChain() {//首个节点为RedirectExec@Overridepublic ClassicHttpResponse proceed(final ClassicHttpRequest request,final Scope scope) throws IOException, HttpException {return next.execute(request, scope);}});
}

2.1、RedirectExec

public ClassicHttpResponse execute(ClassicHttpRequest request,ExecChain.Scope scope,ExecChain chain) {RedirectLocations redirectLocations = context.getRedirectLocations();...RequestConfig config = context.getRequestConfig();for (int redirectCount = 0;;) {ClassicHttpResponse response = chain.proceed(currentRequest, currentScope);...URI redirectUri = this.redirectStrategy.getLocationURI(currentRequest, response, context);...}
}

2.2、ContentCompressionExec

public ClassicHttpResponse execute(ClassicHttpRequest request,ExecChain.Scope scope,ExecChain chain) {/* Signal support for Accept-Encoding transfer encodings. */if (!request.containsHeader(HttpHeaders.ACCEPT_ENCODING) && requestConfig.isContentCompressionEnabled()) {request.addHeader(acceptEncoding);}ClassicHttpResponse response = chain.proceed(request, scope);...
}

2.3、HttpRequestRetryExec

public ClassicHttpResponse execute(ClassicHttpRequest request,ExecChain.Scope scope,ExecChain chain) {for (int execCount = 1;; execCount++) {response = chain.proceed(currentRequest, scope);...final HttpEntity entity = request.getEntity();if (entity != null && !entity.isRepeatable()) {return response;}if (retryStrategy.retryRequest(response, execCount, context)) {response.close();final TimeValue nextInterval = retryStrategy.getRetryInterval(response, execCount, context);if (TimeValue.isPositive(nextInterval)) {nextInterval.sleep();}currentRequest = ClassicRequestCopier.INSTANCE.copy(scope.originalRequest);} else {return response;}}
}

如果请求存在重试策略,则通过for循环实现请求多次请求服务端。

2.4、ProtocolExec

public ClassicHttpResponse execute(ClassicHttpRequest request,ExecChain.Scope scope,ExecChain chain) {for (;;) {//DefaultHttpProcessorhttpProcessor.process(request, request.getEntity(), context);...ClassicHttpResponse response = chain.proceed(request, scope);}
}

2.4.1、DefaultHttpProcessor

for (final HttpRequestInterceptor requestInterceptor : this.requestInterceptors) {requestInterceptor.process(request, entity, context);
}

2.5、ConnectExec

public ClassicHttpResponse execute(ClassicHttpRequest request,ExecChain.Scope scope,ExecChain chain) {ExecRuntime execRuntime = scope.execRuntime;//InternalExecRuntime...execRuntime.acquireEndpoint(exchangeId, route, userToken, context);//#1do {final HttpRoute fact = tracker.toRoute();step = this.routeDirector.nextStep(route, fact);switch (step) {case HttpRouteDirector.CONNECT_TARGET:execRuntime.connectEndpoint(context);tracker.connectTarget(route.isSecure());break;case HttpRouteDirector.CONNECT_PROXY:execRuntime.connectEndpoint(context);final HttpHost proxy  = route.getProxyHost();tracker.connectProxy(proxy, route.isSecure() && !route.isTunnelled());break;...}} while (step > HttpRouteDirector.COMPLETE);}return chain.proceed(request, scope);
}
  1. 步骤1:涉及获取连接池中的连接。

2.6、MainClientExec

public ClassicHttpResponse execute(ClassicHttpRequest request,ExecChain.Scope scope,ExecChain chain) {ClassicHttpResponse response = execRuntime.execute(exchangeId, request, context);//InternalExecRuntime  #1...if (reuseStrategy.keepAlive(request, response, context)) {// Set the idle duration of this connectionfinal TimeValue duration = keepAliveStrategy.getKeepAliveDuration(response, context);if (this.log.isDebugEnabled()) {final String s;if (duration != null) {s = "for " + duration;} else {s = "indefinitely";}}execRuntime.markConnectionReusable(userToken, duration);} else {execRuntime.markConnectionNonReusable();}return new CloseableHttpResponse(response, execRuntime);
}
  1. 步骤1:参考2.6.1之HttpRequestExecutor。

2.6.1、HttpRequestExecutor

public ClassicHttpResponse execute(ClassicHttpRequest request,HttpClientConnection conn,HttpResponseInformationCallback informationCallback,HttpContext context) {...ClassicHttpResponse response = null;while (response == null) {if (expectContinue) {...} else {response = conn.receiveResponseHeader();//#1 状态行 & 响应头if (streamListener != null) {streamListener.onResponseHead(conn, response);}final int status = response.getCode();...if (status < HttpStatus.SC_SUCCESS) {if (informationCallback != null && status != HttpStatus.SC_CONTINUE) {informationCallback.execute(response, conn, context);}response = null;}}}if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) {//conn:DefaultBHttpClientConnectionconn.receiveResponseEntity(response);//#2 响应体IncomingHttpEntity}return response;
}
  1. 步骤2:解析响应体IncomingHttpEntity,响应体中InputStream类型的属性content其取值为ContentLengthInputStream。
public ClassicHttpResponse receiveResponseHeader() throws HttpException, IOException {final SocketHolder socketHolder = ensureOpen();final ClassicHttpResponse response = this.responseParser.parse(this.inBuffer, socketHolder.getInputStream());// #1final ProtocolVersion transportVersion = response.getVersion();this.version = transportVersion;onResponseReceived(response);final int status = response.getCode();if (response.getCode() >= HttpStatus.SC_SUCCESS) {incrementResponseCount();}return response;
}
  1. 步骤1:首先从连接Socket 获取最原始的输入流,其次将流转化到SessionInputBufferImpl中,最终返回http响应报文中的状态行&响应头。

2、SessionInputBufferImpl & SessionOutputBufferImpl

DefaultBHttpClientConnection实例化过程中触发SessionInputBufferImpl、SessionOutputBufferImpl的实例化。

SessionInputBufferImpl:Abstract base class for session input buffers that stream data from an arbitrary {@link InputStream}. This class buffers input data in an internal byte array for optimal input performance。

3、SocketHolder

Utility class that holds a {@link Socket} along with copies of its {@link InputStream} and {@link OutputStream}。

4、ContentLengthInputStream

Input stream that cuts off after a defined number of bytes【被定义好的字节,其实就是指状态行、响应头】。This class is used to receive content of HTTP messages where the end of the content entity is determined by the value of the {@code Content-Length header}【响应体的字节长度是通过响应头中Content-Length其value决定的】。Entities transferred using this stream can be maximum {@link Long#MAX_VALUE} long【使用此流传输的实体可以是long最大值】。

5、响应体IncomingHttpEntity

6、ResponseEntityProxy

http://www.lryc.cn/news/38510.html

相关文章:

  • 浏览器并发行为记录
  • 工厂模式与抽象工厂
  • 什么?你不知道 ConcurrentHashMap 的 kv 不能为 null?
  • SQL复习04 | 复杂查询
  • 【面试题】Java面试题汇总(无解答)
  • C++---背包模型---收服精灵(每日一道算法2023.3.11)
  • day30_JS
  • 【Java学习笔记】19.Java 正则表达式(2)
  • 华为云arm架构轻松安装kubeedge
  • 33--Vue-前端开发-使用Vue脚手架快速搭建项目
  • TMS WEB Core开发Web应用优势说明
  • 人工智能简单应用1-OCR分栏识别:两栏识别三栏识别都可以,本地部署完美拼接
  • Gin框架路由拆分与注册详解析
  • 2020蓝桥杯真题凯撒加密 C语言/C++
  • taro+vue3小程序使用v-html渲染的内容为class写了样式无效
  • MASK-RCNN网络介绍
  • 导航技术调研(CSDN_0023_20221217)
  • 买卖股票的最佳时机 I II III IV
  • STM32—LCD1602
  • 英雄算法学习路线
  • 【设计模式】备忘录模式和迭代器模式
  • rapidcsv 写csv文件实例
  • 数据库--进阶篇--9--存储引擎
  • 物品的管理的隐私政策
  • 深度解析首个Layer3 链 Nautilus Chain,有何优势?
  • 配对变量t检验
  • 蓝桥杯三月刷题 第八天
  • EXCEL技能点3-常用技能1
  • 经典分类模型回顾16-AlexNet实现垃圾分类(Tensorflow2.0版)
  • vue3使用vuex