Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / network / HTTPParsers.cpp
index d833fe1..3763f84 100644 (file)
@@ -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;
+}
+
 }