nsHttp.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* vim:set ts=4 sw=4 sts=4 et cin: */
  3. /* This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. // HttpLog.h should generally be included first
  7. #include "HttpLog.h"
  8. #include "nsHttp.h"
  9. #include "PLDHashTable.h"
  10. #include "mozilla/Mutex.h"
  11. #include "mozilla/HashFunctions.h"
  12. #include "nsCRT.h"
  13. #include <errno.h>
  14. namespace mozilla {
  15. namespace net {
  16. // define storage for all atoms
  17. #define HTTP_ATOM(_name, _value) nsHttpAtom nsHttp::_name = { _value };
  18. #include "nsHttpAtomList.h"
  19. #undef HTTP_ATOM
  20. // find out how many atoms we have
  21. #define HTTP_ATOM(_name, _value) Unused_ ## _name,
  22. enum {
  23. #include "nsHttpAtomList.h"
  24. NUM_HTTP_ATOMS
  25. };
  26. #undef HTTP_ATOM
  27. // we keep a linked list of atoms allocated on the heap for easy clean up when
  28. // the atom table is destroyed. The structure and value string are allocated
  29. // as one contiguous block.
  30. struct HttpHeapAtom {
  31. struct HttpHeapAtom *next;
  32. char value[1];
  33. };
  34. static PLDHashTable *sAtomTable;
  35. static struct HttpHeapAtom *sHeapAtoms = nullptr;
  36. static Mutex *sLock = nullptr;
  37. HttpHeapAtom *
  38. NewHeapAtom(const char *value) {
  39. int len = strlen(value);
  40. HttpHeapAtom *a =
  41. reinterpret_cast<HttpHeapAtom *>(malloc(sizeof(*a) + len));
  42. if (!a)
  43. return nullptr;
  44. memcpy(a->value, value, len + 1);
  45. // add this heap atom to the list of all heap atoms
  46. a->next = sHeapAtoms;
  47. sHeapAtoms = a;
  48. return a;
  49. }
  50. // Hash string ignore case, based on PL_HashString
  51. static PLDHashNumber
  52. StringHash(const void *key)
  53. {
  54. PLDHashNumber h = 0;
  55. for (const char *s = reinterpret_cast<const char*>(key); *s; ++s)
  56. h = AddToHash(h, nsCRT::ToLower(*s));
  57. return h;
  58. }
  59. static bool
  60. StringCompare(const PLDHashEntryHdr *entry, const void *testKey)
  61. {
  62. const void *entryKey =
  63. reinterpret_cast<const PLDHashEntryStub *>(entry)->key;
  64. return PL_strcasecmp(reinterpret_cast<const char *>(entryKey),
  65. reinterpret_cast<const char *>(testKey)) == 0;
  66. }
  67. static const PLDHashTableOps ops = {
  68. StringHash,
  69. StringCompare,
  70. PLDHashTable::MoveEntryStub,
  71. PLDHashTable::ClearEntryStub,
  72. nullptr
  73. };
  74. // We put the atoms in a hash table for speedy lookup.. see ResolveAtom.
  75. nsresult
  76. nsHttp::CreateAtomTable()
  77. {
  78. MOZ_ASSERT(!sAtomTable, "atom table already initialized");
  79. if (!sLock) {
  80. sLock = new Mutex("nsHttp.sLock");
  81. }
  82. // The initial length for this table is a value greater than the number of
  83. // known atoms (NUM_HTTP_ATOMS) because we expect to encounter a few random
  84. // headers right off the bat.
  85. sAtomTable = new PLDHashTable(&ops, sizeof(PLDHashEntryStub),
  86. NUM_HTTP_ATOMS + 10);
  87. // fill the table with our known atoms
  88. const char *const atoms[] = {
  89. #define HTTP_ATOM(_name, _value) nsHttp::_name._val,
  90. #include "nsHttpAtomList.h"
  91. #undef HTTP_ATOM
  92. nullptr
  93. };
  94. for (int i = 0; atoms[i]; ++i) {
  95. auto stub = static_cast<PLDHashEntryStub*>
  96. (sAtomTable->Add(atoms[i], fallible));
  97. if (!stub)
  98. return NS_ERROR_OUT_OF_MEMORY;
  99. MOZ_ASSERT(!stub->key, "duplicate static atom");
  100. stub->key = atoms[i];
  101. }
  102. return NS_OK;
  103. }
  104. void
  105. nsHttp::DestroyAtomTable()
  106. {
  107. delete sAtomTable;
  108. sAtomTable = nullptr;
  109. while (sHeapAtoms) {
  110. HttpHeapAtom *next = sHeapAtoms->next;
  111. free(sHeapAtoms);
  112. sHeapAtoms = next;
  113. }
  114. delete sLock;
  115. sLock = nullptr;
  116. }
  117. Mutex *
  118. nsHttp::GetLock()
  119. {
  120. return sLock;
  121. }
  122. // this function may be called from multiple threads
  123. nsHttpAtom
  124. nsHttp::ResolveAtom(const char *str)
  125. {
  126. nsHttpAtom atom = { nullptr };
  127. if (!str || !sAtomTable)
  128. return atom;
  129. MutexAutoLock lock(*sLock);
  130. auto stub = static_cast<PLDHashEntryStub*>(sAtomTable->Add(str, fallible));
  131. if (!stub)
  132. return atom; // out of memory
  133. if (stub->key) {
  134. atom._val = reinterpret_cast<const char *>(stub->key);
  135. return atom;
  136. }
  137. // if the atom could not be found in the atom table, then we'll go
  138. // and allocate a new atom on the heap.
  139. HttpHeapAtom *heapAtom = NewHeapAtom(str);
  140. if (!heapAtom)
  141. return atom; // out of memory
  142. stub->key = atom._val = heapAtom->value;
  143. return atom;
  144. }
  145. //
  146. // From section 2.2 of RFC 2616, a token is defined as:
  147. //
  148. // token = 1*<any CHAR except CTLs or separators>
  149. // CHAR = <any US-ASCII character (octets 0 - 127)>
  150. // separators = "(" | ")" | "<" | ">" | "@"
  151. // | "," | ";" | ":" | "\" | <">
  152. // | "/" | "[" | "]" | "?" | "="
  153. // | "{" | "}" | SP | HT
  154. // CTL = <any US-ASCII control character
  155. // (octets 0 - 31) and DEL (127)>
  156. // SP = <US-ASCII SP, space (32)>
  157. // HT = <US-ASCII HT, horizontal-tab (9)>
  158. //
  159. static const char kValidTokenMap[128] = {
  160. 0, 0, 0, 0, 0, 0, 0, 0, // 0
  161. 0, 0, 0, 0, 0, 0, 0, 0, // 8
  162. 0, 0, 0, 0, 0, 0, 0, 0, // 16
  163. 0, 0, 0, 0, 0, 0, 0, 0, // 24
  164. 0, 1, 0, 1, 1, 1, 1, 1, // 32
  165. 0, 0, 1, 1, 0, 1, 1, 0, // 40
  166. 1, 1, 1, 1, 1, 1, 1, 1, // 48
  167. 1, 1, 0, 0, 0, 0, 0, 0, // 56
  168. 0, 1, 1, 1, 1, 1, 1, 1, // 64
  169. 1, 1, 1, 1, 1, 1, 1, 1, // 72
  170. 1, 1, 1, 1, 1, 1, 1, 1, // 80
  171. 1, 1, 1, 0, 0, 0, 1, 1, // 88
  172. 1, 1, 1, 1, 1, 1, 1, 1, // 96
  173. 1, 1, 1, 1, 1, 1, 1, 1, // 104
  174. 1, 1, 1, 1, 1, 1, 1, 1, // 112
  175. 1, 1, 1, 0, 1, 0, 1, 0 // 120
  176. };
  177. bool
  178. nsHttp::IsValidToken(const char *start, const char *end)
  179. {
  180. if (start == end)
  181. return false;
  182. for (; start != end; ++start) {
  183. const unsigned char idx = *start;
  184. if (idx > 127 || !kValidTokenMap[idx])
  185. return false;
  186. }
  187. return true;
  188. }
  189. const char*
  190. nsHttp::GetProtocolVersion(uint32_t pv)
  191. {
  192. switch (pv) {
  193. case HTTP_VERSION_2:
  194. case NS_HTTP_VERSION_2_0:
  195. return "h2";
  196. case NS_HTTP_VERSION_1_0:
  197. return "http/1.0";
  198. case NS_HTTP_VERSION_1_1:
  199. return "http/1.1";
  200. default:
  201. NS_WARNING(nsPrintfCString("Unkown protocol version: 0x%X. "
  202. "Please file a bug", pv).get());
  203. return "http/1.1";
  204. }
  205. }
  206. // static
  207. bool
  208. nsHttp::IsReasonableHeaderValue(const nsACString &s)
  209. {
  210. // Header values MUST NOT contain line-breaks. RFC 2616 technically
  211. // permits CTL characters, including CR and LF, in header values provided
  212. // they are quoted. However, this can lead to problems if servers do not
  213. // interpret quoted strings properly. Disallowing CR and LF here seems
  214. // reasonable and keeps things simple. We also disallow a null byte.
  215. const nsACString::char_type* end = s.EndReading();
  216. for (const nsACString::char_type* i = s.BeginReading(); i != end; ++i) {
  217. if (*i == '\r' || *i == '\n' || *i == '\0') {
  218. return false;
  219. }
  220. }
  221. return true;
  222. }
  223. const char *
  224. nsHttp::FindToken(const char *input, const char *token, const char *seps)
  225. {
  226. if (!input)
  227. return nullptr;
  228. int inputLen = strlen(input);
  229. int tokenLen = strlen(token);
  230. if (inputLen < tokenLen)
  231. return nullptr;
  232. const char *inputTop = input;
  233. const char *inputEnd = input + inputLen - tokenLen;
  234. for (; input <= inputEnd; ++input) {
  235. if (PL_strncasecmp(input, token, tokenLen) == 0) {
  236. if (input > inputTop && !strchr(seps, *(input - 1)))
  237. continue;
  238. if (input < inputEnd && !strchr(seps, *(input + tokenLen)))
  239. continue;
  240. return input;
  241. }
  242. }
  243. return nullptr;
  244. }
  245. bool
  246. nsHttp::ParseInt64(const char *input, const char **next, int64_t *r)
  247. {
  248. MOZ_ASSERT(input);
  249. MOZ_ASSERT(r);
  250. char *end = nullptr;
  251. errno = 0; // Clear errno to make sure its value is set by strtoll
  252. int64_t value = strtoll(input, &end, /* base */ 10);
  253. // Fail if: - the parsed number overflows.
  254. // - the end points to the start of the input string.
  255. // - we parsed a negative value. Consumers don't expect that.
  256. if (errno != 0 || end == input || value < 0) {
  257. LOG(("nsHttp::ParseInt64 value=%ld errno=%d", value, errno));
  258. return false;
  259. }
  260. if (next) {
  261. *next = end;
  262. }
  263. *r = value;
  264. return true;
  265. }
  266. bool
  267. nsHttp::IsPermanentRedirect(uint32_t httpStatus)
  268. {
  269. return httpStatus == 301 || httpStatus == 308;
  270. }
  271. template<typename T> void
  272. localEnsureBuffer(UniquePtr<T[]> &buf, uint32_t newSize,
  273. uint32_t preserve, uint32_t &objSize)
  274. {
  275. if (objSize >= newSize)
  276. return;
  277. // Leave a little slop on the new allocation - add 2KB to
  278. // what we need and then round the result up to a 4KB (page)
  279. // boundary.
  280. objSize = (newSize + 2048 + 4095) & ~4095;
  281. static_assert(sizeof(T) == 1, "sizeof(T) must be 1");
  282. auto tmp = MakeUnique<T[]>(objSize);
  283. if (preserve) {
  284. memcpy(tmp.get(), buf.get(), preserve);
  285. }
  286. buf = Move(tmp);
  287. }
  288. void EnsureBuffer(UniquePtr<char[]> &buf, uint32_t newSize,
  289. uint32_t preserve, uint32_t &objSize)
  290. {
  291. localEnsureBuffer<char> (buf, newSize, preserve, objSize);
  292. }
  293. void EnsureBuffer(UniquePtr<uint8_t[]> &buf, uint32_t newSize,
  294. uint32_t preserve, uint32_t &objSize)
  295. {
  296. localEnsureBuffer<uint8_t> (buf, newSize, preserve, objSize);
  297. }
  298. ///
  299. void
  300. ParsedHeaderValueList::Tokenize(char *input, uint32_t inputLen, char **token,
  301. uint32_t *tokenLen, bool *foundEquals, char **next)
  302. {
  303. if (foundEquals) {
  304. *foundEquals = false;
  305. }
  306. if (next) {
  307. *next = nullptr;
  308. }
  309. if (inputLen < 1 || !input || !token) {
  310. return;
  311. }
  312. bool foundFirst = false;
  313. bool inQuote = false;
  314. bool foundToken = false;
  315. *token = input;
  316. *tokenLen = inputLen;
  317. for (uint32_t index = 0; !foundToken && index < inputLen; ++index) {
  318. // strip leading cruft
  319. if (!foundFirst &&
  320. (input[index] == ' ' || input[index] == '"' || input[index] == '\t')) {
  321. (*token)++;
  322. } else {
  323. foundFirst = true;
  324. }
  325. if (input[index] == '"') {
  326. inQuote = !inQuote;
  327. continue;
  328. }
  329. if (inQuote) {
  330. continue;
  331. }
  332. if (input[index] == '=' || input[index] == ';') {
  333. *tokenLen = (input + index) - *token;
  334. if (next && ((index + 1) < inputLen)) {
  335. *next = input + index + 1;
  336. }
  337. foundToken = true;
  338. if (foundEquals && input[index] == '=') {
  339. *foundEquals = true;
  340. }
  341. break;
  342. }
  343. }
  344. if (!foundToken) {
  345. *tokenLen = (input + inputLen) - *token;
  346. }
  347. // strip trailing cruft
  348. for (char *index = *token + *tokenLen - 1; index >= *token; --index) {
  349. if (*index != ' ' && *index != '\t' && *index != '"') {
  350. break;
  351. }
  352. --(*tokenLen);
  353. if (*index == '"') {
  354. break;
  355. }
  356. }
  357. }
  358. ParsedHeaderValueList::ParsedHeaderValueList(char *t, uint32_t len)
  359. {
  360. char *name = nullptr;
  361. uint32_t nameLen = 0;
  362. char *value = nullptr;
  363. uint32_t valueLen = 0;
  364. char *next = nullptr;
  365. bool foundEquals;
  366. while (t) {
  367. Tokenize(t, len, &name, &nameLen, &foundEquals, &next);
  368. if (next) {
  369. len -= next - t;
  370. }
  371. t = next;
  372. if (foundEquals && t) {
  373. Tokenize(t, len, &value, &valueLen, nullptr, &next);
  374. if (next) {
  375. len -= next - t;
  376. }
  377. t = next;
  378. }
  379. mValues.AppendElement(ParsedHeaderPair(name, nameLen, value, valueLen));
  380. value = name = nullptr;
  381. valueLen = nameLen = 0;
  382. next = nullptr;
  383. }
  384. }
  385. ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader)
  386. : mFull(fullHeader)
  387. {
  388. char *t = mFull.BeginWriting();
  389. uint32_t len = mFull.Length();
  390. char *last = t;
  391. bool inQuote = false;
  392. for (uint32_t index = 0; index < len; ++index) {
  393. if (t[index] == '"') {
  394. inQuote = !inQuote;
  395. continue;
  396. }
  397. if (inQuote) {
  398. continue;
  399. }
  400. if (t[index] == ',') {
  401. mValues.AppendElement(ParsedHeaderValueList(last, (t + index) - last));
  402. last = t + index + 1;
  403. }
  404. }
  405. if (!inQuote) {
  406. mValues.AppendElement(ParsedHeaderValueList(last, (t + len) - last));
  407. }
  408. }
  409. } // namespace net
  410. } // namespace mozilla