123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "mozilla/dom/InternalHeaders.h"
- #include "mozilla/dom/FetchTypes.h"
- #include "mozilla/ErrorResult.h"
- #include "nsCharSeparatedTokenizer.h"
- #include "nsContentUtils.h"
- #include "nsNetUtil.h"
- #include "nsReadableUtils.h"
- namespace mozilla {
- namespace dom {
- InternalHeaders::InternalHeaders(const nsTArray<Entry>&& aHeaders,
- HeadersGuardEnum aGuard)
- : mGuard(aGuard)
- , mList(aHeaders)
- , mListDirty(true)
- {
- }
- InternalHeaders::InternalHeaders(const nsTArray<HeadersEntry>& aHeadersEntryList,
- HeadersGuardEnum aGuard)
- : mGuard(aGuard)
- , mListDirty(true)
- {
- for (const HeadersEntry& headersEntry : aHeadersEntryList) {
- mList.AppendElement(Entry(headersEntry.name(), headersEntry.value()));
- }
- }
- void
- InternalHeaders::ToIPC(nsTArray<HeadersEntry>& aIPCHeaders,
- HeadersGuardEnum& aGuard)
- {
- aGuard = mGuard;
- aIPCHeaders.Clear();
- for (Entry& entry : mList) {
- aIPCHeaders.AppendElement(HeadersEntry(entry.mName, entry.mValue));
- }
- }
- void
- InternalHeaders::Append(const nsACString& aName, const nsACString& aValue,
- ErrorResult& aRv)
- {
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
- if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
- return;
- }
- SetListDirty();
- mList.AppendElement(Entry(lowerName, aValue));
- }
- void
- InternalHeaders::Delete(const nsACString& aName, ErrorResult& aRv)
- {
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
- if (IsInvalidMutableHeader(lowerName, aRv)) {
- return;
- }
- SetListDirty();
- // remove in reverse order to minimize copying
- for (int32_t i = mList.Length() - 1; i >= 0; --i) {
- if (lowerName == mList[i].mName) {
- mList.RemoveElementAt(i);
- }
- }
- }
- void
- InternalHeaders::Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
- {
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
- if (IsInvalidName(lowerName, aRv)) {
- return;
- }
- const char* delimiter = ",";
- bool firstValueFound = false;
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (lowerName == mList[i].mName) {
- if (firstValueFound) {
- aValue += delimiter;
- }
- aValue += mList[i].mValue;
- firstValueFound = true;
- }
- }
- // No value found, so return null to content
- if (!firstValueFound) {
- aValue.SetIsVoid(true);
- }
- }
- void
- InternalHeaders::GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
- {
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
- if (IsInvalidName(lowerName, aRv)) {
- return;
- }
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (lowerName == mList[i].mName) {
- aValue = mList[i].mValue;
- return;
- }
- }
- // No value found, so return null to content
- aValue.SetIsVoid(true);
- }
- bool
- InternalHeaders::Has(const nsACString& aName, ErrorResult& aRv) const
- {
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
- if (IsInvalidName(lowerName, aRv)) {
- return false;
- }
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (lowerName == mList[i].mName) {
- return true;
- }
- }
- return false;
- }
- void
- InternalHeaders::Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv)
- {
- nsAutoCString lowerName;
- ToLowerCase(aName, lowerName);
- if (IsInvalidMutableHeader(lowerName, aValue, aRv)) {
- return;
- }
- SetListDirty();
- int32_t firstIndex = INT32_MAX;
- // remove in reverse order to minimize copying
- for (int32_t i = mList.Length() - 1; i >= 0; --i) {
- if (lowerName == mList[i].mName) {
- firstIndex = std::min(firstIndex, i);
- mList.RemoveElementAt(i);
- }
- }
- if (firstIndex < INT32_MAX) {
- Entry* entry = mList.InsertElementAt(firstIndex);
- entry->mName = lowerName;
- entry->mValue = aValue;
- } else {
- mList.AppendElement(Entry(lowerName, aValue));
- }
- }
- void
- InternalHeaders::Clear()
- {
- SetListDirty();
- mList.Clear();
- }
- void
- InternalHeaders::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv)
- {
- // The guard is only checked during ::Set() and ::Append() in the spec. It
- // does not require revalidating headers already set.
- mGuard = aGuard;
- }
- InternalHeaders::~InternalHeaders()
- {
- }
- // static
- bool
- InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValue)
- {
- // Note, we must allow a null content-type value here to support
- // get("content-type"), but the IsInvalidValue() check will prevent null
- // from being set or appended.
- return aName.EqualsLiteral("accept") ||
- aName.EqualsLiteral("accept-language") ||
- aName.EqualsLiteral("content-language") ||
- (aName.EqualsLiteral("content-type") &&
- nsContentUtils::IsAllowedNonCorsContentType(aValue));
- }
- // static
- bool
- InternalHeaders::IsRevalidationHeader(const nsACString& aName)
- {
- return aName.EqualsLiteral("if-modified-since") ||
- aName.EqualsLiteral("if-none-match") ||
- aName.EqualsLiteral("if-unmodified-since") ||
- aName.EqualsLiteral("if-match") ||
- aName.EqualsLiteral("if-range");
- }
- //static
- bool
- InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
- {
- if (!NS_IsValidHTTPToken(aName)) {
- NS_ConvertUTF8toUTF16 label(aName);
- aRv.ThrowTypeError<MSG_INVALID_HEADER_NAME>(label);
- return true;
- }
- return false;
- }
- // static
- bool
- InternalHeaders::IsInvalidValue(const nsACString& aValue, ErrorResult& aRv)
- {
- if (!NS_IsReasonableHTTPHeaderValue(aValue)) {
- NS_ConvertUTF8toUTF16 label(aValue);
- aRv.ThrowTypeError<MSG_INVALID_HEADER_VALUE>(label);
- return true;
- }
- return false;
- }
- bool
- InternalHeaders::IsImmutable(ErrorResult& aRv) const
- {
- if (mGuard == HeadersGuardEnum::Immutable) {
- aRv.ThrowTypeError<MSG_HEADERS_IMMUTABLE>();
- return true;
- }
- return false;
- }
- bool
- InternalHeaders::IsForbiddenRequestHeader(const nsACString& aName) const
- {
- return mGuard == HeadersGuardEnum::Request &&
- nsContentUtils::IsForbiddenRequestHeader(aName);
- }
- bool
- InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName) const
- {
- return mGuard == HeadersGuardEnum::Request_no_cors &&
- !IsSimpleHeader(aName, EmptyCString());
- }
- bool
- InternalHeaders::IsForbiddenRequestNoCorsHeader(const nsACString& aName,
- const nsACString& aValue) const
- {
- return mGuard == HeadersGuardEnum::Request_no_cors &&
- !IsSimpleHeader(aName, aValue);
- }
- bool
- InternalHeaders::IsForbiddenResponseHeader(const nsACString& aName) const
- {
- return mGuard == HeadersGuardEnum::Response &&
- nsContentUtils::IsForbiddenResponseHeader(aName);
- }
- void
- InternalHeaders::Fill(const InternalHeaders& aInit, ErrorResult& aRv)
- {
- const nsTArray<Entry>& list = aInit.mList;
- for (uint32_t i = 0; i < list.Length() && !aRv.Failed(); ++i) {
- const Entry& entry = list[i];
- Append(entry.mName, entry.mValue, aRv);
- }
- }
- void
- InternalHeaders::Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv)
- {
- for (uint32_t i = 0; i < aInit.Length() && !aRv.Failed(); ++i) {
- const Sequence<nsCString>& tuple = aInit[i];
- if (tuple.Length() != 2) {
- aRv.ThrowTypeError<MSG_INVALID_HEADER_SEQUENCE>();
- return;
- }
- Append(tuple[0], tuple[1], aRv);
- }
- }
- void
- InternalHeaders::Fill(const Record<nsCString, nsCString>& aInit, ErrorResult& aRv)
- {
- for (auto& entry : aInit.Entries()) {
- Append(entry.mKey, entry.mValue, aRv);
- if (aRv.Failed()) {
- return;
- }
- }
- }
- bool
- InternalHeaders::HasOnlySimpleHeaders() const
- {
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (!IsSimpleHeader(mList[i].mName, mList[i].mValue)) {
- return false;
- }
- }
- return true;
- }
- bool
- InternalHeaders::HasRevalidationHeaders() const
- {
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- if (IsRevalidationHeader(mList[i].mName)) {
- return true;
- }
- }
- return false;
- }
- // static
- already_AddRefed<InternalHeaders>
- InternalHeaders::BasicHeaders(InternalHeaders* aHeaders)
- {
- RefPtr<InternalHeaders> basic = new InternalHeaders(*aHeaders);
- ErrorResult result;
- // The Set-Cookie headers cannot be invalid mutable headers, so the Delete
- // must succeed.
- basic->Delete(NS_LITERAL_CSTRING("Set-Cookie"), result);
- MOZ_ASSERT(!result.Failed());
- basic->Delete(NS_LITERAL_CSTRING("Set-Cookie2"), result);
- MOZ_ASSERT(!result.Failed());
- return basic.forget();
- }
- // static
- already_AddRefed<InternalHeaders>
- InternalHeaders::CORSHeaders(InternalHeaders* aHeaders)
- {
- RefPtr<InternalHeaders> cors = new InternalHeaders(aHeaders->mGuard);
- ErrorResult result;
- nsAutoCString acExposedNames;
- aHeaders->GetFirst(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
- MOZ_ASSERT(!result.Failed());
- AutoTArray<nsCString, 5> exposeNamesArray;
- nsCCharSeparatedTokenizer exposeTokens(acExposedNames, ',');
- while (exposeTokens.hasMoreTokens()) {
- const nsDependentCSubstring& token = exposeTokens.nextToken();
- if (token.IsEmpty()) {
- continue;
- }
- if (!NS_IsValidHTTPToken(token)) {
- NS_WARNING("Got invalid HTTP token in Access-Control-Expose-Headers. Header value is:");
- NS_WARNING(acExposedNames.get());
- exposeNamesArray.Clear();
- break;
- }
- exposeNamesArray.AppendElement(token);
- }
- nsCaseInsensitiveCStringArrayComparator comp;
- for (uint32_t i = 0; i < aHeaders->mList.Length(); ++i) {
- const Entry& entry = aHeaders->mList[i];
- if (entry.mName.EqualsASCII("cache-control") ||
- entry.mName.EqualsASCII("content-language") ||
- entry.mName.EqualsASCII("content-type") ||
- entry.mName.EqualsASCII("expires") ||
- entry.mName.EqualsASCII("last-modified") ||
- entry.mName.EqualsASCII("pragma") ||
- exposeNamesArray.Contains(entry.mName, comp)) {
- cors->Append(entry.mName, entry.mValue, result);
- MOZ_ASSERT(!result.Failed());
- }
- }
- return cors.forget();
- }
- void
- InternalHeaders::GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const
- {
- MOZ_ASSERT(aEntries.IsEmpty());
- aEntries.AppendElements(mList);
- }
- void
- InternalHeaders::GetUnsafeHeaders(nsTArray<nsCString>& aNames) const
- {
- MOZ_ASSERT(aNames.IsEmpty());
- for (uint32_t i = 0; i < mList.Length(); ++i) {
- const Entry& header = mList[i];
- if (!InternalHeaders::IsSimpleHeader(header.mName, header.mValue)) {
- aNames.AppendElement(header.mName);
- }
- }
- }
- void
- InternalHeaders::MaybeSortList()
- {
- class Comparator {
- public:
- bool Equals(const Entry& aA, const Entry& aB) const
- {
- return aA.mName == aB.mName;
- }
- bool LessThan(const Entry& aA, const Entry& aB) const
- {
- return aA.mName < aB.mName;
- }
- };
- if (!mListDirty) {
- return;
- }
- mListDirty = false;
- Comparator comparator;
- mSortedList.Clear();
- for (const Entry& entry : mList) {
- bool found = false;
- for (Entry& sortedEntry : mSortedList) {
- if (sortedEntry.mName == entry.mName) {
- sortedEntry.mValue += ", ";
- sortedEntry.mValue += entry.mValue;
- found = true;
- break;
- }
- }
- if (!found) {
- mSortedList.InsertElementSorted(entry, comparator);
- }
- }
- }
- void
- InternalHeaders::SetListDirty()
- {
- mSortedList.Clear();
- mListDirty = true;
- }
- } // namespace dom
- } // namespace mozilla
|