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

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

Issue 154243006: Add GetExpirationTimes() to HttpResponseHeader. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: move a function body Created 4 years, 7 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 | Annotate | Revision Log
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 759 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698