记一次代码探查过程



对本文有任何问题,可加我的个人微信询问:kymjs666

事情缘起

之所以有这个笔记,是因为这条 Issue:RxVolley #6

在浏览 Volley 代码的时候,产生了一个疑惑
HttpHeaderParser 中有这么一段代码

//...

headerValue = headers.get("Cache-Control");
if (headerValue != null) {
    hasCacheControl = true;
    String[] tokens = headerValue.split(",");
    for (int i = 0; i < tokens.length; i++) {
        String token = tokens[i].trim();
        if (token.equals("no-cache") || token.equals("no-store")) {
            return null;
        } else if (token.startsWith("max-age=")) {
            try {
                maxAge = Long.parseLong(token.substring(8));
            } catch (Exception e) {
            }
        } else if (token.startsWith("stale-while-revalidate=")) {
            try {
                staleWhileRevalidate = Long.parseLong(token.substring(23));
            } catch (Exception e) {
            }
        } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
            mustRevalidate = true;
        }
    }
}

//...

这段代码主要用途是在 http 请求响应后,服务器返回的响应数据中如果响应头中声明了 Cache-Control 则按照服务器端的规则处理缓存数据。

问题在于,这里使用的是 headers.get("Cache-Control") 其中 headers 是一个 Map,那么如果服务器返回的响应头的 key 是一段小写的字符串 cache-control,那么用这个方法岂不是无法正常读取到 value 了?

曲折的故事

可是,接口返回的数据头中是使用小写的cache-control。 使用 Volley 请求这个接口,却发现HttpHeaderParser正常工作,并没有出现我们想象中的headers.get("Cache-Control")找不到对应 value 的情况。

查看代码,HttpHeaderParser的参数 headers 是在 Request#parseNetworkResponse() 中被传入的。

而进一步查看是在NetworkDispatcher中调用了 parseNetworkResponse() ,代码如下

// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");

// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
    request.finish("not-modified");
    continue;
}

// Parse the response here on the worker thread.
Response<?> response = request.parseNetworkResponse(networkResponse);

原来这个值是由mNetwork.performRequest(request)返回的

再找到BasicNetwork#performRequest(),似乎并没有做什么特殊处理啊,为什么这段代码能正常工作呢?

连蒙带猜

难道本身拿到的数据就是大写的Cache-Control?
赶紧用 Postman 看一看
postman请求头

果然是我想多了
Android, kotlin, 开源代码, EventBus

不行,我就是要试试,找到最初发起网络请求的HurlStack类,把网络请求返回的请求头数据都给打印出来

// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
    // -1 is returned by getResponseCode() if the response code could not be retrieved.
    // Signal to the caller that something was wrong with the connection.
    throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
        connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
    response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
    if (header.getKey() != null) {
        Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
        Log.i("kymjs", header.getKey()+"===="+header.getValue().get(0))
        response.addHeader(h);
    }
}
return response;

RxVolley请求结果

Android, kotlin, 开源代码, EventBus

我操,为什么又变成了大写的。

原来如此

看结果,服务器接口返回的是小写,数据请求拿到的是大写,这特么说明在网络过程中数据发生了变化啊。
赶紧去查查 Http 1.1 协议:https://tools.ietf.org/html/rfc7230
https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html

3.2 Header Fields
Each header field consists of a case-insensitive field name followed by a colon (“:”), optional leading whitespace, the field value, and optional trailing whitespace.

4.2 Message Headers
HTTP header fields, which include general-header (section 4.5), request-header (section 5.3), response-header (section 6.2), and entity-header (section 7.1) fields, follow the same generic format as that given in Section 3.1 of RFC 822 [9]. Each header field consists of a name followed by a colon (“:”) and the field value. Field names are case-insensitive.

大意就是说:HTTP头字段遵循相同的通用格式,字段名称是不区分大小写的。

原来如此,那么通用格式肯定就是指Cache-Control这种首字母大写的格式了。
wiki百科查一下,是这样。 HTTP头字段列表 回应字段:https://zh.wikipedia.org/wiki/HTTP%E5%A4%B4%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8

了却了心中的一个疑问,真舒服~