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