diff options
Diffstat (limited to 'Source/platform/network/HTTPParsers.cpp')
-rw-r--r-- | Source/platform/network/HTTPParsers.cpp | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/Source/platform/network/HTTPParsers.cpp b/Source/platform/network/HTTPParsers.cpp index d833fe14e..3763f841a 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; +} + } |