Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/http/nsHttpResponseHead.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 "mozilla/Unused.h"
11
#include "nsHttpResponseHead.h"
12
#include "nsIHttpHeaderVisitor.h"
13
#include "nsPrintfCString.h"
14
#include "prtime.h"
15
#include "plstr.h"
16
#include "nsURLHelper.h"
17
#include <algorithm>
18
19
namespace mozilla {
20
namespace net {
21
22
//-----------------------------------------------------------------------------
23
// nsHttpResponseHead <public>
24
//-----------------------------------------------------------------------------
25
26
nsHttpResponseHead::nsHttpResponseHead(const nsHttpResponseHead &aOther)
27
    : mRecursiveMutex("nsHttpResponseHead.mRecursiveMutex")
28
    , mInVisitHeaders(false)
29
0
{
30
0
    nsHttpResponseHead &other = const_cast<nsHttpResponseHead&>(aOther);
31
0
    RecursiveMutexAutoLock monitor(other.mRecursiveMutex);
32
0
33
0
    mHeaders = other.mHeaders;
34
0
    mVersion = other.mVersion;
35
0
    mStatus = other.mStatus;
36
0
    mStatusText = other.mStatusText;
37
0
    mContentLength = other.mContentLength;
38
0
    mContentType = other.mContentType;
39
0
    mContentCharset = other.mContentCharset;
40
0
    mCacheControlPrivate = other.mCacheControlPrivate;
41
0
    mCacheControlNoStore = other.mCacheControlNoStore;
42
0
    mCacheControlNoCache = other.mCacheControlNoCache;
43
0
    mCacheControlImmutable = other.mCacheControlImmutable;
44
0
    mPragmaNoCache = other.mPragmaNoCache;
45
0
}
46
47
nsHttpResponseHead&
48
nsHttpResponseHead::operator=(const nsHttpResponseHead &aOther)
49
0
{
50
0
    nsHttpResponseHead &other = const_cast<nsHttpResponseHead&>(aOther);
51
0
    RecursiveMutexAutoLock monitorOther(other.mRecursiveMutex);
52
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
53
0
54
0
    mHeaders = other.mHeaders;
55
0
    mVersion = other.mVersion;
56
0
    mStatus = other.mStatus;
57
0
    mStatusText = other.mStatusText;
58
0
    mContentLength = other.mContentLength;
59
0
    mContentType = other.mContentType;
60
0
    mContentCharset = other.mContentCharset;
61
0
    mCacheControlPrivate = other.mCacheControlPrivate;
62
0
    mCacheControlNoStore = other.mCacheControlNoStore;
63
0
    mCacheControlNoCache = other.mCacheControlNoCache;
64
0
    mCacheControlImmutable = other.mCacheControlImmutable;
65
0
    mPragmaNoCache = other.mPragmaNoCache;
66
0
67
0
    return *this;
68
0
}
69
70
HttpVersion
71
nsHttpResponseHead::Version()
72
0
{
73
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
74
0
    return mVersion;
75
0
}
76
77
uint16_t
78
nsHttpResponseHead::Status()
79
0
{
80
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
81
0
    return mStatus;
82
0
}
83
84
void
85
nsHttpResponseHead::StatusText(nsACString &aStatusText)
86
0
{
87
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
88
0
    aStatusText = mStatusText;
89
0
}
90
91
int64_t
92
nsHttpResponseHead::ContentLength()
93
0
{
94
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
95
0
    return mContentLength;
96
0
}
97
98
void
99
nsHttpResponseHead::ContentType(nsACString &aContentType)
100
0
{
101
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
102
0
    aContentType = mContentType;
103
0
}
104
105
void
106
nsHttpResponseHead::ContentCharset(nsACString &aContentCharset)
107
0
{
108
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
109
0
    aContentCharset = mContentCharset;
110
0
}
111
112
bool
113
nsHttpResponseHead::Private()
114
0
{
115
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
116
0
    return mCacheControlPrivate;
117
0
}
118
119
bool
120
nsHttpResponseHead::NoStore()
121
0
{
122
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
123
0
    return mCacheControlNoStore;
124
0
}
125
126
bool
127
nsHttpResponseHead::NoCache()
128
0
{
129
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
130
0
    return (mCacheControlNoCache || mPragmaNoCache);
131
0
}
132
133
bool
134
nsHttpResponseHead::Immutable()
135
0
{
136
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
137
0
    return mCacheControlImmutable;
138
0
}
139
140
nsresult
141
nsHttpResponseHead::SetHeader(const nsACString &hdr,
142
                              const nsACString &val,
143
                              bool merge)
144
0
{
145
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
146
0
147
0
    if (mInVisitHeaders) {
148
0
        return NS_ERROR_FAILURE;
149
0
    }
150
0
151
0
    nsHttpAtom atom = nsHttp::ResolveAtom(PromiseFlatCString(hdr).get());
152
0
    if (!atom) {
153
0
        NS_WARNING("failed to resolve atom");
154
0
        return NS_ERROR_NOT_AVAILABLE;
155
0
    }
156
0
157
0
    return SetHeader_locked(atom, hdr, val, merge);
158
0
}
159
160
nsresult
161
nsHttpResponseHead::SetHeader(nsHttpAtom hdr,
162
                              const nsACString &val,
163
                              bool merge)
164
0
{
165
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
166
0
167
0
    if (mInVisitHeaders) {
168
0
        return NS_ERROR_FAILURE;
169
0
    }
170
0
171
0
    return SetHeader_locked(hdr, EmptyCString(), val, merge);
172
0
}
173
174
nsresult
175
nsHttpResponseHead::SetHeader_locked(nsHttpAtom atom,
176
                                     const nsACString &hdr,
177
                                     const nsACString &val,
178
                                     bool merge)
179
0
{
180
0
    nsresult rv = mHeaders.SetHeader(atom, hdr, val, merge,
181
0
                                     nsHttpHeaderArray::eVarietyResponse);
182
0
    if (NS_FAILED(rv)) return rv;
183
0
184
0
    // respond to changes in these headers.  we need to reparse the entire
185
0
    // header since the change may have merged in additional values.
186
0
    if (atom == nsHttp::Cache_Control)
187
0
        ParseCacheControl(mHeaders.PeekHeader(atom));
188
0
    else if (atom == nsHttp::Pragma)
189
0
        ParsePragma(mHeaders.PeekHeader(atom));
190
0
191
0
    return NS_OK;
192
0
}
193
194
nsresult
195
nsHttpResponseHead::GetHeader(nsHttpAtom h, nsACString &v)
196
0
{
197
0
    v.Truncate();
198
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
199
0
    return mHeaders.GetHeader(h, v);
200
0
}
201
202
void
203
nsHttpResponseHead::ClearHeader(nsHttpAtom h)
204
0
{
205
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
206
0
    mHeaders.ClearHeader(h);
207
0
}
208
209
void
210
nsHttpResponseHead::ClearHeaders()
211
0
{
212
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
213
0
    mHeaders.Clear();
214
0
}
215
216
bool
217
nsHttpResponseHead::HasHeaderValue(nsHttpAtom h, const char *v)
218
0
{
219
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
220
0
    return mHeaders.HasHeaderValue(h, v);
221
0
}
222
223
bool
224
nsHttpResponseHead::HasHeader(nsHttpAtom h)
225
0
{
226
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
227
0
    return mHeaders.HasHeader(h);
228
0
}
229
230
void
231
nsHttpResponseHead::SetContentType(const nsACString &s)
232
0
{
233
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
234
0
    mContentType = s;
235
0
}
236
237
void
238
nsHttpResponseHead::SetContentCharset(const nsACString &s)
239
0
{
240
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
241
0
    mContentCharset = s;
242
0
}
243
244
void
245
nsHttpResponseHead::SetContentLength(int64_t len)
246
0
{
247
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
248
0
249
0
    mContentLength = len;
250
0
    if (len < 0)
251
0
        mHeaders.ClearHeader(nsHttp::Content_Length);
252
0
    else {
253
0
        DebugOnly<nsresult> rv =
254
0
            mHeaders.SetHeader(nsHttp::Content_Length,
255
0
                               nsPrintfCString("%" PRId64, len),
256
0
                               false,
257
0
                               nsHttpHeaderArray::eVarietyResponse);
258
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
259
0
    }
260
0
}
261
262
void
263
nsHttpResponseHead::Flatten(nsACString &buf, bool pruneTransients)
264
0
{
265
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
266
0
    if (mVersion == HttpVersion::v0_9)
267
0
        return;
268
0
269
0
    buf.AppendLiteral("HTTP/");
270
0
    if (mVersion == HttpVersion::v2_0)
271
0
        buf.AppendLiteral("2.0 ");
272
0
    else if (mVersion == HttpVersion::v1_1)
273
0
        buf.AppendLiteral("1.1 ");
274
0
    else
275
0
        buf.AppendLiteral("1.0 ");
276
0
277
0
    buf.Append(nsPrintfCString("%u", unsigned(mStatus)) +
278
0
               NS_LITERAL_CSTRING(" ") +
279
0
               mStatusText +
280
0
               NS_LITERAL_CSTRING("\r\n"));
281
0
282
0
283
0
    mHeaders.Flatten(buf, false, pruneTransients);
284
0
}
285
286
void
287
nsHttpResponseHead::FlattenNetworkOriginalHeaders(nsACString &buf)
288
0
{
289
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
290
0
    if (mVersion == HttpVersion::v0_9) {
291
0
        return;
292
0
    }
293
0
294
0
    mHeaders.FlattenOriginalHeader(buf);
295
0
}
296
297
nsresult
298
nsHttpResponseHead::ParseCachedHead(const char *block)
299
0
{
300
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
301
0
    LOG(("nsHttpResponseHead::ParseCachedHead [this=%p]\n", this));
302
0
303
0
    // this command works on a buffer as prepared by Flatten, as such it is
304
0
    // not very forgiving ;-)
305
0
306
0
    char *p = PL_strstr(block, "\r\n");
307
0
    if (!p)
308
0
        return NS_ERROR_UNEXPECTED;
309
0
310
0
    ParseStatusLine_locked(nsDependentCSubstring(block, p - block));
311
0
312
0
    do {
313
0
        block = p + 2;
314
0
315
0
        if (*block == 0)
316
0
            break;
317
0
318
0
        p = PL_strstr(block, "\r\n");
319
0
        if (!p)
320
0
            return NS_ERROR_UNEXPECTED;
321
0
322
0
        Unused << ParseHeaderLine_locked(nsDependentCSubstring(block, p - block), false);
323
0
324
0
    } while (true);
325
0
326
0
    return NS_OK;
327
0
}
328
329
nsresult
330
nsHttpResponseHead::ParseCachedOriginalHeaders(char *block)
331
0
{
332
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
333
0
    LOG(("nsHttpResponseHead::ParseCachedOriginalHeader [this=%p]\n", this));
334
0
335
0
    // this command works on a buffer as prepared by FlattenOriginalHeader,
336
0
    // as such it is not very forgiving ;-)
337
0
338
0
    if (!block) {
339
0
        return NS_ERROR_UNEXPECTED;
340
0
    }
341
0
342
0
    char *p = block;
343
0
    nsHttpAtom hdr = {nullptr};
344
0
    nsAutoCString headerNameOriginal;
345
0
    nsAutoCString val;
346
0
    nsresult rv;
347
0
348
0
    do {
349
0
        block = p;
350
0
351
0
        if (*block == 0)
352
0
            break;
353
0
354
0
        p = PL_strstr(block, "\r\n");
355
0
        if (!p)
356
0
            return NS_ERROR_UNEXPECTED;
357
0
358
0
        *p = 0;
359
0
        if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(
360
0
            nsDependentCString(block, p - block), &hdr, &headerNameOriginal, &val))) {
361
0
362
0
            return NS_OK;
363
0
        }
364
0
365
0
        rv = mHeaders.SetResponseHeaderFromCache(hdr,
366
0
                                                 headerNameOriginal,
367
0
                                                 val,
368
0
                                                 nsHttpHeaderArray::eVarietyResponseNetOriginal);
369
0
370
0
        if (NS_FAILED(rv)) {
371
0
            return rv;
372
0
        }
373
0
374
0
        p = p + 2;
375
0
    } while (true);
376
0
377
0
    return NS_OK;
378
0
}
379
380
void
381
nsHttpResponseHead::AssignDefaultStatusText()
382
0
{
383
0
    LOG(("response status line needs default reason phrase\n"));
384
0
385
0
    // if a http response doesn't contain a reason phrase, put one in based
386
0
    // on the status code. The reason phrase is totally meaningless so its
387
0
    // ok to have a default catch all here - but this makes debuggers and addons
388
0
    // a little saner to use if we don't map things to "404 OK" or other nonsense.
389
0
    // In particular, HTTP/2 does not use reason phrases at all so they need to
390
0
    // always be injected.
391
0
392
0
    switch (mStatus) {
393
0
        // start with the most common
394
0
    case 200:
395
0
        mStatusText.AssignLiteral("OK");
396
0
        break;
397
0
    case 404:
398
0
        mStatusText.AssignLiteral("Not Found");
399
0
        break;
400
0
    case 301:
401
0
        mStatusText.AssignLiteral("Moved Permanently");
402
0
        break;
403
0
    case 304:
404
0
        mStatusText.AssignLiteral("Not Modified");
405
0
        break;
406
0
    case 307:
407
0
        mStatusText.AssignLiteral("Temporary Redirect");
408
0
        break;
409
0
    case 500:
410
0
        mStatusText.AssignLiteral("Internal Server Error");
411
0
        break;
412
0
413
0
        // also well known
414
0
    case 100:
415
0
        mStatusText.AssignLiteral("Continue");
416
0
        break;
417
0
    case 101:
418
0
        mStatusText.AssignLiteral("Switching Protocols");
419
0
        break;
420
0
    case 201:
421
0
        mStatusText.AssignLiteral("Created");
422
0
        break;
423
0
    case 202:
424
0
        mStatusText.AssignLiteral("Accepted");
425
0
        break;
426
0
    case 203:
427
0
        mStatusText.AssignLiteral("Non Authoritative");
428
0
        break;
429
0
    case 204:
430
0
        mStatusText.AssignLiteral("No Content");
431
0
        break;
432
0
    case 205:
433
0
        mStatusText.AssignLiteral("Reset Content");
434
0
        break;
435
0
    case 206:
436
0
        mStatusText.AssignLiteral("Partial Content");
437
0
        break;
438
0
    case 207:
439
0
        mStatusText.AssignLiteral("Multi-Status");
440
0
        break;
441
0
    case 208:
442
0
        mStatusText.AssignLiteral("Already Reported");
443
0
        break;
444
0
    case 300:
445
0
        mStatusText.AssignLiteral("Multiple Choices");
446
0
        break;
447
0
    case 302:
448
0
        mStatusText.AssignLiteral("Found");
449
0
        break;
450
0
    case 303:
451
0
        mStatusText.AssignLiteral("See Other");
452
0
        break;
453
0
    case 305:
454
0
        mStatusText.AssignLiteral("Use Proxy");
455
0
        break;
456
0
    case 308:
457
0
        mStatusText.AssignLiteral("Permanent Redirect");
458
0
        break;
459
0
    case 400:
460
0
        mStatusText.AssignLiteral("Bad Request");
461
0
        break;
462
0
    case 401:
463
0
        mStatusText.AssignLiteral("Unauthorized");
464
0
        break;
465
0
    case 402:
466
0
        mStatusText.AssignLiteral("Payment Required");
467
0
        break;
468
0
    case 403:
469
0
        mStatusText.AssignLiteral("Forbidden");
470
0
        break;
471
0
    case 405:
472
0
        mStatusText.AssignLiteral("Method Not Allowed");
473
0
        break;
474
0
    case 406:
475
0
        mStatusText.AssignLiteral("Not Acceptable");
476
0
        break;
477
0
    case 407:
478
0
        mStatusText.AssignLiteral("Proxy Authentication Required");
479
0
        break;
480
0
    case 408:
481
0
        mStatusText.AssignLiteral("Request Timeout");
482
0
        break;
483
0
    case 409:
484
0
        mStatusText.AssignLiteral("Conflict");
485
0
        break;
486
0
    case 410:
487
0
        mStatusText.AssignLiteral("Gone");
488
0
        break;
489
0
    case 411:
490
0
        mStatusText.AssignLiteral("Length Required");
491
0
        break;
492
0
    case 412:
493
0
        mStatusText.AssignLiteral("Precondition Failed");
494
0
        break;
495
0
    case 413:
496
0
        mStatusText.AssignLiteral("Request Entity Too Large");
497
0
        break;
498
0
    case 414:
499
0
        mStatusText.AssignLiteral("Request URI Too Long");
500
0
        break;
501
0
    case 415:
502
0
        mStatusText.AssignLiteral("Unsupported Media Type");
503
0
        break;
504
0
    case 416:
505
0
        mStatusText.AssignLiteral("Requested Range Not Satisfiable");
506
0
        break;
507
0
    case 417:
508
0
        mStatusText.AssignLiteral("Expectation Failed");
509
0
        break;
510
0
    case 421:
511
0
        mStatusText.AssignLiteral("Misdirected Request");
512
0
        break;
513
0
    case 501:
514
0
        mStatusText.AssignLiteral("Not Implemented");
515
0
        break;
516
0
    case 502:
517
0
        mStatusText.AssignLiteral("Bad Gateway");
518
0
        break;
519
0
    case 503:
520
0
        mStatusText.AssignLiteral("Service Unavailable");
521
0
        break;
522
0
    case 504:
523
0
        mStatusText.AssignLiteral("Gateway Timeout");
524
0
        break;
525
0
    case 505:
526
0
        mStatusText.AssignLiteral("HTTP Version Unsupported");
527
0
        break;
528
0
    default:
529
0
        mStatusText.AssignLiteral("No Reason Phrase");
530
0
        break;
531
0
    }
532
0
}
533
534
void
535
nsHttpResponseHead::ParseStatusLine(const nsACString &line)
536
0
{
537
0
538
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
539
0
    ParseStatusLine_locked(line);
540
0
}
541
542
void
543
nsHttpResponseHead::ParseStatusLine_locked(const nsACString &line)
544
0
{
545
0
    //
546
0
    // Parse Status-Line:: HTTP-Version SP Status-Code SP Reason-Phrase CRLF
547
0
    //
548
0
549
0
    const char *start = line.BeginReading();
550
0
    const char *end = line.EndReading();
551
0
    const char *p = start;
552
0
553
0
    // HTTP-Version
554
0
    ParseVersion(start);
555
0
556
0
    int32_t index = line.FindChar(' ');
557
0
558
0
    if ((mVersion == HttpVersion::v0_9) || (index == -1)) {
559
0
        mStatus = 200;
560
0
        AssignDefaultStatusText();
561
0
    }
562
0
    else {
563
0
        // Status-Code
564
0
        p += index + 1;
565
0
        mStatus = (uint16_t) atoi(p);
566
0
        if (mStatus == 0) {
567
0
            LOG(("mal-formed response status; assuming status = 200\n"));
568
0
            mStatus = 200;
569
0
        }
570
0
571
0
        // Reason-Phrase is whatever is remaining of the line
572
0
        index = line.FindChar(' ', p - start);
573
0
        if (index == -1) {
574
0
            AssignDefaultStatusText();
575
0
        }
576
0
        else {
577
0
            p = start + index + 1;
578
0
            mStatusText = nsDependentCSubstring(p, end - p);
579
0
        }
580
0
    }
581
0
582
0
    LOG(("Have status line [version=%u status=%u statusText=%s]\n",
583
0
        unsigned(mVersion), unsigned(mStatus), mStatusText.get()));
584
0
}
585
586
nsresult
587
nsHttpResponseHead::ParseHeaderLine(const nsACString &line)
588
0
{
589
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
590
0
    return ParseHeaderLine_locked(line, true);
591
0
}
592
593
nsresult
594
nsHttpResponseHead::ParseHeaderLine_locked(const nsACString &line, bool originalFromNetHeaders)
595
0
{
596
0
    nsHttpAtom hdr = {nullptr};
597
0
    nsAutoCString headerNameOriginal;
598
0
    nsAutoCString val;
599
0
600
0
    if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(line, &hdr, &headerNameOriginal, &val))) {
601
0
        return NS_OK;
602
0
    }
