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