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