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 "plstr.h"
16 #include "nsCRT.h"
17 #include "nsURLHelper.h"
18 #include "CacheControlParser.h"
19 #include <algorithm>
20 
21 namespace mozilla {
22 namespace net {
23 
24 //-----------------------------------------------------------------------------
25 // nsHttpResponseHead <public>
26 //-----------------------------------------------------------------------------
27 
28 // Note that the code below MUST be synchronized with the IPC
29 // serialization/deserialization in PHttpChannelParams.h.
nsHttpResponseHead(const nsHttpResponseHead & aOther)30 nsHttpResponseHead::nsHttpResponseHead(const nsHttpResponseHead& aOther) {
31   nsHttpResponseHead& other = const_cast<nsHttpResponseHead&>(aOther);
32   RecursiveMutexAutoLock monitor(other.mRecursiveMutex);
33 
34   mHeaders = other.mHeaders;
35   mVersion = other.mVersion;
36   mStatus = other.mStatus;
37   mStatusText = other.mStatusText;
38   mContentLength = other.mContentLength;
39   mContentType = other.mContentType;
40   mContentCharset = other.mContentCharset;
41   mHasCacheControl = other.mHasCacheControl;
42   mCacheControlPublic = other.mCacheControlPublic;
43   mCacheControlPrivate = other.mCacheControlPrivate;
44   mCacheControlNoStore = other.mCacheControlNoStore;
45   mCacheControlNoCache = other.mCacheControlNoCache;
46   mCacheControlImmutable = other.mCacheControlImmutable;
47   mCacheControlStaleWhileRevalidateSet =
48       other.mCacheControlStaleWhileRevalidateSet;
49   mCacheControlStaleWhileRevalidate = other.mCacheControlStaleWhileRevalidate;
50   mCacheControlMaxAgeSet = other.mCacheControlMaxAgeSet;
51   mCacheControlMaxAge = other.mCacheControlMaxAge;
52   mPragmaNoCache = other.mPragmaNoCache;
53 }
54 
operator =(const nsHttpResponseHead & aOther)55 nsHttpResponseHead& nsHttpResponseHead::operator=(
56     const nsHttpResponseHead& aOther) {
57   nsHttpResponseHead& other = const_cast<nsHttpResponseHead&>(aOther);
58   RecursiveMutexAutoLock monitorOther(other.mRecursiveMutex);
59   RecursiveMutexAutoLock monitor(mRecursiveMutex);
60 
61   mHeaders = other.mHeaders;
62   mVersion = other.mVersion;
63   mStatus = other.mStatus;
64   mStatusText = other.mStatusText;
65   mContentLength = other.mContentLength;
66   mContentType = other.mContentType;
67   mContentCharset = other.mContentCharset;
68   mHasCacheControl = other.mHasCacheControl;
69   mCacheControlPublic = other.mCacheControlPublic;
70   mCacheControlPrivate = other.mCacheControlPrivate;
71   mCacheControlNoStore = other.mCacheControlNoStore;
72   mCacheControlNoCache = other.mCacheControlNoCache;
73   mCacheControlImmutable = other.mCacheControlImmutable;
74   mCacheControlStaleWhileRevalidateSet =
75       other.mCacheControlStaleWhileRevalidateSet;
76   mCacheControlStaleWhileRevalidate = other.mCacheControlStaleWhileRevalidate;
77   mCacheControlMaxAgeSet = other.mCacheControlMaxAgeSet;
78   mCacheControlMaxAge = other.mCacheControlMaxAge;
79   mPragmaNoCache = other.mPragmaNoCache;
80 
81   return *this;
82 }
83 
Version()84 HttpVersion nsHttpResponseHead::Version() {
85   RecursiveMutexAutoLock monitor(mRecursiveMutex);
86   return mVersion;
87 }
88 
Status() const89 uint16_t nsHttpResponseHead::Status() const {
90   RecursiveMutexAutoLock monitor(mRecursiveMutex);
91   return mStatus;
92 }
93 
StatusText(nsACString & aStatusText)94 void nsHttpResponseHead::StatusText(nsACString& aStatusText) {
95   RecursiveMutexAutoLock monitor(mRecursiveMutex);
96   aStatusText = mStatusText;
97 }
98 
ContentLength()99 int64_t nsHttpResponseHead::ContentLength() {
100   RecursiveMutexAutoLock monitor(mRecursiveMutex);
101   return mContentLength;
102 }
103 
ContentType(nsACString & aContentType) const104 void nsHttpResponseHead::ContentType(nsACString& aContentType) const {
105   RecursiveMutexAutoLock monitor(mRecursiveMutex);
106   aContentType = mContentType;
107 }
108 
ContentCharset(nsACString & aContentCharset)109 void nsHttpResponseHead::ContentCharset(nsACString& aContentCharset) {
110   RecursiveMutexAutoLock monitor(mRecursiveMutex);
111   aContentCharset = mContentCharset;
112 }
113 
Public()114 bool nsHttpResponseHead::Public() {
115   RecursiveMutexAutoLock monitor(mRecursiveMutex);
116   return mCacheControlPublic;
117 }
118 
Private()119 bool nsHttpResponseHead::Private() {
120   RecursiveMutexAutoLock monitor(mRecursiveMutex);
121   return mCacheControlPrivate;
122 }
123 
NoStore()124 bool nsHttpResponseHead::NoStore() {
125   RecursiveMutexAutoLock monitor(mRecursiveMutex);
126   return mCacheControlNoStore;
127 }
128 
NoCache()129 bool nsHttpResponseHead::NoCache() {
130   RecursiveMutexAutoLock monitor(mRecursiveMutex);
131   return NoCache_locked();
132 }
133 
Immutable()134 bool nsHttpResponseHead::Immutable() {
135   RecursiveMutexAutoLock monitor(mRecursiveMutex);
136   return mCacheControlImmutable;
137 }
138 
SetHeader(const nsACString & hdr,const nsACString & val,bool merge)139 nsresult nsHttpResponseHead::SetHeader(const nsACString& hdr,
140                                        const nsACString& val, bool merge) {
141   RecursiveMutexAutoLock monitor(mRecursiveMutex);
142 
143   if (mInVisitHeaders) {
144     return NS_ERROR_FAILURE;
145   }
146 
147   nsHttpAtom atom = nsHttp::ResolveAtom(hdr);
148   if (!atom) {
149     NS_WARNING("failed to resolve atom");
150     return NS_ERROR_NOT_AVAILABLE;
151   }
152 
153   return SetHeader_locked(atom, hdr, val, merge);
154 }
155 
SetHeader(const nsHttpAtom & hdr,const nsACString & val,bool merge)156 nsresult nsHttpResponseHead::SetHeader(const nsHttpAtom& hdr,
157                                        const nsACString& val, bool merge) {
158   RecursiveMutexAutoLock monitor(mRecursiveMutex);
159 
160   if (mInVisitHeaders) {
161     return NS_ERROR_FAILURE;
162   }
163 
164   return SetHeader_locked(hdr, ""_ns, val, merge);
165 }
166 
SetHeader_locked(const nsHttpAtom & atom,const nsACString & hdr,const nsACString & val,bool merge)167 nsresult nsHttpResponseHead::SetHeader_locked(const nsHttpAtom& atom,
168                                               const nsACString& hdr,
169                                               const nsACString& val,
170                                               bool merge) {
171   nsresult rv = mHeaders.SetHeader(atom, hdr, val, merge,
172                                    nsHttpHeaderArray::eVarietyResponse);
173   if (NS_FAILED(rv)) return rv;
174 
175   // respond to changes in these headers.  we need to reparse the entire
176   // header since the change may have merged in additional values.
177   if (atom == nsHttp::Cache_Control) {
178     ParseCacheControl(mHeaders.PeekHeader(atom));
179   } else if (atom == nsHttp::Pragma) {
180     ParsePragma(mHeaders.PeekHeader(atom));
181   }
182 
183   return NS_OK;
184 }
185 
GetHeader(const nsHttpAtom & h,nsACString & v)186 nsresult nsHttpResponseHead::GetHeader(const nsHttpAtom& h, nsACString& v) {
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   char* p = PL_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 = PL_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 = PL_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     const char* val =
907         aOther->mHeaders.PeekHeaderAt(i, header, headerNameOriginal);
908 
909     if (!val) {
910       continue;
911     }
912 
913     // Ignore any hop-by-hop headers...
914     if (header == nsHttp::Connection || header == nsHttp::Proxy_Connection ||
915         header == nsHttp::Keep_Alive || header == nsHttp::Proxy_Authenticate ||
916         header == nsHttp::Proxy_Authorization ||  // not a response header!
917         header == nsHttp::TE || header == nsHttp::Trailer ||
918         header == nsHttp::Transfer_Encoding || header == nsHttp::Upgrade ||
919         // Ignore any non-modifiable headers...
920         header == nsHttp::Content_Location || header == nsHttp::Content_MD5 ||
921         header == nsHttp::ETag ||
922         // Assume Cache-Control: "no-transform"
923         header == nsHttp::Content_Encoding || header == nsHttp::Content_Range ||
924         header == nsHttp::Content_Type ||
925         // Ignore wacky headers too...
926         // this one is for MS servers that send "Content-Length: 0"
927         // on 304 responses
928         header == nsHttp::Content_Length) {
929       LOG(("ignoring response header [%s: %s]\n", header.get(), val));
930     } else {
931       LOG(("new response header [%s: %s]\n", header.get(), val));
932 
933       // overwrite the current header value with the new value...
934       DebugOnly<nsresult> rv =
935           SetHeader_locked(header, headerNameOriginal, nsDependentCString(val));
936       MOZ_ASSERT(NS_SUCCEEDED(rv));
937     }
938   }
939 }
940 
Reset()941 void nsHttpResponseHead::Reset() {
942   LOG(("nsHttpResponseHead::Reset\n"));
943 
944   RecursiveMutexAutoLock monitor(mRecursiveMutex);
945 
946   mHeaders.Clear();
947 
948   mVersion = HttpVersion::v1_1;
949   mStatus = 200;
950   mContentLength = -1;
951   mHasCacheControl = false;
952   mCacheControlPublic = false;
953   mCacheControlPrivate = false;
954   mCacheControlNoStore = false;
955   mCacheControlNoCache = false;
956   mCacheControlImmutable = false;
957   mCacheControlStaleWhileRevalidateSet = false;
958   mCacheControlStaleWhileRevalidate = 0;
959   mCacheControlMaxAgeSet = false;
960   mCacheControlMaxAge = 0;
961   mPragmaNoCache = false;
962   mStatusText.Truncate();
963   mContentType.Truncate();
964   mContentCharset.Truncate();
965 }
966 
ParseDateHeader(const nsHttpAtom & header,uint32_t * result) const967 nsresult nsHttpResponseHead::ParseDateHeader(const nsHttpAtom& header,
968                                              uint32_t* result) const {
969   const char* val = mHeaders.PeekHeader(header);
970   if (!val) return NS_ERROR_NOT_AVAILABLE;
971 
972   PRTime time;
973   PRStatus st = PR_ParseTimeString(val, true, &time);
974   if (st != PR_SUCCESS) return NS_ERROR_NOT_AVAILABLE;
975 
976   *result = PRTimeToSeconds(time);
977   return NS_OK;
978 }
979 
GetAgeValue(uint32_t * result)980 nsresult nsHttpResponseHead::GetAgeValue(uint32_t* result) {
981   RecursiveMutexAutoLock monitor(mRecursiveMutex);
982   return GetAgeValue_locked(result);
983 }
984 
GetAgeValue_locked(uint32_t * result) const985 nsresult nsHttpResponseHead::GetAgeValue_locked(uint32_t* result) const {
986   const char* val = mHeaders.PeekHeader(nsHttp::Age);
987   if (!val) return NS_ERROR_NOT_AVAILABLE;
988 
989   *result = (uint32_t)atoi(val);
990   return NS_OK;
991 }
992 
993 // Return the value of the (HTTP 1.1) max-age directive, which itself is a
994 // component of the Cache-Control response header
GetMaxAgeValue(uint32_t * result)995 nsresult nsHttpResponseHead::GetMaxAgeValue(uint32_t* result) {
996   RecursiveMutexAutoLock monitor(mRecursiveMutex);
997   return GetMaxAgeValue_locked(result);
998 }
999 
GetMaxAgeValue_locked(uint32_t * result) const1000 nsresult nsHttpResponseHead::GetMaxAgeValue_locked(uint32_t* result) const {
1001   if (!mCacheControlMaxAgeSet) {
1002     return NS_ERROR_NOT_AVAILABLE;
1003   }
1004 
1005   *result = mCacheControlMaxAge;
1006   return NS_OK;
1007 }
1008 
GetDateValue(uint32_t * result)1009 nsresult nsHttpResponseHead::GetDateValue(uint32_t* result) {
1010   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1011   return GetDateValue_locked(result);
1012 }
1013 
GetExpiresValue(uint32_t * result)1014 nsresult nsHttpResponseHead::GetExpiresValue(uint32_t* result) {
1015   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1016   return GetExpiresValue_locked(result);
1017 }
1018 
GetExpiresValue_locked(uint32_t * result) const1019 nsresult nsHttpResponseHead::GetExpiresValue_locked(uint32_t* result) const {
1020   const char* val = mHeaders.PeekHeader(nsHttp::Expires);
1021   if (!val) return NS_ERROR_NOT_AVAILABLE;
1022 
1023   PRTime time;
1024   PRStatus st = PR_ParseTimeString(val, true, &time);
1025   if (st != PR_SUCCESS) {
1026     // parsing failed... RFC 2616 section 14.21 says we should treat this
1027     // as an expiration time in the past.
1028     *result = 0;
1029     return NS_OK;
1030   }
1031 
1032   if (time < 0) {
1033     *result = 0;
1034   } else {
1035     *result = PRTimeToSeconds(time);
1036   }
1037   return NS_OK;
1038 }
1039 
GetLastModifiedValue(uint32_t * result)1040 nsresult nsHttpResponseHead::GetLastModifiedValue(uint32_t* result) {
1041   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1042   return ParseDateHeader(nsHttp::Last_Modified, result);
1043 }
1044 
operator ==(const nsHttpResponseHead & aOther) const1045 bool nsHttpResponseHead::operator==(const nsHttpResponseHead& aOther) const {
1046   nsHttpResponseHead& curr = const_cast<nsHttpResponseHead&>(*this);
1047   nsHttpResponseHead& other = const_cast<nsHttpResponseHead&>(aOther);
1048   RecursiveMutexAutoLock monitorOther(other.mRecursiveMutex);
1049   RecursiveMutexAutoLock monitor(curr.mRecursiveMutex);
1050 
1051   return mHeaders == aOther.mHeaders && mVersion == aOther.mVersion &&
1052          mStatus == aOther.mStatus && mStatusText == aOther.mStatusText &&
1053          mContentLength == aOther.mContentLength &&
1054          mContentType == aOther.mContentType &&
1055          mContentCharset == aOther.mContentCharset &&
1056          mHasCacheControl == aOther.mHasCacheControl &&
1057          mCacheControlPublic == aOther.mCacheControlPublic &&
1058          mCacheControlPrivate == aOther.mCacheControlPrivate &&
1059          mCacheControlNoCache == aOther.mCacheControlNoCache &&
1060          mCacheControlNoStore == aOther.mCacheControlNoStore &&
1061          mCacheControlImmutable == aOther.mCacheControlImmutable &&
1062          mCacheControlStaleWhileRevalidateSet ==
1063              aOther.mCacheControlStaleWhileRevalidateSet &&
1064          mCacheControlStaleWhileRevalidate ==
1065              aOther.mCacheControlStaleWhileRevalidate &&
1066          mCacheControlMaxAgeSet == aOther.mCacheControlMaxAgeSet &&
1067          mCacheControlMaxAge == aOther.mCacheControlMaxAge &&
1068          mPragmaNoCache == aOther.mPragmaNoCache;
1069 }
1070 
TotalEntitySize()1071 int64_t nsHttpResponseHead::TotalEntitySize() {
1072   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1073   const char* contentRange = mHeaders.PeekHeader(nsHttp::Content_Range);
1074   if (!contentRange) return mContentLength;
1075 
1076   // Total length is after a slash
1077   const char* slash = strrchr(contentRange, '/');
1078   if (!slash) return -1;  // No idea what the length is
1079 
1080   slash++;
1081   if (*slash == '*') {  // Server doesn't know the length
1082     return -1;
1083   }
1084 
1085   int64_t size;
1086   if (!nsHttp::ParseInt64(slash, &size)) size = UINT64_MAX;
1087   return size;
1088 }
1089 
1090 //-----------------------------------------------------------------------------
1091 // nsHttpResponseHead <private>
1092 //-----------------------------------------------------------------------------
1093 
ParseVersion(const char * str)1094 void nsHttpResponseHead::ParseVersion(const char* str) {
1095   // Parse HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT
1096 
1097   LOG(("nsHttpResponseHead::ParseVersion [version=%s]\n", str));
1098 
1099   Tokenizer t(str, nullptr, "");
1100   // make sure we have HTTP at the beginning
1101   if (!t.CheckWord("HTTP")) {
1102     if (nsCRT::strncasecmp(str, "ICY ", 4) == 0) {
1103       // ShoutCast ICY is HTTP/1.0-like. Assume it is HTTP/1.0.
1104       LOG(("Treating ICY as HTTP 1.0\n"));
1105       mVersion = HttpVersion::v1_0;
1106       return;
1107     }
1108     LOG(("looks like a HTTP/0.9 response\n"));
1109     mVersion = HttpVersion::v0_9;
1110     return;
1111   }
1112 
1113   if (!t.CheckChar('/')) {
1114     LOG(("server did not send a version number; assuming HTTP/1.0\n"));
1115     // NCSA/1.5.2 has a bug in which it fails to send a version number
1116     // if the request version is HTTP/1.1, so we fall back on HTTP/1.0
1117     mVersion = HttpVersion::v1_0;
1118     return;
1119   }
1120 
1121   uint32_t major;
1122   if (!t.ReadInteger(&major)) {
1123     LOG(("server did not send a correct version number; assuming HTTP/1.0"));
1124     mVersion = HttpVersion::v1_0;
1125     return;
1126   }
1127 
1128   if (major == 3) {
1129     mVersion = HttpVersion::v3_0;
1130     return;
1131   }
1132 
1133   if (major == 2) {
1134     mVersion = HttpVersion::v2_0;
1135     return;
1136   }
1137 
1138   if (major != 1) {
1139     LOG(("server did not send a correct version number; assuming HTTP/1.0"));
1140     mVersion = HttpVersion::v1_0;
1141     return;
1142   }
1143 
1144   if (!t.CheckChar('.')) {
1145     LOG(("mal-formed server version; assuming HTTP/1.0\n"));
1146     mVersion = HttpVersion::v1_0;
1147     return;
1148   }
1149 
1150   uint32_t minor;
1151   if (!t.ReadInteger(&minor)) {
1152     LOG(("server did not send a correct version number; assuming HTTP/1.0"));
1153     mVersion = HttpVersion::v1_0;
1154     return;
1155   }
1156 
1157   if (minor >= 1) {
1158     // at least HTTP/1.1
1159     mVersion = HttpVersion::v1_1;
1160   } else {
1161     // treat anything else as version 1.0
1162     mVersion = HttpVersion::v1_0;
1163   }
1164 }
1165 
ParseCacheControl(const char * val)1166 void nsHttpResponseHead::ParseCacheControl(const char* val) {
1167   if (!(val && *val)) {
1168     // clear flags
1169     mHasCacheControl = false;
1170     mCacheControlPublic = false;
1171     mCacheControlPrivate = false;
1172     mCacheControlNoCache = false;
1173     mCacheControlNoStore = false;
1174     mCacheControlImmutable = false;
1175     mCacheControlStaleWhileRevalidateSet = false;
1176     mCacheControlStaleWhileRevalidate = 0;
1177     mCacheControlMaxAgeSet = false;
1178     mCacheControlMaxAge = 0;
1179     return;
1180   }
1181 
1182   nsDependentCString cacheControlRequestHeader(val);
1183   CacheControlParser cacheControlRequest(cacheControlRequestHeader);
1184 
1185   mHasCacheControl = true;
1186   mCacheControlPublic = cacheControlRequest.Public();
1187   mCacheControlPrivate = cacheControlRequest.Private();
1188   mCacheControlNoCache = cacheControlRequest.NoCache();
1189   mCacheControlNoStore = cacheControlRequest.NoStore();
1190   mCacheControlImmutable = cacheControlRequest.Immutable();
1191   mCacheControlStaleWhileRevalidateSet =
1192       cacheControlRequest.StaleWhileRevalidate(
1193           &mCacheControlStaleWhileRevalidate);
1194   mCacheControlMaxAgeSet = cacheControlRequest.MaxAge(&mCacheControlMaxAge);
1195 }
1196 
ParsePragma(const char * val)1197 void nsHttpResponseHead::ParsePragma(const char* val) {
1198   LOG(("nsHttpResponseHead::ParsePragma [val=%s]\n", val));
1199 
1200   if (!(val && *val)) {
1201     // clear no-cache flag
1202     mPragmaNoCache = false;
1203     return;
1204   }
1205 
1206   // Although 'Pragma: no-cache' is not a standard HTTP response header (it's a
1207   // request header), caching is inhibited when this header is present so as to
1208   // match existing Navigator behavior.
1209   mPragmaNoCache = nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS);
1210 }
1211 
VisitHeaders(nsIHttpHeaderVisitor * visitor,nsHttpHeaderArray::VisitorFilter filter)1212 nsresult nsHttpResponseHead::VisitHeaders(
1213     nsIHttpHeaderVisitor* visitor, nsHttpHeaderArray::VisitorFilter filter) {
1214   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1215   mInVisitHeaders = true;
1216   nsresult rv = mHeaders.VisitHeaders(visitor, filter);
1217   mInVisitHeaders = false;
1218   return rv;
1219 }
1220 
GetOriginalHeader(const nsHttpAtom & aHeader,nsIHttpHeaderVisitor * aVisitor)1221 nsresult nsHttpResponseHead::GetOriginalHeader(const nsHttpAtom& aHeader,
1222                                                nsIHttpHeaderVisitor* aVisitor) {
1223   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1224   mInVisitHeaders = true;
1225   nsresult rv = mHeaders.GetOriginalHeader(aHeader, aVisitor);
1226   mInVisitHeaders = false;
1227   return rv;
1228 }
1229 
HasContentType() const1230 bool nsHttpResponseHead::HasContentType() const {
1231   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1232   return !mContentType.IsEmpty();
1233 }
1234 
HasContentCharset()1235 bool nsHttpResponseHead::HasContentCharset() {
1236   RecursiveMutexAutoLock monitor(mRecursiveMutex);
1237   return !mContentCharset.IsEmpty();
1238 }
1239 
GetContentTypeOptionsHeader(nsACString & aOutput)1240 bool nsHttpResponseHead::GetContentTypeOptionsHeader(nsACString& aOutput) {
1241   aOutput.Truncate();
1242 
1243   nsAutoCString contentTypeOptionsHeader;
1244   Unused << GetHeader(nsHttp::X_Content_Type_Options, contentTypeOptionsHeader);
1245   if (contentTypeOptionsHeader.IsEmpty()) {
1246     // if there is no XCTO header, then there is nothing to do.
1247     return false;
1248   }
1249 
1250   // XCTO header might contain multiple values which are comma separated, so:
1251   // a) let's skip all subsequent values
1252   //     e.g. "   NoSniFF   , foo " will be "   NoSniFF   "
1253   int32_t idx = contentTypeOptionsHeader.Find(",");
1254   if (idx > 0) {
1255     contentTypeOptionsHeader = Substring(contentTypeOptionsHeader, 0, idx);
1256   }
1257   // b) let's trim all surrounding whitespace
1258   //    e.g. "   NoSniFF   " -> "NoSniFF"
1259   nsHttp::TrimHTTPWhitespace(contentTypeOptionsHeader,
1260                              contentTypeOptionsHeader);
1261 
1262   aOutput.Assign(contentTypeOptionsHeader);
1263   return true;
1264 }
1265 
1266 }  // namespace net
1267 }  // namespace mozilla
1268