1、使用协助工具 Restlet Client
扩展内容:
为了更准确的了解HTTP过程,同时减少编写代码、测试时间的浪费,可以采取一些手段来进行HTTP请求与响应的测试。
如:浏览器开发者工具、curl 、Chrome插件(Postman、 Restlet Client等)
插件安装:将资料中的Restlet-Client-REST-API-Testing_v2.8.0.1.crx拖入Chrome内核的浏览器。
方法 点击 Chrome菜单-更多工具-扩展程序,把下载好的.crx文件拖入chrome浏览器“扩展程序”页面。就会提示“拖放以安装”-“添加xxx吗?”,点“添加扩展程序”就安装好了。
2、HTTP 源码分析
2、1 获取 OkHttpClient对象
先得到内部类 Builder 对象实例, 在里面拿到所有的OkHttpClient的成员变量
public OkHttpClient() { this(new Builder());
}
2、2 OKHttpClient 的 GET 方法
先创建 Request 对象,然后调用 newCall 得到RealCall,
Call call = okHttpClient.newCall(request);
@Override
public Call newCall(Request request) { return RealCall.newRealCall(this, request, false /* for web socket */); }
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { // Safely publish the Call instance to the EventListener. RealCall call = new RealCall(client, originalRequest, forWebSocket); call.eventListener = client.eventListenerFactory().create(call); return call; }
再调它的enqueue 方法,最后交给dispatcher调度器 进行调度,dispatcher 是在Builder 里面构建的。
call.enqueue(new Callback() {
}
@Override public void enqueue(Callback responseCallback) { //TODO 不能重复执行 synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); eventListener.callStart(this); //TODO 交给 dispatcher调度器 进行调度 client.dispatcher().enqueue(new AsyncCall(responseCallback)); }
交给Dispatcher的enqueue 方法,将请求加入队列
synchronized void enqueue(AsyncCall call) { //TODO 同时请求不能超过并发数(64,可配置调度器调整) //TODO okhttp会使用共享主机即 地址相同的会共享socket //TODO 同一个host最多允许5条线程通知执行请求 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { //TODO 加入运行队列 并交给线程池执行 //TODO AsyncCall 是一个runnable,查看其execute实现 runningAsyncCalls.add(call); executorService().execute(call); } else { //TODO 加入等候队列 readyAsyncCalls.add(call); } }
AsyncCall 继续NamedRunnable,NamedRunnable 又 实现 Runnable 接口,
AsyncCall 实际是一个线程,将线程加入线程池,执行
NamedRunnable 的 execute();
再看下线程池的配置,
public synchronized ExecutorService executorService() { if (executorService == null) { //TODO 线程池 //TODO 核心线程 最大线程 非核心线程闲置60秒回收 任务队列 executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }
接着看执行方法 ,AsyncCall 的 execute ()
@Override protected void execute() { boolean signalledCallback = false; try { //TODO 责任链模式 //TODO 拦截器链 执行请求 Response response = getResponseWithInterceptorChain(); //回调结果 if (retryAndFollowUpInterceptor.isCanceled()) { signalledCallback = true; responseCallback.onFailure(RealCall.this, new IOException("Canceled")); } else { signalledCallback = true; responseCallback.onResponse(RealCall.this, response); } } catch (IOException e) { if (signalledCallback) { // Do not signal the callback twice! Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e); } else { eventListener.callFailed(RealCall.this, e); responseCallback.onFailure(RealCall.this, e); } } finally { //TODO 移除队列 client.dispatcher().finished(this); } }不管请求结果如何 finally 移除队列,
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) { int runningCallsCount; Runnable idleCallback; synchronized (this) { //TODO 移除队列 if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); //TODO 检查执行 readyAsyncCalls 中的请求 if (promoteCalls) promoteCalls(); runningCallsCount = runningCallsCount(); idleCallback = this.idleCallback; } //闲置调用 if (runningCallsCount == 0 && idleCallback != null) { idleCallback.run(); }
}
private void promoteCalls() { //TODO 检查 运行队列 与 等待队列 if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); //TODO 相同host的请求没有达到最大 if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }
下面看 getResponseWithInterceptorChain();
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. //TODO 责任链 倒序调用 List<Interceptor> interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); //TODO 5、处理重试与重定向 interceptors.add(retryAndFollowUpInterceptor); //TODO 4、处理 配置请求头等信息 interceptors.add(new BridgeInterceptor(client.cookieJar())); //TODO 3、处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应 //TODO 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改) interceptors.add(new CacheInterceptor(client.internalCache())); //TODO 2、连接服务器 interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { interceptors.addAll(client.networkInterceptors()); } //TODO 1、执行流操作(写出请求体、获得响应数据) //TODO 进行http请求报文的封装与请求报文的解析 interceptors.add(new CallServerInterceptor(forWebSocket)); Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); return chain.proceed(originalRequest); }
利用 interceptors创建拦截链, index 等于0,
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis());
最后返回 chain.proceed(originalRequest)是 RealInterceptorChain中的 proceed方法。创建 新的拦截链 next,
此时参数是index + 1。
//创建新的拦截链,链中的拦截器集合index+1 // Call the next interceptor in the chain. RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); //执行当前的拦截器 默认是:retryAndFollowUpInterceptor Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next);
intercept(...)执行的是RetryAndFollowUpInterceptor里面的intercept(...)
1)、拦截器 retryAndFollowUpInterceptor,处理重试与重定向
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RealInterceptorChain realChain = (RealInterceptorChain) chain; Call call = realChain.call(); EventListener eventListener = realChain.eventListener(); //TODO 核心 协调连接、请求/响应以及复用 StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()), call, eventListener, callStackTrace); this.streamAllocation = streamAllocation; int followUpCount = 0; Response priorResponse = null; while (true) { if (canceled) { streamAllocation.release(); throw new IOException("Canceled"); } Response response; boolean releaseConnection = true; try { //TODO 执行责任链 实际上就是下一个拦截器 response = realChain.proceed(request, streamAllocation, null, null); releaseConnection = false; } catch (RouteException e) { // The attempt to connect via a route failed. The request will not have been sent. if (!recover(e.getLastConnectException(), streamAllocation, false, request)) { throw e.getLastConnectException(); }
...
2)、BridgeInterceptor 添加请求头信息@Override public Response intercept(Chain chain) throws IOException { Request userRequest = chain.request(); Request.Builder requestBuilder = userRequest.newBuilder(); RequestBody body = userRequest.body(); if (body != null) { MediaType contentType = body.contentType(); if (contentType != null) { requestBuilder.header("Content-Type", contentType.toString()); } long contentLength = body.contentLength(); if (contentLength != -1) { requestBuilder.header("Content-Length", Long.toString(contentLength)); requestBuilder.removeHeader("Transfer-Encoding"); } else { requestBuilder.header("Transfer-Encoding", "chunked"); requestBuilder.removeHeader("Content-Length"); } } if (userRequest.header("Host") == null) { requestBuilder.header("Host", hostHeader(userRequest.url(), false)); } if (userRequest.header("Connection") == null) { requestBuilder.header("Connection", "Keep-Alive"); } // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing // the transfer stream. boolean transparentGzip = false; if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { transparentGzip = true; requestBuilder.header("Accept-Encoding", "gzip"); } List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url()); if (!cookies.isEmpty()) { requestBuilder.header("Cookie", cookieHeader(cookies)); } if (userRequest.header("User-Agent") == null) { requestBuilder.header("User-Agent", Version.userAgent()); } //TODO 执行下一个拦截器 Response networkResponse = chain.proceed(requestBuilder.build()); HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); Response.Builder responseBuilder = networkResponse.newBuilder() .request(userRequest); if (transparentGzip && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) && HttpHeaders.hasBody(networkResponse)) { GzipSource responseBody = new GzipSource(networkResponse.body().source()); Headers strippedHeaders = networkResponse.headers().newBuilder() .removeAll("Content-Encoding") .removeAll("Content-Length") .build(); responseBuilder.headers(strippedHeaders); String contentType = networkResponse.header("Content-Type"); responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody))); } return responseBuilder.build(); }3)、 CacheInterceptor, 处理缓存配置
@Override public Response intercept(Chain chain) throws IOException { //TODO request对应缓存的Response Response cacheCandidate = cache != null ? cache.get(chain.request()) : null; //TODO 执行响应缓存策略 long now = System.currentTimeMillis(); CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); Request networkRequest = strategy.networkRequest; Response cacheResponse = strategy.cacheResponse; if (cache != null) { cache.trackResponse(strategy); } //TODO 缓存无效 关闭资源 if (cacheCandidate != null && cacheResponse == null) { closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. } // If we're forbidden from using the network and the cache is insufficient, fail. if (networkRequest == null && cacheResponse == null) { return new Response.Builder() .request(chain.request()) .protocol(Protocol.HTTP_1_1) .code(504) .message("Unsatisfiable Request (only-if-cached)") .body(Util.EMPTY_RESPONSE) .sentRequestAtMillis(-1L) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); } //TODO 不使用网络请求直接返回响应 // If we don't need the network, we're done. if (networkRequest == null) { return cacheResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build(); } //TODO 执行下一个拦截器 Response networkResponse = null; try { networkResponse = chain.proceed(networkRequest); } finally { // If we're crashing on I/O or otherwise, don't leak the cache body. if (networkResponse == null && cacheCandidate != null) { closeQuietly(cacheCandidate.body()); } } // If we have a cache response too, then we're doing a conditional get. //TODO 如果存在缓存 更新 if (cacheResponse != null) { //TODO 304响应码 自从上次请求后,请求需要响应的内容未发生改变 if (networkResponse.code() == HTTP_NOT_MODIFIED) { Response response = cacheResponse.newBuilder() .headers(combine(cacheResponse.headers(), networkResponse.headers())) .sentRequestAtMillis(networkResponse.sentRequestAtMillis()) .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis()) .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); networkResponse.body().close(); // Update the cache after combining headers but before stripping the // Content-Encoding header (as performed by initContentStream()). cache.trackConditionalCacheHit(); cache.update(cacheResponse, response); return response; } else { closeQuietly(cacheResponse.body()); } } //TODO 缓存Response Response response = networkResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); if (cache != null) { if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) { // Offer this request to the cache. CacheRequest cacheRequest = cache.put(response); return cacheWritingResponse(cacheRequest, response); } if (HttpMethod.invalidatesCache(networkRequest.method())) { try { cache.remove(networkRequest); } catch (IOException ignored) { // The cache cannot be written. } } } return response; }进入看下缓存策略
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
public CacheStrategy get() { CacheStrategy candidate = getCandidate(); if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) { // We're forbidden from using the network and the cache is insufficient. return new CacheStrategy(null, null); } return candidate; } /** Returns a strategy to use assuming the request can use the network. */ private CacheStrategy getCandidate() { // No cached response. // TODO 没有缓存 if (cacheResponse == null) { return new CacheStrategy(request, null); } //TODO 缺少SSL握手信息 删除 // Drop the cached response if it's missing a required handshake. if (request.isHttps() && cacheResponse.handshake() == null) { return new CacheStrategy(request, null); } //TODO 检查缓存是否有效 其实在CacheInterceptor中通过Request从Cache中get这个缓存。 //TODO 如果规则不会变化,这里其实是多余的 //TODO 因为在放入缓存的时候就会进行这种检查 //TODO 如:响应码302和307 表示临时性重定向,即本次请求会临时的URI响应 //TODO 则 响应中需包含 Expires(过期时间) // If this response shouldn't have been stored, it should never be used // as a response source. This check should be redundant as long as the // persistence store is well-behaved and the rules are constant. if (!isCacheable(cacheResponse, request)) { return new CacheStrategy(request, null); } CacheControl requestCaching = request.cacheControl(); //TODO 请求缓存控制检查 如果不缓存或者请求中没有包含缓存页面的最后修改时间或者没有判断资源是否改变的请求头 if (requestCaching.noCache() || hasConditions(request)) { return new CacheStrategy(request, null); } CacheControl responseCaching = cacheResponse.cacheControl(); //TODO 响应缓存检查 如果响应缓存设置为不变的 request置为null 直接使用缓存 if (responseCaching.immutable()) { return new CacheStrategy(null, cacheResponse); } long ageMillis = cacheResponseAge(); /** * 请求头的检查 */ //TODO 响应的最大失效时间 long freshMillis = computeFreshnessLifetime(); if (requestCaching.maxAgeSeconds() != -1) { freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds())); } //TODO 刷新时间 最小有效时间 long minFreshMillis = 0; if (requestCaching.minFreshSeconds() != -1) { minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds()); } /** * 响应头 */ //TODO 最大失效时间 long maxStaleMillis = 0; if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) { maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds()); } //TODO 响应缓存的有效时间检查 在有效期内则使用缓存 if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) { Response.Builder builder = cacheResponse.newBuilder(); if (ageMillis + minFreshMillis >= freshMillis) { builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\""); } long oneDayMillis = 24 * 60 * 60 * 1000L; if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) { builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\""); } return new CacheStrategy(null, builder.build()); } // Find a condition to add to the request. If the condition is satisfied, the response body // will not be transmitted. String conditionName; String conditionValue; //TODO 如果资源未改变请求服务器会返回304 Not Modified if (etag != null) { //TODO ETags配合使用 验证资源是否改变 conditionName = "If-None-Match"; conditionValue = etag; } else if (lastModified != null) { //TODO lastModified配合使用 验证资源是否改变 conditionName = "If-Modified-Since"; conditionValue = lastModifiedString; } else if (servedDate != null) { //TODO servedDate配合使用 验证资源是否改变 conditionName = "If-Modified-Since"; conditionValue = servedDateString; } else { return new CacheStrategy(request, null); // No condition! Make a regular request. } Headers.Builder conditionalRequestHeaders = request.headers().newBuilder(); Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue); Request conditionalRequest = request.newBuilder() .headers(conditionalRequestHeaders.build()) .build(); return new CacheStrategy(conditionalRequest, cacheResponse); }4)、
ConnectInterceptor 连接服务器,进行TCP 连接
@Override public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (RealInterceptorChain) chain; Request request = realChain.request(); StreamAllocation streamAllocation = realChain.streamAllocation(); // We need the network to satisfy this request. Possibly for validating a conditional GET. boolean doExtensiveHealthChecks = !request.method().equals("GET"); //TODO 连接服务器/复用socket HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks); RealConnection connection = streamAllocation.connection(); return realChain.proceed(request, streamAllocation, httpCodec, connection); }
看下newStream(...)
public HttpCodec newStream( OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) { int connectTimeout = chain.connectTimeoutMillis(); int readTimeout = chain.readTimeoutMillis(); int writeTimeout = chain.writeTimeoutMillis(); int pingIntervalMillis = client.pingIntervalMillis(); boolean connectionRetryEnabled = client.retryOnConnectionFailure(); try { //TODO RealConnection 对Socket连接的封装 //TODO TPC/IP协议是传输层协议,主要解决数据如何在网络中传输 //TODO Socket则是对TCP/IP协议的封装和应用(程序员层面上)。 //TODO Http 应用层协议,解决如何包装数据 //TODO 使用Http协议封装数据,借助TCP/IP协议的实现:Socket 进行数据传输 RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks); //TODO HttpCodec 处理解析请求与响应的工具类 HttpCodec resultCodec = resultConnection.newCodec(client, chain, this); synchronized (connectionPool) { codec = resultCodec; return resultCodec; } } catch (IOException e) { throw new RouteException(e); } }
查找健康连接 findHealthyConnection
/** * Finds a connection and returns it if it is healthy. If it is unhealthy the process is * repeated * until a healthy connection is found. * TODO 查找连接并在健康状况下返回。 如果不健康,则重复该过程直到找到健康的连接一直没找到,最终创建新的socket连接。 */ private RealConnection findHealthyConnection(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks) throws IOException { while (true) { //TODO 真正找连接的方法 RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled); // If this is a brand new connection, we can skip the extensive health checks. synchronized (connectionPool) { if (candidate.successCount == 0) { return candidate; } } // Do a (potentially slow) check to confirm that the pooled connection is still good. // If it // isn't, take it out of the pool and start again. if (!candidate.isHealthy(doExtensiveHealthChecks)) { noNewStreams(); continue; } return candidate; } }
while 中通过findConnection 循环查找
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException { boolean foundPooledConnection = false; RealConnection result = null; Route selectedRoute = null; Connection releasedConnection; Socket toClose; synchronized (connectionPool) { if (released) throw new IllegalStateException("released"); if (codec != null) throw new IllegalStateException("codec != null"); if (canceled) throw new IOException("Canceled"); // Attempt to use an already-allocated connection. We need to be careful here because // our already-allocated connection may have been restricted from creating new streams. releasedConnection = this.connection; toClose = releaseIfNoNewStreams(); if (this.connection != null) { // We had an already-allocated connection and it's good. result = this.connection; releasedConnection = null; } if (!reportedAcquired) { // If the connection was never reported acquired, don't report it as released! releasedConnection = null; } //TODO 从连接池中获取连接 (先看put加入连接池) if (result == null) { // Attempt to get a connection from the pool. Internal.instance.get(connectionPool, address, this, null); if (connection != null) { foundPooledConnection = true; result = connection; } else { selectedRoute = route; } } } closeQuietly(toClose); if (releasedConnection != null) { eventListener.connectionReleased(call, releasedConnection); } if (foundPooledConnection) { eventListener.connectionAcquired(call, result); } //TODO 连接可用就返回 否则需要创建新的连接 if (result != null) { // If we found an already-allocated or pooled connection, we're done. return result; } // If we need a route selection, make one. This is a blocking operation. boolean newRouteSelection = false; if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) { newRouteSelection = true; routeSelection = routeSelector.next(); } synchronized (connectionPool) { if (canceled) throw new IOException("Canceled"); if (newRouteSelection) { // Now that we have a set of IP addresses, make another attempt at getting a // connection from // the pool. This could match due to connection coalescing. List<Route> routes = routeSelection.getAll(); for (int i = 0, size = routes.size(); i < size; i++) { Route route = routes.get(i); Internal.instance.get(connectionPool, address, this, route); if (connection != null) { foundPooledConnection = true; result = connection; this.route = route; break; } } } if (!foundPooledConnection) { if (selectedRoute == null) { selectedRoute = routeSelection.next(); } // Create a connection and assign it to this allocation immediately. This makes // it possible // for an asynchronous cancel() to interrupt the handshake we're about to do. route = selectedRoute; refusedStreamCount = 0; //TODO 创建新的连接 result = new RealConnection(connectionPool, selectedRoute); //TODO 使用弱引用进行引用计数 acquire(result, false); } } // If we found a pooled connection on the 2nd time around, we're done. if (foundPooledConnection) { eventListener.connectionAcquired(call, result); return result; } //TODO 执行连接 // Do TCP + TLS handshakes. This is a blocking operation. result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled, call, eventListener); routeDatabase().connected(result.route()); Socket socket = null; synchronized (connectionPool) { reportedAcquired = true; //TODO 加入连接池 // Pool the connection. Internal.instance.put(connectionPool, result); // If another multiplexed connection to the same address was created concurrently, then // release this connection and acquire that one. if (result.isMultiplexed()) { socket = Internal.instance.deduplicate(connectionPool, address, this); result = connection; } } closeQuietly(socket); eventListener.connectionAcquired(call, result); return result; }
在上面 result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis, connectionRetryEnabled, call, eventListener);
里面执行了connectSocket(connectTimeout, readTimeout, call, eventListener);自己建立socket 连接,
然后把socket放到连接池里,就有了这里复用socket。
//TODO 连接服务器/复用socket HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks); RealConnection connection = streamAllocation.connection();
5)、CallServerInterceptor 执行流操作(写出请求体、获得响应数据),
进行http请求报文的封装与请求报文的解析,返回一个 respose .
3、总结
OKHttpClient 发请求过程,通过newCall得到一个RealCall,发起 AyncCall,交给Dispatcher 马上执行或者加入等待队列,执行完毕返回一个Response. 最后从运行队列中移除就完成了请求过程。