DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (1aeaa33a64f9)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 ts=8 et tw=80 : */
/* 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/. */

#ifndef nsHttpHeaderArray_h__
#define nsHttpHeaderArray_h__

#include "nsHttp.h"
#include "nsTArray.h"
#include "nsString.h"

class nsIHttpHeaderVisitor;

// This needs to be forward declared here so we can include only this header
// without also including PHttpChannelParams.h
namespace IPC {
template <typename>
struct ParamTraits;
}  // namespace IPC

namespace mozilla {
namespace net {

class nsHttpHeaderArray {
 public:
  const char *PeekHeader(nsHttpAtom header) const;

  // For nsHttpResponseHead nsHttpHeaderArray will keep track of the original
  // headers as they come from the network and the parse headers used in
  // firefox.
  // If the original and the firefox header are the same, we will keep just
  // one copy and marked it as eVarietyResponseNetOriginalAndResponse.
  // If firefox header representation changes a header coming from the
  // network (e.g. merged it) or a eVarietyResponseNetOriginalAndResponse
  // header has been changed by SetHeader method, we will keep the original
  // header as eVarietyResponseNetOriginal and make a copy for the new header
  // and mark it as eVarietyResponse.
  enum HeaderVariety {
    eVarietyUnknown,
    // Used only for request header.
    eVarietyRequestOverride,
    eVarietyRequestDefault,
    // Used only for response header.
    eVarietyResponseNetOriginalAndResponse,
    eVarietyResponseNetOriginal,
    eVarietyResponse
  };

  // Used by internal setters: to set header from network use SetHeaderFromNet
  MOZ_MUST_USE nsresult SetHeader(const nsACString &headerName,
                                  const nsACString &value, bool merge,
                                  HeaderVariety variety);
  MOZ_MUST_USE nsresult SetHeader(nsHttpAtom header, const nsACString &value,
                                  bool merge, HeaderVariety variety);
  MOZ_MUST_USE nsresult SetHeader(nsHttpAtom header,
                                  const nsACString &headerName,
                                  const nsACString &value, bool merge,
                                  HeaderVariety variety);

  // Used by internal setters to set an empty header
  MOZ_MUST_USE nsresult SetEmptyHeader(const nsACString &headerName,
                                       HeaderVariety variety);

  // Merges supported headers. For other duplicate values, determines if error
  // needs to be thrown or 1st value kept.
  // For the response header we keep the original headers as well.
  MOZ_MUST_USE nsresult SetHeaderFromNet(nsHttpAtom header,
                                         const nsACString &headerNameOriginal,
                                         const nsACString &value,
                                         bool response);

  MOZ_MUST_USE nsresult SetResponseHeaderFromCache(
      nsHttpAtom header, const nsACString &headerNameOriginal,
      const nsACString &value, HeaderVariety variety);

  MOZ_MUST_USE nsresult GetHeader(nsHttpAtom header, nsACString &value) const;
  MOZ_MUST_USE nsresult GetOriginalHeader(nsHttpAtom aHeader,
                                          nsIHttpHeaderVisitor *aVisitor);
  void ClearHeader(nsHttpAtom h);

  // Find the location of the given header value, or null if none exists.
  const char *FindHeaderValue(nsHttpAtom header, const char *value) const {
    return nsHttp::FindToken(PeekHeader(header), value, HTTP_HEADER_VALUE_SEPS);
  }

  // Determine if the given header value exists.
  bool HasHeaderValue(nsHttpAtom header, const char *value) const {
    return FindHeaderValue(header, value) != nullptr;
  }

  bool HasHeader(nsHttpAtom header) const;

  enum VisitorFilter {
    eFilterAll,
    eFilterSkipDefault,
    eFilterResponse,
    eFilterResponseOriginal
  };

  MOZ_MUST_USE nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor,
                                     VisitorFilter filter = eFilterAll);

  // parse a header line, return the header atom and a pointer to the
  // header value (the substring of the header line -- do not free).
  static MOZ_MUST_USE nsresult ParseHeaderLine(
      const nsACString &line, nsHttpAtom *header = nullptr,
      nsACString *headerNameOriginal = nullptr, nsACString *value = nullptr);

  void Flatten(nsACString &, bool pruneProxyHeaders, bool pruneTransients);
  void FlattenOriginalHeader(nsACString &);

  uint32_t Count() const { return mHeaders.Length(); }

  const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header,
                           nsACString &headerNameOriginal) const;

  void Clear();

  // Must be copy-constructable and assignable
  struct nsEntry {
    nsHttpAtom header;
    nsCString headerNameOriginal;
    nsCString value;
    HeaderVariety variety = eVarietyUnknown;

    struct MatchHeader {
      bool Equals(const nsEntry &aEntry, const nsHttpAtom &aHeader) const {
        return aEntry.header == aHeader;
      }
    };

    bool operator==(const nsEntry &aOther) const {
      return header == aOther.header && value == aOther.value;
    }
  };

  bool operator==(const nsHttpHeaderArray &aOther) const {
    return mHeaders == aOther.mHeaders;
  }