603
0
    nsresult rv;
604
0
    if (originalFromNetHeaders) {
605
0
        rv = mHeaders.SetHeaderFromNet(hdr,
606
0
                                       headerNameOriginal,
607
0
                                       val,
608
0
                                       true);
609
0
    } else {
610
0
        rv = mHeaders.SetResponseHeaderFromCache(hdr,
611
0
                                                 headerNameOriginal,
612
0
                                                 val,
613
0
                                                 nsHttpHeaderArray::eVarietyResponse);
614
0
    }
615
0
    if (NS_FAILED(rv)) {
616
0
        return rv;
617
0
    }
618
0
619
0
    // leading and trailing LWS has been removed from |val|
620
0
621
0
    // handle some special case headers...
622
0
    if (hdr == nsHttp::Content_Length) {
623
0
        int64_t len;
624
0
        const char *ignored;
625
0
        // permit only a single value here.
626
0
        if (nsHttp::ParseInt64(val.get(), &ignored, &len)) {
627
0
            mContentLength = len;
628
0
        }
629
0
        else {
630
0
            // If this is a negative content length then just ignore it
631
0
            LOG(("invalid content-length! %s\n", val.get()));
632
0
        }
633
0
    }
634
0
    else if (hdr == nsHttp::Content_Type) {
635
0
        LOG(("ParseContentType [type=%s]\n", val.get()));
636
0
        bool dummy;
637
0
        net_ParseContentType(val,
638
0
                             mContentType, mContentCharset, &dummy);
639
0
    }
