tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsHttpHeaderArray.h (12652B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et 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 #ifndef nsHttpHeaderArray_h__
      8 #define nsHttpHeaderArray_h__
      9 
     10 #include "nsHttp.h"
     11 #include "nsTArray.h"
     12 #include "nsString.h"
     13 
     14 class nsIHttpHeaderVisitor;
     15 
     16 // This needs to be forward declared here so we can include only this header
     17 // without also including PHttpChannelParams.h
     18 namespace IPC {
     19 template <typename>
     20 struct ParamTraits;
     21 }  // namespace IPC
     22 
     23 namespace mozilla {
     24 namespace net {
     25 
     26 class nsHttpHeaderArray {
     27 public:
     28  const char* PeekHeader(const nsHttpAtom& header) const;
     29 
     30  // For nsHttpResponseHead nsHttpHeaderArray will keep track of the original
     31  // headers as they come from the network and the parse headers used in
     32  // firefox.
     33  // If the original and the firefox header are the same, we will keep just
     34  // one copy and marked it as eVarietyResponseNetOriginalAndResponse.
     35  // If firefox header representation changes a header coming from the
     36  // network (e.g. merged it) or a eVarietyResponseNetOriginalAndResponse
     37  // header has been changed by SetHeader method, we will keep the original
     38  // header as eVarietyResponseNetOriginal and make a copy for the new header
     39  // and mark it as eVarietyResponse.
     40  enum HeaderVariety {
     41    eVarietyUnknown,
     42    // Used only for request header.
     43    eVarietyRequestOverride,
     44    eVarietyRequestDefault,
     45    eVarietyRequestEnforceDefault,
     46    // Used only for response header.
     47    eVarietyResponseNetOriginalAndResponse,
     48    eVarietyResponseNetOriginal,
     49    eVarietyResponse,
     50    eVarietyResponseOverride,
     51  };
     52 
     53  // Used by internal setters: to set header from network use SetHeaderFromNet
     54  [[nodiscard]] nsresult SetHeader(const nsACString& headerName,
     55                                   const nsACString& value, bool merge,
     56                                   HeaderVariety variety);
     57  [[nodiscard]] nsresult SetHeader(const nsHttpAtom& header,
     58                                   const nsACString& value, bool merge,
     59                                   HeaderVariety variety);
     60  [[nodiscard]] nsresult SetHeader(const nsHttpAtom& header,
     61                                   const nsACString& headerName,
     62                                   const nsACString& value, bool merge,
     63                                   HeaderVariety variety);
     64 
     65  // Used by internal setters to set an empty header
     66  [[nodiscard]] nsresult SetEmptyHeader(const nsACString& headerName,
     67                                        HeaderVariety variety);
     68 
     69  // Merges supported headers. For other duplicate values, determines if error
     70  // needs to be thrown or 1st value kept.
     71  // For the response header we keep the original headers as well.
     72  [[nodiscard]] nsresult SetHeaderFromNet(const nsHttpAtom& header,
     73                                          const nsACString& headerNameOriginal,
     74                                          const nsACString& value,
     75                                          bool response);
     76 
     77  [[nodiscard]] nsresult SetResponseHeaderFromCache(
     78      const nsHttpAtom& header, const nsACString& headerNameOriginal,
     79      const nsACString& value, HeaderVariety variety);
     80 
     81  [[nodiscard]] nsresult GetHeader(const nsHttpAtom& header,
     82                                   nsACString& result) const;
     83  [[nodiscard]] nsresult GetOriginalHeader(const nsHttpAtom& aHeader,
     84                                           nsIHttpHeaderVisitor* aVisitor);
     85  void ClearHeader(const nsHttpAtom& h);
     86 
     87  // Find the location of the given header value, or null if none exists.
     88  const char* FindHeaderValue(const nsHttpAtom& header,
     89                              const char* value) const {
     90    return nsHttp::FindToken(PeekHeader(header), value, HTTP_HEADER_VALUE_SEPS);
     91  }
     92 
     93  // Determine if the given header value exists.
     94  bool HasHeaderValue(const nsHttpAtom& header, const char* value) const {
     95    return FindHeaderValue(header, value) != nullptr;
     96  }
     97 
     98  bool HasHeader(const nsHttpAtom& header) const;
     99 
    100  enum VisitorFilter {
    101    eFilterAll,
    102    eFilterSkipDefault,
    103    eFilterResponse,
    104    eFilterResponseOriginal
    105  };
    106 
    107  [[nodiscard]] nsresult VisitHeaders(nsIHttpHeaderVisitor* visitor,
    108                                      VisitorFilter filter = eFilterAll);
    109 
    110  // parse a header line, return the header atom, the header name, and the
    111  // header value
    112  [[nodiscard]] static nsresult ParseHeaderLine(
    113      const nsACString& line, nsHttpAtom* hdr = nullptr,
    114      nsACString* headerNameOriginal = nullptr, nsACString* value = nullptr);
    115 
    116  void Flatten(nsACString&, bool pruneProxyHeaders, bool pruneTransients);
    117  void FlattenOriginalHeader(nsACString&);
    118 
    119  uint32_t Count() const { return mHeaders.Length(); }
    120 
    121  const char* PeekHeaderAt(uint32_t i, nsHttpAtom& header,
    122                           nsACString& headerNameOriginal) const;
    123 
    124  void Clear();
    125 
    126  // Must be copy-constructable and assignable
    127  struct nsEntry {
    128    nsHttpAtom header;
    129    nsCString headerNameOriginal;
    130    nsCString value;
    131    HeaderVariety variety = eVarietyUnknown;
    132 
    133    struct MatchHeader {
    134      bool Equals(const nsEntry& aEntry, const nsHttpAtom& aHeader) const {
    135        return aEntry.header == aHeader;
    136      }
    137    };
    138 
    139    bool operator==(const nsEntry& aOther) const {
    140      return header == aOther.header && value == aOther.value;
    141    }
    142  };
    143 
    144  bool operator==(const nsHttpHeaderArray& aOther) const {
    145    return mHeaders == aOther.mHeaders;
    146  }
    147 
    148 private:
    149  // LookupEntry function will never return eVarietyResponseNetOriginal.
    150  // It will ignore original headers from the network.
    151  int32_t LookupEntry(const nsHttpAtom& header, const nsEntry**) const;
    152  int32_t LookupEntry(const nsHttpAtom& header, nsEntry**);
    153  [[nodiscard]] nsresult MergeHeader(const nsHttpAtom& header, nsEntry* entry,
    154                                     const nsACString& value,
    155                                     HeaderVariety variety);
    156  [[nodiscard]] nsresult SetHeader_internal(const nsHttpAtom& header,
    157                                            const nsACString& headerName,
    158                                            const nsACString& value,
    159                                            HeaderVariety variety);
    160 
    161  // Header cannot be merged: only one value possible
    162  bool IsSingletonHeader(const nsHttpAtom& header);
    163  // Header cannot be merged, and subsequent values should be ignored
    164  bool IsIgnoreMultipleHeader(const nsHttpAtom& header);
    165 
    166  // Subset of singleton headers: should never see multiple, different
    167  // instances of these, else something fishy may be going on (like CLRF
    168  // injection)
    169  bool IsSuspectDuplicateHeader(const nsHttpAtom& header);
    170 
    171  // Removes duplicate header values entries
    172  // Will return unmodified header value if the header values contains
    173  // non-duplicate entries
    174  void RemoveDuplicateHeaderValues(const nsACString& aHeaderValue,
    175                                   nsACString& aResult);
    176 
    177  // All members must be copy-constructable and assignable
    178  CopyableTArray<nsEntry> mHeaders;
    179 
    180  friend struct IPC::ParamTraits<nsHttpHeaderArray>;
    181  friend class nsHttpRequestHead;
    182 };
    183 
    184 //-----------------------------------------------------------------------------
    185 // nsHttpHeaderArray <private>: inline functions
    186 //-----------------------------------------------------------------------------
    187 
    188 inline int32_t nsHttpHeaderArray::LookupEntry(const nsHttpAtom& header,
    189                                              const nsEntry** entry) const {
    190  uint32_t index = 0;
    191  while (index != UINT32_MAX) {
    192    index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
    193    if (index != UINT32_MAX) {
    194      if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
    195        *entry = &mHeaders[index];
    196        return index;
    197      }
    198      index++;
    199    }
    200  }
    201 
    202  return index;
    203 }
    204 
    205 inline int32_t nsHttpHeaderArray::LookupEntry(const nsHttpAtom& header,
    206                                              nsEntry** entry) {
    207  uint32_t index = 0;
    208  while (index != UINT32_MAX) {
    209    index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
    210    if (index != UINT32_MAX) {
    211      if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
    212        *entry = &mHeaders[index];
    213        return index;
    214      }
    215      index++;
    216    }
    217  }
    218  return index;
    219 }
    220 
    221 inline bool nsHttpHeaderArray::IsSingletonHeader(const nsHttpAtom& header) {
    222  return header == nsHttp::Content_Type ||
    223         header == nsHttp::Content_Disposition ||
    224         header == nsHttp::Content_Length || header == nsHttp::User_Agent ||
    225         header == nsHttp::Referer || header == nsHttp::Host ||
    226         header == nsHttp::Authorization ||
    227         header == nsHttp::Proxy_Authorization ||
    228         header == nsHttp::If_Modified_Since ||
    229         header == nsHttp::If_Unmodified_Since || header == nsHttp::From ||
    230         header == nsHttp::Location || header == nsHttp::Max_Forwards ||
    231         header == nsHttp::GlobalPrivacyControl ||
    232         // Ignore-multiple-headers are singletons in the sense that they
    233         // shouldn't be merged.
    234         IsIgnoreMultipleHeader(header);
    235 }
    236 
    237 // These are headers for which, in the presence of multiple values, we only
    238 // consider the first.
    239 inline bool nsHttpHeaderArray::IsIgnoreMultipleHeader(
    240    const nsHttpAtom& header) {
    241  // https://tools.ietf.org/html/rfc6797#section-8:
    242  //
    243  //     If a UA receives more than one STS header field in an HTTP
    244  //     response message over secure transport, then the UA MUST process
    245  //     only the first such header field.
    246  return header == nsHttp::Strict_Transport_Security;
    247 }
    248 
    249 [[nodiscard]] inline nsresult nsHttpHeaderArray::MergeHeader(
    250    const nsHttpAtom& header, nsEntry* entry, const nsACString& value,
    251    nsHttpHeaderArray::HeaderVariety variety) {
    252  // merge of empty header = no-op
    253  if (value.IsEmpty() && header != nsHttp::X_Frame_Options) {
    254    return NS_OK;
    255  }
    256 
    257  // x-frame-options having an empty header value still has an effect so we make
    258  // sure that we retain encountering it
    259  nsCString newValue = entry->value;
    260  if (!newValue.IsEmpty() || header == nsHttp::X_Frame_Options) {
    261    // Append the new value to the existing value
    262    if (header == nsHttp::Set_Cookie || header == nsHttp::WWW_Authenticate ||
    263        header == nsHttp::Proxy_Authenticate) {
    264      // Special case these headers and use a newline delimiter to
    265      // delimit the values from one another as commas may appear
    266      // in the values of these headers contrary to what the spec says.
    267      newValue.Append('\n');
    268    } else {
    269      // Delimit each value from the others using a comma (per HTTP spec)
    270      newValue.AppendLiteral(", ");
    271    }
    272  }
    273 
    274  newValue.Append(value);
    275  if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
    276    MOZ_ASSERT(variety == eVarietyResponse);
    277    entry->variety = eVarietyResponseNetOriginal;
    278    // Copy entry->headerNameOriginal because in SetHeader_internal we are going
    279    // to a new one and a realocation can happen.
    280    nsCString headerNameOriginal = entry->headerNameOriginal;
    281    nsresult rv = SetHeader_internal(header, headerNameOriginal, newValue,
    282                                     eVarietyResponse);
    283    if (NS_FAILED(rv)) {
    284      return rv;
    285    }
    286  } else {
    287    entry->value = newValue;
    288    entry->variety = variety;
    289  }
    290  return NS_OK;
    291 }
    292 
    293 inline bool nsHttpHeaderArray::IsSuspectDuplicateHeader(
    294    const nsHttpAtom& header) {
    295  bool retval = header == nsHttp::Content_Length ||
    296                header == nsHttp::Content_Disposition ||
    297                header == nsHttp::Location;
    298 
    299  MOZ_ASSERT(!retval || IsSingletonHeader(header),
    300             "Only non-mergeable headers should be in this list\n");
    301 
    302  return retval;
    303 }
    304 
    305 inline void nsHttpHeaderArray::RemoveDuplicateHeaderValues(
    306    const nsACString& aHeaderValue, nsACString& aResult) {
    307  mozilla::Maybe<nsAutoCString> result;
    308  for (const nsACString& token :
    309       nsCCharSeparatedTokenizer(aHeaderValue, ',').ToRange()) {
    310    if (result.isNothing()) {
    311      // assign the first value
    312      result.emplace(token);
    313      continue;
    314    }
    315    if (*result != token) {
    316      // non-identical header values. Do not change the header values
    317      result.reset();
    318      break;
    319    }
    320  }
    321 
    322  if (result.isSome()) {
    323    aResult = *result;
    324  } else {
    325    // either header values do not have multiple values or
    326    // has unequal multiple values
    327    // for both the cases restore the original header value
    328    aResult = aHeaderValue;
    329  }
    330 }
    331 
    332 }  // namespace net
    333 }  // namespace mozilla
    334 
    335 #endif