Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: net/http/http_response_headers.cc

Issue 455623003: stale-while-revalidate experimental implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: GetFreshnessLifetimes now returns two times. Duplicate AsyncValidations for the same URL are discar… Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // The rules for header parsing were borrowed from Firefox: 5 // The rules for header parsing were borrowed from Firefox:
6 // http://lxr.mozilla.org/seamonkey/source/netwerk/protocol/http/src/nsHttpRespo nseHead.cpp 6 // http://lxr.mozilla.org/seamonkey/source/netwerk/protocol/http/src/nsHttpRespo nseHead.cpp
7 // The rules for parsing content-types were also borrowed from Firefox: 7 // The rules for parsing content-types were also borrowed from Firefox:
8 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834 8 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834
9 9
10 #include "net/http/http_response_headers.h" 10 #include "net/http/http_response_headers.h"
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 bool is_continuation() const { return name_begin == name_end; } 126 bool is_continuation() const { return name_begin == name_end; }
127 127
128 std::string::const_iterator name_begin; 128 std::string::const_iterator name_begin;
129 std::string::const_iterator name_end; 129 std::string::const_iterator name_end;
130 std::string::const_iterator value_begin; 130 std::string::const_iterator value_begin;
131 std::string::const_iterator value_end; 131 std::string::const_iterator value_end;
132 }; 132 };
133 133
134 //----------------------------------------------------------------------------- 134 //-----------------------------------------------------------------------------
135 135
136 HttpResponseHeaders::FreshnessLifetimes::FreshnessLifetimes() {}
137 HttpResponseHeaders::FreshnessLifetimes::FreshnessLifetimes(
138 const TimeDelta& fresh)
139 : fresh(fresh) {}
140 HttpResponseHeaders::FreshnessLifetimes::~FreshnessLifetimes() {}
141
142 //-----------------------------------------------------------------------------
143
136 HttpResponseHeaders::HttpResponseHeaders(const std::string& raw_input) 144 HttpResponseHeaders::HttpResponseHeaders(const std::string& raw_input)
137 : response_code_(-1) { 145 : response_code_(-1) {
138 Parse(raw_input); 146 Parse(raw_input);
139 147
140 // The most important thing to do with this histogram is find out 148 // The most important thing to do with this histogram is find out
141 // the existence of unusual HTTP status codes. As it happens 149 // the existence of unusual HTTP status codes. As it happens
142 // right now, there aren't double-constructions of response headers 150 // right now, there aren't double-constructions of response headers
143 // using this constructor, so our counts should also be accurate, 151 // using this constructor, so our counts should also be accurate,
144 // without instantiating the histogram in two places. It is also 152 // without instantiating the histogram in two places. It is also
145 // important that this histogram not collect data in the other 153 // important that this histogram not collect data in the other
(...skipping 803 matching lines...) Expand 10 before | Expand all | Expand 10 after
949 957
950 // From RFC 2616 section 13.2.4: 958 // From RFC 2616 section 13.2.4:
951 // 959 //
952 // The calculation to determine if a response has expired is quite simple: 960 // The calculation to determine if a response has expired is quite simple:
953 // 961 //
954 // response_is_fresh = (freshness_lifetime > current_age) 962 // response_is_fresh = (freshness_lifetime > current_age)
955 // 963 //
956 // Of course, there are other factors that can force a response to always be 964 // Of course, there are other factors that can force a response to always be
957 // validated or re-fetched. 965 // validated or re-fetched.
958 // 966 //
959 bool HttpResponseHeaders::RequiresValidation(const Time& request_time, 967 // From RFC 5861 section 3, a stale response may be used while revalidation is
960 const Time& response_time, 968 // performed in the background if
961 const Time& current_time) const { 969 //
962 TimeDelta lifetime = 970 // freshness_lifetime + stale_while_revalidate > current_age
963 GetFreshnessLifetime(response_time); 971 //
964 if (lifetime == TimeDelta()) 972 ValidationType HttpResponseHeaders::RequiresValidation(
965 return true; 973 const Time& request_time,
974 const Time& response_time,
975 const Time& current_time) const {
976 FreshnessLifetimes lifetimes = GetFreshnessLifetimes(response_time);
977 if (lifetimes.fresh == TimeDelta() && lifetimes.stale == TimeDelta())
978 return VALIDATION_SYNCHRONOUS;
966 979
967 return lifetime <= GetCurrentAge(request_time, response_time, current_time); 980 TimeDelta age = GetCurrentAge(request_time, response_time, current_time);
981
982 if (lifetimes.fresh > age)
983 return VALIDATION_NONE;
984
985 if (lifetimes.fresh + lifetimes.stale > age)
986 return VALIDATION_ASYNCHRONOUS;
987
988 return VALIDATION_SYNCHRONOUS;
968 } 989 }
969 990
970 // From RFC 2616 section 13.2.4: 991 // From RFC 2616 section 13.2.4:
971 // 992 //
972 // The max-age directive takes priority over Expires, so if max-age is present 993 // The max-age directive takes priority over Expires, so if max-age is present
973 // in a response, the calculation is simply: 994 // in a response, the calculation is simply:
974 // 995 //
975 // freshness_lifetime = max_age_value 996 // freshness_lifetime = max_age_value
976 // 997 //
977 // Otherwise, if Expires is present in the response, the calculation is: 998 // Otherwise, if Expires is present in the response, the calculation is:
978 // 999 //
979 // freshness_lifetime = expires_value - date_value 1000 // freshness_lifetime = expires_value - date_value
980 // 1001 //
981 // Note that neither of these calculations is vulnerable to clock skew, since 1002 // Note that neither of these calculations is vulnerable to clock skew, since
982 // all of the information comes from the origin server. 1003 // all of the information comes from the origin server.
983 // 1004 //
984 // Also, if the response does have a Last-Modified time, the heuristic 1005 // Also, if the response does have a Last-Modified time, the heuristic
985 // expiration value SHOULD be no more than some fraction of the interval since 1006 // expiration value SHOULD be no more than some fraction of the interval since
986 // that time. A typical setting of this fraction might be 10%: 1007 // that time. A typical setting of this fraction might be 10%:
987 // 1008 //
988 // freshness_lifetime = (date_value - last_modified_value) * 0.10 1009 // freshness_lifetime = (date_value - last_modified_value) * 0.10
989 // 1010 //
990 TimeDelta HttpResponseHeaders::GetFreshnessLifetime( 1011 // If the stale-while-revalidate directive is present, then it is used to set
991 const Time& response_time) const { 1012 // the |stale| time, unless it overridden by another directive.
1013 //
1014 HttpResponseHeaders::FreshnessLifetimes
1015 HttpResponseHeaders::GetFreshnessLifetimes(const Time& response_time) const {
992 // Check for headers that force a response to never be fresh. For backwards 1016 // Check for headers that force a response to never be fresh. For backwards
993 // compat, we treat "Pragma: no-cache" as a synonym for "Cache-Control: 1017 // compat, we treat "Pragma: no-cache" as a synonym for "Cache-Control:
994 // no-cache" even though RFC 2616 does not specify it. 1018 // no-cache" even though RFC 2616 does not specify it.
995 if (HasHeaderValue("cache-control", "no-cache") || 1019 if (HasHeaderValue("cache-control", "no-cache") ||
996 HasHeaderValue("cache-control", "no-store") || 1020 HasHeaderValue("cache-control", "no-store") ||
997 HasHeaderValue("pragma", "no-cache") || 1021 HasHeaderValue("pragma", "no-cache") ||
998 HasHeaderValue("vary", "*")) // see RFC 2616 section 13.6 1022 HasHeaderValue("vary", "*")) // see RFC 2616 section 13.6
rvargas (doing something else) 2014/09/11 02:33:07 nit: Add {}
Adam Rice 2014/09/12 01:46:48 Done.
999 return TimeDelta(); // not fresh 1023 return FreshnessLifetimes();
1024
1025 FreshnessLifetimes lifetimes;
1026
1027 if (!GetStaleWhileRevalidateValue(&lifetimes.stale))
1028 DCHECK(lifetimes.stale == TimeDelta());
1000 1029
1001 // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the 1030 // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the
1002 // Expires header after checking for max-age in GetFreshnessLifetime. This 1031 // Expires header after checking for max-age in GetFreshnessLifetimes. This
1003 // is important since "Expires: <date in the past>" means not fresh, but 1032 // is important since "Expires: <date in the past>" means not fresh, but
1004 // it should not trump a max-age value. 1033 // it should not trump a max-age value.
1005 1034 if (GetMaxAgeValue(&lifetimes.fresh))
1006 TimeDelta max_age_value; 1035 return lifetimes;
1007 if (GetMaxAgeValue(&max_age_value))
1008 return max_age_value;
1009 1036
1010 // If there is no Date header, then assume that the server response was 1037 // If there is no Date header, then assume that the server response was
1011 // generated at the time when we received the response. 1038 // generated at the time when we received the response.
1012 Time date_value; 1039 Time date_value;
1013 if (!GetDateValue(&date_value)) 1040 if (!GetDateValue(&date_value))
1014 date_value = response_time; 1041 date_value = response_time;
1015 1042
1016 Time expires_value; 1043 Time expires_value;
1017 if (GetExpiresValue(&expires_value)) { 1044 if (GetExpiresValue(&expires_value)) {
1018 // The expires value can be a date in the past! 1045 // The expires value can be a date in the past!
1019 if (expires_value > date_value) 1046 if (expires_value > date_value) {
1020 return expires_value - date_value; 1047 lifetimes.fresh = expires_value - date_value;
1048 return lifetimes;
1049 }
1021 1050
1022 return TimeDelta(); // not fresh 1051 DCHECK(lifetimes.fresh == TimeDelta());
1052 return lifetimes;
1023 } 1053 }
1024 1054
1025 // From RFC 2616 section 13.4: 1055 // From RFC 2616 section 13.4:
1026 // 1056 //
1027 // A response received with a status code of 200, 203, 206, 300, 301 or 410 1057 // A response received with a status code of 200, 203, 206, 300, 301 or 410
1028 // MAY be stored by a cache and used in reply to a subsequent request, 1058 // MAY be stored by a cache and used in reply to a subsequent request,
1029 // subject to the expiration mechanism, unless a cache-control directive 1059 // subject to the expiration mechanism, unless a cache-control directive
1030 // prohibits caching. 1060 // prohibits caching.
1031 // ... 1061 // ...
1032 // A response received with any other status code (e.g. status codes 302 1062 // A response received with any other status code (e.g. status codes 302
1033 // and 307) MUST NOT be returned in a reply to a subsequent request unless 1063 // and 307) MUST NOT be returned in a reply to a subsequent request unless
1034 // there are cache-control directives or another header(s) that explicitly 1064 // there are cache-control directives or another header(s) that explicitly
1035 // allow it. 1065 // allow it.
1036 // 1066 //
1037 // From RFC 2616 section 14.9.4: 1067 // From RFC 2616 section 14.9.4:
1038 // 1068 //
1039 // When the must-revalidate directive is present in a response received by 1069 // When the must-revalidate directive is present in a response received by
1040 // a cache, that cache MUST NOT use the entry after it becomes stale to 1070 // a cache, that cache MUST NOT use the entry after it becomes stale to
1041 // respond to a subsequent request without first revalidating it with the 1071 // respond to a subsequent request without first revalidating it with the
1042 // origin server. (I.e., the cache MUST do an end-to-end revalidation every 1072 // origin server. (I.e., the cache MUST do an end-to-end revalidation every
1043 // time, if, based solely on the origin server's Expires or max-age value, 1073 // time, if, based solely on the origin server's Expires or max-age value,
1044 // the cached response is stale.) 1074 // the cached response is stale.)
1045 // 1075 //
1046 // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an 1076 // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an
1047 // experimental RFC that adds 308 permanent redirect as well, for which "any 1077 // experimental RFC that adds 308 permanent redirect as well, for which "any
1048 // future references ... SHOULD use one of the returned URIs." 1078 // future references ... SHOULD use one of the returned URIs."
1079 //
1080 // TODO(ricea): RFC7234 adds extra cacheable status codes and stops
1081 // "must-revalidate" from influencing heuristic expiration. This code should
rvargas (doing something else) 2014/09/11 02:33:06 I'm missing the must-revalidate part of this comme
Adam Rice 2014/09/12 01:46:49 RFC7234 is missing the "based solely on the origin
rvargas (doing something else) 2014/09/12 18:33:12 I don't see how the removal of that sentence chang
Adam Rice 2014/09/16 11:53:00 I removed the comment. If the meaning of RFC2616 a
1082 // be changed to match once the RFC has had time to mature.
1049 if ((response_code_ == 200 || response_code_ == 203 || 1083 if ((response_code_ == 200 || response_code_ == 203 ||
1050 response_code_ == 206) && 1084 response_code_ == 206) &&
1051 !HasHeaderValue("cache-control", "must-revalidate")) { 1085 !HasHeaderValue("cache-control", "must-revalidate")) {
1052 // TODO(darin): Implement a smarter heuristic. 1086 // TODO(darin): Implement a smarter heuristic.
1053 Time last_modified_value; 1087 Time last_modified_value;
1054 if (GetLastModifiedValue(&last_modified_value)) { 1088 if (GetLastModifiedValue(&last_modified_value)) {
1055 // The last-modified value can be a date in the past! 1089 // The last-modified value can be a date in the future!
1056 if (last_modified_value <= date_value) 1090 if (last_modified_value <= date_value) {
1057 return (date_value - last_modified_value) / 10; 1091 lifetimes.fresh = (date_value - last_modified_value) / 10;
1092 return lifetimes;
1093 }
1058 } 1094 }
1059 } 1095 }
1060 1096
1061 // These responses are implicitly fresh (unless otherwise overruled): 1097 // These responses are implicitly fresh (unless otherwise overruled):
1062 if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 || 1098 if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 ||
1063 response_code_ == 410) { 1099 response_code_ == 410) {
1064 return TimeDelta::Max(); 1100 return FreshnessLifetimes(TimeDelta::Max());
1065 } 1101 }
1066 1102
1067 return TimeDelta(); // not fresh 1103 // Our heuristic freshness estimate for this resource is 0 seconds, in
1104 // accordance with common browser behaviour. However, stale-while-revalidate
1105 // may still apply.
1106 DCHECK(lifetimes.fresh == TimeDelta());
1107 return lifetimes;
1068 } 1108 }
1069 1109
1070 // From RFC 2616 section 13.2.3: 1110 // From RFC 2616 section 13.2.3:
1071 // 1111 //
1072 // Summary of age calculation algorithm, when a cache receives a response: 1112 // Summary of age calculation algorithm, when a cache receives a response:
1073 // 1113 //
1074 // /* 1114 // /*
1075 // * age_value 1115 // * age_value
1076 // * is the value of Age: header received by the cache with 1116 // * is the value of Age: header received by the cache with
1077 // * this response. 1117 // * this response.
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after
1397 return true; 1437 return true;
1398 } 1438 }
1399 1439
1400 bool HttpResponseHeaders::IsChunkEncoded() const { 1440 bool HttpResponseHeaders::IsChunkEncoded() const {
1401 // Ignore spurious chunked responses from HTTP/1.0 servers and proxies. 1441 // Ignore spurious chunked responses from HTTP/1.0 servers and proxies.
1402 return GetHttpVersion() >= HttpVersion(1, 1) && 1442 return GetHttpVersion() >= HttpVersion(1, 1) &&
1403 HasHeaderValue("Transfer-Encoding", "chunked"); 1443 HasHeaderValue("Transfer-Encoding", "chunked");
1404 } 1444 }
1405 1445
1406 } // namespace net 1446 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698