640
0
    else if (hdr == nsHttp::Cache_Control)
641
0
        ParseCacheControl(val.get());
642
0
    else if (hdr == nsHttp::Pragma)
643
0
        ParsePragma(val.get());
644
0
    return NS_OK;
645
0
}
646
647
// From section 13.2.3 of RFC2616, we compute the current age of a cached
648
// response as follows:
649
//
650
//    currentAge = max(max(0, responseTime - dateValue), ageValue)
651
//               + now - requestTime
652
//
653
//    where responseTime == now
654
//
655
// This is typically a very small number.
656
//
657
nsresult
658
nsHttpResponseHead::ComputeCurrentAge(uint32_t now,
659
                                      uint32_t requestTime,
660
                                      uint32_t *result)
661
0
{
662
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
663
0
    uint32_t dateValue;
664
0
    uint32_t ageValue;
665
0
666
0
    *result = 0;
667
0
668
0
    if (requestTime > now) {
669
0
        // for calculation purposes lets not allow the request to happen in the future
670
0
        requestTime = now;
671
0
    }
672
0
673
0
    if (NS_FAILED(GetDateValue_locked(&dateValue))) {
674
0
        LOG(("nsHttpResponseHead::ComputeCurrentAge [this=%p] "
675
0
             "Date response header not set!\n", this));
676
0
        // Assume we have a fast connection and that our clock
677
0
        // is in sync with the server.
678
0
        dateValue = now;
679
0
    }
680
0
681
0
    // Compute apparent age
682
0
    if (now > dateValue)
683
0
        *result = now - dateValue;
684
0
685
0
    // Compute corrected received age
686
0
    if (NS_SUCCEEDED(GetAgeValue_locked(&ageValue)))
687
0
        *result = std::max(*result, ageValue);
688
0
689
0
    // Compute current age
690
0
    *result += (now - requestTime);
691
0
    return NS_OK;
692
0
}
693
694
// From section 13.2.4 of RFC2616, we compute the freshness lifetime of a cached
695
// response as follows:
696
//
697
//     freshnessLifetime = max_age_value
698
// <or>
699
//     freshnessLifetime = expires_value - date_value
700
// <or>
701
//     freshnessLifetime = min(one-week,(date_value - last_modified_value) * 0.10)
702
// <or>
703
//     freshnessLifetime = 0
704
//
705
nsresult
706
nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result)
707
0
{
708
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
709
0
    *result = 0;
710
0
711
0
    // Try HTTP/1.1 style max-age directive...
712
0
    if (NS_SUCCEEDED(GetMaxAgeValue_locked(result)))
713
0
        return NS_OK;
714
0
715
0
    *result = 0;
716
0
717
0
    uint32_t date = 0, date2 = 0;
718
0
    if (NS_FAILED(GetDateValue_locked(&date)))
719
0
        date = NowInSeconds(); // synthesize a date header if none exists
720
0
721
0
    // Try HTTP/1.0 style expires header...
722
0
    if (NS_SUCCEEDED(GetExpiresValue_locked(&date2))) {
723
0
        if (date2 > date)
724
0
            *result = date2 - date;
725
0
        // the Expires header can specify a date in the past.
726
0
        return NS_OK;
727
0
    }
728
0
729
0
    // These responses can be cached indefinitely.
730
0
    if ((mStatus == 300) || (mStatus == 410) || nsHttp::IsPermanentRedirect(mStatus)) {
731
0
        LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %p] "
732
0
             "Assign an infinite heuristic lifetime\n", this));
