OkHttp3源码阅读2之请求和响应数据的解析

系列

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;
  }

Request解析

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的数据

Response解析

  1. 解析响应协议,响应码,响应信息

  2. 解析响应头

  3. 解析响应体

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;
}

}

你可能感兴趣的:(okhttp3)