关于Okhttp3(八)-CallServerInterceptor

本系列文章终于要到尾声了。上篇我们已经成功连接到服务了,那解析来应该做什么呢,相比聪明的你已经猜到了,那就是发送接收数据。

读写数据

  1. 第一步,写入请求头
  2. 第二步,写入请求头
  3. 第三步,读取响应头
  4. 第四步,读取响应体

前面我们已经说过,okhttp的流程是完全独立的,同样读写数据也是交给相关的类来处理,这个类就是HttpCodec(解码器)

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Override public Response intercept(Chain chain) throws IOException {
  HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream();
  StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
  Request request = chain.request();
// 第一步,写入请求头
  long sentRequestMillis = System.currentTimeMillis();
  httpCodec.writeRequestHeaders(request);

  Response.Builder responseBuilder = null;
  if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
    // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
    // Continue" response before transmitting the request body. If we don't get that, return what
    // we did get (such as a 4xx response) without ever transmitting the request body.
    if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
      httpCodec.flushRequest();
      responseBuilder = httpCodec.readResponseHeaders(true);
    }

    //第二步,写入请求头
    // Write the request body, unless an "Expect: 100-continue" expectation failed.
    if (responseBuilder == null) {
      Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
      BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
      request.body().writeTo(bufferedRequestBody);
      bufferedRequestBody.close();
    }
  }

  httpCodec.finishRequest();
// 第三步,读取响应头
  if (responseBuilder == null) {
    responseBuilder = httpCodec.readResponseHeaders(false);
  }

  Response response = responseBuilder
      .request(request)
      .handshake(streamAllocation.connection().handshake())
      .sentRequestAtMillis(sentRequestMillis)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build();
// 第四步,读取响应体
  int code = response.code();
  if (forWebSocket && code == 101) {
    // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
    response = response.newBuilder()
        .body(Util.EMPTY_RESPONSE)
        .build();
  } else {
    response = response.newBuilder()
        .body(httpCodec.openResponseBody(response))
        .build();
  }

  if ("close".equalsIgnoreCase(response.request().header("Connection"))
      || "close".equalsIgnoreCase(response.header("Connection"))) {
    streamAllocation.noNewStreams();
  }

  if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
    throw new ProtocolException(
        "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
  }

  return response;
}

写入请求头

每一次的网络请求必然携带有请求头,这是http协议里面的东西,建议大家对http相关知识要熟悉掌握,这对开发工作大有好处。

这里我们看上面第一步,调用到了,httpcodec的写入方法

1
httpCodec.writeRequestHeaders(request);

我们跟进去看看

1
2
3
4
5
6
7
@Override public void writeRequestHeaders(Request request) throws IOException {
 // 创建一个请求行 比如GET /index.html HTTP/1.1。
 String requestLine = RequestLine.get(
      request, streamAllocation.connection().route().proxy().type());
 // 写入请求头
 writeRequest(request.headers(), requestLine);
}

继续看writeRequest()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 此处有状态的判断
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;
}

sink是Okio里面的内容,可以理解为outputstream,

  1. 首先写入请求行
  2. 循环写入请求头

完成后,会判断是否需要请求体,比如get就没有

写入请求体

1
2
3
4
5
6
7
// 其他略
if (responseBuilder == null) {
  Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
  BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
  request.body().writeTo(bufferedRequestBody);
  bufferedRequestBody.close();
}

写入请求体比较简单,对于post的请求体本质上就是一个写入流,当然这里是Okio的东西,再次封装而已,通过连接成功后获取到的读写流,我们就可以把请求体写出。

写完后会有一个,

1
finishRequest()// 时间上就是flush(),把流中的数据刷出去

读响应头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@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.parse(source.readUtf8LineStrict());

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

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

解析相应头

1
2
3
4
5
6
7
8
9
10
public Headers readHeaders() throws IOException {
// 创建一个builder
  Headers.Builder headers = new Headers.Builder();
  // parse the result headers until the first blank line
  // 一行行读出数据
  for (String line; (line = source.readUtf8LineStrict()).length() != 0; ) {
    Internal.instance.addLenient(headers, line);
  }
  return headers.build();
}

此处存放在一个list中

1
2
3
4
5
6
7
0     1    2    3   4 5 6
key value key value ...
Builder addLenient(String name, String value) {
  namesAndValues.add(name);
  namesAndValues.add(value.trim());
  return this;
}

最后调用build(),转化成Headers。

读取响应体

解析完成头后会根据状态判断响应是否成功,

1
2
3
4
5
6
7
8
9
10
11
12
13
int code = response.code();
// web socket
if (forWebSocket && code == 101) {
  // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
  response = response.newBuilder()
      .body(Util.EMPTY_RESPONSE)
      .build();
} else {
// 响应成功
  response = response.newBuilder()
      .body(httpCodec.openResponseBody(response))
      .build();
}

打开响应体

1
2
3
4
@Override public ResponseBody openResponseBody(Response response) throws IOException {
  Source source = getTransferStream(response);
  return new RealResponseBody(response.headers(), Okio.buffer(source));
}

注意:这里并没有解析,响应结果,而是关联起了一个Source,okio知识

至此整个请求过程完成。

然后往上一层拦截器返回结果,即回到上一个拦截器的intercept()方法,把响应结果交给上一个拦截器处理,比如缓存拦截器,会处理是不是要缓存下来等。

最后getResponseWithInterceptorChain()返回,我们再来看看这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Response getResponseWithInterceptorChain() throws IOException {
  // Build a full stack of interceptors.
  List<Interceptor> interceptors = new ArrayList<>();
  interceptors.addAll(client.interceptors());
  interceptors.add(retryAndFollowUpInterceptor);
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  interceptors.add(new CacheInterceptor(client.internalCache()));
  interceptors.add(new ConnectInterceptor(client));
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  interceptors.add(new CallServerInterceptor(forWebSocket));

  Interceptor.Chain chain = new RealInterceptorChain(
      interceptors, null, null, null, 0, originalRequest);
  return chain.proceed(originalRequest);//返回结果
}

关于拦截器: 拦截器有点像上楼梯,一层层上去,让后在一层层倒回来,拿到结果给调用入口。

调用入口

我们再回过头来看看调用入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  @Override protected void execute() {
    boolean signalledCallback = false;
    try {
    // 通过一系列的拦截器最终难道结果
      Response response = getResponseWithInterceptorChain();
      // 中途取消, callback回调
      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 {
        responseCallback.onFailure(RealCall.this, e);
      }
    } finally {
    // 最终一定会调用finished
      client.dispatcher().finished(this);
    }
  }
}

总结

通过本系列文章,你一定对Okhttp有了清晰的认识,至少大体流程会有一个清晰的脉络,至于有些细节,还需要聪明的你去,慢慢研读。本系列文章对细节并没有详细说明,我们学习的并不是具体的实现而是一个思路,真正的技术实现都是各个api的调用而已。Thanks!!!

系列文章

  1. 关于Okhttp(一)-基本使用
  2. 关于Okhttp(二)-如何下载查看源码
  3. 关于Okhttp3(三)-请求流程
  4. 关于Okhttp3(四)-RetryAndFollowUpInterceptor
  5. 关于Okhttp3(五)-BridgeInterceptor
  6. 关于Okhttp3(六)-CacheInterceptor
  7. 关于Okhttp3(七)-ConnectInterceptor
  8. 关于Okhttp3(八)-CallServerInterceptor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值