OkHttp3源码阅读1之同步和异步请求的实现
OkHttp3源码阅读2之请求和响应数据的解析
本文基于Okhttp3版本
compile ‘com.squareup.okhttp3:okhttp:3.4.1’
https://github.com/square/okhttp
在网络数据传输过程中,不管是客户端请求的数据,还是服务端响应的数据,都是网络字节序数据,是二进制的字节流。
在Okhttp中,一个请求就是一个Request对象,而一个响应就是一个Response对象。其中,Request对象内部含有了所有需要发送的数据,比如请求头,请求参数等等,为方便开发者,这些请求的参数;
Response对象内部则包含了所有服务端响应的数据,比如响应码,响应数据等等。
为了方便开发者,Request和Response保存的数据一般都不会直接转换成二进制的字节流,只有当真正执行网络传输的时候,才会转换成网络字节序数据的。
public final class Request {
private final HttpUrl url; //请求址数据
private final String method; //请求的方法
private final Headers headers; //请求头数据
private final RequestBody body; //请求体数据
private final Object tag;
// 其他省略
}
public final class Response implements Closeable {
private final Request request; //请求
private final Protocol protocol; //协议
private final int code; //响应码
private final String message; //响应消息
private final Handshake handshake;
private final Headers headers;
private final ResponseBody body;
private final Response networkResponse;
private final Response cacheResponse;
private final Response priorResponse;
private final long sentRequestAtMillis;
private final long receivedResponseAtMillis;
// 其他省略
}
CallServerInterceptor:真正执行网络传输的拦截器,最终执行网络传输的时,会调用到其intercept方法
HttpCodec:用来对Request和Response解码和编码的类,实现类有:Http1Codec,Http2Codec
Sink:okio开源项目的类,对IO操作进行了一些额外的处理,使得IO操作更加简便,高效,可以这样理解,Sink相当于OutputStream
Source:Source相当于InputStream
CallServerInterceptor.java
public Response intercept(Chain chain) throws IOException {
// 具体代码省略
// 将请求头写入到Sink中
httpCodec.writeRequestHeaders(request);
// 进行检测是否能够写入请求体
Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
// 发送网络数据
httpCodec.finishRequest();
// 读取并构建响应的数据
Response response = httpCodec.readResponseHeaders()
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
// 如果不是websocket或者响应码不是101,则读取响应体数据
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
return response;
}
Http1Codec是用来处理Http1协议的,而Http2Codec是用来处理Http2协议的。
目前主流的还是Http1协议,因此,我们还是从Http1Codec类来分析
Request解析:
1.写入RequestLine
2.写入请求头
3.写入请求参数
就来读写Header为例子
public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}
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;
}
RequestLine.get(),返回的是,”GET http://android.com/foo HTTP/1.1”或者”GET /foo HTTP/1.1”
request.headers(),返回的是一个Headers对象,内部使用String数组,存储key和value的数据
解析响应协议,响应码,响应信息
解析响应头
解析响应体
public Response.Builder readResponse() throws IOException {
if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
throw new IllegalStateException(“state: ” + state);
}
try {
while (true) {
// 解析:协议,响应码,响应信息,如:HTTP/1.1 200 OK
StatusLine statusLine = StatusLine.parse(source.readUtf8LineStrict());
Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders()); // 按行解析响应头部,遇到"\r\n"或者"\n"换行
if (statusLine.code != HTTP_CONTINUE) {
state = STATE_OPEN_RESPONSE_BODY;
return responseBuilder;
}
}
} catch (EOFException e) {
// Provide more context if the server ends the stream before sending a response.
IOException exception = new IOException("unexpected end of stream on " + streamAllocation);
exception.initCause(e);
throw exception;
}
}