733
0
        *result = uint32_t(-1);
734
0
        return NS_OK;
735
0
    }
736
0
737
0
    if (mStatus >= 400) {
738
0
        LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %p] "
739
0
             "Do not calculate heuristic max-age for most responses >= 400\n", this));
740
0
        return NS_OK;
741
0
    }
742
0
743
0
    // Fallback on heuristic using last modified header...
744
0
    if (NS_SUCCEEDED(GetLastModifiedValue_locked(&date2))) {
745
0
        LOG(("using last-modified to determine freshness-lifetime\n"));
746
0
        LOG(("last-modified = %u, date = %u\n", date2, date));
747
0
        if (date2 <= date) {
748
0
            // this only makes sense if last-modified is actually in the past
749
0
            *result = (date - date2) / 10;
750
0
            const uint32_t kOneWeek = 60 * 60 * 24 * 7;
751
0
            *result = std::min(kOneWeek, *result);
752
0
            return NS_OK;
753
0
        }
754
0
    }
755
0
756
0
    LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %p] "
757
0
         "Insufficient information to compute a non-zero freshness "
758
0
         "lifetime!\n", this));
759
0
760
0
    return NS_OK;
761
0
}
762
763
bool
764
nsHttpResponseHead::MustValidate()
765
0
{
766
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
767
0
    LOG(("nsHttpResponseHead::MustValidate ??\n"));
768
0
769
0
    // Some response codes are cacheable, but the rest are not.  This switch
770
0
    // should stay in sync with the list in nsHttpChannel::ProcessResponse
771
0
    switch (mStatus) {
772
0
        // Success codes
773
0
    case 200:
774
0
    case 203:
775
0
    case 206:
776
0
        // Cacheable redirects
777
0
    case 300:
778
0
    case 301:
779
0
    case 302:
780
0
    case 304:
781
0
    case 307:
782
0
    case 308:
783
0
        // Gone forever
784
0
    case 410:
785
0
        break;
786
0
        // Uncacheable redirects
787
0
    case 303:
788
0
    case 305:
789
0
        // Other known errors
790
0
    case 401:
791
0
    case 407:
792
0
    case 412:
793
0
    case 416:
794
0
    default:  // revalidate unknown error pages
795
0
        LOG(("Must validate since response is an uncacheable error page\n"));
796
0
        return true;
797
0
    }
798
0
799
0
    // The no-cache response header indicates that we must validate this
800
0
    // cached response before reusing.
801
0
    if (mCacheControlNoCache || mPragmaNoCache) {
802
0
        LOG(("Must validate since response contains 'no-cache' header\n"));
803
0
        return true;
804
0
    }
805
0
806
0
    // Likewise, if the response is no-store, then we must validate this
807
0
    // cached response before reusing.  NOTE: it may seem odd that a no-store
808
0
    // response may be cached, but indeed all responses are cached in order
809
0
    // to support File->SaveAs, View->PageSource, and other browser features.
810
0
    if (mCacheControlNoStore) {
811
0
        LOG(("Must validate since response contains 'no-store' header\n"));
812
0
        return true;
813
0
    }
814
0
815
0
    // Compare the Expires header to the Date header.  If the server sent an
816
0
    // Expires header with a timestamp in the past, then we must validate this
817
0
    // cached response before reusing.
818
0
    if (ExpiresInPast_locked()) {
819
0
        LOG(("Must validate since Expires < Date\n"));
820
0
        return true;
821
0
    }
822
0
823
0
    LOG(("no mandatory validation requirement\n"));
824
0
    return false;
825
0
}
826
827
bool
828
nsHttpResponseHead::MustValidateIfExpired()
829
0
{
830
0
    // according to RFC2616, section 14.9.4:
831
0
    //
832
0
    //  When the must-revalidate directive is present in a response received by a
833
0
    //  cache, that cache MUST NOT use the entry after it becomes stale to respond to
834
0
    //  a subsequent request without first revalidating it with the origin server.
835
0
    //
836
0
    return HasHeaderValue(nsHttp::Cache_Control, "must-revalidate");
837
0
}
838
839
bool
840
nsHttpResponseHead::IsResumable()
841
0
{
842
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
843
0
    // even though some HTTP/1.0 servers may support byte range requests, we're not
844
0
    // going to bother with them, since those servers wouldn't understand If-Range.
845
0
    // Also, while in theory it may be possible to resume when the status code
846
0
    // is not 200, it is unlikely to be worth the trouble, especially for
847
0
    // non-2xx responses.
848
0
    return mStatus == 200 &&
849
0
           mVersion >= HttpVersion::v1_1 &&
850
0
           mHeaders.PeekHeader(nsHttp::Content_Length) &&
851
0
           (mHeaders.PeekHeader(nsHttp::ETag) ||
852
0
            mHeaders.PeekHeader(nsHttp::Last_Modified)) &&
853
0
           mHeaders.HasHeaderValue(nsHttp::Accept_Ranges, "bytes");
854
0
}
855
856
bool
857
nsHttpResponseHead::ExpiresInPast()
858
0
{
859
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
860
0
    return ExpiresInPast_locked();
861
0
}
862
863
bool
864
nsHttpResponseHead::ExpiresInPast_locked() const
865
0
{
866
0
    uint32_t maxAgeVal, expiresVal, dateVal;
867
0
868
0
    // Bug #203271. Ensure max-age directive takes precedence over Expires
869
0
    if (NS_SUCCEEDED(GetMaxAgeValue_locked(&maxAgeVal))) {
870
0
        return false;
871
0
    }
872
0
873
0
    return NS_SUCCEEDED(GetExpiresValue_locked(&expiresVal)) &&
874
0
           NS_SUCCEEDED(GetDateValue_locked(&dateVal)) &&
875
0
           expiresVal < dateVal;
876
0
}
877
878
nsresult
879
nsHttpResponseHead::UpdateHeaders(nsHttpResponseHead *aOther)
880
0
{
881
0
    LOG(("nsHttpResponseHead::UpdateHeaders [this=%p]\n", this));
882
0
883
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
884
0
    RecursiveMutexAutoLock monitorOther(aOther->mRecursiveMutex);
885
0
886
0
    uint32_t i, count = aOther->mHeaders.Count();
887
0
    for (i=0; i<count; ++i) {
888
0
        nsHttpAtom header;
889
0
        nsAutoCString headerNameOriginal;
890
0
        const char *val = aOther->mHeaders.PeekHeaderAt(i, header, headerNameOriginal);
891
0
892
0
        if (!val) {
893
0
            continue;
894
0
        }
895
0
896
0
        // Ignore any hop-by-hop headers...
897
0
        if (header == nsHttp::Connection          ||
898
0
            header == nsHttp::Proxy_Connection    ||
899
0
            header == nsHttp::Keep_Alive          ||
900
0
            header == nsHttp::Proxy_Authenticate  ||
901
0
            header == nsHttp::Proxy_Authorization || // not a response header!
902
0
            header == nsHttp::TE                  ||
903
0
            header == nsHttp::Trailer             ||
904
0
            header == nsHttp::Transfer_Encoding   ||
905
0
            header == nsHttp::Upgrade             ||
906
0
        // Ignore any non-modifiable headers...
907
0
            header == nsHttp::Content_Location    ||
908
0
            header == nsHttp::Content_MD5         ||
909
0
            header == nsHttp::ETag                ||
910
0
        // Assume Cache-Control: "no-transform"
911
0
            header == nsHttp::Content_Encoding    ||
912
0
            header == nsHttp::Content_Range       ||
913
0
            header == nsHttp::Content_Type        ||
914
0
        // Ignore wacky headers too...
915
0
            // this one is for MS servers that send "Content-Length: 0"
916
0
            // on 304 responses
917
0
            header == nsHttp::Content_Length) {
918
0
            LOG(("ignoring response header [%s: %s]\n", header.get(), val));
919
0
        }
920
0
        else {
921
0
            LOG(("new response header [%s: %s]\n", header.get(), val));
922
0
923
0
            // overwrite the current header value with the new value...
924
0
            DebugOnly<nsresult> rv = SetHeader_locked(header, headerNameOriginal,
925
0
                                                      nsDependentCString(val));
926
0
            MOZ_ASSERT(NS_SUCCEEDED(rv));
927
0
        }
928
0
    }
929
0
930
0
    return NS_OK;
931
0
}
932
933
void
934
nsHttpResponseHead::Reset()
935
0
{
936
0
    LOG(("nsHttpResponseHead::Reset\n"));
937
0
938
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
939
0
940
0
    mHeaders.Clear();
941
0
942
0
    mVersion = HttpVersion::v1_1;
943
0
    mStatus = 200;
944
0
    mContentLength = -1;
945
0
    mCacheControlPrivate = false;
946
0
    mCacheControlNoStore = false;
947
0
    mCacheControlNoCache = false;
948
0
    mCacheControlImmutable = false;
949
0
    mPragmaNoCache = false;
950
0
    mStatusText.Truncate();
951
0
    mContentType.Truncate();
952
0
    mContentCharset.Truncate();
953
0
}
954
955
nsresult
956
nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, uint32_t *result) const
957
0
{
958
0
    const char *val = mHeaders.PeekHeader(header);
959
0
    if (!val)
960
0
        return NS_ERROR_NOT_AVAILABLE;
961
0
962
0
    PRTime time;
963
0
    PRStatus st = PR_ParseTimeString(val, true, &time);
964
0
    if (st != PR_SUCCESS)
965
0
        return NS_ERROR_NOT_AVAILABLE;
966
0
967
0
    *result = PRTimeToSeconds(time);
968
0
    return NS_OK;
969
0
}
970
971
nsresult
972
nsHttpResponseHead::GetAgeValue(uint32_t *result)
973
0
{
974
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
975
0
    return GetAgeValue_locked(result);
976
0
}
977
978
nsresult
979
nsHttpResponseHead::GetAgeValue_locked(uint32_t *result) const
980
0
{
981
0
    const char *val = mHeaders.PeekHeader(nsHttp::Age);
982
0
    if (!val)
983
0
        return NS_ERROR_NOT_AVAILABLE;
984
0
985
0
    *result = (uint32_t) atoi(val);
986
0
    return NS_OK;
987
0
}
988
989
// Return the value of the (HTTP 1.1) max-age directive, which itself is a
990
// component of the Cache-Control response header
991
nsresult
992
nsHttpResponseHead::GetMaxAgeValue(uint32_t *result)
993
0
{
994
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
995
0
    return GetMaxAgeValue_locked(result);
996
0
}
997
998
nsresult
999
nsHttpResponseHead::GetMaxAgeValue_locked(uint32_t *result) const
1000
0
{
1001
0
    const char *val = mHeaders.PeekHeader(nsHttp::Cache_Control);
1002
0
    if (!val)
1003
0
        return NS_ERROR_NOT_AVAILABLE;
1004
0
1005
0
    const char *p = nsHttp::FindToken(val, "max-age", HTTP_HEADER_VALUE_SEPS "=");
1006
0
    if (!p)
1007
0
        return NS_ERROR_NOT_AVAILABLE;
1008
0
    p += 7;
1009
0
    while (*p == ' ' || *p == '\t')
1010
0
        ++p;
1011
0
    if (*p != '=')
1012
0
        return NS_ERROR_NOT_AVAILABLE;
1013
0
    ++p;
1014
0
    while (*p == ' ' || *p == '\t')
1015
0
        ++p;
1016
0
1017
0
    int maxAgeValue = atoi(p);
1018
0
    if (maxAgeValue < 0)
1019
0
        maxAgeValue = 0;
1020
0
    *result = static_cast<uint32_t>(maxAgeValue);
1021
0
    return NS_OK;
1022
0
}
1023
1024
nsresult
1025
nsHttpResponseHead::GetDateValue(uint32_t *result)
1026
0
{
1027
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1028
0
    return GetDateValue_locked(result);
1029
0
}
1030
1031
nsresult
1032
nsHttpResponseHead::GetExpiresValue(uint32_t *result)
1033
0
{
1034
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1035
0
    return GetExpiresValue_locked(result);
1036
0
}
1037
1038
nsresult
1039
nsHttpResponseHead::GetExpiresValue_locked(uint32_t *result) const
1040
0
{
1041
0
    const char *val = mHeaders.PeekHeader(nsHttp::Expires);
1042
0
    if (!val)
1043
0
        return NS_ERROR_NOT_AVAILABLE;
1044
0
1045
0
    PRTime time;
1046
0
    PRStatus st = PR_ParseTimeString(val, true, &time);
1047
0
    if (st != PR_SUCCESS) {
1048
0
        // parsing failed... RFC 2616 section 14.21 says we should treat this
1049
0
        // as an expiration time in the past.
1050
0
        *result = 0;
1051
0
        return NS_OK;
1052
0
    }
1053
0
1054
0
    if (time < 0)
1055
0
        *result = 0;
1056
0
    else
1057
0
        *result = PRTimeToSeconds(time);
1058
0
    return NS_OK;
1059
0
}
1060
1061
nsresult
1062
nsHttpResponseHead::GetLastModifiedValue(uint32_t *result)
1063
0
{
1064
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1065
0
    return ParseDateHeader(nsHttp::Last_Modified, result);
1066
0
}
1067
1068
bool
1069
nsHttpResponseHead::operator==(const nsHttpResponseHead& aOther) const
1070
0
{
1071
0
    nsHttpResponseHead &curr = const_cast<nsHttpResponseHead&>(*this);
1072
0
    nsHttpResponseHead &other = const_cast<nsHttpResponseHead&>(aOther);
1073
0
    RecursiveMutexAutoLock monitorOther(other.mRecursiveMutex);
1074
0
    RecursiveMutexAutoLock monitor(curr.mRecursiveMutex);
1075
0
1076
0
    return mHeaders == aOther.mHeaders &&
1077
0
           mVersion == aOther.mVersion &&
1078
0
           mStatus == aOther.mStatus &&
1079
0
           mStatusText == aOther.mStatusText &&
1080
0
           mContentLength == aOther.mContentLength &&
1081
0
           mContentType == aOther.mContentType &&
1082
0
           mContentCharset == aOther.mContentCharset &&
1083
0
           mCacheControlPrivate == aOther.mCacheControlPrivate &&
1084
0
           mCacheControlNoCache == aOther.mCacheControlNoCache &&
1085
0
           mCacheControlNoStore == aOther.mCacheControlNoStore &&
1086
0
           mCacheControlImmutable == aOther.mCacheControlImmutable &&
1087
0
           mPragmaNoCache == aOther.mPragmaNoCache;
1088
0
}
1089
1090
int64_t
1091
nsHttpResponseHead::TotalEntitySize()
1092
0
{
1093
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1094
0
    const char* contentRange = mHeaders.PeekHeader(nsHttp::Content_Range);
1095
0
    if (!contentRange)
1096
0
        return mContentLength;
1097
0
1098
0
    // Total length is after a slash
1099
0
    const char* slash = strrchr(contentRange, '/');
1100
0
    if (!slash)
1101
0
        return -1; // No idea what the length is
1102
0
1103
0
    slash++;
1104
0
    if (*slash == '*') // Server doesn't know the length
1105
0
        return -1;
1106
0
1107
0
    int64_t size;
1108
0
    if (!nsHttp::ParseInt64(slash, &size))
1109
0
        size = UINT64_MAX;
1110
0
    return size;
1111
0
}
1112
1113
//-----------------------------------------------------------------------------
1114
// nsHttpResponseHead <private>
1115
//-----------------------------------------------------------------------------
1116
1117
void
1118
nsHttpResponseHead::ParseVersion(const char *str)
1119
0
{
1120
0
    // Parse HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT
1121
0
1122
0
    LOG(("nsHttpResponseHead::ParseVersion [version=%s]\n", str));
1123
0
1124
0
    // make sure we have HTTP at the beginning
1125
0
    if (PL_strncasecmp(str, "HTTP", 4) != 0) {
1126
0
        if (PL_strncasecmp(str, "ICY ", 4) == 0) {
1127
0
            // ShoutCast ICY is HTTP/1.0-like. Assume it is HTTP/1.0.
1128
0
            LOG(("Treating ICY as HTTP 1.0\n"));
1129
0
            mVersion = HttpVersion::v1_0;
1130
0
            return;
1131
0
        }
1132
0
        LOG(("looks like a HTTP/0.9 response\n"));
1133
0
        mVersion = HttpVersion::v0_9;
1134
0
        return;
1135
0
    }
1136
0
    str += 4;
1137
0
1138
0
    if (*str != '/') {
1139
0
        LOG(("server did not send a version number; assuming HTTP/1.0\n"));
1140
0
        // NCSA/1.5.2 has a bug in which it fails to send a version number
1141
0
        // if the request version is HTTP/1.1, so we fall back on HTTP/1.0
1142
0
        mVersion = HttpVersion::v1_0;
1143
0
        return;
1144
0
    }
1145
0
1146
0
    char *p = PL_strchr(str, '.');
1147
0
    if (p == nullptr) {
1148
0
        LOG(("mal-formed server version; assuming HTTP/1.0\n"));
1149
0
        mVersion = HttpVersion::v1_0;
1150
0
        return;
1151
0
    }
1152
0
1153
0
    ++p; // let b point to the minor version
1154
0
1155
0
    int major = atoi(str + 1);
1156
0
    int minor = atoi(p);
1157
0
1158
0
    if ((major > 2) || ((major == 2) && (minor >= 0)))
1159
0
        mVersion = HttpVersion::v2_0;
1160
0
    else if ((major == 1) && (minor >= 1))
1161
0
        // at least HTTP/1.1
1162
0
        mVersion = HttpVersion::v1_1;
1163
0
    else
1164
0
        // treat anything else as version 1.0
1165
0
        mVersion = HttpVersion::v1_0;
1166
0
}
1167
1168
void
1169
nsHttpResponseHead::ParseCacheControl(const char *val)
1170
0
{
1171
0
    if (!(val && *val)) {
1172
0
        // clear flags
1173
0
        mCacheControlPrivate = false;
1174
0
        mCacheControlNoCache = false;
1175
0
        mCacheControlNoStore = false;
1176
0
        mCacheControlImmutable = false;
1177
0
        return;
1178
0
    }
1179
0
1180
0
    // search header value for occurrence of "private"
1181
0
    if (nsHttp::FindToken(val, "private", HTTP_HEADER_VALUE_SEPS))
1182
0
        mCacheControlPrivate = true;
1183
0
1184
0
    // search header value for occurrence(s) of "no-cache" but ignore
1185
0
    // occurrence(s) of "no-cache=blah"
1186
0
    if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
1187
0
        mCacheControlNoCache = true;
1188
0
1189
0
    // search header value for occurrence of "no-store"
1190
0
    if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS))
