summaryrefslogtreecommitdiff
path: root/netwerk/protocol/http/nsHttp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/http/nsHttp.cpp')
-rw-r--r--netwerk/protocol/http/nsHttp.cpp481
1 files changed, 481 insertions, 0 deletions
diff --git a/netwerk/protocol/http/nsHttp.cpp b/netwerk/protocol/http/nsHttp.cpp
new file mode 100644
index 0000000000..faf9e21ffd
--- /dev/null
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -0,0 +1,481 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
+#include "nsHttp.h"
+#include "PLDHashTable.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/HashFunctions.h"
+#include "nsCRT.h"
+#include <errno.h>
+
+namespace mozilla {
+namespace net {
+
+// define storage for all atoms
+#define HTTP_ATOM(_name, _value) nsHttpAtom nsHttp::_name = { _value };
+#include "nsHttpAtomList.h"
+#undef HTTP_ATOM
+
+// find out how many atoms we have
+#define HTTP_ATOM(_name, _value) Unused_ ## _name,
+enum {
+#include "nsHttpAtomList.h"
+ NUM_HTTP_ATOMS
+};
+#undef HTTP_ATOM
+
+// we keep a linked list of atoms allocated on the heap for easy clean up when
+// the atom table is destroyed. The structure and value string are allocated
+// as one contiguous block.
+
+struct HttpHeapAtom {
+ struct HttpHeapAtom *next;
+ char value[1];
+};
+
+static PLDHashTable *sAtomTable;
+static struct HttpHeapAtom *sHeapAtoms = nullptr;
+static Mutex *sLock = nullptr;
+
+HttpHeapAtom *
+NewHeapAtom(const char *value) {
+ int len = strlen(value);
+
+ HttpHeapAtom *a =
+ reinterpret_cast<HttpHeapAtom *>(malloc(sizeof(*a) + len));
+ if (!a)
+ return nullptr;
+ memcpy(a->value, value, len + 1);
+
+ // add this heap atom to the list of all heap atoms
+ a->next = sHeapAtoms;
+ sHeapAtoms = a;
+
+ return a;
+}
+
+// Hash string ignore case, based on PL_HashString
+static PLDHashNumber
+StringHash(const void *key)
+{
+ PLDHashNumber h = 0;
+ for (const char *s = reinterpret_cast<const char*>(key); *s; ++s)
+ h = AddToHash(h, nsCRT::ToLower(*s));
+ return h;
+}
+
+static bool
+StringCompare(const PLDHashEntryHdr *entry, const void *testKey)
+{
+ const void *entryKey =
+ reinterpret_cast<const PLDHashEntryStub *>(entry)->key;
+
+ return PL_strcasecmp(reinterpret_cast<const char *>(entryKey),
+ reinterpret_cast<const char *>(testKey)) == 0;
+}
+
+static const PLDHashTableOps ops = {
+ StringHash,
+ StringCompare,
+ PLDHashTable::MoveEntryStub,
+ PLDHashTable::ClearEntryStub,
+ nullptr
+};
+
+// We put the atoms in a hash table for speedy lookup.. see ResolveAtom.
+nsresult
+nsHttp::CreateAtomTable()
+{
+ MOZ_ASSERT(!sAtomTable, "atom table already initialized");
+
+ if (!sLock) {
+ sLock = new Mutex("nsHttp.sLock");
+ }
+
+ // The initial length for this table is a value greater than the number of
+ // known atoms (NUM_HTTP_ATOMS) because we expect to encounter a few random
+ // headers right off the bat.
+ sAtomTable = new PLDHashTable(&ops, sizeof(PLDHashEntryStub),
+ NUM_HTTP_ATOMS + 10);
+
+ // fill the table with our known atoms
+ const char *const atoms[] = {
+#define HTTP_ATOM(_name, _value) nsHttp::_name._val,
+#include "nsHttpAtomList.h"
+#undef HTTP_ATOM
+ nullptr
+ };
+
+ for (int i = 0; atoms[i]; ++i) {
+ auto stub = static_cast<PLDHashEntryStub*>
+ (sAtomTable->Add(atoms[i], fallible));
+ if (!stub)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ MOZ_ASSERT(!stub->key, "duplicate static atom");
+ stub->key = atoms[i];
+ }
+
+ return NS_OK;
+}
+
+void
+nsHttp::DestroyAtomTable()
+{
+ delete sAtomTable;
+ sAtomTable = nullptr;
+
+ while (sHeapAtoms) {
+ HttpHeapAtom *next = sHeapAtoms->next;
+ free(sHeapAtoms);
+ sHeapAtoms = next;
+ }
+
+ delete sLock;
+ sLock = nullptr;
+}
+
+Mutex *
+nsHttp::GetLock()
+{
+ return sLock;
+}
+
+// this function may be called from multiple threads
+nsHttpAtom
+nsHttp::ResolveAtom(const char *str)
+{
+ nsHttpAtom atom = { nullptr };
+
+ if (!str || !sAtomTable)
+ return atom;
+
+ MutexAutoLock lock(*sLock);
+
+ auto stub = static_cast<PLDHashEntryStub*>(sAtomTable->Add(str, fallible));
+ if (!stub)
+ return atom; // out of memory
+
+ if (stub->key) {
+ atom._val = reinterpret_cast<const char *>(stub->key);
+ return atom;
+ }
+
+ // if the atom could not be found in the atom table, then we'll go
+ // and allocate a new atom on the heap.
+ HttpHeapAtom *heapAtom = NewHeapAtom(str);
+ if (!heapAtom)
+ return atom; // out of memory
+
+ stub->key = atom._val = heapAtom->value;
+ return atom;
+}
+
+//
+// From section 2.2 of RFC 2616, a token is defined as:
+//
+// token = 1*<any CHAR except CTLs or separators>
+// CHAR = <any US-ASCII character (octets 0 - 127)>
+// separators = "(" | ")" | "<" | ">" | "@"
+// | "," | ";" | ":" | "\" | <">
+// | "/" | "[" | "]" | "?" | "="
+// | "{" | "}" | SP | HT
+// CTL = <any US-ASCII control character
+// (octets 0 - 31) and DEL (127)>
+// SP = <US-ASCII SP, space (32)>
+// HT = <US-ASCII HT, horizontal-tab (9)>
+//
+static const char kValidTokenMap[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, // 8
+ 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 0, 0, 0, 0, 0, 0, 0, 0, // 24
+
+ 0, 1, 0, 1, 1, 1, 1, 1, // 32
+ 0, 0, 1, 1, 0, 1, 1, 0, // 40
+ 1, 1, 1, 1, 1, 1, 1, 1, // 48
+ 1, 1, 0, 0, 0, 0, 0, 0, // 56
+
+ 0, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, // 72
+ 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 0, 0, 0, 1, 1, // 88
+
+ 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, // 104
+ 1, 1, 1, 1, 1, 1, 1, 1, // 112
+ 1, 1, 1, 0, 1, 0, 1, 0 // 120
+};
+bool
+nsHttp::IsValidToken(const char *start, const char *end)
+{
+ if (start == end)
+ return false;
+
+ for (; start != end; ++start) {
+ const unsigned char idx = *start;
+ if (idx > 127 || !kValidTokenMap[idx])
+ return false;
+ }
+
+ return true;
+}
+
+const char*
+nsHttp::GetProtocolVersion(uint32_t pv)
+{
+ switch (pv) {
+ case HTTP_VERSION_2:
+ case NS_HTTP_VERSION_2_0:
+ return "h2";
+ case NS_HTTP_VERSION_1_0:
+ return "http/1.0";
+ case NS_HTTP_VERSION_1_1:
+ return "http/1.1";
+ default:
+ NS_WARNING(nsPrintfCString("Unkown protocol version: 0x%X. "
+ "Please file a bug", pv).get());
+ return "http/1.1";
+ }
+}
+
+// static
+bool
+nsHttp::IsReasonableHeaderValue(const nsACString &s)
+{
+ // Header values MUST NOT contain line-breaks. RFC 2616 technically
+ // permits CTL characters, including CR and LF, in header values provided
+ // they are quoted. However, this can lead to problems if servers do not
+ // interpret quoted strings properly. Disallowing CR and LF here seems
+ // reasonable and keeps things simple. We also disallow a null byte.
+ const nsACString::char_type* end = s.EndReading();
+ for (const nsACString::char_type* i = s.BeginReading(); i != end; ++i) {
+ if (*i == '\r' || *i == '\n' || *i == '\0') {
+ return false;
+ }
+ }
+ return true;
+}
+
+const char *
+nsHttp::FindToken(const char *input, const char *token, const char *seps)
+{
+ if (!input)
+ return nullptr;
+
+ int inputLen = strlen(input);
+ int tokenLen = strlen(token);
+
+ if (inputLen < tokenLen)
+ return nullptr;
+
+ const char *inputTop = input;
+ const char *inputEnd = input + inputLen - tokenLen;
+ for (; input <= inputEnd; ++input) {
+ if (PL_strncasecmp(input, token, tokenLen) == 0) {
+ if (input > inputTop && !strchr(seps, *(input - 1)))
+ continue;
+ if (input < inputEnd && !strchr(seps, *(input + tokenLen)))
+ continue;
+ return input;
+ }
+ }
+
+ return nullptr;
+}
+
+bool
+nsHttp::ParseInt64(const char *input, const char **next, int64_t *r)
+{
+ MOZ_ASSERT(input);
+ MOZ_ASSERT(r);
+
+ char *end = nullptr;
+ errno = 0; // Clear errno to make sure its value is set by strtoll
+ int64_t value = strtoll(input, &end, /* base */ 10);
+
+ // Fail if: - the parsed number overflows.
+ // - the end points to the start of the input string.
+ // - we parsed a negative value. Consumers don't expect that.
+ if (errno != 0 || end == input || value < 0) {
+ LOG(("nsHttp::ParseInt64 value=%ld errno=%d", value, errno));
+ return false;
+ }
+
+ if (next) {
+ *next = end;
+ }
+ *r = value;
+ return true;
+}
+
+bool
+nsHttp::IsPermanentRedirect(uint32_t httpStatus)
+{
+ return httpStatus == 301 || httpStatus == 308;
+}
+
+
+template<typename T> void
+localEnsureBuffer(UniquePtr<T[]> &buf, uint32_t newSize,
+ uint32_t preserve, uint32_t &objSize)
+{
+ if (objSize >= newSize)
+ return;
+
+ // Leave a little slop on the new allocation - add 2KB to
+ // what we need and then round the result up to a 4KB (page)
+ // boundary.
+
+ objSize = (newSize + 2048 + 4095) & ~4095;
+
+ static_assert(sizeof(T) == 1, "sizeof(T) must be 1");
+ auto tmp = MakeUnique<T[]>(objSize);
+ if (preserve) {
+ memcpy(tmp.get(), buf.get(), preserve);
+ }
+ buf = Move(tmp);
+}
+
+void EnsureBuffer(UniquePtr<char[]> &buf, uint32_t newSize,
+ uint32_t preserve, uint32_t &objSize)
+{
+ localEnsureBuffer<char> (buf, newSize, preserve, objSize);
+}
+
+void EnsureBuffer(UniquePtr<uint8_t[]> &buf, uint32_t newSize,
+ uint32_t preserve, uint32_t &objSize)
+{
+ localEnsureBuffer<uint8_t> (buf, newSize, preserve, objSize);
+}
+///
+
+void
+ParsedHeaderValueList::Tokenize(char *input, uint32_t inputLen, char **token,
+ uint32_t *tokenLen, bool *foundEquals, char **next)
+{
+ if (foundEquals) {
+ *foundEquals = false;
+ }
+ if (next) {
+ *next = nullptr;
+ }
+ if (inputLen < 1 || !input || !token) {
+ return;
+ }
+
+ bool foundFirst = false;
+ bool inQuote = false;
+ bool foundToken = false;
+ *token = input;
+ *tokenLen = inputLen;
+
+ for (uint32_t index = 0; !foundToken && index < inputLen; ++index) {
+ // strip leading cruft
+ if (!foundFirst &&
+ (input[index] == ' ' || input[index] == '"' || input[index] == '\t')) {
+ (*token)++;
+ } else {
+ foundFirst = true;
+ }
+
+ if (input[index] == '"') {
+ inQuote = !inQuote;
+ continue;
+ }
+
+ if (inQuote) {
+ continue;
+ }
+
+ if (input[index] == '=' || input[index] == ';') {
+ *tokenLen = (input + index) - *token;
+ if (next && ((index + 1) < inputLen)) {
+ *next = input + index + 1;
+ }
+ foundToken = true;
+ if (foundEquals && input[index] == '=') {
+ *foundEquals = true;
+ }
+ break;
+ }
+ }
+
+ if (!foundToken) {
+ *tokenLen = (input + inputLen) - *token;
+ }
+
+ // strip trailing cruft
+ for (char *index = *token + *tokenLen - 1; index >= *token; --index) {
+ if (*index != ' ' && *index != '\t' && *index != '"') {
+ break;
+ }
+ --(*tokenLen);
+ if (*index == '"') {
+ break;
+ }
+ }
+}
+
+ParsedHeaderValueList::ParsedHeaderValueList(char *t, uint32_t len)
+{
+ char *name = nullptr;
+ uint32_t nameLen = 0;
+ char *value = nullptr;
+ uint32_t valueLen = 0;
+ char *next = nullptr;
+ bool foundEquals;
+
+ while (t) {
+ Tokenize(t, len, &name, &nameLen, &foundEquals, &next);
+ if (next) {
+ len -= next - t;
+ }
+ t = next;
+ if (foundEquals && t) {
+ Tokenize(t, len, &value, &valueLen, nullptr, &next);
+ if (next) {
+ len -= next - t;
+ }
+ t = next;
+ }
+ mValues.AppendElement(ParsedHeaderPair(name, nameLen, value, valueLen));
+ value = name = nullptr;
+ valueLen = nameLen = 0;
+ next = nullptr;
+ }
+}
+
+ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader)
+ : mFull(fullHeader)
+{
+ char *t = mFull.BeginWriting();
+ uint32_t len = mFull.Length();
+ char *last = t;
+ bool inQuote = false;
+ for (uint32_t index = 0; index < len; ++index) {
+ if (t[index] == '"') {
+ inQuote = !inQuote;
+ continue;
+ }
+ if (inQuote) {
+ continue;
+ }
+ if (t[index] == ',') {
+ mValues.AppendElement(ParsedHeaderValueList(last, (t + index) - last));
+ last = t + index + 1;
+ }
+ }
+ if (!inQuote) {
+ mValues.AppendElement(ParsedHeaderValueList(last, (t + len) - last));
+ }
+}
+
+} // namespace net
+} // namespace mozilla