зеркало из https://github.com/mozilla/gecko-dev.git
fixes bug 132957 "Implement full support for Vary header [was: Content negotiation prohibits caching]" r=suresh sr=alecf a=asa
This commit is contained in:
Родитель
8fd72dabae
Коммит
3aa8a71fef
|
@ -97,6 +97,8 @@ typedef PRUint8 nsHttpVersion;
|
||||||
#define NS_HTTP_DEFAULT_PORT 80
|
#define NS_HTTP_DEFAULT_PORT 80
|
||||||
#define NS_HTTPS_DEFAULT_PORT 443
|
#define NS_HTTPS_DEFAULT_PORT 443
|
||||||
|
|
||||||
|
#define NS_HTTP_HEADER_SEPS ", \t"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// http atoms...
|
// http atoms...
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -829,6 +829,66 @@ nsHttpChannel::ProxyFailover()
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
nsHttpChannel::ResponseWouldVary()
|
||||||
|
{
|
||||||
|
PRBool result = PR_FALSE;
|
||||||
|
nsCAutoString buf, metaKey;
|
||||||
|
mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
|
||||||
|
if (!buf.IsEmpty()) {
|
||||||
|
NS_NAMED_LITERAL_CSTRING(prefix, "request-");
|
||||||
|
|
||||||
|
// enumerate the elements of the Vary header...
|
||||||
|
char *val = NS_CONST_CAST(char *, buf.get()); // going to munge buf
|
||||||
|
char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
|
||||||
|
while (token) {
|
||||||
|
//
|
||||||
|
// if "*", then assume response would vary. technically speaking,
|
||||||
|
// "Vary: header, *" is not permitted, but we allow it anyways.
|
||||||
|
//
|
||||||
|
// if the response depends on the value of the "Cookie" header, then
|
||||||
|
// bail since we do not store cookies in the cache. this is done
|
||||||
|
// for the following reasons:
|
||||||
|
//
|
||||||
|
// 1- cookies can be very large in size
|
||||||
|
//
|
||||||
|
// 2- cookies may contain sensitive information. (for parity with
|
||||||
|
// out policy of not storing Set-cookie headers in the cache
|
||||||
|
// meta data, we likewise do not want to store cookie headers
|
||||||
|
// here.)
|
||||||
|
//
|
||||||
|
// this implementation is obviously not fully standards compliant, but
|
||||||
|
// it is perhaps most prudent given the above issues.
|
||||||
|
//
|
||||||
|
if ((*token == '*') || (PL_strcasecmp(token, "cookie") == 0)) {
|
||||||
|
result = PR_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// build cache meta data key...
|
||||||
|
metaKey = prefix + nsDependentCString(token);
|
||||||
|
|
||||||
|
// check the last value of the given request header to see if it has
|
||||||
|
// since changed. if so, then indeed the cached response is invalid.
|
||||||
|
nsXPIDLCString lastVal;
|
||||||
|
mCacheEntry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
|
||||||
|
if (lastVal) {
|
||||||
|
nsHttpAtom atom = nsHttp::ResolveAtom(token);
|
||||||
|
const char *newVal = mRequestHead.PeekHeader(atom);
|
||||||
|
if (newVal && (strcmp(newVal, lastVal) != 0)) {
|
||||||
|
result = PR_TRUE; // yes, response would vary
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// next token...
|
||||||
|
token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// nsHttpChannel <byte-range>
|
// nsHttpChannel <byte-range>
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -1244,6 +1304,11 @@ nsHttpChannel::CheckCache()
|
||||||
LOG(("Validating based on MustValidate() returning TRUE\n"));
|
LOG(("Validating based on MustValidate() returning TRUE\n"));
|
||||||
doValidation = PR_TRUE;
|
doValidation = PR_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (ResponseWouldVary()) {
|
||||||
|
LOG(("Validating based on Vary headers returning TRUE\n"));
|
||||||
|
doValidation = PR_TRUE;
|
||||||
|
}
|
||||||
// Check if the cache entry has expired...
|
// Check if the cache entry has expired...
|
||||||
else {
|
else {
|
||||||
PRUint32 time = 0; // a temporary variable for storing time values...
|
PRUint32 time = 0; // a temporary variable for storing time values...
|
||||||
|
@ -1474,6 +1539,40 @@ nsHttpChannel::InitCacheEntry()
|
||||||
rv = StoreAuthorizationMetaData();
|
rv = StoreAuthorizationMetaData();
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
|
// Iterate over the headers listed in the Vary response header, and
|
||||||
|
// store the value of the corresponding request header so we can verify
|
||||||
|
// that it has not varied when we try to re-use the cached response at
|
||||||
|
// a later time. Take care not to store "Cookie" headers though. We
|
||||||
|
// take care of "Vary: cookie" in ResponseWouldVary.
|
||||||
|
//
|
||||||
|
// NOTE: if "Vary: accept, cookie", then we will store the "accept" header
|
||||||
|
// in the cache. we could try to avoid needlessly storing the "accept"
|
||||||
|
// header in this case, but it doesn't seem worth the extra code to perform
|
||||||
|
// the check.
|
||||||
|
{
|
||||||
|
nsCAutoString buf, metaKey;
|
||||||
|
mResponseHead->GetHeader(nsHttp::Vary, buf);
|
||||||
|
if (!buf.IsEmpty()) {
|
||||||
|
NS_NAMED_LITERAL_CSTRING(prefix, "request-");
|
||||||
|
|
||||||
|
char *val = NS_CONST_CAST(char *, buf.get()); // going to munge buf
|
||||||
|
char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
|
||||||
|
while (token) {
|
||||||
|
if ((*token != '*') && (PL_strcasecmp(token, "cookie") != 0)) {
|
||||||
|
nsHttpAtom atom = nsHttp::ResolveAtom(token);
|
||||||
|
const char *requestVal = mRequestHead.PeekHeader(atom);
|
||||||
|
if (requestVal) {
|
||||||
|
// build cache meta data key and set meta data element...
|
||||||
|
metaKey = prefix + nsDependentCString(token);
|
||||||
|
mCacheEntry->SetMetaDataElement(metaKey.get(), requestVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Store the received HTTP head with the cache entry as an element of
|
// Store the received HTTP head with the cache entry as an element of
|
||||||
// the meta data.
|
// the meta data.
|
||||||
nsCAutoString head;
|
nsCAutoString head;
|
||||||
|
|
|
@ -118,6 +118,7 @@ private:
|
||||||
nsresult ProcessRedirection(PRUint32 httpStatus);
|
nsresult ProcessRedirection(PRUint32 httpStatus);
|
||||||
nsresult ProcessAuthentication(PRUint32 httpStatus);
|
nsresult ProcessAuthentication(PRUint32 httpStatus);
|
||||||
nsresult GetCallback(const nsIID &aIID, void **aResult);
|
nsresult GetCallback(const nsIID &aIID, void **aResult);
|
||||||
|
PRBool ResponseWouldVary();
|
||||||
|
|
||||||
// redirection specific methods
|
// redirection specific methods
|
||||||
void HandleAsyncRedirect();
|
void HandleAsyncRedirect();
|
||||||
|
|
|
@ -328,25 +328,6 @@ nsHttpResponseHead::MustValidate()
|
||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the Vary header. Per comments on bug 37609, most of the request
|
|
||||||
// headers that we generate do not vary with the exception of Accept-Charset
|
|
||||||
// and Accept-Language, so we force validation only if these headers or "*"
|
|
||||||
// are listed with the Vary response header.
|
|
||||||
//
|
|
||||||
// XXX this may not be sufficient if embedders start tweaking or adding HTTP
|
|
||||||
// request headers.
|
|
||||||
//
|
|
||||||
// XXX will need to add the Accept header to this list if we start sending
|
|
||||||
// a full Accept header, since the addition of plugins could change this
|
|
||||||
// header (see bug 58040).
|
|
||||||
val = PeekHeader(nsHttp::Vary);
|
|
||||||
if (val && (PL_strstr(val, "*") ||
|
|
||||||
PL_strcasestr(val, "accept-charset") ||
|
|
||||||
PL_strcasestr(val, "accept-language"))) {
|
|
||||||
LOG(("Must validate based on \"%s\" header\n", val));
|
|
||||||
return PR_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(("no mandatory validation requirement\n"));
|
LOG(("no mandatory validation requirement\n"));
|
||||||
return PR_FALSE;
|
return PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче