美文网首页
40. OkHttp之-拦截器-CallServerInterc

40. OkHttp之-拦截器-CallServerInterc

作者: 任振铭 | 来源:发表于2021-03-21 14:28 被阅读0次

    CallServerInterceptor ,利用 HttpCodec 发出请求到服务器并且解析生成 Response

    首先调用 httpCodec.writeRequestHeaders(request); 将请求头写入到缓存中(直到调用 flushRequest() 才真正发送给服务器)。然后马上进行第一个逻辑判断

            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();
                    realChain.eventListener().responseHeadersStart(realChain.call());
                    responseBuilder = httpCodec.readResponseHeaders(true);
                }
    
                if (responseBuilder == null) {
                    // Write the request body if the "Expect: 100-continue" expectation was met.
                    realChain.eventListener().requestBodyStart(realChain.call());
                    long contentLength = request.body().contentLength();
                    CountingSink requestBodyOut =
                            new CountingSink(httpCodec.createRequestBody(request, contentLength));
                    BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
    
                    request.body().writeTo(bufferedRequestBody);
                    bufferedRequestBody.close();
                    realChain.eventListener()
                            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
                } else if (!connection.isMultiplexed()) {
                    // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1
                  // connection
                    // from being reused. Otherwise we're still obligated to transmit the request
                  // body to
                    // leave the connection in a consistent state.
                    streamAllocation.noNewStreams();
                }
            }
    

    整个if都和一个请求头有关: Expect: 100-continue 。这个请求头代表了在发送请求体之前需要和服务器确定是 否愿意接受客户端发送的请求体。所以 permitsRequestBody 判断为是否会携带请求体的方式(POST),如果命中 if,则会先给服务器发起一次查询是否愿意接收请求体,这时候如果服务器愿意会响应100(没有响应体, responseBuilder 即为nul)。这时候才能够继续发送剩余请求数据。

    但是如果服务器不同意接受请求体,那么我们就需要标记该连接不能再被复用,调用 noNewStreams() 关闭相关的 Socket。

            if (responseBuilder == null) {
                realChain.eventListener().responseHeadersStart(realChain.call());
                responseBuilder = httpCodec.readResponseHeaders(false);
            }
    
            Response response = responseBuilder
                    .request(request)
                    .handshake(streamAllocation.connection().handshake())
                    .sentRequestAtMillis(sentRequestMillis)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();
    

    这时 responseBuilder 的情况即为:
    1、POST方式请求,请求头中包含 Expect ,服务器允许接受请求体,并且已经发出了请求体, responseBuilder
    为null;
    2、POST方式请求,请求头中包含 Expect ,服务器不允许接受请求体, responseBuilder 不为null 3、POST方式请求,未包含 Expect ,直接发出请求体, responseBuilder 为null; 4、POST方式请求,没有请求体, responseBuilder 为null;
    5、GET方式请求, responseBuilder 为null

    对应上面的5种情况,读取响应头并且组成响应 Response ,注意:此 Response 没有响应体。同时需要注意的是, 如果服务器接受 Expect: 100-continue 这是不是意味着我们发起了两次 Request ?那此时的响应头是第一次查询 服务器是否支持接受请求体的,而不是真正的请求对应的结果响应。所以紧接着:

           if (code == 100) {
               // server sent a 100-continue even though we did not request one.
               // try again to read the actual response
               responseBuilder = httpCodec.readResponseHeaders(false);
    
               response = responseBuilder
                       .request(request)
                       .handshake(streamAllocation.connection().handshake())
                       .sentRequestAtMillis(sentRequestMillis)
                       .receivedResponseAtMillis(System.currentTimeMillis())
                       .build();
    
               code = response.code();
           }
    

    如果响应是100,这代表了是请求 Expect: 100-continue 成功的响应,需要马上再次读取一份响应头,这才是真正 的请求对应结果响应头。

    然后收尾

           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;
    

    forWebSocket 代表websocket的请求,我们直接进入else,这里就是读取响应体数据。然后判断请求和服务器是 不是都希望长连接,一旦有一方指明 close ,那么就需要关闭 socket 。而如果服务器返回204/205,一般情况而 言不会存在这些返回码,但是一旦出现这意味着没有响应体,但是解析到的响应头中包含 Content-Lenght 且不为 0,这表响应体的数据字节长度。此时出现了冲突,直接抛出协议异常

    总结

    在这个拦截器中就是完成HTTP协议报文的封装与解析

    相关文章

      网友评论

          本文标题:40. OkHttp之-拦截器-CallServerInterc

          本文链接:https://www.haomeiwen.com/subject/gqgncltx.html