Index: Source/platform/network/HTTPParsers.cpp |
diff --git a/Source/platform/network/HTTPParsers.cpp b/Source/platform/network/HTTPParsers.cpp |
index d833fe14ed5b47ed7adf36d141551e9c46bfd2c4..3763f841a982adc2bdb7d428899166039bd6517f 100644 |
--- a/Source/platform/network/HTTPParsers.cpp |
+++ b/Source/platform/network/HTTPParsers.cpp |
@@ -34,6 +34,7 @@ |
#include "platform/network/HTTPParsers.h" |
#include "wtf/DateMath.h" |
+#include "wtf/MathExtras.h" |
#include "wtf/text/CString.h" |
#include "wtf/text/StringBuilder.h" |
#include "wtf/text/WTFString.h" |
@@ -684,4 +685,146 @@ size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned cha |
return length; |
} |
+static bool isCacheHeaderSeparator(UChar c) |
+{ |
+ // See RFC 2616, Section 2.2 |
+ switch (c) { |
+ case '(': |
+ case ')': |
+ case '<': |
+ case '>': |
+ case '@': |
+ case ',': |
+ case ';': |
+ case ':': |
+ case '\\': |
+ case '"': |
+ case '/': |
+ case '[': |
+ case ']': |
+ case '?': |
+ case '=': |
+ case '{': |
+ case '}': |
+ case ' ': |
+ case '\t': |
+ return true; |
+ default: |
+ return false; |
+ } |
+} |
+ |
+static bool isControlCharacter(UChar c) |
+{ |
+ return c < ' ' || c == 127; |
+} |
+ |
+static inline String trimToNextSeparator(const String& str) |
+{ |
+ return str.substring(0, str.find(isCacheHeaderSeparator)); |
+} |
+ |
+static void parseCacheHeader(const String& header, Vector<pair<String, String> >& result) |
+{ |
+ const String safeHeader = header.removeCharacters(isControlCharacter); |
+ unsigned max = safeHeader.length(); |
+ for (unsigned pos = 0; pos < max; /* pos incremented in loop */) { |
+ size_t nextCommaPosition = safeHeader.find(',', pos); |
+ size_t nextEqualSignPosition = safeHeader.find('=', pos); |
+ if (nextEqualSignPosition != kNotFound && (nextEqualSignPosition < nextCommaPosition || nextCommaPosition == kNotFound)) { |
+ // Get directive name, parse right hand side of equal sign, then add to map |
+ String directive = trimToNextSeparator(safeHeader.substring(pos, nextEqualSignPosition - pos).stripWhiteSpace()); |
+ pos += nextEqualSignPosition - pos + 1; |
+ |
+ String value = safeHeader.substring(pos, max - pos).stripWhiteSpace(); |
+ if (value[0] == '"') { |
+ // The value is a quoted string |
+ size_t nextDoubleQuotePosition = value.find('"', 1); |
+ if (nextDoubleQuotePosition != kNotFound) { |
+ // Store the value as a quoted string without quotes |
+ result.append(pair<String, String>(directive, value.substring(1, nextDoubleQuotePosition - 1).stripWhiteSpace())); |
+ pos += (safeHeader.find('"', pos) - pos) + nextDoubleQuotePosition + 1; |
+ // Move past next comma, if there is one |
+ size_t nextCommaPosition2 = safeHeader.find(',', pos); |
+ if (nextCommaPosition2 != kNotFound) |
+ pos += nextCommaPosition2 - pos + 1; |
+ else |
+ return; // Parse error if there is anything left with no comma |
+ } else { |
+ // Parse error; just use the rest as the value |
+ result.append(pair<String, String>(directive, trimToNextSeparator(value.substring(1, value.length() - 1).stripWhiteSpace()))); |
+ return; |
+ } |
+ } else { |
+ // The value is a token until the next comma |
+ size_t nextCommaPosition2 = value.find(','); |
+ if (nextCommaPosition2 != kNotFound) { |
+ // The value is delimited by the next comma |
+ result.append(pair<String, String>(directive, trimToNextSeparator(value.substring(0, nextCommaPosition2).stripWhiteSpace()))); |
+ pos += (safeHeader.find(',', pos) - pos) + 1; |
+ } else { |
+ // The rest is the value; no change to value needed |
+ result.append(pair<String, String>(directive, trimToNextSeparator(value))); |
+ return; |
+ } |
+ } |
+ } else if (nextCommaPosition != kNotFound && (nextCommaPosition < nextEqualSignPosition || nextEqualSignPosition == kNotFound)) { |
+ // Add directive to map with empty string as value |
+ result.append(pair<String, String>(trimToNextSeparator(safeHeader.substring(pos, nextCommaPosition - pos).stripWhiteSpace()), "")); |
+ pos += nextCommaPosition - pos + 1; |
+ } else { |
+ // Add last directive to map with empty string as value |
+ result.append(pair<String, String>(trimToNextSeparator(safeHeader.substring(pos, max - pos).stripWhiteSpace()), "")); |
+ return; |
+ } |
+ } |
+} |
+ |
+CacheControlHeader parseCacheControlDirectives(const AtomicString& cacheControlValue, const AtomicString& pragmaValue) |
+{ |
+ CacheControlHeader cacheControlHeader; |
+ cacheControlHeader.parsed = true; |
+ cacheControlHeader.maxAge = std::numeric_limits<double>::quiet_NaN(); |
+ |
+ DEFINE_STATIC_LOCAL(const AtomicString, noCacheDirective, ("no-cache", AtomicString::ConstructFromLiteral)); |
+ DEFINE_STATIC_LOCAL(const AtomicString, noStoreDirective, ("no-store", AtomicString::ConstructFromLiteral)); |
+ DEFINE_STATIC_LOCAL(const AtomicString, mustRevalidateDirective, ("must-revalidate", AtomicString::ConstructFromLiteral)); |
+ DEFINE_STATIC_LOCAL(const AtomicString, maxAgeDirective, ("max-age", AtomicString::ConstructFromLiteral)); |
+ |
+ if (!cacheControlValue.isEmpty()) { |
+ Vector<pair<String, String> > directives; |
+ parseCacheHeader(cacheControlValue, directives); |
+ |
+ size_t directivesSize = directives.size(); |
+ for (size_t i = 0; i < directivesSize; ++i) { |
+ // RFC2616 14.9.1: A no-cache directive with a value is only meaningful for proxy caches. |
+ // It should be ignored by a browser level cache. |
+ if (equalIgnoringCase(directives[i].first, noCacheDirective) && directives[i].second.isEmpty()) { |
+ cacheControlHeader.containsNoCache = true; |
+ } else if (equalIgnoringCase(directives[i].first, noStoreDirective)) { |
+ cacheControlHeader.containsNoStore = true; |
+ } else if (equalIgnoringCase(directives[i].first, mustRevalidateDirective)) { |
+ cacheControlHeader.containsMustRevalidate = true; |
+ } else if (equalIgnoringCase(directives[i].first, maxAgeDirective)) { |
+ if (!std::isnan(cacheControlHeader.maxAge)) { |
+ // First max-age directive wins if there are multiple ones. |
+ continue; |
+ } |
+ bool ok; |
+ double maxAge = directives[i].second.toDouble(&ok); |
+ if (ok) |
+ cacheControlHeader.maxAge = maxAge; |
+ } |
+ } |
+ } |
+ |
+ if (!cacheControlHeader.containsNoCache) { |
+ // Handle Pragma: no-cache |
+ // This is deprecated and equivalent to Cache-control: no-cache |
+ // Don't bother tokenizing the value, it is not important |
+ cacheControlHeader.containsNoCache = pragmaValue.lower().contains(noCacheDirective); |
+ } |
+ return cacheControlHeader; |
+} |
+ |
} |