OLD | NEW |
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 926 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
937 bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) { | 937 bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) { |
938 // Users probably want to see 300 (multiple choice) pages, so we don't count | 938 // Users probably want to see 300 (multiple choice) pages, so we don't count |
939 // them as redirects that need to be followed. | 939 // them as redirects that need to be followed. |
940 return (response_code == 301 || | 940 return (response_code == 301 || |
941 response_code == 302 || | 941 response_code == 302 || |
942 response_code == 303 || | 942 response_code == 303 || |
943 response_code == 307 || | 943 response_code == 307 || |
944 response_code == 308); | 944 response_code == 308); |
945 } | 945 } |
946 | 946 |
947 // From RFC 2616 section 13.2.4: | 947 // We calculate and return |corrected_response_time|, defined by: |
948 // | 948 // |
949 // The calculation to determine if a response has expired is quite simple: | 949 // current_age = now - corrected_response_time |
950 // | 950 // |
951 // response_is_fresh = (freshness_lifetime > current_age) | 951 // So a caller can store an ExpirationTimes object and use it to determine |
| 952 // freshness throughout the objects lifetime. |
952 // | 953 // |
953 // Of course, there are other factors that can force a response to always be | 954 // Of course, there are other factors that can force a response to always be |
954 // validated or re-fetched. | 955 // validated or re-fetched. |
955 // | 956 HttpResponseHeaders::ExpirationTimes HttpResponseHeaders::GetExpirationTimes( |
956 // From RFC 5861 section 3, a stale response may be used while revalidation is | |
957 // performed in the background if | |
958 // | |
959 // freshness_lifetime + stale_while_revalidate > current_age | |
960 // | |
961 ValidationType HttpResponseHeaders::RequiresValidation( | |
962 const Time& request_time, | 957 const Time& request_time, |
963 const Time& response_time, | 958 const Time& response_time) const { |
964 const Time& current_time) const { | 959 // From RFC 2616 section 13.2.3: |
965 FreshnessLifetimes lifetimes = GetFreshnessLifetimes(response_time); | 960 // |
966 if (lifetimes.freshness.is_zero() && lifetimes.staleness.is_zero()) | 961 // Summary of age calculation algorithm, when a cache receives a response: |
967 return VALIDATION_SYNCHRONOUS; | 962 // |
| 963 // /* |
| 964 // * age_value |
| 965 // * is the value of Age: header received by the cache with |
| 966 // * this response. |
| 967 // * date_value |
| 968 // * is the value of the origin server's Date: header |
| 969 // * request_time |
| 970 // * is the (local) time when the cache made the request |
| 971 // * that resulted in this cached response |
| 972 // * response_time |
| 973 // * is the (local) time when the cache received the |
| 974 // * response |
| 975 // * now |
| 976 // * is the current (local) time |
| 977 // */ |
| 978 // apparent_age = max(0, response_time - date_value); |
| 979 // corrected_received_age = max(apparent_age, age_value); |
| 980 // response_delay = response_time - request_time; |
| 981 // corrected_initial_age = corrected_received_age + response_delay; |
| 982 // resident_time = now - response_time; |
| 983 // current_age = corrected_initial_age + resident_time; |
968 | 984 |
969 TimeDelta age = GetCurrentAge(request_time, response_time, current_time); | 985 // If there is no Date header, then assume that the server response was |
| 986 // generated at the time when we received the response. |
| 987 Time date_value; |
| 988 if (!GetDateValue(&date_value)) |
| 989 date_value = response_time; |
970 | 990 |
971 if (lifetimes.freshness > age) | 991 // If there is no Age header, then assume age is zero. GetAgeValue does not |
972 return VALIDATION_NONE; | 992 // modify its out param if the value does not exist. |
| 993 TimeDelta age_value; |
| 994 GetAgeValue(&age_value); |
973 | 995 |
974 if (lifetimes.freshness + lifetimes.staleness > age) | 996 TimeDelta apparent_age = std::max(TimeDelta(), response_time - date_value); |
975 return VALIDATION_ASYNCHRONOUS; | 997 TimeDelta corrected_received_age = std::max(apparent_age, age_value); |
| 998 TimeDelta response_delay = response_time - request_time; |
| 999 TimeDelta corrected_initial_age = corrected_received_age + response_delay; |
976 | 1000 |
977 return VALIDATION_SYNCHRONOUS; | 1001 // From the age calculation algorithm above, we can derive the corrected |
| 1002 // response time, the |now| for which |current_age == 0|. Substituting into |
| 1003 // the RFC 2616 13.2.3 equations above, and simplyfing: |
| 1004 // |
| 1005 // corrected_initial_age + resident_time == 0 |
| 1006 // corrected_initial_age + corrected_response_time - response_time == 0 |
| 1007 // corrected_response_time == response_time - corrected_initial_age |
| 1008 |
| 1009 ExpirationTimes expirations; |
| 1010 expirations.corrected_response_time = response_time - corrected_initial_age; |
| 1011 CalculateLifetimes(response_time, &expirations.freshness_lifetime, |
| 1012 &expirations.staleness_lifetime); |
| 1013 return expirations; |
978 } | 1014 } |
979 | 1015 |
980 // From RFC 2616 section 13.2.4: | 1016 // From RFC 2616 section 13.2.4: |
981 // | 1017 // |
982 // The max-age directive takes priority over Expires, so if max-age is present | 1018 // The max-age directive takes priority over Expires, so if max-age is present |
983 // in a response, the calculation is simply: | 1019 // in a response, the calculation is simply: |
984 // | 1020 // |
985 // freshness_lifetime = max_age_value | 1021 // freshness_lifetime = max_age_value |
986 // | 1022 // |
987 // Otherwise, if Expires is present in the response, the calculation is: | 1023 // Otherwise, if Expires is present in the response, the calculation is: |
988 // | 1024 // |
989 // freshness_lifetime = expires_value - date_value | 1025 // freshness_lifetime = expires_value - date_value |
990 // | 1026 // |
991 // Note that neither of these calculations is vulnerable to clock skew, since | 1027 // Note that neither of these calculations is vulnerable to clock skew, since |
992 // all of the information comes from the origin server. | 1028 // all of the information comes from the origin server. |
993 // | 1029 // |
994 // Also, if the response does have a Last-Modified time, the heuristic | 1030 // Also, if the response does have a Last-Modified time, the heuristic |
995 // expiration value SHOULD be no more than some fraction of the interval since | 1031 // expiration value SHOULD be no more than some fraction of the interval since |
996 // that time. A typical setting of this fraction might be 10%: | 1032 // that time. A typical setting of this fraction might be 10%: |
997 // | 1033 // |
998 // freshness_lifetime = (date_value - last_modified_value) * 0.10 | 1034 // freshness_lifetime = (date_value - last_modified_value) * 0.10 |
999 // | 1035 // |
1000 // If the stale-while-revalidate directive is present, then it is used to set | 1036 // If the stale-while-revalidate directive is present, then it is used to set |
1001 // the |staleness| time, unless it overridden by another directive. | 1037 // the |staleness| time, unless it overridden by another directive. |
1002 // | 1038 // |
1003 HttpResponseHeaders::FreshnessLifetimes | 1039 void HttpResponseHeaders::CalculateLifetimes( |
1004 HttpResponseHeaders::GetFreshnessLifetimes(const Time& response_time) const { | 1040 const Time& response_time, |
1005 FreshnessLifetimes lifetimes; | 1041 TimeDelta* out_freshness_lifetime, |
| 1042 TimeDelta* out_staleness_lifetime) const { |
| 1043 *out_freshness_lifetime = TimeDelta(); |
| 1044 *out_staleness_lifetime = TimeDelta(); |
| 1045 |
1006 // Check for headers that force a response to never be fresh. For backwards | 1046 // Check for headers that force a response to never be fresh. For backwards |
1007 // compat, we treat "Pragma: no-cache" as a synonym for "Cache-Control: | 1047 // compat, we treat "Pragma: no-cache" as a synonym for "Cache-Control: |
1008 // no-cache" even though RFC 2616 does not specify it. | 1048 // no-cache" even though RFC 2616 does not specify it. |
1009 if (HasHeaderValue("cache-control", "no-cache") || | 1049 if (HasHeaderValue("cache-control", "no-cache") || |
1010 HasHeaderValue("cache-control", "no-store") || | 1050 HasHeaderValue("cache-control", "no-store") || |
1011 HasHeaderValue("pragma", "no-cache") || | 1051 HasHeaderValue("pragma", "no-cache") || |
1012 // Vary: * is never usable: see RFC 2616 section 13.6. | 1052 // Vary: * is never usable: see RFC 2616 section 13.6. |
1013 HasHeaderValue("vary", "*")) { | 1053 HasHeaderValue("vary", "*")) { |
1014 return lifetimes; | 1054 return; |
1015 } | 1055 } |
1016 | 1056 |
1017 // Cache-Control directive must_revalidate overrides stale-while-revalidate. | 1057 // Cache-Control directive must_revalidate overrides stale-while-revalidate. |
1018 bool must_revalidate = HasHeaderValue("cache-control", "must-revalidate"); | 1058 bool must_revalidate = HasHeaderValue("cache-control", "must-revalidate"); |
1019 | 1059 |
1020 if (must_revalidate || !GetStaleWhileRevalidateValue(&lifetimes.staleness)) { | 1060 if (must_revalidate || |
1021 DCHECK_EQ(TimeDelta(), lifetimes.staleness); | 1061 !GetStaleWhileRevalidateValue(out_staleness_lifetime)) { |
| 1062 DCHECK_EQ(TimeDelta(), *out_staleness_lifetime); |
1022 } | 1063 } |
1023 | 1064 |
1024 // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the | 1065 // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the |
1025 // Expires header after checking for max-age in GetFreshnessLifetimes. This | 1066 // Expires header after checking for max-age in GetFreshnessLifetimes. This |
1026 // is important since "Expires: <date in the past>" means not fresh, but | 1067 // is important since "Expires: <date in the past>" means not fresh, but |
1027 // it should not trump a max-age value. | 1068 // it should not trump a max-age value. |
1028 if (GetMaxAgeValue(&lifetimes.freshness)) | 1069 if (GetMaxAgeValue(out_freshness_lifetime)) |
1029 return lifetimes; | 1070 return; |
1030 | 1071 |
1031 // If there is no Date header, then assume that the server response was | 1072 // If there is no Date header, then assume that the server response was |
1032 // generated at the time when we received the response. | 1073 // generated at the time when we received the response. |
1033 Time date_value; | 1074 Time date_value; |
1034 if (!GetDateValue(&date_value)) | 1075 if (!GetDateValue(&date_value)) |
1035 date_value = response_time; | 1076 date_value = response_time; |
1036 | 1077 |
1037 Time expires_value; | 1078 Time expires_value; |
1038 if (GetExpiresValue(&expires_value)) { | 1079 if (GetExpiresValue(&expires_value)) { |
1039 // The expires value can be a date in the past! | 1080 // The expires value can be a date in the past! |
1040 if (expires_value > date_value) { | 1081 if (expires_value > date_value) { |
1041 lifetimes.freshness = expires_value - date_value; | 1082 *out_freshness_lifetime = expires_value - date_value; |
1042 return lifetimes; | 1083 return; |
1043 } | 1084 } |
1044 | 1085 |
1045 DCHECK_EQ(TimeDelta(), lifetimes.freshness); | 1086 DCHECK_EQ(TimeDelta(), *out_freshness_lifetime); |
1046 return lifetimes; | 1087 return; |
1047 } | 1088 } |
1048 | 1089 |
1049 // From RFC 2616 section 13.4: | 1090 // From RFC 2616 section 13.4: |
1050 // | 1091 // |
1051 // A response received with a status code of 200, 203, 206, 300, 301 or 410 | 1092 // A response received with a status code of 200, 203, 206, 300, 301 or 410 |
1052 // MAY be stored by a cache and used in reply to a subsequent request, | 1093 // MAY be stored by a cache and used in reply to a subsequent request, |
1053 // subject to the expiration mechanism, unless a cache-control directive | 1094 // subject to the expiration mechanism, unless a cache-control directive |
1054 // prohibits caching. | 1095 // prohibits caching. |
1055 // ... | 1096 // ... |
1056 // A response received with any other status code (e.g. status codes 302 | 1097 // A response received with any other status code (e.g. status codes 302 |
(...skipping 13 matching lines...) Expand all Loading... |
1070 // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an | 1111 // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an |
1071 // experimental RFC that adds 308 permanent redirect as well, for which "any | 1112 // experimental RFC that adds 308 permanent redirect as well, for which "any |
1072 // future references ... SHOULD use one of the returned URIs." | 1113 // future references ... SHOULD use one of the returned URIs." |
1073 if ((response_code_ == 200 || response_code_ == 203 || | 1114 if ((response_code_ == 200 || response_code_ == 203 || |
1074 response_code_ == 206) && !must_revalidate) { | 1115 response_code_ == 206) && !must_revalidate) { |
1075 // TODO(darin): Implement a smarter heuristic. | 1116 // TODO(darin): Implement a smarter heuristic. |
1076 Time last_modified_value; | 1117 Time last_modified_value; |
1077 if (GetLastModifiedValue(&last_modified_value)) { | 1118 if (GetLastModifiedValue(&last_modified_value)) { |
1078 // The last-modified value can be a date in the future! | 1119 // The last-modified value can be a date in the future! |
1079 if (last_modified_value <= date_value) { | 1120 if (last_modified_value <= date_value) { |
1080 lifetimes.freshness = (date_value - last_modified_value) / 10; | 1121 *out_freshness_lifetime = (date_value - last_modified_value) / 10; |
1081 return lifetimes; | 1122 return; |
1082 } | 1123 } |
1083 } | 1124 } |
1084 } | 1125 } |
1085 | 1126 |
1086 // These responses are implicitly fresh (unless otherwise overruled): | 1127 // These responses are implicitly fresh (unless otherwise overruled): |
1087 if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 || | 1128 if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 || |
1088 response_code_ == 410) { | 1129 response_code_ == 410) { |
1089 lifetimes.freshness = TimeDelta::Max(); | 1130 *out_freshness_lifetime = TimeDelta::Max(); |
1090 lifetimes.staleness = TimeDelta(); // It should never be stale. | 1131 *out_staleness_lifetime = TimeDelta(); // It should never be stale. |
1091 return lifetimes; | 1132 return; |
1092 } | 1133 } |
1093 | 1134 |
1094 // Our heuristic freshness estimate for this resource is 0 seconds, in | 1135 // Our heuristic freshness estimate for this resource is 0 seconds, in |
1095 // accordance with common browser behaviour. However, stale-while-revalidate | 1136 // accordance with common browser behaviour. However, stale-while-revalidate |
1096 // may still apply. | 1137 // may still apply. |
1097 DCHECK_EQ(TimeDelta(), lifetimes.freshness); | 1138 DCHECK_EQ(TimeDelta(), *out_freshness_lifetime); |
1098 return lifetimes; | 1139 return; |
1099 } | |
1100 | |
1101 // From RFC 2616 section 13.2.3: | |
1102 // | |
1103 // Summary of age calculation algorithm, when a cache receives a response: | |
1104 // | |
1105 // /* | |
1106 // * age_value | |
1107 // * is the value of Age: header received by the cache with | |
1108 // * this response. | |
1109 // * date_value | |
1110 // * is the value of the origin server's Date: header | |
1111 // * request_time | |
1112 // * is the (local) time when the cache made the request | |
1113 // * that resulted in this cached response | |
1114 // * response_time | |
1115 // * is the (local) time when the cache received the | |
1116 // * response | |
1117 // * now | |
1118 // * is the current (local) time | |
1119 // */ | |
1120 // apparent_age = max(0, response_time - date_value); | |
1121 // corrected_received_age = max(apparent_age, age_value); | |
1122 // response_delay = response_time - request_time; | |
1123 // corrected_initial_age = corrected_received_age + response_delay; | |
1124 // resident_time = now - response_time; | |
1125 // current_age = corrected_initial_age + resident_time; | |
1126 // | |
1127 TimeDelta HttpResponseHeaders::GetCurrentAge(const Time& request_time, | |
1128 const Time& response_time, | |
1129 const Time& current_time) const { | |
1130 // If there is no Date header, then assume that the server response was | |
1131 // generated at the time when we received the response. | |
1132 Time date_value; | |
1133 if (!GetDateValue(&date_value)) | |
1134 date_value = response_time; | |
1135 | |
1136 // If there is no Age header, then assume age is zero. GetAgeValue does not | |
1137 // modify its out param if the value does not exist. | |
1138 TimeDelta age_value; | |
1139 GetAgeValue(&age_value); | |
1140 | |
1141 TimeDelta apparent_age = std::max(TimeDelta(), response_time - date_value); | |
1142 TimeDelta corrected_received_age = std::max(apparent_age, age_value); | |
1143 TimeDelta response_delay = response_time - request_time; | |
1144 TimeDelta corrected_initial_age = corrected_received_age + response_delay; | |
1145 TimeDelta resident_time = current_time - response_time; | |
1146 TimeDelta current_age = corrected_initial_age + resident_time; | |
1147 | |
1148 return current_age; | |
1149 } | 1140 } |
1150 | 1141 |
1151 bool HttpResponseHeaders::GetMaxAgeValue(TimeDelta* result) const { | 1142 bool HttpResponseHeaders::GetMaxAgeValue(TimeDelta* result) const { |
1152 return GetCacheControlDirective("max-age", result); | 1143 return GetCacheControlDirective("max-age", result); |
1153 } | 1144 } |
1154 | 1145 |
1155 bool HttpResponseHeaders::GetAgeValue(TimeDelta* result) const { | 1146 bool HttpResponseHeaders::GetAgeValue(TimeDelta* result) const { |
1156 std::string value; | 1147 std::string value; |
1157 if (!EnumerateHeader(nullptr, "Age", &value)) | 1148 if (!EnumerateHeader(nullptr, "Age", &value)) |
1158 return false; | 1149 return false; |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1465 return true; | 1456 return true; |
1466 } | 1457 } |
1467 | 1458 |
1468 bool HttpResponseHeaders::IsChunkEncoded() const { | 1459 bool HttpResponseHeaders::IsChunkEncoded() const { |
1469 // Ignore spurious chunked responses from HTTP/1.0 servers and proxies. | 1460 // Ignore spurious chunked responses from HTTP/1.0 servers and proxies. |
1470 return GetHttpVersion() >= HttpVersion(1, 1) && | 1461 return GetHttpVersion() >= HttpVersion(1, 1) && |
1471 HasHeaderValue("Transfer-Encoding", "chunked"); | 1462 HasHeaderValue("Transfer-Encoding", "chunked"); |
1472 } | 1463 } |
1473 | 1464 |
1474 } // namespace net | 1465 } // namespace net |
OLD | NEW |