Coverage Report

Created: 2018-09-25 14:53

/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