 private:
  // LookupEntry function will never return eVarietyResponseNetOriginal.
  // It will ignore original headers from the network.
  int32_t LookupEntry(nsHttpAtom header, const nsEntry **) const;
  int32_t LookupEntry(nsHttpAtom header, nsEntry **);
  MOZ_MUST_USE nsresult MergeHeader(nsHttpAtom header, nsEntry *entry,
                                    const nsACString &value,
                                    HeaderVariety variety);
  MOZ_MUST_USE nsresult SetHeader_internal(nsHttpAtom header,
                                           const nsACString &headeName,
                                           const nsACString &value,
                                           HeaderVariety variety);

  // Header cannot be merged: only one value possible
  bool IsSingletonHeader(nsHttpAtom header);
  // Header cannot be merged, and subsequent values should be ignored
  bool IsIgnoreMultipleHeader(nsHttpAtom header);

  // Subset of singleton headers: should never see multiple, different
  // instances of these, else something fishy may be going on (like CLRF
  // injection)
  bool IsSuspectDuplicateHeader(nsHttpAtom header);

  // All members must be copy-constructable and assignable
  nsTArray<nsEntry> mHeaders;

  friend struct IPC::ParamTraits<nsHttpHeaderArray>;
  friend class nsHttpRequestHead;
};

//-----------------------------------------------------------------------------
// nsHttpHeaderArray <private>: inline functions
//-----------------------------------------------------------------------------

inline int32_t nsHttpHeaderArray::LookupEntry(nsHttpAtom header,
                                              const nsEntry **entry) const {
  uint32_t index = 0;
  while (index != UINT32_MAX) {
    index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
    if (index != UINT32_MAX) {
      if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
        *entry = &mHeaders[index];
        return index;
      }
      index++;
    }
  }

  return index;
}

inline int32_t nsHttpHeaderArray::LookupEntry(nsHttpAtom header,
                                              nsEntry **entry) {
  uint32_t index = 0;
  while (index != UINT32_MAX) {
    index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader());
    if (index != UINT32_MAX) {
      if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) {
        *entry = &mHeaders[index];
        return index;
      }
      index++;
    }
  }
  return index;
}

inline bool nsHttpHeaderArray::IsSingletonHeader(nsHttpAtom header) {
  return header == nsHttp::Content_Type ||
         header == nsHttp::Content_Disposition ||
         header == nsHttp::Content_Length || header == nsHttp::User_Agent ||
         header == nsHttp::Referer || header == nsHttp::Host ||
         header == nsHttp::Authorization ||
         header == nsHttp::Proxy_Authorization ||
         header == nsHttp::If_Modified_Since ||
         header == nsHttp::If_Unmodified_Since || header == nsHttp::From ||
         header == nsHttp::Location || header == nsHttp::Max_Forwards ||
         // Ignore-multiple-headers are singletons in the sense that they
         // shouldn't be merged.
         IsIgnoreMultipleHeader(header);
}

// These are headers for which, in the presence of multiple values, we only
// consider the first.
inline bool nsHttpHeaderArray::IsIgnoreMultipleHeader(nsHttpAtom header) {
  // https://tools.ietf.org/html/rfc6797#section-8:
  //
  //     If a UA receives more than one STS header field in an HTTP
  //     response message over secure transport, then the UA MUST process
  //     only the first such header field.
  return header == nsHttp::Strict_Transport_Security;
}

inline MOZ_MUST_USE nsresult nsHttpHeaderArray::MergeHeader(
    nsHttpAtom header, nsEntry *entry, const nsACString &value,
    nsHttpHeaderArray::HeaderVariety variety) {
  if (value.IsEmpty()) return NS_OK;  // merge of empty header = no-op

  nsCString newValue = entry->value;
  if (!newValue.IsEmpty()) {
    // Append the new value to the existing value
    if (header == nsHttp::Set_Cookie || header == nsHttp::WWW_Authenticate ||
        header == nsHttp::Proxy_Authenticate) {
      // Special case these headers and use a newline delimiter to
      // delimit the values from one another as commas may appear
      // in the values of these headers contrary to what the spec says.
      newValue.Append('\n');
    } else {
      // Delimit each value from the others using a comma (per HTTP spec)
      newValue.AppendLiteral(", ");
    }
  }

  newValue.Append(value);
  if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
    MOZ_ASSERT(variety == eVarietyResponse);
    entry->variety = eVarietyResponseNetOriginal;
    // Copy entry->headerNameOriginal because in SetHeader_internal we are going
    // to a new one and a realocation can happen.
    nsCString headerNameOriginal = entry->headerNameOriginal;
    nsresult rv = SetHeader_internal(header, headerNameOriginal, newValue,
                                     eVarietyResponse);
    if (NS_FAILED(rv)) {
      return rv;
    }
  } else {
    entry->value = newValue;
    entry->variety = variety;
  }
  return NS_OK;
}

inline bool nsHttpHeaderArray::IsSuspectDuplicateHeader(nsHttpAtom header) {
  bool retval = header == nsHttp::Content_Length ||
                header == nsHttp::Content_Disposition ||
                header == nsHttp::Location;

  MOZ_ASSERT(!retval || IsSingletonHeader(header),
             "Only non-mergeable headers should be in this list\n");

  return retval;
}

}  // namespace net
}  // namespace mozilla

#endif