/src/mozilla-central/netwerk/protocol/http/nsHttp.cpp
Line | Count | Source (jump to first uncovered line) |
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 "CacheControlParser.h" |
12 | | #include "PLDHashTable.h" |
13 | | #include "mozilla/Mutex.h" |
14 | | #include "mozilla/HashFunctions.h" |
15 | | #include "nsCRT.h" |
16 | | #include "nsHttpRequestHead.h" |
17 | | #include "nsHttpResponseHead.h" |
18 | | #include "nsHttpHandler.h" |
19 | | #include "nsICacheEntry.h" |
20 | | #include "nsIRequest.h" |
21 | | #include <errno.h> |
22 | | #include <functional> |
23 | | |
24 | | namespace mozilla { |
25 | | namespace net { |
26 | | |
27 | | // define storage for all atoms |
28 | | namespace nsHttp { |
29 | | #define HTTP_ATOM(_name, _value) nsHttpAtom _name = { _value }; |
30 | | #include "nsHttpAtomList.h" |
31 | | #undef HTTP_ATOM |
32 | | } |
33 | | |
34 | | // find out how many atoms we have |
35 | | #define HTTP_ATOM(_name, _value) Unused_ ## _name, |
36 | | enum { |
37 | | #include "nsHttpAtomList.h" |
38 | | NUM_HTTP_ATOMS |
39 | | }; |
40 | | #undef HTTP_ATOM |
41 | | |
42 | | // we keep a linked list of atoms allocated on the heap for easy clean up when |
43 | | // the atom table is destroyed. The structure and value string are allocated |
44 | | // as one contiguous block. |
45 | | |
46 | | struct HttpHeapAtom { |
47 | | struct HttpHeapAtom *next; |
48 | | char value[1]; |
49 | | }; |
50 | | |
51 | | static PLDHashTable *sAtomTable; |
52 | | static struct HttpHeapAtom *sHeapAtoms = nullptr; |
53 | | static Mutex *sLock = nullptr; |
54 | | |
55 | | HttpHeapAtom * |
56 | 0 | NewHeapAtom(const char *value) { |
57 | 0 | int len = strlen(value); |
58 | 0 |
|
59 | 0 | HttpHeapAtom *a = |
60 | 0 | reinterpret_cast<HttpHeapAtom *>(malloc(sizeof(*a) + len)); |
61 | 0 | if (!a) |
62 | 0 | return nullptr; |
63 | 0 | memcpy(a->value, value, len + 1); |
64 | 0 |
|
65 | 0 | // add this heap atom to the list of all heap atoms |
66 | 0 | a->next = sHeapAtoms; |
67 | 0 | sHeapAtoms = a; |
68 | 0 |
|
69 | 0 | return a; |
70 | 0 | } |
71 | | |
72 | | // Hash string ignore case, based on PL_HashString |
73 | | static PLDHashNumber |
74 | | StringHash(const void *key) |
75 | 80 | { |
76 | 80 | PLDHashNumber h = 0; |
77 | 944 | for (const char *s = reinterpret_cast<const char*>(key); *s; ++s) |
78 | 864 | h = AddToHash(h, nsCRT::ToLower(*s)); |
79 | 80 | return h; |
80 | 80 | } |
81 | | |
82 | | static bool |
83 | | StringCompare(const PLDHashEntryHdr *entry, const void *testKey) |
84 | 0 | { |
85 | 0 | const void *entryKey = |
86 | 0 | reinterpret_cast<const PLDHashEntryStub *>(entry)->key; |
87 | 0 |
|
88 | 0 | return PL_strcasecmp(reinterpret_cast<const char *>(entryKey), |
89 | 0 | reinterpret_cast<const char *>(testKey)) == 0; |
90 | 0 | } |
91 | | |
92 | | static const PLDHashTableOps ops = { |
93 | | StringHash, |
94 | | StringCompare, |
95 | | PLDHashTable::MoveEntryStub, |
96 | | PLDHashTable::ClearEntryStub, |
97 | | nullptr |
98 | | }; |
99 | | |
100 | | // We put the atoms in a hash table for speedy lookup.. see ResolveAtom. |
101 | | namespace nsHttp { |
102 | | nsresult |
103 | | CreateAtomTable() |
104 | 1 | { |
105 | 1 | MOZ_ASSERT(!sAtomTable, "atom table already initialized"); |
106 | 1 | |
107 | 1 | if (!sLock) { |
108 | 1 | sLock = new Mutex("nsHttp.sLock"); |
109 | 1 | } |
110 | 1 | |
111 | 1 | // The initial length for this table is a value greater than the number of |
112 | 1 | // known atoms (NUM_HTTP_ATOMS) because we expect to encounter a few random |
113 | 1 | // headers right off the bat. |
114 | 1 | sAtomTable = new PLDHashTable(&ops, sizeof(PLDHashEntryStub), |
115 | 1 | NUM_HTTP_ATOMS + 10); |
116 | 1 | |
117 | 1 | // fill the table with our known atoms |
118 | 1 | const char *const atoms[] = { |
119 | 80 | #define HTTP_ATOM(_name, _value) _name._val, |
120 | 1 | #include "nsHttpAtomList.h" |
121 | 1 | #undef HTTP_ATOM |
122 | 1 | nullptr |
123 | 1 | }; |
124 | 1 | |
125 | 81 | for (int i = 0; atoms[i]; ++i) { |
126 | 80 | auto stub = static_cast<PLDHashEntryStub*> |
127 | 80 | (sAtomTable->Add(atoms[i], fallible)); |
128 | 80 | if (!stub) |
129 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
130 | 80 | |
131 | 80 | MOZ_ASSERT(!stub->key, "duplicate static atom"); |
132 | 80 | stub->key = atoms[i]; |
133 | 80 | } |
134 | 1 | |
135 | 1 | return NS_OK; |
136 | 1 | } |
137 | | |
138 | | void |
139 | | DestroyAtomTable() |
140 | 0 | { |
141 | 0 | delete sAtomTable; |
142 | 0 | sAtomTable = nullptr; |
143 | 0 |
|
144 | 0 | while (sHeapAtoms) { |
145 | 0 | HttpHeapAtom *next = sHeapAtoms->next; |
146 | 0 | free(sHeapAtoms); |
147 | 0 | sHeapAtoms = next; |
148 | 0 | } |
149 | 0 |
|
150 | 0 | delete sLock; |
151 | 0 | sLock = nullptr; |
152 | 0 | } |
153 | | |
154 | | Mutex * |
155 | | GetLock() |
156 | 0 | { |
157 | 0 | return sLock; |
158 | 0 | } |
159 | | |
160 | | // this function may be called from multiple threads |
161 | | nsHttpAtom |
162 | | ResolveAtom(const char *str) |
163 | 0 | { |
164 | 0 | nsHttpAtom atom = { nullptr }; |
165 | 0 |
|
166 | 0 | if (!str || !sAtomTable) |
167 | 0 | return atom; |
168 | 0 | |
169 | 0 | MutexAutoLock lock(*sLock); |
170 | 0 |
|
171 | 0 | auto stub = static_cast<PLDHashEntryStub*>(sAtomTable->Add(str, fallible)); |
172 | 0 | if (!stub) |
173 | 0 | return atom; // out of memory |
174 | 0 | |
175 | 0 | if (stub->key) { |
176 | 0 | atom._val = reinterpret_cast<const char *>(stub->key); |
177 | 0 | return atom; |
178 | 0 | } |
179 | 0 | |
180 | 0 | // if the atom could not be found in the atom table, then we'll go |
181 | 0 | // and allocate a new atom on the heap. |
182 | 0 | HttpHeapAtom *heapAtom = NewHeapAtom(str); |
183 | 0 | if (!heapAtom) |
184 | 0 | return atom; // out of memory |
185 | 0 | |
186 | 0 | stub->key = atom._val = heapAtom->value; |
187 | 0 | return atom; |
188 | 0 | } |
189 | | |
190 | | // |
191 | | // From section 2.2 of RFC 2616, a token is defined as: |
192 | | // |
193 | | // token = 1*<any CHAR except CTLs or separators> |
194 | | // CHAR = <any US-ASCII character (octets 0 - 127)> |
195 | | // separators = "(" | ")" | "<" | ">" | "@" |
196 | | // | "," | ";" | ":" | "\" | <"> |
197 | | // | "/" | "[" | "]" | "?" | "=" |
198 | | // | "{" | "}" | SP | HT |
199 | | // CTL = <any US-ASCII control character |
200 | | // (octets 0 - 31) and DEL (127)> |
201 | | // SP = <US-ASCII SP, space (32)> |
202 | | // HT = <US-ASCII HT, horizontal-tab (9)> |
203 | | // |
204 | | static const char kValidTokenMap[128] = { |
205 | | 0, 0, 0, 0, 0, 0, 0, 0, // 0 |
206 | | 0, 0, 0, 0, 0, 0, 0, 0, // 8 |
207 | | 0, 0, 0, 0, 0, 0, 0, 0, // 16 |
208 | | 0, 0, 0, 0, 0, 0, 0, 0, // 24 |
209 | | |
210 | | 0, 1, 0, 1, 1, 1, 1, 1, // 32 |
211 | | 0, 0, 1, 1, 0, 1, 1, 0, // 40 |
212 | | 1, 1, 1, 1, 1, 1, 1, 1, // 48 |
213 | | 1, 1, 0, 0, 0, 0, 0, 0, // 56 |
214 | | |
215 | | 0, 1, 1, 1, 1, 1, 1, 1, // 64 |
216 | | 1, 1, 1, 1, 1, 1, 1, 1, // 72 |
217 | | 1, 1, 1, 1, 1, 1, 1, 1, // 80 |
218 | | 1, 1, 1, 0, 0, 0, 1, 1, // 88 |
219 | | |
220 | | 1, 1, 1, 1, 1, 1, 1, 1, // 96 |
221 | | 1, 1, 1, 1, 1, 1, 1, 1, // 104 |
222 | | 1, 1, 1, 1, 1, 1, 1, 1, // 112 |
223 | | 1, 1, 1, 0, 1, 0, 1, 0 // 120 |
224 | | }; |
225 | | bool |
226 | | IsValidToken(const char *start, const char *end) |
227 | 0 | { |
228 | 0 | if (start == end) |
229 | 0 | return false; |
230 | 0 | |
231 | 0 | for (; start != end; ++start) { |
232 | 0 | const unsigned char idx = *start; |
233 | 0 | if (idx > 127 || !kValidTokenMap[idx]) |
234 | 0 | return false; |
235 | 0 | } |
236 | 0 |
|
237 | 0 | return true; |
238 | 0 | } |
239 | | |
240 | | const char* |
241 | | GetProtocolVersion(HttpVersion pv) |
242 | 0 | { |
243 | 0 | switch (pv) { |
244 | 0 | case HttpVersion::v2_0: |
245 | 0 | return "h2"; |
246 | 0 | case HttpVersion::v1_0: |
247 | 0 | return "http/1.0"; |
248 | 0 | case HttpVersion::v1_1: |
249 | 0 | return "http/1.1"; |
250 | 0 | default: |
251 | 0 | NS_WARNING(nsPrintfCString("Unkown protocol version: 0x%X. " |
252 | 0 | "Please file a bug", static_cast<uint32_t>(pv)).get()); |
253 | 0 | return "http/1.1"; |
254 | 0 | } |
255 | 0 | } |
256 | | |
257 | | // static |
258 | | void |
259 | | TrimHTTPWhitespace(const nsACString& aSource, nsACString& aDest) |
260 | 0 | { |
261 | 0 | nsAutoCString str(aSource); |
262 | 0 |
|
263 | 0 | // HTTP whitespace 0x09: '\t', 0x0A: '\n', 0x0D: '\r', 0x20: ' ' |
264 | 0 | static const char kHTTPWhitespace[] = "\t\n\r "; |
265 | 0 | str.Trim(kHTTPWhitespace); |
266 | 0 | aDest.Assign(str); |
267 | 0 | } |
268 | | |
269 | | // static |
270 | | bool |
271 | | IsReasonableHeaderValue(const nsACString &s) |
272 | 0 | { |
273 | 0 | // Header values MUST NOT contain line-breaks. RFC 2616 technically |
274 | 0 | // permits CTL characters, including CR and LF, in header values provided |
275 | 0 | // they are quoted. However, this can lead to problems if servers do not |
276 | 0 | // interpret quoted strings properly. Disallowing CR and LF here seems |
277 | 0 | // reasonable and keeps things simple. We also disallow a null byte. |
278 | 0 | const nsACString::char_type* end = s.EndReading(); |
279 | 0 | for (const nsACString::char_type* i = s.BeginReading(); i != end; ++i) { |
280 | 0 | if (*i == '\r' || *i == '\n' || *i == '\0') { |
281 | 0 | return false; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | return true; |
285 | 0 | } |
286 | | |
287 | | const char * |
288 | | FindToken(const char *input, const char *token, const char *seps) |
289 | 0 | { |
290 | 0 | if (!input) |
291 | 0 | return nullptr; |
292 | 0 | |
293 | 0 | int inputLen = strlen(input); |
294 | 0 | int tokenLen = strlen(token); |
295 | 0 |
|
296 | 0 | if (inputLen < tokenLen) |
297 | 0 | return nullptr; |
298 | 0 | |
299 | 0 | const char *inputTop = input; |
300 | 0 | const char *inputEnd = input + inputLen - tokenLen; |
301 | 0 | for (; input <= inputEnd; ++input) { |
302 | 0 | if (PL_strncasecmp(input, token, tokenLen) == 0) { |
303 | 0 | if (input > inputTop && !strchr(seps, *(input - 1))) |
304 | 0 | continue; |
305 | 0 | if (input < inputEnd && !strchr(seps, *(input + tokenLen))) |
306 | 0 | continue; |
307 | 0 | return input; |
308 | 0 | } |
309 | 0 | } |
310 | 0 |
|
311 | 0 | return nullptr; |
312 | 0 | } |
313 | | |
314 | | bool |
315 | | ParseInt64(const char *input, const char **next, int64_t *r) |
316 | 0 | { |
317 | 0 | MOZ_ASSERT(input); |
318 | 0 | MOZ_ASSERT(r); |
319 | 0 |
|
320 | 0 | char *end = nullptr; |
321 | 0 | errno = 0; // Clear errno to make sure its value is set by strtoll |
322 | 0 | int64_t value = strtoll(input, &end, /* base */ 10); |
323 | 0 |
|
324 | 0 | // Fail if: - the parsed number overflows. |
325 | 0 | // - the end points to the start of the input string. |
326 | 0 | // - we parsed a negative value. Consumers don't expect that. |
327 | 0 | if (errno != 0 || end == input || value < 0) { |
328 | 0 | LOG(("nsHttp::ParseInt64 value=%" PRId64 " errno=%d", value, errno)); |
329 | 0 | return false; |
330 | 0 | } |
331 | 0 |
|
332 | 0 | if (next) { |
333 | 0 | *next = end; |
334 | 0 | } |
335 | 0 | *r = value; |
336 | 0 | return true; |
337 | 0 | } |
338 | | |
339 | | bool |
340 | | IsPermanentRedirect(uint32_t httpStatus) |
341 | 0 | { |
342 | 0 | return httpStatus == 301 || httpStatus == 308; |
343 | 0 | } |
344 | | |
345 | | bool |
346 | | ValidationRequired(bool isForcedValid, nsHttpResponseHead *cachedResponseHead, |
347 | | uint32_t loadFlags, bool allowStaleCacheContent, |
348 | | bool isImmutable, bool customConditionalRequest, |
349 | | nsHttpRequestHead &requestHead, |
350 | | nsICacheEntry *entry, CacheControlParser &cacheControlRequest, |
351 | | bool fromPreviousSession) |
352 | 0 | { |
353 | 0 | // Check isForcedValid to see if it is possible to skip validation. |
354 | 0 | // Don't skip validation if we have serious reason to believe that this |
355 | 0 | // content is invalid (it's expired). |
356 | 0 | // See netwerk/cache2/nsICacheEntry.idl for details |
357 | 0 | if (isForcedValid && |
358 | 0 | (!cachedResponseHead->ExpiresInPast() || |
359 | 0 | !cachedResponseHead->MustValidateIfExpired())) { |
360 | 0 | LOG(("NOT validating based on isForcedValid being true.\n")); |
361 | 0 | return false; |
362 | 0 | } |
363 | 0 |
|
364 | 0 | // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used |
365 | 0 | if (loadFlags & nsIRequest::LOAD_FROM_CACHE || allowStaleCacheContent) { |
366 | 0 | LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n")); |
367 | 0 | return false; |
368 | 0 | } |
369 | 0 |
|
370 | 0 | // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until |
371 | 0 | // it's revalidated with the server. |
372 | 0 | if ((loadFlags & nsIRequest::VALIDATE_ALWAYS) && !isImmutable) { |
373 | 0 | LOG(("Validating based on VALIDATE_ALWAYS load flag\n")); |
374 | 0 | return true; |
375 | 0 | } |
376 | 0 |
|
377 | 0 | // Even if the VALIDATE_NEVER flag is set, there are still some cases in |
378 | 0 | // which we must validate the cached response with the server. |
379 | 0 | if (loadFlags & nsIRequest::VALIDATE_NEVER) { |
380 | 0 | LOG(("VALIDATE_NEVER set\n")); |
381 | 0 | // if no-store validate cached response (see bug 112564) |
382 | 0 | if (cachedResponseHead->NoStore()) { |
383 | 0 | LOG(("Validating based on no-store logic\n")); |
384 | 0 | return true; |
385 | 0 | } |
386 | 0 | LOG(("NOT validating based on VALIDATE_NEVER load flag\n")); |
387 | 0 | return false; |
388 | 0 | } |
389 | 0 | |
390 | 0 | // check if validation is strictly required... |
391 | 0 | if (cachedResponseHead->MustValidate()) { |
392 | 0 | LOG(("Validating based on MustValidate() returning TRUE\n")); |
393 | 0 | return true; |
394 | 0 | } |
395 | 0 |
|
396 | 0 | // possibly serve from cache for a custom If-Match/If-Unmodified-Since |
397 | 0 | // conditional request |
398 | 0 | if (customConditionalRequest && |
399 | 0 | !requestHead.HasHeader(nsHttp::If_Match) && |
400 | 0 | !requestHead.HasHeader(nsHttp::If_Unmodified_Since)) { |
401 | 0 | LOG(("Validating based on a custom conditional request\n")); |
402 | 0 | return true; |
403 | 0 | } |
404 | 0 |
|
405 | 0 | // previously we also checked for a query-url w/out expiration |
406 | 0 | // and didn't do heuristic on it. but defacto that is allowed now. |
407 | 0 | // |
408 | 0 | // Check if the cache entry has expired... |
409 | 0 |
|
410 | 0 | bool doValidation = true; |
411 | 0 | uint32_t now = NowInSeconds(); |
412 | 0 |
|
413 | 0 | uint32_t age = 0; |
414 | 0 | nsresult rv = cachedResponseHead->ComputeCurrentAge(now, now, &age); |
415 | 0 | if (NS_FAILED(rv)) { |
416 | 0 | return true; |
417 | 0 | } |
418 | 0 | |
419 | 0 | uint32_t freshness = 0; |
420 | 0 | rv = cachedResponseHead->ComputeFreshnessLifetime(&freshness); |
421 | 0 | if (NS_FAILED(rv)) { |
422 | 0 | return true; |
423 | 0 | } |
424 | 0 | |
425 | 0 | uint32_t expiration = 0; |
426 | 0 | rv = entry->GetExpirationTime(&expiration); |
427 | 0 | if (NS_FAILED(rv)) { |
428 | 0 | return true; |
429 | 0 | } |
430 | 0 | |
431 | 0 | uint32_t maxAgeRequest, maxStaleRequest, minFreshRequest; |
432 | 0 |
|
433 | 0 | LOG((" NowInSeconds()=%u, expiration time=%u, freshness lifetime=%u, age=%u", |
434 | 0 | now, expiration, freshness, age)); |
435 | 0 |
|
436 | 0 | if (cacheControlRequest.NoCache()) { |
437 | 0 | LOG((" validating, no-cache request")); |
438 | 0 | doValidation = true; |
439 | 0 | } else if (cacheControlRequest.MaxStale(&maxStaleRequest)) { |
440 | 0 | uint32_t staleTime = age > freshness ? age - freshness : 0; |
441 | 0 | doValidation = staleTime > maxStaleRequest; |
442 | 0 | LOG((" validating=%d, max-stale=%u requested", doValidation, maxStaleRequest)); |
443 | 0 | } else if (cacheControlRequest.MaxAge(&maxAgeRequest)) { |
444 | 0 | // The input information for age and freshness calculation are in seconds. |
445 | 0 | // Hence, the internal logic can't have better resolution than seconds too. |
446 | 0 | // To make max-age=0 case work even for requests made in less than a second |
447 | 0 | // after the last response has been received, we use >= for compare. This |
448 | 0 | // is correct because of the rounding down of the age calculated value. |
449 | 0 | doValidation = age >= maxAgeRequest; |
450 | 0 | LOG((" validating=%d, max-age=%u requested", doValidation, maxAgeRequest)); |
451 | 0 | } else if (cacheControlRequest.MinFresh(&minFreshRequest)) { |
452 | 0 | uint32_t freshTime = freshness > age ? freshness - age : 0; |
453 | 0 | doValidation = freshTime < minFreshRequest; |
454 | 0 | LOG((" validating=%d, min-fresh=%u requested", doValidation, minFreshRequest)); |
455 | 0 | } else if (now < expiration) { |
456 | 0 | doValidation = false; |
457 | 0 | LOG((" not validating, expire time not in the past")); |
458 | 0 | } else if (cachedResponseHead->MustValidateIfExpired()) { |
459 | 0 | doValidation = true; |
460 | 0 | } else if (loadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) { |
461 | 0 | // If the cached response does not include expiration infor- |
462 | 0 | // mation, then we must validate the response, despite whether |
463 | 0 | // or not this is the first access this session. This behavior |
464 | 0 | // is consistent with existing browsers and is generally expected |
465 | 0 | // by web authors. |
466 | 0 | if (freshness == 0) |
467 | 0 | doValidation = true; |
468 | 0 | else |
469 | 0 | doValidation = fromPreviousSession; |
470 | 0 | } else { |
471 | 0 | doValidation = true; |
472 | 0 | } |
473 | 0 |
|
474 | 0 | LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v")); |
475 | 0 | return doValidation; |
476 | 0 | } |
477 | | |
478 | | nsresult |
479 | | GetHttpResponseHeadFromCacheEntry(nsICacheEntry *entry, nsHttpResponseHead *cachedResponseHead) |
480 | 0 | { |
481 | 0 | nsCString buf; |
482 | 0 | // A "original-response-headers" metadata element holds network original headers, |
483 | 0 | // i.e. the headers in the form as they arrieved from the network. |
484 | 0 | // We need to get the network original headers first, because we need to keep them |
485 | 0 | // in order. |
486 | 0 | nsresult rv = entry->GetMetaDataElement("original-response-headers", getter_Copies(buf)); |
487 | 0 | if (NS_SUCCEEDED(rv)) { |
488 | 0 | rv = cachedResponseHead->ParseCachedOriginalHeaders((char *) buf.get()); |
489 | 0 | if (NS_FAILED(rv)) { |
490 | 0 | LOG((" failed to parse original-response-headers\n")); |
491 | 0 | } |
492 | 0 | } |
493 | 0 |
|
494 | 0 | buf.Adopt(nullptr); |
495 | 0 | // A "response-head" metadata element holds response head, e.g. response status |
496 | 0 | // line and headers in the form Firefox uses them internally (no dupicate |
497 | 0 | // headers, etc.). |
498 | 0 | rv = entry->GetMetaDataElement("response-head", getter_Copies(buf)); |
499 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
500 | 0 |
|
501 | 0 | // Parse string stored in a "response-head" metadata element. |
502 | 0 | // These response headers will be merged with the orignal headers (i.e. the |
503 | 0 | // headers stored in a "original-response-headers" metadata element). |
504 | 0 | rv = cachedResponseHead->ParseCachedHead(buf.get()); |
505 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
506 | 0 | buf.Adopt(nullptr); |
507 | 0 |
|
508 | 0 | return NS_OK; |
509 | 0 | } |
510 | | |
511 | | nsresult |
512 | | CheckPartial(nsICacheEntry* aEntry, int64_t *aSize, int64_t *aContentLength, |
513 | | nsHttpResponseHead *responseHead) |
514 | 0 | { |
515 | 0 | nsresult rv; |
516 | 0 |
|
517 | 0 | rv = aEntry->GetDataSize(aSize); |
518 | 0 |
|
519 | 0 | if (NS_ERROR_IN_PROGRESS == rv) { |
520 | 0 | *aSize = -1; |
521 | 0 | rv = NS_OK; |
522 | 0 | } |
523 | 0 |
|
524 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
525 | 0 |
|
526 | 0 | if (!responseHead) { |
527 | 0 | return NS_ERROR_UNEXPECTED; |
528 | 0 | } |
529 | 0 | |
530 | 0 | *aContentLength = responseHead->ContentLength(); |
531 | 0 |
|
532 | 0 | return NS_OK; |
533 | 0 | } |
534 | | |
535 | | void |
536 | | DetermineFramingAndImmutability(nsICacheEntry *entry, |
537 | | nsHttpResponseHead *responseHead, bool isHttps, |
538 | | bool *weaklyFramed, bool *isImmutable) |
539 | 0 | { |
540 | 0 | nsCString framedBuf; |
541 | 0 | nsresult rv = entry->GetMetaDataElement("strongly-framed", getter_Copies(framedBuf)); |
542 | 0 | // describe this in terms of explicitly weakly framed so as to be backwards |
543 | 0 | // compatible with old cache contents which dont have strongly-framed makers |
544 | 0 | *weaklyFramed = NS_SUCCEEDED(rv) && framedBuf.EqualsLiteral("0"); |
545 | 0 | *isImmutable = !*weaklyFramed && isHttps && responseHead->Immutable(); |
546 | 0 | } |
547 | | |
548 | | bool |
549 | | IsBeforeLastActiveTabLoadOptimization(TimeStamp const & when) |
550 | 0 | { |
551 | 0 | return gHttpHandler && gHttpHandler->IsBeforeLastActiveTabLoadOptimization(when); |
552 | 0 | } |
553 | | |
554 | | void |
555 | | NotifyActiveTabLoadOptimization() |
556 | 0 | { |
557 | 0 | if (gHttpHandler) { |
558 | 0 | gHttpHandler->NotifyActiveTabLoadOptimization(); |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | TimeStamp const GetLastActiveTabLoadOptimizationHit() |
563 | 0 | { |
564 | 0 | return gHttpHandler ? gHttpHandler->GetLastActiveTabLoadOptimizationHit() : TimeStamp(); |
565 | 0 | } |
566 | | |
567 | | void |
568 | | SetLastActiveTabLoadOptimizationHit(TimeStamp const &when) |
569 | 0 | { |
570 | 0 | if (gHttpHandler) { |
571 | 0 | gHttpHandler->SetLastActiveTabLoadOptimizationHit(when); |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | | HttpVersion |
576 | | GetHttpVersionFromSpdy(SpdyVersion sv) |
577 | 0 | { |
578 | 0 | MOZ_DIAGNOSTIC_ASSERT(sv != SpdyVersion::NONE); |
579 | 0 | MOZ_ASSERT(sv == SpdyVersion::HTTP_2); |
580 | 0 |
|
581 | 0 | return HttpVersion::v2_0; |
582 | 0 | } |
583 | | |
584 | | } // namespace nsHttp |
585 | | |
586 | | |
587 | | template<typename T> void |
588 | | localEnsureBuffer(UniquePtr<T[]> &buf, uint32_t newSize, |
589 | | uint32_t preserve, uint32_t &objSize) |
590 | 0 | { |
591 | 0 | if (objSize >= newSize) |
592 | 0 | return; |
593 | 0 | |
594 | 0 | // Leave a little slop on the new allocation - add 2KB to |
595 | 0 | // what we need and then round the result up to a 4KB (page) |
596 | 0 | // boundary. |
597 | 0 | |
598 | 0 | objSize = (newSize + 2048 + 4095) & ~4095; |
599 | 0 |
|
600 | 0 | static_assert(sizeof(T) == 1, "sizeof(T) must be 1"); |
601 | 0 | auto tmp = MakeUnique<T[]>(objSize); |
602 | 0 | if (preserve) { |
603 | 0 | memcpy(tmp.get(), buf.get(), preserve); |
604 | 0 | } |
605 | 0 | buf = std::move(tmp); |
606 | 0 | } Unexecuted instantiation: void mozilla::net::localEnsureBuffer<char>(mozilla::UniquePtr<char [], mozilla::DefaultDelete<char []> >&, unsigned int, unsigned int, unsigned int&) Unexecuted instantiation: void mozilla::net::localEnsureBuffer<unsigned char>(mozilla::UniquePtr<unsigned char [], mozilla::DefaultDelete<unsigned char []> >&, unsigned int, unsigned int, unsigned int&) |
607 | | |
608 | | void EnsureBuffer(UniquePtr<char[]> &buf, uint32_t newSize, |
609 | | uint32_t preserve, uint32_t &objSize) |
610 | 0 | { |
611 | 0 | localEnsureBuffer<char> (buf, newSize, preserve, objSize); |
612 | 0 | } |
613 | | |
614 | | void EnsureBuffer(UniquePtr<uint8_t[]> &buf, uint32_t newSize, |
615 | | uint32_t preserve, uint32_t &objSize) |
616 | 0 | { |
617 | 0 | localEnsureBuffer<uint8_t> (buf, newSize, preserve, objSize); |
618 | 0 | } |
619 | | |
620 | | static bool |
621 | 0 | IsTokenSymbol(signed char chr) { |
622 | 0 | if (chr < 33 || chr == 127 || |
623 | 0 | chr == '(' || chr == ')' || chr == '<' || chr == '>' || |
624 | 0 | chr == '@' || chr == ',' || chr == ';' || chr == ':' || |
625 | 0 | chr == '"' || chr == '/' || chr == '[' || chr == ']' || |
626 | 0 | chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') { |
627 | 0 | return false; |
628 | 0 | } |
629 | 0 | return true; |
630 | 0 | } |
631 | | |
632 | | ParsedHeaderPair::ParsedHeaderPair(const char *name, int32_t nameLen, |
633 | | const char *val, int32_t valLen, |
634 | | bool isQuotedValue) |
635 | | : mName(nsDependentCSubstring(nullptr, 0u)) |
636 | | , mValue(nsDependentCSubstring(nullptr, 0u)) |
637 | | , mIsQuotedValue(isQuotedValue) |
638 | 0 | { |
639 | 0 | if (nameLen > 0) { |
640 | 0 | mName.Rebind(name, name + nameLen); |
641 | 0 | } |
642 | 0 | if (valLen > 0) { |
643 | 0 | if (mIsQuotedValue) { |
644 | 0 | RemoveQuotedStringEscapes(val, valLen); |
645 | 0 | mValue.Rebind(mUnquotedValue.BeginWriting(), mUnquotedValue.Length()); |
646 | 0 | } else { |
647 | 0 | mValue.Rebind(val, val + valLen); |
648 | 0 | } |
649 | 0 | } |
650 | 0 | } |
651 | | |
652 | | void |
653 | | ParsedHeaderPair::RemoveQuotedStringEscapes(const char *val, int32_t valLen) |
654 | 0 | { |
655 | 0 | mUnquotedValue.Truncate(); |
656 | 0 | const char *c = val; |
657 | 0 | for (int32_t i = 0; i < valLen; ++i) { |
658 | 0 | if (c[i] == '\\' && c[i + 1]) { |
659 | 0 | ++i; |
660 | 0 | } |
661 | 0 | mUnquotedValue.Append(c[i]); |
662 | 0 | } |
663 | 0 | } |
664 | | |
665 | | static |
666 | | void Tokenize(const char *input, uint32_t inputLen, const char token, |
667 | | const std::function<void(const char *, uint32_t)>& consumer) |
668 | 0 | { |
669 | 0 | auto trimWhitespace = |
670 | 0 | [] (const char *in, uint32_t inLen, const char **out, uint32_t *outLen) { |
671 | 0 | *out = in; |
672 | 0 | *outLen = inLen; |
673 | 0 | if (inLen == 0) { |
674 | 0 | return; |
675 | 0 | } |
676 | 0 | |
677 | 0 | // Trim leading space |
678 | 0 | while (nsCRT::IsAsciiSpace(**out)) { |
679 | 0 | (*out)++; |
680 | 0 | --(*outLen); |
681 | 0 | } |
682 | 0 |
|
683 | 0 | // Trim tailing space |
684 | 0 | for (const char *i = *out + *outLen - 1; i >= *out; --i) { |
685 | 0 | if (!nsCRT::IsAsciiSpace(*i)) { |
686 | 0 | break; |
687 | 0 | } |
688 | 0 | --(*outLen); |
689 | 0 | } |
690 | 0 | }; |
691 | 0 |
|
692 | 0 | const char *first = input; |
693 | 0 | bool inQuote = false; |
694 | 0 | const char *result = nullptr; |
695 | 0 | uint32_t resultLen = 0; |
696 | 0 | for (uint32_t index = 0; index < inputLen; ++index) { |
697 | 0 | if (inQuote && input[index] == '\\' && input[index + 1]) { |
698 | 0 | index++; |
699 | 0 | continue; |
700 | 0 | } |
701 | 0 | if (input[index] == '"') { |
702 | 0 | inQuote = !inQuote; |
703 | 0 | continue; |
704 | 0 | } |
705 | 0 | if (inQuote) { |
706 | 0 | continue; |
707 | 0 | } |
708 | 0 | if (input[index] == token) { |
709 | 0 | trimWhitespace(first, (input + index) - first, |
710 | 0 | &result, &resultLen); |
711 | 0 | consumer(result, resultLen); |
712 | 0 | first = input + index + 1; |
713 | 0 | } |
714 | 0 | } |
715 | 0 |
|
716 | 0 | trimWhitespace(first, (input + inputLen) - first, |
717 | 0 | &result, &resultLen); |
718 | 0 | consumer(result, resultLen); |
719 | 0 | } |
720 | | |
721 | | ParsedHeaderValueList::ParsedHeaderValueList(const char *t, |
722 | | uint32_t len, |
723 | | bool allowInvalidValue) |
724 | 0 | { |
725 | 0 | if (!len) { |
726 | 0 | return; |
727 | 0 | } |
728 | 0 | |
729 | 0 | ParsedHeaderValueList *self = this; |
730 | 0 | auto consumer = [=] (const char *output, uint32_t outputLength) { |
731 | 0 | self->ParseNameAndValue(output, allowInvalidValue); |
732 | 0 | }; |
733 | 0 |
|
734 | 0 | Tokenize(t, len, ';', consumer); |
735 | 0 | } |
736 | | |
737 | | void |
738 | | ParsedHeaderValueList::ParseNameAndValue(const char *input, bool allowInvalidValue) |
739 | 0 | { |
740 | 0 | const char *nameStart = input; |
741 | 0 | const char *nameEnd = nullptr; |
742 | 0 | const char *valueStart = input; |
743 | 0 | const char *valueEnd = nullptr; |
744 | 0 | bool isQuotedString = false; |
745 | 0 | bool invalidValue = false; |
746 | 0 |
|
747 | 0 | for (; *input && *input != ';' && *input != ',' && |
748 | 0 | !nsCRT::IsAsciiSpace(*input) && *input != '='; input++) |
749 | 0 | ; |
750 | 0 |
|
751 | 0 | nameEnd = input; |
752 | 0 |
|
753 | 0 | if (!(nameEnd - nameStart)) { |
754 | 0 | return; |
755 | 0 | } |
756 | 0 | |
757 | 0 | // Check whether param name is a valid token. |
758 | 0 | for (const char *c = nameStart; c < nameEnd; c++) { |
759 | 0 | if (!IsTokenSymbol(*c)) { |
760 | 0 | nameEnd = c; |
761 | 0 | break; |
762 | 0 | } |
763 | 0 | } |
764 | 0 |
|
765 | 0 | if (!(nameEnd - nameStart)) { |
766 | 0 | return; |
767 | 0 | } |
768 | 0 | |
769 | 0 | while (nsCRT::IsAsciiSpace(*input)) { |
770 | 0 | ++input; |
771 | 0 | } |
772 | 0 |
|
773 | 0 | if (!*input || *input++ != '=') { |
774 | 0 | mValues.AppendElement(ParsedHeaderPair(nameStart, nameEnd - nameStart, |
775 | 0 | nullptr, 0, false)); |
776 | 0 | return; |
777 | 0 | } |
778 | 0 | |
779 | 0 | while (nsCRT::IsAsciiSpace(*input)) { |
780 | 0 | ++input; |
781 | 0 | } |
782 | 0 |
|
783 | 0 | if (*input != '"') { |
784 | 0 | // The value is a token, not a quoted string. |
785 | 0 | valueStart = input; |
786 | 0 | for (valueEnd = input; |
787 | 0 | *valueEnd && !nsCRT::IsAsciiSpace (*valueEnd) && |
788 | 0 | *valueEnd != ';' && *valueEnd != ','; |
789 | 0 | valueEnd++) |
790 | 0 | ; |
791 | 0 | input = valueEnd; |
792 | 0 | if (!allowInvalidValue) { |
793 | 0 | for (const char *c = valueStart; c < valueEnd; c++) { |
794 | 0 | if (!IsTokenSymbol(*c)) { |
795 | 0 | valueEnd = c; |
796 | 0 | break; |
797 | 0 | } |
798 | 0 | } |
799 | 0 | } |
800 | 0 | } else { |
801 | 0 | bool foundQuotedEnd = false; |
802 | 0 | isQuotedString = true; |
803 | 0 |
|
804 | 0 | ++input; |
805 | 0 | valueStart = input; |
806 | 0 | for (valueEnd = input; *valueEnd; ++valueEnd) { |
807 | 0 | if (*valueEnd == '\\' && *(valueEnd + 1)) { |
808 | 0 | ++valueEnd; |
809 | 0 | } |
810 | 0 | else if (*valueEnd == '"') { |
811 | 0 | foundQuotedEnd = true; |
812 | 0 | break; |
813 | 0 | } |
814 | 0 | } |
815 | 0 | if (!foundQuotedEnd) { |
816 | 0 | invalidValue = true; |
817 | 0 | } |
818 | 0 |
|
819 | 0 | input = valueEnd; |
820 | 0 | // *valueEnd != null means that *valueEnd is quote character. |
821 | 0 | if (*valueEnd) { |
822 | 0 | input++; |
823 | 0 | } |
824 | 0 | } |
825 | 0 |
|
826 | 0 | if (invalidValue) { |
827 | 0 | valueEnd = valueStart; |
828 | 0 | } |
829 | 0 |
|
830 | 0 | mValues.AppendElement(ParsedHeaderPair(nameStart, nameEnd - nameStart, |
831 | 0 | valueStart, valueEnd - valueStart, |
832 | 0 | isQuotedString)); |
833 | 0 | } |
834 | | |
835 | | ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader, |
836 | | bool allowInvalidValue) |
837 | | : mFull(fullHeader) |
838 | 0 | { |
839 | 0 | auto &values = mValues; |
840 | 0 | auto consumer = |
841 | 0 | [&values, allowInvalidValue] (const char *output, uint32_t outputLength) { |
842 | 0 | values.AppendElement(ParsedHeaderValueList(output, |
843 | 0 | outputLength, |
844 | 0 | allowInvalidValue)); |
845 | 0 | }; |
846 | 0 |
|
847 | 0 | Tokenize(mFull.BeginReading(), mFull.Length(), ',', consumer); |
848 | 0 | } |
849 | | |
850 | | void LogCallingScriptLocation(void* instance) |
851 | 0 | { |
852 | 0 | if (!LOG4_ENABLED()) { |
853 | 0 | return; |
854 | 0 | } |
855 | 0 | |
856 | 0 | JSContext* cx = nsContentUtils::GetCurrentJSContext(); |
857 | 0 | if (!cx) { |
858 | 0 | return; |
859 | 0 | } |
860 | 0 | |
861 | 0 | nsAutoCString fileNameString; |
862 | 0 | uint32_t line = 0, col = 0; |
863 | 0 | if (!nsJSUtils::GetCallingLocation(cx, fileNameString, &line, &col)) { |
864 | 0 | return; |
865 | 0 | } |
866 | 0 | |
867 | 0 | LOG(("%p called from script: %s:%u:%u", instance, fileNameString.get(), line, col)); |
868 | 0 | } |
869 | | |
870 | | } // namespace net |
871 | | } // namespace mozilla |