4.3.2. Handling a Received Validation Request
这一节主要讲当cache收到validation request(conditional request)时如何处理
核心处理思路
当cache收到contional header时:
- 如果能在本地找足够接近的representation(不用validation就能直接满足reuse条件),则利用它在本地实现contional header的语义
- 也可以选择直接发起validation(不管本地此时是否有足够新的信息)获取足够新的representation,然后实现语义
关键点就是,cache手里的representation足够接近,用它来实现语义结果就不会和origin server实现语义的结果差太多!
result = semantic(representation)
是否处理(SHOULD/MUST NOT)
足够接近的representation
If a cache receives a request that can be satisfied by reusing one of
its stored 200 (OK) or 206 (Partial Content) responses, the cache
SHOULD evaluate any applicable conditional header field preconditions
received in that request with respect to the corresponding validators
contained within the selected response.
“Reusing”:
- 指的是满足4.1中定义的reuse条件的stored responses;它们可以是直接满足,或者是被validation以后变得满足。
SHOULD
TODO
对于某个request,当不考虑它的conditional header时,能被它reuse的representation 有一个或者多个。cache应该(SHOULD)对其中一个执行evaluate操作。
显然,能被reuse的representation必然足够接近最近的representation,不然reuse也没有意义。
206 response能被复用则表示请求中的Range header能被它满足(例如请求0-400,本地保存的是0-600)(RFC_GUSS)
MUST NOT
A cache MUST NOT evaluate conditional header fields that are only applicable to an origin
server, found in a request with semantics that cannot be satisfied
with a cached response, or applied to a target resource for which it
has no stored responses; such preconditions are likely intended for
some other (inbound) server.
rfc说当遇到如下情况的时候不能(MUST NOT) evaluate,但是他秒的又没说后续该怎么办!:
- only applicable to an origin server
- found in a request with semantics that cannot be satisfied with a cached response
- applied to a target resource for which it has no stored responses
(1.)指的就是If-Match和If-Unmodified-Since在rfc 7232中被定义为不能被cache使用
// 3.3. If-Modified-Since
The If-Match header field can be ignored by caches and intermediaries
because it is not applicable to a stored response.
// 3.4. If-Unmodified-Since
The If-Unmodified-Since header field can be ignored by caches and
intermediaries because it is not applicable to a stored response.
"can be ignored"是什么意思?可以直接无视 If-Match 接着处理其他的 preconditions (如果有)或者"处理不了就转发到下一跳"的意思(RFC_GUSS)?
(2.)目前理解为,如果这些request的结果不能被缓存,则不要(MUST NOT)进行evaluate。毕竟不能被缓存,cache收到reques时也找不到执行evaluate的目标(RFC_GUSS)。
(3.)指的是在不考虑preconditions的情况下,cache就算发起了validation了以后也没法给request提供可复用的representation。
筛选结果
所以目前看cache能执行evaluate的情况如下:
- GET/HEAD + If-None-Match/If-Modified-Since/If-Range
那POST呢?懵了。
evaluate规则
The proper evaluation of conditional requests by a cache depends on
the received precondition header fields and their precedence, as
defined in Section 6 of [RFC7232]. The If-Match and
If-Unmodified-Since conditional header fields are not applicable to a
cache.
综合理解下来evaluate规则如下:
evaluate规则 = rfc 7234 4.3.2. Handling a Received Validation Request + rfc 7232的合集;rfc 7234 4.3.2. Handling a Received Validation Request的规则的优先级更高
优先级
The proper evaluation of conditional requests by a cache depends on
the received precondition header fields and their precedence, as
defined in Section 6 of [RFC7232]. The If-Match and
If-Unmodified-Since conditional header fields are not applicable to a
cache.
当cache收到一个request,其中包含多个conditional header时,evaluate的主要参照rfc 7232 6. Precedence,但是略有不同。
假设cache收到如下请求:
// request
1. If-Match
2. If-Unmodified-Since
3. If-None-Match
4. If-Modified-Since
5. If-Range
可能有如下两种顺序:
- cache无视(can be ignored)1./2.,然后优先级参照rfc 7232 6. Precedence
- cache采取保守策略,直接将请求转发到下一跳
If-None-Match
以下讨论排除了上述MUST NOT的情况。
client发送If-None-Match表示想验证自己的stored response。
TODO:真难受,这里不知道怎么描述精髓
本地有足够接近的representation
cache先在尝试在本地找一个它认为足够接近的representation,然后用类似算法对 If-None-Match执行evaluate。
//此时已经过了SHOULD/MUST NOT
//伪代码
fun evaluate(
request:Request,
storedReponse
):Response{
val etagStored = storedResponse.entityTag
if (fieldValue === "*"||request.entityTags.contains(etagStored)){
return create304Response(
storedResponse.metadata
)
}
else{
return storedResponse
}
}
If the field-value is "*", or if the field-value is a list of entity-tags and at least one of them matches
the entity-tag of the selected stored response, a cache recipient
SHOULD generate a 304 (Not Modified) response (using the metadata of
the selected stored response) instead of sending that stored
response.
根据结果返回:
- 304 或者
- sending that stored response
直接发起validation,然后执行evaluate
可选的validation发起时机
再次注意,rfc中没有定义validation的发起时机,所以cache可以在需要的时候发起validation来获取representation的最新状态。而在当前情况下,cache收到携带If-None-Match的request以后可以立即发起validation,然后利用返回结果来执行evluate操作,此时可能有2种情况:
- 本地有足够接近的representation,但是cache选择了先发起validation
- 本地有representation但是不接近
“本地没有对应的representation” 这种情况,没法发起validation,因为你想要验证的representation都没有,你能验证什么?这种时候直接转发到下一跳好了。(RFC_GUSS)
发起validation时将收到的request中的If-None-Match的值替换
When a cache decides to revalidate its own stored responses for a
request that contains an If-None-Match list of entity-tags, the cache
MAY combine the received list with a list of entity-tags from its own
stored set of responses (fresh or stale) and send the union of the
two lists as a replacement If-None-Match header field value in the
forwarded request. If a stored response contains only partial
content, the cache MUST NOT include its entity-tag in the union
unless the request is for a range that would be fully satisfied by
that partial stored response.
需要将值替换为1./2./3.的集合:
- request中的entity-tags
- uri match+method match(RFC_GUSS:这里我感觉应该是这样,但是不知道怎么表达)的所有完整的stored responses的entity-tags合集
- 所有partial stored responses的entity-tags,只要它们可以满足request的Range header
处理validation结果
validation返回有如下可能的情况:
- 服务器evaluate结果为true,执行对应的method,返回了response,或者
- 服务器evaluate结果为false,返回304 response
- A:其中携带的etag落在(集合1.)中,或者
- B:其中携带的etag落在(集合2./3.)中
If the response to the forwarded
request is 304 (Not Modified) and has an ETag header field value with
an entity-tag that is not in the client's list, the cache MUST
generate a 200 (OK) response for the client by reusing its
corresponding stored response, as updated by the 304 response
metadata (Section 4.3.4).
rfc中只对(304-B)的情况进行了MUST要求:
- 使用304 response更新corresponding stored response的metadate部分,然后用它构建一个200 response返回给client。
(304-B)意味着什么?它意味着最新的representation在cache手中而不是在client手中,于是为了实现相同的语义,cache给client返回了200 response。
让我们来推断一下rfc没有定义处理方式的其他情况(RFC_UNDEFINED).
- 对于(1.),这意味着client和cache手里都没有最新的representation,按照语义此时应继续处理If-Range;cache可以先用validation更新缓存。
- 对于(304-A),这意味着client有最新的representation而cache手中没有,按照语义此时应该返回304 reponse;cache可以先利用validation 中的metadata更新缓存,然后从缓存中构建一个304 response返回。
“corresponding stored response”如何找到?或者换个说法,当更新缓存以后,如果构建需要的302/200 response?(RFC_UNDEFINED)
If-Modified-Since
A cache recipient SHOULD
generate a 304 (Not Modified) response (using the metadata of the
selected stored response) if one of the following cases is true: 1)
the selected stored response has a Last-Modified field-value that is
earlier than or equal to the conditional timestamp; 2) no
Last-Modified field is present in the selected stored response, but
it has a Date field-value that is earlier than or equal to the
conditional timestamp; or, 3) neither Last-Modified nor Date is
present in the selected stored response, but the cache recorded it as
having been received at a time earlier than or equal to the
conditional timestamp.
上述rfc定义的返回304的情况,可以翻译为如下伪代码
// 伪代码304
val storedResponse = findLocalReuseResponse()
if (storedResponse != null) {
if (evaluateIfModifiedSince(storedResponse,request)) {
// SHOULD
return create304Response(storedResponse.metadata)
} else {
// A:这种情况如何处理?(RFC_UNDEFINED)
}
} else {
// B:本地没有可复用的representation怎么办?(RFC_UNDEFINED)
}
fun evaluateIfModifiedSince(storedResponse,request): Boolean{
val firstExistsTime = firstExists(
storedResponse.lastModified,
storedResponse.date,
receivedTime
)
return firstExistsTime <= request.headerValue
}
fun validationWhenIfModifiedSince() {
// C:当轮到处理If-Modified-Since时,如果cache想要发起validation该如何处理?(RFC_UNDEFINED)
}
If-Modified-Since的语义是如果最后修改时间 <= headerValue(RFC_GUSS),则应该(SHOULD)返回304。于是rfc定义如果cache在本地能找个足够有效的最后修改时间和headerValue比较,如果 最后修改时间 < headerValue 则返回304.
rfc没有定义上述A/B/C三种情况时该如何处理,现在来分别讨论一下合理可能的处理方式。
A: cache的representation足够新,client的不够新。
//更新cache
updateStoreReponses(response)
if(request.headers.contains("If-Range")){
//处理If-Range
}else{
// 没有If-Range了,将足够新的representation直接返回给client
return response
}
B: cache本地有representation,但是不够新,rfc只规定了这种情况不能执行evaluate(MUST NOT),但是没有说后续应该怎么处理。可以用如下方式处理:
// 处理情况B的伪代码
// 转发到下一跳
val r = forwardToServer(request)
// 刷新缓存
update(r)
if(request.headers.contains("If-Range")){
//处理If-Range
}else{
// 没有If-Range了,将足够新的representation直接返回给client
return response
}
C:
参考If-None-Match中的讨论,当cache在处理If-Modified-Sinces时发起validation时,可能出于如下2种情况
- 本地有足够接近的representation,但是cache选择了先发起validation,
- 本地有representation,但是不够接近;validation正好可以将本地的缓存刷成最新的然后在本地执行语义。
我们可以利用validation刷新本地缓存,复用前面伪代码304
val r = forwardToServer(request)
// 更新
// 更新方式参考rfc 7234 4.3.3. Handling a Validation Response
update(r)
// 现在本地有足够接近的representation了,执行伪代码304
If-Range
A cache that implements partial responses to range requests, as
defined in [RFC7233], also needs to evaluate a received If-Range
header field (Section 3.2 of [RFC7233]) with respect to its selected
stored response.
rfc 7234对于cache如何处理If-Range并没有写的很仔细。目前我有这些疑问:
- 如果请求中至少包含Range header时,如何验证SHOULD?
- 当cache在处理If-Range时,如果选择发起validation,该如何实现?
// cache处理If-Range的伪代码
// 在处理conditional headers之前,先满SHOULD
// storedResponse至少满足1.,优先满足2,再次满足3.否则为null
//
// 1.Range header 要求的范围
// 2.rfc 7234 4.1 中定义的reusing规则
// 3.uri match + method match,后续可以通过validation刷新缓存
// TODO:如果请求中至少包含Range header如何验证SHOULD?
val storedResponse = findStoredResponse()
if (storedResponse == null) {
// cache啥也干不了,智能转发到下一跳
val r = forwardToServer()
// 如果能存就存到cache
tryStoreToCache(r)
return r
}
// 处理If-None-Match(met)
// 处理If-Modified-Since(met)
// 前面的处理可能导致本地缓存刷新,这里重新获取一下
var r = findStoredResponse()
val header = request.header
if (header.contains("If-Range")
// rfc 7233 3.2. If-Range:server MUST ignore If-Range 当有If-Range但是没有Range header的时候
&& !header.contains("Range")
&& request.method == "GET") {
if(!r.isReusingImmediately()){
// 本地有,但是不够接近,验证一下
validationWhenIfRange()
// 缓存刷新了,重新获取一下
r = findStoredResponse()
}
// 接下来在cache本地处理If-Range + Range
val headerValue = header.get("If-Range")
val ok = if (headerValue.isEtag) {
// 期望etag对比,但是没有,比什么?
val storedEtag = r.etage ?: break;
// rfc 7233 3.2. If-Range:使用强对比
strongComparison(headerValue,r.)
} else{
// 格式不对,直接忽略
if(!headerValue.isValidHttpDate())
break;
// rfc 7233 3.2. If-Range:如果是HTTP date,要一模一样
val storedLM = getLastModified(r, receivedTime)
headerValue === storedLM
}
if(ok){
// rfc 7233 4.1. 206 Partial Content
return create216Response()
}
} else {
// 交给第六步处理
break;
}
fun validationWhenIfRange() {
// #
// TODO:If-Range的时候如何验证?
}