michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:set ts=4 sw=4 sts=4 et cin: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: michael@0: #include "nsHttp.h" michael@0: #include "pldhash.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/HashFunctions.h" michael@0: #include "nsCRT.h" michael@0: michael@0: #if defined(PR_LOGGING) michael@0: PRLogModuleInfo *gHttpLog = nullptr; michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: // define storage for all atoms michael@0: #define HTTP_ATOM(_name, _value) nsHttpAtom nsHttp::_name = { _value }; michael@0: #include "nsHttpAtomList.h" michael@0: #undef HTTP_ATOM michael@0: michael@0: // find out how many atoms we have michael@0: #define HTTP_ATOM(_name, _value) Unused_ ## _name, michael@0: enum { michael@0: #include "nsHttpAtomList.h" michael@0: NUM_HTTP_ATOMS michael@0: }; michael@0: #undef HTTP_ATOM michael@0: michael@0: // we keep a linked list of atoms allocated on the heap for easy clean up when michael@0: // the atom table is destroyed. The structure and value string are allocated michael@0: // as one contiguous block. michael@0: michael@0: struct HttpHeapAtom { michael@0: struct HttpHeapAtom *next; michael@0: char value[1]; michael@0: }; michael@0: michael@0: static struct PLDHashTable sAtomTable = {0}; michael@0: static struct HttpHeapAtom *sHeapAtoms = nullptr; michael@0: static Mutex *sLock = nullptr; michael@0: michael@0: HttpHeapAtom * michael@0: NewHeapAtom(const char *value) { michael@0: int len = strlen(value); michael@0: michael@0: HttpHeapAtom *a = michael@0: reinterpret_cast(malloc(sizeof(*a) + len)); michael@0: if (!a) michael@0: return nullptr; michael@0: memcpy(a->value, value, len + 1); michael@0: michael@0: // add this heap atom to the list of all heap atoms michael@0: a->next = sHeapAtoms; michael@0: sHeapAtoms = a; michael@0: michael@0: return a; michael@0: } michael@0: michael@0: // Hash string ignore case, based on PL_HashString michael@0: static PLDHashNumber michael@0: StringHash(PLDHashTable *table, const void *key) michael@0: { michael@0: PLDHashNumber h = 0; michael@0: for (const char *s = reinterpret_cast(key); *s; ++s) michael@0: h = AddToHash(h, nsCRT::ToLower(*s)); michael@0: return h; michael@0: } michael@0: michael@0: static bool michael@0: StringCompare(PLDHashTable *table, const PLDHashEntryHdr *entry, michael@0: const void *testKey) michael@0: { michael@0: const void *entryKey = michael@0: reinterpret_cast(entry)->key; michael@0: michael@0: return PL_strcasecmp(reinterpret_cast(entryKey), michael@0: reinterpret_cast(testKey)) == 0; michael@0: } michael@0: michael@0: static const PLDHashTableOps ops = { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: StringHash, michael@0: StringCompare, michael@0: PL_DHashMoveEntryStub, michael@0: PL_DHashClearEntryStub, michael@0: PL_DHashFinalizeStub, michael@0: nullptr michael@0: }; michael@0: michael@0: // We put the atoms in a hash table for speedy lookup.. see ResolveAtom. michael@0: nsresult michael@0: nsHttp::CreateAtomTable() michael@0: { michael@0: MOZ_ASSERT(!sAtomTable.ops, "atom table already initialized"); michael@0: michael@0: if (!sLock) { michael@0: sLock = new Mutex("nsHttp.sLock"); michael@0: } michael@0: michael@0: // The capacity for this table is initialized to a value greater than the michael@0: // number of known atoms (NUM_HTTP_ATOMS) because we expect to encounter a michael@0: // few random headers right off the bat. michael@0: if (!PL_DHashTableInit(&sAtomTable, &ops, nullptr, michael@0: sizeof(PLDHashEntryStub), michael@0: NUM_HTTP_ATOMS + 10, fallible_t())) { michael@0: sAtomTable.ops = nullptr; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: // fill the table with our known atoms michael@0: const char *const atoms[] = { michael@0: #define HTTP_ATOM(_name, _value) nsHttp::_name._val, michael@0: #include "nsHttpAtomList.h" michael@0: #undef HTTP_ATOM michael@0: nullptr michael@0: }; michael@0: michael@0: for (int i = 0; atoms[i]; ++i) { michael@0: PLDHashEntryStub *stub = reinterpret_cast michael@0: (PL_DHashTableOperate(&sAtomTable, atoms[i], PL_DHASH_ADD)); michael@0: if (!stub) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: MOZ_ASSERT(!stub->key, "duplicate static atom"); michael@0: stub->key = atoms[i]; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsHttp::DestroyAtomTable() michael@0: { michael@0: if (sAtomTable.ops) { michael@0: PL_DHashTableFinish(&sAtomTable); michael@0: sAtomTable.ops = nullptr; michael@0: } michael@0: michael@0: while (sHeapAtoms) { michael@0: HttpHeapAtom *next = sHeapAtoms->next; michael@0: free(sHeapAtoms); michael@0: sHeapAtoms = next; michael@0: } michael@0: michael@0: if (sLock) { michael@0: delete sLock; michael@0: sLock = nullptr; michael@0: } michael@0: } michael@0: michael@0: Mutex * michael@0: nsHttp::GetLock() michael@0: { michael@0: return sLock; michael@0: } michael@0: michael@0: // this function may be called from multiple threads michael@0: nsHttpAtom michael@0: nsHttp::ResolveAtom(const char *str) michael@0: { michael@0: nsHttpAtom atom = { nullptr }; michael@0: michael@0: if (!str || !sAtomTable.ops) michael@0: return atom; michael@0: michael@0: MutexAutoLock lock(*sLock); michael@0: michael@0: PLDHashEntryStub *stub = reinterpret_cast michael@0: (PL_DHashTableOperate(&sAtomTable, str, PL_DHASH_ADD)); michael@0: if (!stub) michael@0: return atom; // out of memory michael@0: michael@0: if (stub->key) { michael@0: atom._val = reinterpret_cast(stub->key); michael@0: return atom; michael@0: } michael@0: michael@0: // if the atom could not be found in the atom table, then we'll go michael@0: // and allocate a new atom on the heap. michael@0: HttpHeapAtom *heapAtom = NewHeapAtom(str); michael@0: if (!heapAtom) michael@0: return atom; // out of memory michael@0: michael@0: stub->key = atom._val = heapAtom->value; michael@0: return atom; michael@0: } michael@0: michael@0: // michael@0: // From section 2.2 of RFC 2616, a token is defined as: michael@0: // michael@0: // token = 1* michael@0: // CHAR = michael@0: // separators = "(" | ")" | "<" | ">" | "@" michael@0: // | "," | ";" | ":" | "\" | <"> michael@0: // | "/" | "[" | "]" | "?" | "=" michael@0: // | "{" | "}" | SP | HT michael@0: // CTL = michael@0: // SP = michael@0: // HT = michael@0: // michael@0: static const char kValidTokenMap[128] = { michael@0: 0, 0, 0, 0, 0, 0, 0, 0, // 0 michael@0: 0, 0, 0, 0, 0, 0, 0, 0, // 8 michael@0: 0, 0, 0, 0, 0, 0, 0, 0, // 16 michael@0: 0, 0, 0, 0, 0, 0, 0, 0, // 24 michael@0: michael@0: 0, 1, 0, 1, 1, 1, 1, 1, // 32 michael@0: 0, 0, 1, 1, 0, 1, 1, 0, // 40 michael@0: 1, 1, 1, 1, 1, 1, 1, 1, // 48 michael@0: 1, 1, 0, 0, 0, 0, 0, 0, // 56 michael@0: michael@0: 0, 1, 1, 1, 1, 1, 1, 1, // 64 michael@0: 1, 1, 1, 1, 1, 1, 1, 1, // 72 michael@0: 1, 1, 1, 1, 1, 1, 1, 1, // 80 michael@0: 1, 1, 1, 0, 0, 0, 1, 1, // 88 michael@0: michael@0: 1, 1, 1, 1, 1, 1, 1, 1, // 96 michael@0: 1, 1, 1, 1, 1, 1, 1, 1, // 104 michael@0: 1, 1, 1, 1, 1, 1, 1, 1, // 112 michael@0: 1, 1, 1, 0, 1, 0, 1, 0 // 120 michael@0: }; michael@0: bool michael@0: nsHttp::IsValidToken(const char *start, const char *end) michael@0: { michael@0: if (start == end) michael@0: return false; michael@0: michael@0: for (; start != end; ++start) { michael@0: const unsigned char idx = *start; michael@0: if (idx > 127 || !kValidTokenMap[idx]) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: const char * michael@0: nsHttp::FindToken(const char *input, const char *token, const char *seps) michael@0: { michael@0: if (!input) michael@0: return nullptr; michael@0: michael@0: int inputLen = strlen(input); michael@0: int tokenLen = strlen(token); michael@0: michael@0: if (inputLen < tokenLen) michael@0: return nullptr; michael@0: michael@0: const char *inputTop = input; michael@0: const char *inputEnd = input + inputLen - tokenLen; michael@0: for (; input <= inputEnd; ++input) { michael@0: if (PL_strncasecmp(input, token, tokenLen) == 0) { michael@0: if (input > inputTop && !strchr(seps, *(input - 1))) michael@0: continue; michael@0: if (input < inputEnd && !strchr(seps, *(input + tokenLen))) michael@0: continue; michael@0: return input; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsHttp::ParseInt64(const char *input, const char **next, int64_t *r) michael@0: { michael@0: const char *start = input; michael@0: *r = 0; michael@0: while (*input >= '0' && *input <= '9') { michael@0: int64_t next = 10 * (*r) + (*input - '0'); michael@0: if (next < *r) // overflow? michael@0: return false; michael@0: *r = next; michael@0: ++input; michael@0: } michael@0: if (input == start) // nothing parsed? michael@0: return false; michael@0: if (next) michael@0: *next = input; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsHttp::IsPermanentRedirect(uint32_t httpStatus) michael@0: { michael@0: return httpStatus == 301 || httpStatus == 308; michael@0: } michael@0: michael@0: } // namespace mozilla::net michael@0: } // namespace mozilla