40. OkHttp之-攔截器-CallServerInterceptor

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協議報文的封裝與解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章