summaryrefslogtreecommitdiff
path: root/Source/platform/network/HTTPParsers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/platform/network/HTTPParsers.cpp')
-rw-r--r--Source/platform/network/HTTPParsers.cpp143
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;
+}
+
}