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 759 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
770 StringPiece(value.begin() + directive_size + 1, value.end()), | 770 StringPiece(value.begin() + directive_size + 1, value.end()), |
771 &seconds); | 771 &seconds); |
772 *result = TimeDelta::FromSeconds(seconds); | 772 *result = TimeDelta::FromSeconds(seconds); |
773 return true; | 773 return true; |
774 } | 774 } |
775 } | 775 } |
776 | 776 |
777 return false; | 777 return false; |
778 } | 778 } |
779 | 779 |
780 // From RFC 2616 section 13.2.4: | |
781 // | |
782 // The max-age directive takes priority over Expires, so if max-age is present | |
783 // in a response, the calculation is simply: | |
784 // | |
785 // freshness_lifetime = max_age_value | |
786 // | |
787 // Otherwise, if Expires is present in the response, the calculation is: | |
788 // | |
789 // freshness_lifetime = expires_value - date_value | |
790 // | |
791 // Note that neither of these calculations is vulnerable to clock skew, since | |
792 // all of the information comes from the origin server. | |
793 // | |
794 // Also, if the response does have a Last-Modified time, the heuristic | |
795 // expiration value SHOULD be no more than some fraction of the interval since | |
796 // that time. A typical setting of this fraction might be 10%: | |
797 // | |
798 // freshness_lifetime = (date_value - last_modified_value) * 0.10 | |
799 // | |
800 // If the stale-while-revalidate directive is present, then it is used to set | |
801 // the |staleness| time, unless it overridden by another directive. | |
802 // | |
803 void HttpResponseHeaders::CalculateLifetimes( | |
mmenke
2016/04/29 16:15:53
Definition order should match delcaration order.
| |
804 const Time& response_time, | |
mmenke
2016/04/29 16:15:53
nit: Believe the preferred style is not to pass t
| |
805 TimeDelta* out_freshness_lifetime, | |
806 TimeDelta* out_staleness_lifetime) const { | |
807 *out_freshness_lifetime = TimeDelta(); | |
808 *out_staleness_lifetime = TimeDelta(); | |
809 | |
810 // Check for headers that force a response to never be fresh. For backwards | |
811 // compat, we treat "Pragma: no-cache" as a synonym for "Cache-Control: | |
812 // no-cache" even though RFC 2616 does not specify it. | |
813 if (HasHeaderValue("cache-control", "no-cache") || | |
814 HasHeaderValue("cache-control", "no-store") || | |
815 HasHeaderValue("pragma", "no-cache") || | |
816 // Vary: * is never usable: see RFC 2616 section 13.6. | |
817 HasHeaderValue("vary", "*")) { | |
818 return; | |
819 } | |
820 | |
821 // Cache-Control directive must_revalidate overrides stale-while-revalidate. | |
mmenke
2016/04/29 16:15:53
nit: must-revalidate
| |
822 bool must_revalidate = HasHeaderValue("cache-control", "must-revalidate"); | |
823 | |
824 if (must_revalidate || | |
825 !GetStaleWhileRevalidateValue(out_staleness_lifetime)) { | |
826 DCHECK_EQ(TimeDelta(), *out_staleness_lifetime); | |
mmenke
2016/04/29 16:15:53
This DCHECK seems out of place - we aren't DCHECKi
| |
827 } | |
828 | |
829 // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the | |
830 // Expires header after checking for max-age in GetFreshnessLifetimes. This | |
Randy Smith (Not in Mondays)
2016/05/04 19:22:15
This comment seems out of date, since I don't thin
| |
831 // is important since "Expires: <date in the past>" means not fresh, but | |
832 // it should not trump a max-age value. | |
833 if (GetMaxAgeValue(out_freshness_lifetime)) | |
834 return; | |
835 | |
836 // If there is no Date header, then assume that the server response was | |
837 // generated at the time when we received the response. | |
838 Time date_value; | |
839 if (!GetDateValue(&date_value)) | |
840 date_value = response_time; | |
841 | |
842 Time expires_value; | |
843 if (GetExpiresValue(&expires_value)) { | |
844 // The expires value can be a date in the past! | |
Randy Smith (Not in Mondays)
2016/05/04 19:22:15
nit, suggestion: I'd value a comment here indicati
| |
845 if (expires_value > date_value) { | |
846 *out_freshness_lifetime = expires_value - date_value; | |
847 return; | |
848 } | |
849 | |
850 DCHECK_EQ(TimeDelta(), *out_freshness_lifetime); | |
mmenke
2016/04/29 16:15:53
Should this be under GetDateValue? How many DCHEC
| |
851 return; | |
852 } | |
853 | |
854 // From RFC 2616 section 13.4: | |
855 // | |
856 // A response received with a status code of 200, 203, 206, 300, 301 or 410 | |
857 // MAY be stored by a cache and used in reply to a subsequent request, | |
858 // subject to the expiration mechanism, unless a cache-control directive | |
859 // prohibits caching. | |
860 // ... | |
861 // A response received with any other status code (e.g. status codes 302 | |
862 // and 307) MUST NOT be returned in a reply to a subsequent request unless | |
863 // there are cache-control directives or another header(s) that explicitly | |
864 // allow it. | |
865 // | |
866 // From RFC 2616 section 14.9.4: | |
867 // | |
868 // When the must-revalidate directive is present in a response received by | |
869 // a cache, that cache MUST NOT use the entry after it becomes stale to | |
870 // respond to a subsequent request without first revalidating it with the | |
871 // origin server. (I.e., the cache MUST do an end-to-end revalidation every | |
872 // time, if, based solely on the origin server's Expires or max-age value, | |
873 // the cached response is stale.) | |
874 // | |
875 // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an | |
876 // experimental RFC that adds 308 permanent redirect as well, for which "any | |
877 // future references ... SHOULD use one of the returned URIs." | |
878 if ((response_code_ == 200 || response_code_ == 203 || | |
879 response_code_ == 206) && !must_revalidate) { | |
880 // TODO(darin): Implement a smarter heuristic. | |
881 Time last_modified_value; | |
882 if (GetLastModifiedValue(&last_modified_value)) { | |
883 // The last-modified value can be a date in the future! | |
884 if (last_modified_value <= date_value) { | |
885 *out_freshness_lifetime = (date_value - last_modified_value) / 10; | |
mmenke
2016/04/29 16:15:53
This "/ 10" is just a magic heuristic? Maybe comm
Randy Smith (Not in Mondays)
2016/05/04 19:22:15
There's a comment about it at the top of the funct
| |
886 return; | |
887 } | |
888 } | |
889 } | |
890 | |
891 // These responses are implicitly fresh (unless otherwise overruled): | |
mmenke
2016/04/29 16:15:53
"Otherwise overruled" does not include having the
| |
892 if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 || | |
893 response_code_ == 410) { | |
894 *out_freshness_lifetime = TimeDelta::Max(); | |
895 *out_staleness_lifetime = TimeDelta(); // It should never be stale. | |
mmenke
2016/04/29 16:15:53
In the calling function, should we just always set
| |
896 return; | |
897 } | |
898 | |
899 // Our heuristic freshness estimate for this resource is 0 seconds, in | |
900 // accordance with common browser behaviour. However, stale-while-revalidate | |
901 // may still apply. | |
902 DCHECK_EQ(TimeDelta(), *out_freshness_lifetime); | |
903 return; | |
904 } | |
905 | |
780 void HttpResponseHeaders::AddHeader(std::string::const_iterator name_begin, | 906 void HttpResponseHeaders::AddHeader(std::string::const_iterator name_begin, |
781 std::string::const_iterator name_end, | 907 std::string::const_iterator name_end, |
782 std::string::const_iterator values_begin, | 908 std::string::const_iterator values_begin, |
783 std::string::const_iterator values_end) { | 909 std::string::const_iterator values_end) { |
784 // If the header can be coalesced, then we should split it up. | 910 // If the header can be coalesced, then we should split it up. |
785 if (values_begin == values_end || | 911 if (values_begin == values_end || |
786 HttpUtil::IsNonCoalescingHeader(name_begin, name_end)) { | 912 HttpUtil::IsNonCoalescingHeader(name_begin, name_end)) { |
787 AddToParsed(name_begin, name_end, values_begin, values_end); | 913 AddToParsed(name_begin, name_end, values_begin, values_end); |
788 } else { | 914 } else { |
789 HttpUtil::ValuesIterator it(values_begin, values_end, ','); | 915 HttpUtil::ValuesIterator it(values_begin, values_end, ','); |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
937 bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) { | 1063 bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) { |
938 // Users probably want to see 300 (multiple choice) pages, so we don't count | 1064 // Users probably want to see 300 (multiple choice) pages, so we don't count |
939 // them as redirects that need to be followed. | 1065 // them as redirects that need to be followed. |
940 return (response_code == 301 || | 1066 return (response_code == 301 || |
941 response_code == 302 || | 1067 response_code == 302 || |
942 response_code == 303 || | 1068 response_code == 303 || |
943 response_code == 307 || | 1069 response_code == 307 || |
944 response_code == 308); | 1070 response_code == 308); |
945 } | 1071 } |
946 | 1072 |
947 // From RFC 2616 section 13.2.4: | 1073 // We calculate and return |corrected_response_time|, defined by: |
948 // | 1074 // |
949 // The calculation to determine if a response has expired is quite simple: | 1075 // current_age = now - corrected_response_time |
950 // | 1076 // |
951 // response_is_fresh = (freshness_lifetime > current_age) | 1077 // So a caller can store an ExpirationTimes object and use it to determine |
1078 // freshness throughout the objects lifetime. | |
952 // | 1079 // |
953 // Of course, there are other factors that can force a response to always be | 1080 // Of course, there are other factors that can force a response to always be |
954 // validated or re-fetched. | 1081 // validated or re-fetched. |
955 // | 1082 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, | 1083 const Time& request_time, |
963 const Time& response_time, | 1084 const Time& response_time) const { |
964 const Time& current_time) const { | 1085 // From RFC 2616 section 13.2.3: |
965 FreshnessLifetimes lifetimes = GetFreshnessLifetimes(response_time); | 1086 // |
966 if (lifetimes.freshness.is_zero() && lifetimes.staleness.is_zero()) | 1087 // Summary of age calculation algorithm, when a cache receives a response: |
967 return VALIDATION_SYNCHRONOUS; | 1088 // |
968 | 1089 // /* |
969 TimeDelta age = GetCurrentAge(request_time, response_time, current_time); | 1090 // * age_value |
970 | 1091 // * is the value of Age: header received by the cache with |
971 if (lifetimes.freshness > age) | 1092 // * this response. |
972 return VALIDATION_NONE; | 1093 // * date_value |
973 | 1094 // * is the value of the origin server's Date: header |
974 if (lifetimes.freshness + lifetimes.staleness > age) | 1095 // * request_time |
975 return VALIDATION_ASYNCHRONOUS; | 1096 // * is the (local) time when the cache made the request |
976 | 1097 // * that resulted in this cached response |
977 return VALIDATION_SYNCHRONOUS; | 1098 // * response_time |
978 } | 1099 // * is the (local) time when the cache received the |
979 | 1100 // * response |
980 // From RFC 2616 section 13.2.4: | 1101 // * now |
981 // | 1102 // * is the current (local) time |
982 // The max-age directive takes priority over Expires, so if max-age is present | 1103 // */ |
983 // in a response, the calculation is simply: | 1104 // apparent_age = max(0, response_time - date_value); |
984 // | 1105 // corrected_received_age = max(apparent_age, age_value); |
985 // freshness_lifetime = max_age_value | 1106 // response_delay = response_time - request_time; |
986 // | 1107 // corrected_initial_age = corrected_received_age + response_delay; |
987 // Otherwise, if Expires is present in the response, the calculation is: | 1108 // resident_time = now - response_time; |
988 // | 1109 // current_age = corrected_initial_age + resident_time; |
989 // freshness_lifetime = expires_value - date_value | |
990 // | |
991 // Note that neither of these calculations is vulnerable to clock skew, since | |
992 // all of the information comes from the origin server. | |
993 // | |
994 // 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 | |
996 // that time. A typical setting of this fraction might be 10%: | |
997 // | |
998 // freshness_lifetime = (date_value - last_modified_value) * 0.10 | |
999 // | |
1000 // If the stale-while-revalidate directive is present, then it is used to set | |
1001 // the |staleness| time, unless it overridden by another directive. | |
1002 // | |
1003 HttpResponseHeaders::FreshnessLifetimes | |
1004 HttpResponseHeaders::GetFreshnessLifetimes(const Time& response_time) const { | |
1005 FreshnessLifetimes lifetimes; | |
1006 // 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: | |
1008 // no-cache" even though RFC 2616 does not specify it. | |
1009 if (HasHeaderValue("cache-control", "no-cache") || | |
1010 HasHeaderValue("cache-control", "no-store") || | |
1011 HasHeaderValue("pragma", "no-cache") || | |
1012 // Vary: * is never usable: see RFC 2616 section 13.6. | |
1013 HasHeaderValue("vary", "*")) { | |
1014 return lifetimes; | |
1015 } | |
1016 | |
1017 // Cache-Control directive must_revalidate overrides stale-while-revalidate. | |
1018 bool must_revalidate = HasHeaderValue("cache-control", "must-revalidate"); | |
1019 | |
1020 if (must_revalidate || !GetStaleWhileRevalidateValue(&lifetimes.staleness)) { | |
1021 DCHECK_EQ(TimeDelta(), lifetimes.staleness); | |
1022 } | |
1023 | |
1024 // NOTE: "Cache-Control: max-age" overrides Expires, so we only check the | |
1025 // Expires header after checking for max-age in GetFreshnessLifetimes. This | |
1026 // is important since "Expires: <date in the past>" means not fresh, but | |
1027 // it should not trump a max-age value. | |
1028 if (GetMaxAgeValue(&lifetimes.freshness)) | |
1029 return lifetimes; | |
1030 | 1110 |
1031 // If there is no Date header, then assume that the server response was | 1111 // If there is no Date header, then assume that the server response was |
1032 // generated at the time when we received the response. | 1112 // generated at the time when we received the response. |
1033 Time date_value; | 1113 Time date_value; |
1034 if (!GetDateValue(&date_value)) | |
1035 date_value = response_time; | |
1036 | |
1037 Time expires_value; | |
1038 if (GetExpiresValue(&expires_value)) { | |
1039 // The expires value can be a date in the past! | |
1040 if (expires_value > date_value) { | |
1041 lifetimes.freshness = expires_value - date_value; | |
1042 return lifetimes; | |
1043 } | |
1044 | |
1045 DCHECK_EQ(TimeDelta(), lifetimes.freshness); | |
1046 return lifetimes; | |
1047 } | |
1048 | |
1049 // From RFC 2616 section 13.4: | |
1050 // | |
1051 // 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, | |
1053 // subject to the expiration mechanism, unless a cache-control directive | |
1054 // prohibits caching. | |
1055 // ... | |
1056 // A response received with any other status code (e.g. status codes 302 | |
1057 // and 307) MUST NOT be returned in a reply to a subsequent request unless | |
1058 // there are cache-control directives or another header(s) that explicitly | |
1059 // allow it. | |
1060 // | |
1061 // From RFC 2616 section 14.9.4: | |
1062 // | |
1063 // When the must-revalidate directive is present in a response received by | |
1064 // a cache, that cache MUST NOT use the entry after it becomes stale to | |
1065 // respond to a subsequent request without first revalidating it with the | |
1066 // origin server. (I.e., the cache MUST do an end-to-end revalidation every | |
1067 // time, if, based solely on the origin server's Expires or max-age value, | |
1068 // the cached response is stale.) | |
1069 // | |
1070 // 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 | |
1072 // future references ... SHOULD use one of the returned URIs." | |
1073 if ((response_code_ == 200 || response_code_ == 203 || | |
1074 response_code_ == 206) && !must_revalidate) { | |
1075 // TODO(darin): Implement a smarter heuristic. | |
1076 Time last_modified_value; | |
1077 if (GetLastModifiedValue(&last_modified_value)) { | |
1078 // The last-modified value can be a date in the future! | |
1079 if (last_modified_value <= date_value) { | |
1080 lifetimes.freshness = (date_value - last_modified_value) / 10; | |
1081 return lifetimes; | |
1082 } | |
1083 } | |
1084 } | |
1085 | |
1086 // These responses are implicitly fresh (unless otherwise overruled): | |
1087 if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 || | |
1088 response_code_ == 410) { | |
1089 lifetimes.freshness = TimeDelta::Max(); | |
1090 lifetimes.staleness = TimeDelta(); // It should never be stale. | |
1091 return lifetimes; | |
1092 } | |
1093 | |
1094 // Our heuristic freshness estimate for this resource is 0 seconds, in | |
1095 // accordance with common browser behaviour. However, stale-while-revalidate | |
1096 // may still apply. | |
1097 DCHECK_EQ(TimeDelta(), lifetimes.freshness); | |
1098 return lifetimes; | |
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)) | 1114 if (!GetDateValue(&date_value)) |
1134 date_value = response_time; | 1115 date_value = response_time; |
1135 | 1116 |
1136 // If there is no Age header, then assume age is zero. GetAgeValue does not | 1117 // 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. | 1118 // modify its out param if the value does not exist. |
1138 TimeDelta age_value; | 1119 TimeDelta age_value; |
1139 GetAgeValue(&age_value); | 1120 GetAgeValue(&age_value); |
1140 | 1121 |
1141 TimeDelta apparent_age = std::max(TimeDelta(), response_time - date_value); | 1122 TimeDelta apparent_age = std::max(TimeDelta(), response_time - date_value); |
1142 TimeDelta corrected_received_age = std::max(apparent_age, age_value); | 1123 TimeDelta corrected_received_age = std::max(apparent_age, age_value); |
1143 TimeDelta response_delay = response_time - request_time; | 1124 TimeDelta response_delay = response_time - request_time; |
1144 TimeDelta corrected_initial_age = corrected_received_age + response_delay; | 1125 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 | 1126 |
1148 return current_age; | 1127 // From the age calculation algorithm above, we can derive the corrected |
1128 // response time, the |now| for which |current_age == 0|. Substituting into | |
1129 // the RFC 2616 13.2.3 equations above, and simplyfing: | |
1130 // | |
1131 // corrected_initial_age + resident_time == 0 | |
1132 // corrected_initial_age + corrected_response_time - response_time == 0 | |
1133 // corrected_response_time == response_time - corrected_initial_age | |
1134 | |
1135 ExpirationTimes expirations; | |
1136 expirations.corrected_response_time = response_time - corrected_initial_age; | |
1137 CalculateLifetimes(response_time, &expirations.freshness_lifetime, | |
1138 &expirations.staleness_lifetime); | |
1139 return expirations; | |
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 |