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