LCOV - code coverage report
Current view: top level - dom/fetch - InternalHeaders.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 69 227 30.4 %
Date: 2017-07-14 16:53:18 Functions: 16 38 42.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       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             : #include "mozilla/dom/InternalHeaders.h"
       8             : 
       9             : #include "mozilla/dom/FetchTypes.h"
      10             : #include "mozilla/ErrorResult.h"
      11             : 
      12             : #include "nsCharSeparatedTokenizer.h"
      13             : #include "nsContentUtils.h"
      14             : #include "nsIHttpHeaderVisitor.h"
      15             : #include "nsNetUtil.h"
      16             : #include "nsReadableUtils.h"
      17             : 
      18             : namespace mozilla {
      19             : namespace dom {
      20             : 
      21           0 : InternalHeaders::InternalHeaders(const nsTArray<Entry>&& aHeaders,
      22           0 :                                  HeadersGuardEnum aGuard)
      23             :   : mGuard(aGuard)
      24           0 :   , mList(aHeaders)
      25             : {
      26           0 : }
      27             : 
      28           0 : InternalHeaders::InternalHeaders(const nsTArray<HeadersEntry>& aHeadersEntryList,
      29           0 :                                  HeadersGuardEnum aGuard)
      30           0 :   : mGuard(aGuard)
      31             : {
      32           0 :   for (const HeadersEntry& headersEntry : aHeadersEntryList) {
      33           0 :     mList.AppendElement(Entry(headersEntry.name(), headersEntry.value()));
      34             :   }
      35           0 : }
      36             : 
      37             : void
      38           0 : InternalHeaders::ToIPC(nsTArray<HeadersEntry>& aIPCHeaders,
      39             :                        HeadersGuardEnum& aGuard)
      40             : {
      41           0 :   aGuard = mGuard;
      42             : 
      43           0 :   aIPCHeaders.Clear();
      44           0 :   for (Entry& entry : mList) {
      45           0 :     aIPCHeaders.AppendElement(HeadersEntry(entry.mName, entry.mValue));
      46             :   }
      47           0 : }
      48             : 
      49             : void
      50           4 : InternalHeaders::Append(const nsACString& aName, const nsACString& aValue,
      51             :                         ErrorResult& aRv)
      52             : {
      53           8 :   nsAutoCString lowerName;
      54           4 :   ToLowerCase(aName, lowerName);
      55           8 :   nsAutoCString trimValue;
      56           4 :   NS_TrimHTTPWhitespace(aValue, trimValue);
      57             : 
      58           4 :   if (IsInvalidMutableHeader(lowerName, trimValue, aRv)) {
      59           0 :     return;
      60             :   }
      61             : 
      62           4 :   mList.AppendElement(Entry(lowerName, trimValue));
      63             : }
      64             : 
      65             : void
      66           2 : InternalHeaders::Delete(const nsACString& aName, ErrorResult& aRv)
      67             : {
      68           2 :   nsAutoCString lowerName;
      69           2 :   ToLowerCase(aName, lowerName);
      70             : 
      71           2 :   if (IsInvalidMutableHeader(lowerName, aRv)) {
      72           2 :     return;
      73             :   }
      74             : 
      75             :   // remove in reverse order to minimize copying
      76           0 :   for (int32_t i = mList.Length() - 1; i >= 0; --i) {
      77           0 :     if (lowerName == mList[i].mName) {
      78           0 :       mList.RemoveElementAt(i);
      79             :     }
      80             :   }
      81             : }
      82             : 
      83             : void
      84           3 : InternalHeaders::Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
      85             : {
      86           6 :   nsAutoCString lowerName;
      87           3 :   ToLowerCase(aName, lowerName);
      88             : 
      89           3 :   if (IsInvalidName(lowerName, aRv)) {
      90           0 :     return;
      91             :   }
      92             : 
      93           3 :   const char* delimiter = ", ";
      94           3 :   bool firstValueFound = false;
      95             : 
      96           5 :   for (uint32_t i = 0; i < mList.Length(); ++i) {
      97           2 :     if (lowerName == mList[i].mName) {
      98           1 :       if (firstValueFound) {
      99           0 :         aValue += delimiter;
     100             :       }
     101           1 :       aValue += mList[i].mValue;
     102           1 :       firstValueFound = true;
     103             :     }
     104             :   }
     105             : 
     106             :   // No value found, so return null to content
     107           3 :   if (!firstValueFound) {
     108           2 :     aValue.SetIsVoid(true);
     109             :   }
     110             : }
     111             : 
     112             : void
     113           0 : InternalHeaders::GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
     114             : {
     115           0 :   nsAutoCString lowerName;
     116           0 :   ToLowerCase(aName, lowerName);
     117             : 
     118           0 :   if (IsInvalidName(lowerName, aRv)) {
     119           0 :     return;
     120             :   }
     121             : 
     122           0 :   for (uint32_t i = 0; i < mList.Length(); ++i) {
     123           0 :     if (lowerName == mList[i].mName) {
     124           0 :       aValue = mList[i].mValue;
     125           0 :       return;
     126             :     }
     127             :   }
     128             : 
     129             :   // No value found, so return null to content
     130           0 :   aValue.SetIsVoid(true);
     131             : }
     132             : 
     133             : bool
     134           0 : InternalHeaders::Has(const nsACString& aName, ErrorResult& aRv) const
     135             : {
     136           0 :   nsAutoCString lowerName;
     137           0 :   ToLowerCase(aName, lowerName);
     138             : 
     139           0 :   if (IsInvalidName(lowerName, aRv)) {
     140           0 :     return false;
     141             :   }
     142             : 
     143           0 :   for (uint32_t i = 0; i < mList.Length(); ++i) {
     144           0 :     if (lowerName == mList[i].mName) {
     145           0 :       return true;
     146             :     }
     147             :   }
     148           0 :   return false;
     149             : }
     150             : 
     151             : void
     152           0 : InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
     153             : {
     154           0 :   nsAutoCString lowerName;
     155           0 :   ToLowerCase(aName, lowerName);
     156           0 :   nsAutoCString trimValue;
     157           0 :   NS_TrimHTTPWhitespace(aValue, trimValue);
     158             : 
     159           0 :   if (IsInvalidMutableHeader(lowerName, trimValue, aRv)) {
     160           0 :     return;
     161             :   }
     162             : 
     163           0 :   int32_t firstIndex = INT32_MAX;
     164             : 
     165             :   // remove in reverse order to minimize copying
     166           0 :   for (int32_t i = mList.Length() - 1; i >= 0; --i) {
     167           0 :     if (lowerName == mList[i].mName) {
     168           0 :       firstIndex = std::min(firstIndex, i);
     169           0 :       mList.RemoveElementAt(i);
     170             :     }
     171             :   }
     172             : 
     173           0 :   if (firstIndex < INT32_MAX) {
     174           0 :     Entry* entry = mList.InsertElementAt(firstIndex);
     175           0 :     entry->mName = lowerName;
     176           0 :     entry->mValue = trimValue;
     177             :   } else {
     178           0 :     mList.AppendElement(Entry(lowerName, trimValue));
     179             :   }
     180             : }
     181             : 
     182             : void
     183           1 : InternalHeaders::Clear()
     184             : {
     185           1 :   mList.Clear();
     186           1 : }
     187             : 
     188             : void
     189           1 : InternalHeaders::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
     190             : {
     191             :   // The guard is only checked during ::Set() and ::Append() in the spec.  It
     192             :   // does not require revalidating headers already set.
     193           1 :   mGuard = aGuard;
     194           1 : }
     195             : 
     196          10 : InternalHeaders::~InternalHeaders()
     197             : {
     198          15 : }
     199             : 
     200             : // static
     201             : bool
     202           0 : InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValue)
     203             : {
     204             :   // Note, we must allow a null content-type value here to support
     205             :   // get("content-type"), but the IsInvalidValue() check will prevent null
     206             :   // from being set or appended.
     207           0 :   return aName.EqualsLiteral("accept") ||
     208           0 :          aName.EqualsLiteral("accept-language") ||
     209           0 :          aName.EqualsLiteral("content-language") ||
     210           0 :          (aName.EqualsLiteral("content-type") &&
     211           0 :           nsContentUtils::IsAllowedNonCorsContentType(aValue));
     212             : }
     213             : 
     214             : // static
     215             : bool
     216           0 : InternalHeaders::IsRevalidationHeader(const nsACString& aName)
     217             : {
     218           0 :   return aName.EqualsLiteral("if-modified-since") ||
     219           0 :          aName.EqualsLiteral("if-none-match") ||
     220           0 :          aName.EqualsLiteral("if-unmodified-since") ||
     221           0 :          aName.EqualsLiteral("if-match") ||
     222           0 :          aName.EqualsLiteral("if-range");
     223             : }
     224             : 
     225             : //static
     226             : bool
     227           9 : InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
     228             : {
     229           9 :   if (!NS_IsValidHTTPToken(aName)) {
     230           0 :     NS_ConvertUTF8toUTF16 label(aName);
     231           0 :     aRv.ThrowTypeError<MSG_INVALID_HEADER_NAME>(label);
     232           0 :     return true;
     233             :   }
     234             : 
     235           9 :   return false;
     236             : }
     237             : 
     238             : // static
     239             : bool
     240           6 : InternalHeaders::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv)
     241             : {
     242           6 :   if (!NS_IsReasonableHTTPHeaderValue(aValue)) {
     243           0 :     NS_ConvertUTF8toUTF16 label(aValue);
     244           0 :     aRv.ThrowTypeError<MSG_INVALID_HEADER_VALUE>(label);
     245           0 :     return true;
     246             :   }
     247           6 :   return false;
     248             : }
     249             : 
     250             : bool
     251           6 : InternalHeaders::IsImmutable(ErrorResult& aRv) const
     252             : {
     253           6 :   if (mGuard == HeadersGuardEnum::Immutable) {
     254           0 :     aRv.ThrowTypeError<MSG_HEADERS_IMMUTABLE>();
     255           0 :     return true;
     256             :   }
     257           6 :   return false;
     258             : }
     259             : 
     260             : bool
     261           6 : InternalHeaders::IsForbiddenRequestHeader(const nsACString& aName) const
     262             : {
     263           6 :   return mGuard == HeadersGuardEnum::Request &&
     264           6 :          nsContentUtils::IsForbiddenRequestHeader(aName);
     265             : }
     266             : 
     267             : bool
     268           0 : InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName) const
     269             : {
     270           0 :   return mGuard == HeadersGuardEnum::Request_no_cors &&
     271           0 :          !IsSimpleHeader(aName, EmptyCString());
     272             : }
     273             : 
     274             : bool
     275           6 : InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName,
     276             :                                                 const nsACString& aValue) const
     277             : {
     278           6 :   return mGuard == HeadersGuardEnum::Request_no_cors &&
     279           6 :          !IsSimpleHeader(aName, aValue);
     280             : }
     281             : 
     282             : bool
     283           6 : InternalHeaders::IsForbiddenResponseHeader(const nsACString& aName) const
     284             : {
     285          10 :   return mGuard == HeadersGuardEnum::Response &&
     286          10 :          nsContentUtils::IsForbiddenResponseHeader(aName);
     287             : }
     288             : 
     289             : void
     290           4 : InternalHeaders::Fill(const InternalHeaders& aInit, ErrorResult& aRv)
     291             : {
     292           4 :   const nsTArray<Entry>& list = aInit.mList;
     293           6 :   for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
     294           2 :     const Entry& entry = list[i];
     295           2 :     Append(entry.mName, entry.mValue, aRv);
     296             :   }
     297           4 : }
     298             : 
     299             : void
     300           0 : InternalHeaders::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv)
     301             : {
     302           0 :   for (uint32_t i = 0; i < aInit.Length() && !aRv.Failed(); ++i) {
     303           0 :     const Sequence<nsCString>& tuple = aInit[i];
     304           0 :     if (tuple.Length() != 2) {
     305           0 :       aRv.ThrowTypeError<MSG_INVALID_HEADER_SEQUENCE>();
     306           0 :       return;
     307             :     }
     308           0 :     Append(tuple[0], tuple[1], aRv);
     309             :   }
     310             : }
     311             : 
     312             : void
     313           0 : InternalHeaders::Fill(const Record<nsCString, nsCString>& aInit, ErrorResult& aRv)
     314             : {
     315           0 :   for (auto& entry : aInit.Entries()) {
     316           0 :     Append(entry.mKey, entry.mValue, aRv);
     317           0 :     if (aRv.Failed()) {
     318           0 :       return;
     319             :     }
     320             :   }
     321             : }
     322             : 
     323             : namespace {
     324             : 
     325             : class FillHeaders final : public nsIHttpHeaderVisitor
     326             : {
     327             :   RefPtr<InternalHeaders> mInternalHeaders;
     328             : 
     329           0 :   ~FillHeaders() = default;
     330             : 
     331             : public:
     332             :   NS_DECL_ISUPPORTS
     333             : 
     334           0 :   explicit FillHeaders(InternalHeaders* aInternalHeaders)
     335           0 :     : mInternalHeaders(aInternalHeaders)
     336             :   {
     337           0 :     MOZ_DIAGNOSTIC_ASSERT(mInternalHeaders);
     338           0 :   }
     339             : 
     340             :   NS_IMETHOD
     341           0 :   VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
     342             :   {
     343           0 :     IgnoredErrorResult result;
     344           0 :     mInternalHeaders->Append(aHeader, aValue, result);
     345           0 :     return NS_OK;
     346             :   }
     347             : };
     348             : 
     349           0 : NS_IMPL_ISUPPORTS(FillHeaders, nsIHttpHeaderVisitor)
     350             : 
     351             : } // namespace
     352             : 
     353             : void
     354           0 : InternalHeaders::FillResponseHeaders(nsIRequest* aRequest)
     355             : {
     356           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
     357           0 :   if (!httpChannel) {
     358           0 :     return;
     359             :   }
     360             : 
     361           0 :   RefPtr<FillHeaders> visitor = new FillHeaders(this);
     362           0 :   nsresult rv = httpChannel->VisitResponseHeaders(visitor);
     363           0 :   if (NS_FAILED(rv)) {
     364           0 :     NS_WARNING("failed to fill headers");
     365             :   }
     366             : }
     367             : 
     368             : bool
     369           0 : InternalHeaders::HasOnlySimpleHeaders() const
     370             : {
     371           0 :   for (uint32_t i = 0; i < mList.Length(); ++i) {
     372           0 :     if (!IsSimpleHeader(mList[i].mName, mList[i].mValue)) {
     373           0 :       return false;
     374             :     }
     375             :   }
     376             : 
     377           0 :   return true;
     378             : }
     379             : 
     380             : bool
     381           0 : InternalHeaders::HasRevalidationHeaders() const
     382             : {
     383           0 :   for (uint32_t i = 0; i < mList.Length(); ++i) {
     384           0 :     if (IsRevalidationHeader(mList[i].mName)) {
     385           0 :       return true;
     386             :     }
     387             :   }
     388             : 
     389           0 :   return false;
     390             : }
     391             : 
     392             : // static
     393             : already_AddRefed<InternalHeaders>
     394           1 : InternalHeaders::BasicHeaders(InternalHeaders* aHeaders)
     395             : {
     396           2 :   RefPtr<InternalHeaders> basic = new InternalHeaders(*aHeaders);
     397           2 :   ErrorResult result;
     398             :   // The Set-Cookie headers cannot be invalid mutable headers, so the Delete
     399             :   // must succeed.
     400           1 :   basic->Delete(NS_LITERAL_CSTRING("Set-Cookie"), result);
     401           1 :   MOZ_ASSERT(!result.Failed());
     402           1 :   basic->Delete(NS_LITERAL_CSTRING("Set-Cookie2"), result);
     403           1 :   MOZ_ASSERT(!result.Failed());
     404           2 :   return basic.forget();
     405             : }
     406             : 
     407             : // static
     408             : already_AddRefed<InternalHeaders>
     409           0 : InternalHeaders::CORSHeaders(InternalHeaders* aHeaders)
     410             : {
     411           0 :   RefPtr<InternalHeaders> cors = new InternalHeaders(aHeaders->mGuard);
     412           0 :   ErrorResult result;
     413             : 
     414           0 :   nsAutoCString acExposedNames;
     415           0 :   aHeaders->GetFirst(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
     416           0 :   MOZ_ASSERT(!result.Failed());
     417             : 
     418           0 :   AutoTArray<nsCString, 5> exposeNamesArray;
     419           0 :   nsCCharSeparatedTokenizer exposeTokens(acExposedNames, ',');
     420           0 :   while (exposeTokens.hasMoreTokens()) {
     421           0 :     const nsDependentCSubstring& token = exposeTokens.nextToken();
     422           0 :     if (token.IsEmpty()) {
     423           0 :       continue;
     424             :     }
     425             : 
     426           0 :     if (!NS_IsValidHTTPToken(token)) {
     427           0 :       NS_WARNING("Got invalid HTTP token in Access-Control-Expose-Headers. Header value is:");
     428           0 :       NS_WARNING(acExposedNames.get());
     429           0 :       exposeNamesArray.Clear();
     430           0 :       break;
     431             :     }
     432             : 
     433           0 :     exposeNamesArray.AppendElement(token);
     434             :   }
     435             : 
     436             :   nsCaseInsensitiveCStringArrayComparator comp;
     437           0 :   for (uint32_t i = 0; i < aHeaders->mList.Length(); ++i) {
     438           0 :     const Entry& entry = aHeaders->mList[i];
     439           0 :     if (entry.mName.EqualsASCII("cache-control") ||
     440           0 :         entry.mName.EqualsASCII("content-language") ||
     441           0 :         entry.mName.EqualsASCII("content-type") ||
     442           0 :         entry.mName.EqualsASCII("expires") ||
     443           0 :         entry.mName.EqualsASCII("last-modified") ||
     444           0 :         entry.mName.EqualsASCII("pragma") ||
     445           0 :         exposeNamesArray.Contains(entry.mName, comp)) {
     446           0 :       cors->Append(entry.mName, entry.mValue, result);
     447           0 :       MOZ_ASSERT(!result.Failed());
     448             :     }
     449             :   }
     450             : 
     451           0 :   return cors.forget();
     452             : }
     453             : 
     454             : void
     455           0 : InternalHeaders::GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const
     456             : {
     457           0 :   MOZ_ASSERT(aEntries.IsEmpty());
     458           0 :   aEntries.AppendElements(mList);
     459           0 : }
     460             : 
     461             : void
     462           1 : InternalHeaders::GetUnsafeHeaders(nsTArray<nsCString>& aNames) const
     463             : {
     464           1 :   MOZ_ASSERT(aNames.IsEmpty());
     465           1 :   for (uint32_t i = 0; i < mList.Length(); ++i) {
     466           0 :     const Entry& header = mList[i];
     467           0 :     if (!InternalHeaders::IsSimpleHeader(header.mName, header.mValue)) {
     468           0 :       aNames.AppendElement(header.mName);
     469             :     }
     470             :   }
     471           1 : }
     472             : 
     473             : } // namespace dom
     474             : } // namespace mozilla

Generated by: LCOV version 1.13