| 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;
|
| +}
|
| +
|
| }
|
|
|