123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- /* -*- 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
|