系列索引
本系列文章基于 OkHttp3.14
OkHttp3.14 源码剖析系列(一)——请求的发起及拦截器机制概述
OkHttp3.14 源码剖析系列(二)——拦截器大体流程分析
OkHttp3.14 源码剖析系列(六)——连接复用机制及连接的建立
OkHttp3.14 源码剖析系列(七)——请求的发起及响应的读取
前言
终于来到了我们 OkHttp 的最后一个部分——请求的发起。让我们回顾一下 CallServerInterceptor
的大体流程:
- 调用
exchange.writeRequestHeaders
写入请求头 - 调用
exchange.createRequestBody
获取Sink
- 调用
ResponseBody.writeTo
写入请求体 - 调用
exchange.readResponseHeaders
读入响应头 - 调用
exchange.openResponseBody
方法读取响应体
而我们知道,Exchange
最后实际上转调到了 ExchangeCodec
中的对应方法,而 ExchangeCodec
有两个实现——Http1ExchangeCodec
及 Http2ExchangeCodec
。
它们的创建过程在创建连接的过程中的 RealConnection.newCodec
方法中实现:
ExchangeCodec newCodec(OkHttpClient client, Interceptor.Chain chain) throws SocketException {
if (http2Connection != null) {
return new Http2ExchangeCodec(client, this, chain, http2Connection);
} else {
socket.setSoTimeout(chain.readTimeoutMillis());
source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
return new Http1ExchangeCodec(client, this, source, sink);
}
}
实际上是根据 Http2Connection
是否为 null 进行判断。
下面我们分别对 HTTP1 中及 HTTP2 中的处理进行分析:
HTTP/1.x
writeRequestHeaders
@Override
public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, realConnection.route().proxy().type());
writeRequest(request.headers(), requestLine);
}
这里首先调用了 RequestLine.get
方法获取到了 requestLine
这个 String,之后通过调用 writeRequest
方法将其写入。
我们首先看到 RequestLine.get
方法:
/**
* Returns the request status line, like "GET / HTTP/1.1". This is exposed to the application by
* {@link HttpURLConnection#getHeaderFields}, so it needs to be set even if the transport is
* HTTP/2.
*/
public static String get(Request request, Proxy.Type proxyType) {
StringBuilder result = new StringBuilder();
result.append(request.method());
result.append(' ');
if (includeAuthorityInRequestLine(request, proxyType)) {
result.append(request.url());
} else {
result.append(requestPath(request.url()));
}
result.append(" HTTP/1.1");
return result.toString();
}
这里实际上就是在构建 HTTP 协议中的第一行,包括请求的 method、url、HTTP版本等信息。
我们接着看到 writeRequest
方法:
/**
* Returns bytes of a request header for sending on an HTTP transport.
*/
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
sink.writeUtf8(requestLine).writeUtf8("\r\n");
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
这里首先写入了 statusLine
,之后将 Header 以 key:value
的形式写入,最后写入了一个空行,到这里我们的请求头就成功写入了(具体可以看到 HTTP/1.x 的请求格式)。
createRequestBody
@Override
public Sink createRequestBody(Request request, long contentLength) throws IOException {
if (request.body() != null && request.body().isDuplex()) {
throw new ProtocolException("Duplex connections are not supported for HTTP/1");
}
if ("chunked".equalsIgnoreCase(request.header("Transfer-Encoding"))) {
// Stream a request body of unknown length.
return newChunkedSink();
}
if (contentLength != -1L) {
// Stream a request body of a known length.
return newKnownLengthSink();
}
throw new IllegalStateException(
"Cannot stream a request body without chunked encoding or a known content length!");
}
这里首先对 Transfer-Encoding:chunked
的情况进行了处理,返回了 newChunkedSink
方法的结果,之若 contentLength
是确定的,则返回 newKnownLengthSink
方法的结果。
让我们分别看到这两个方法。
newChunkedSink
private Sink newChunkedSink() {
if (state != STATE_OPEN_REQUEST_BODY) throw new IllegalStateException("state: " + state);
state = STATE_WRITING_REQUEST_BODY;
return new ChunkedSink();
}
其实这里就是构建并返回了一个继承于 Sink
的 ChunkedSink
对象,我们可以看看它的 write
方法:
@Override
public void write(Buffer source, long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed");
if (byteCount == 0) return;
sink.writeHexadecimalUnsignedLong(byteCount);
sink.writeUtf8("\r\n");
sink.write(source, byteCount);
sink.writeUtf8("\r\n");
}
首先写入了十六进制的数据大小,之后写入了数据。
newKnownLengthSink
private Sink newKnownLengthSink() {
if (state != STATE_OPEN_REQUEST_BODY) throw new IllegalStateException("state: " + state);
state = STATE_WRITING_REQUEST_BODY;
return new KnownLengthSink();
}
这里也是构建并返回了一个继承于 Sink
的 KnownLengthSink
对象,我们可以看到其 write
方法:
@Override
public void write(Buffer source, long byteCount) throws IOException {
if (closed) throw new IllegalStateException("closed");
checkOffsetAndCount(source.size(), 0, byteCount);
sink.write(source, byteCount);
}
可以看到,其实就是对数据进行写入,没有非常特别的地方。
readRequestHeaders
@Override
public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
throw new IllegalStateException("state: " + state);
}
try {
// 读取statusLine
StatusLine statusLine = StatusLine.parse(readHeaderLine());
// 构建 Response(包含status信息及Header)
Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders());
if (expectContinue && statusLine.code == HTTP_CONTINUE) {
return null;
} else if (statusLine.code == HTTP_CONTINUE) {
state = STATE_READ_RESPONSE_HEADERS;
return responseBuilder;
}
state = STATE_OPEN_RESPONSE_BODY;
return responseBuilder;
} catch (EOFException e) {
// Provide more context if the server ends the stream before sending a response.
String address = "unknown";
if (realConnection != null) {
address = realConnection.route().