CallServerInterceptor拦截器分析

在分析完ConnectInterceptor拦截器后,终于来到了最后一个拦截器CallServerInterceptor,其主要作用就是向服务器发送请求,最终返回response。
还是先看下它的intercept方法:

public Response intercept(Chain chain) throws IOException {
        final RealInterceptorChain realChain = (RealInterceptorChain) chain;

    // 获取对象
    Call call = realChain.call();
    final HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(call);
    // 发送状态行和头部数据到服务端
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(call, request);

    Response.Builder responseBuilder = null;
    // 判断是否有请求实体的请求(不是GET请求)
    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.
        // 如果头部信息添加了"Expect: 100-continue",这个请求头字段的作用是在发送RequestBody前向服务器确认是否接受RequestBody,如果服务器不接受也就没有发送的必要了。
        // 有这个字段,相当于一次简单的握手操作,会等待拿到服务器返回的ResponseHeaders之后再继续,如果服务器接收RequestBody,会返回null。
        if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
            httpCodec.flushRequest();
            realChain.eventListener().responseHeadersStart(call);
            // 服务器返回
            responseBuilder = httpCodec.readResponseHeaders(true);
        }

        // 服务器同意接收,开始向流中写入RequestBody
        if (responseBuilder == null) {
            if (request.body() instanceof DuplexRequestBody) {
                // Prepare a duplex body so that the application can send a request body later.
                httpCodec.flushRequest();
                CountingSink requestBodyOut = new CountingSink(httpCodec.createRequestBody(request, -1L));
                ((DuplexRequestBody) request.body()).foldSink(requestBodyOut);
            } else {
                // Write the request body if the "Expect: 100-continue" expectation was met.
                realChain.eventListener().requestBodyStart(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(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 (!(request.body() instanceof DuplexRequestBody)) {
        // 调用了sink.flush();
        httpCodec.finishRequest();
    }

    // 读取头部信息、状态码等
    if (responseBuilder == null) {
        realChain.eventListener().responseHeadersStart(call);
        responseBuilder = httpCodec.readResponseHeaders(false);
    }

    // 构建Response, 写入原请求,握手情况,请求时间,得到的响应时间
    responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis());
    Internal.instance.initCodec(responseBuilder, httpCodec);
    Response response = responseBuilder.build();

    // 返回的状态码
    int code = response.code();
    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);

        responseBuilder
                .request(request)
                .handshake(streamAllocation.connection().handshake())
                .sentRequestAtMillis(sentRequestMillis)
                .receivedResponseAtMillis(System.currentTimeMillis());
        Internal.instance.initCodec(responseBuilder, httpCodec);
        response = responseBuilder.build();

        code = response.code();
    }

    realChain.eventListener().responseHeadersEnd(call, response);

    // ResponseBody
    if (forWebSocket && code == 101) {
        // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
        // 设置一个空的Body
        response = response.newBuilder()
                .body(Util.EMPTY_RESPONSE)
                .build();
    } else {
        // 读取Body信息
        response = response.newBuilder()
                .body(httpCodec.openResponseBody(response))
                .build();
    }

    // 如果设置了连接关闭,则断开连接
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
            || "close".equalsIgnoreCase(response.header("Connection"))) {
        streamAllocation.noNewStreams();
    }

    //HTTP 204(no content) 代表响应报文中包含若干首部和一个状态行,但是没有实体的主体内容。
    //HTTP 205(reset content) 表示响应执行成功,重置页面(Form表单),方便用户下次输入
    //这里做了同样的处理,就是抛出协议异常。
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
        throw new ProtocolException(
                "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
}

基本执行流程:
1、获取request、httpCodec等对象。
2、通过httpCodec发送状态行和头部数据到服务端。
3、判断是否是有请求实体的请求,如果是,判断当前是否有设置Expect: 100-continue请求头。
4、往流中写入请求实体内容。
5、读取服务器返回的头部信息、状态码等,构建responseBuilder对象并返回。
6、通过responseBuilder对象来构建response对象。
7、判断当前状态码,如果是100,则重新请求一次,得到response对象。
8、给Response对象设置body。
9、判断是否需要断开连接。

发送状态行和头部数据到服务端

httpCodec.writeRequestHeaders(request);

这里我们拿Http1Codec来分析:

public void writeRequestHeaders(Request request) throws IOException {
    // 获取状态行数据,返回类似"GET / HTTP/1.1"的字符串
    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可看成是个封装了socket连接的输出流,将状态栏和头部数据输出到服务端
    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;
}

这里使用了okio的Sink对象(可以看成封装了socket连接的输出流,详细可看okio)来将状态行和头部数据传到服务端。

读取服务器返回的头部信息、状态码并返回responseBuilder对象

@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(readHeaderLine());

        // 构建Response.Builder对象
        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;
        } else if (statusLine.code == HTTP_CONTINUE) {
            state = STATE_READ_RESPONSE_HEADERS;
            return responseBuilder;
        }

        state = STATE_OPEN_RESPONSE_BODY;
        return responseBuilder;
    } catch (EOFException e) {
        // Provide more context if the server ends the stream before sending a response.
        throw new IOException("unexpected end of stream on " + streamAllocation, e);
    }
}

获取响应行数据,然后用响应行的数据构建Response.Builder对象,如果状态码为100且expectContinue为true,则会返回个null对象。如果只是状态码为100,则直接返回个Response.Builder对象,返回后往下也会去处理100这个状态。

总结

该拦截器HttpCodec对象中的source、Sink对象来对向服务器发送和接收状态行或实体内容,进行些状态码判断、从而完成对Response对象的构建,再返回到上一级拦截器做处理。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值