1191
0
        mCacheControlNoStore = true;
1192
0
1193
0
    // search header value for occurrence of "immutable"
1194
0
    if (nsHttp::FindToken(val, "immutable", HTTP_HEADER_VALUE_SEPS)) {
1195
0
        mCacheControlImmutable = true;
1196
0
    }
1197
0
}
1198
1199
void
1200
nsHttpResponseHead::ParsePragma(const char *val)
1201
0
{
1202
0
    LOG(("nsHttpResponseHead::ParsePragma [val=%s]\n", val));
1203
0
1204
0
    if (!(val && *val)) {
1205
0
        // clear no-cache flag
1206
0
        mPragmaNoCache = false;
1207
0
        return;
1208
0
    }
1209
0
1210
0
    // Although 'Pragma: no-cache' is not a standard HTTP response header (it's
1211
0
    // a request header), caching is inhibited when this header is present so
1212
0
    // as to match existing Navigator behavior.
1213
0
    if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
1214
0
        mPragmaNoCache = true;
1215
0
}
1216
1217
nsresult
1218
nsHttpResponseHead::VisitHeaders(nsIHttpHeaderVisitor *visitor,
1219
                                 nsHttpHeaderArray::VisitorFilter filter)
1220
0
{
1221
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1222
0
    mInVisitHeaders = true;
1223
0
    nsresult rv = mHeaders.VisitHeaders(visitor, filter);
1224
0
    mInVisitHeaders = false;
1225
0
    return rv;
1226
0
}
1227
1228
nsresult
1229
nsHttpResponseHead::GetOriginalHeader(nsHttpAtom aHeader,
1230
                                      nsIHttpHeaderVisitor *aVisitor)
1231
0
{
1232
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1233
0
    mInVisitHeaders = true;
1234
0
    nsresult rv = mHeaders.GetOriginalHeader(aHeader, aVisitor);
1235
0
    mInVisitHeaders = false;
1236
0
    return rv;
1237
0
}
1238
1239
bool
1240
nsHttpResponseHead::HasContentType()
1241
0
{
1242
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1243
0
    return !mContentType.IsEmpty();
1244
0
}
1245
1246
bool
1247
nsHttpResponseHead::HasContentCharset()
1248
0
{
1249
0
    RecursiveMutexAutoLock monitor(mRecursiveMutex);
1250
0
    return !mContentCharset.IsEmpty();
1251
0
}
1252
1253
} // namespace net
1254
} // namespace mozilla