事情缘起
之所以有这个笔记,是因为这条 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 看一看
果然是我想多了
不行,我就是要试试,找到最初发起网络请求的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;
我操,为什么又变成了大写的。
原来如此
看结果,服务器接口返回的是小写,数据请求拿到的是大写,这特么说明在网络过程中数据发生了变化啊。
赶紧去查查 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
了却了心中的一个疑问,真舒服~