1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* vim: set sw=4 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(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 // Used only for response header. 46 eVarietyResponseNetOriginalAndResponse, 47 eVarietyResponseNetOriginal, 48 eVarietyResponse 49 }; 50 51 // Used by internal setters: to set header from network use SetHeaderFromNet 52 MOZ_MUST_USE nsresult SetHeader(const nsACString &headerName, 53 const nsACString &value, bool merge, 54 HeaderVariety variety); 55 MOZ_MUST_USE nsresult SetHeader(nsHttpAtom header, const nsACString &value, 56 bool merge, HeaderVariety variety); 57 MOZ_MUST_USE nsresult SetHeader(nsHttpAtom header, 58 const nsACString &headerName, 59 const nsACString &value, bool merge, 60 HeaderVariety variety); 61 62 // Used by internal setters to set an empty header 63 MOZ_MUST_USE nsresult SetEmptyHeader(const nsACString &headerName, 64 HeaderVariety variety); 65 66 // Merges supported headers. For other duplicate values, determines if error 67 // needs to be thrown or 1st value kept. 68 // For the response header we keep the original headers as well. 69 MOZ_MUST_USE nsresult SetHeaderFromNet(nsHttpAtom header, 70 const nsACString &headerNameOriginal, 71 const nsACString &value, 72 bool response); 73 74 MOZ_MUST_USE nsresult SetResponseHeaderFromCache( 75 nsHttpAtom header, const nsACString &headerNameOriginal, 76 const nsACString &value, HeaderVariety variety); 77 78 MOZ_MUST_USE nsresult GetHeader(nsHttpAtom header, nsACString &value) const; 79 MOZ_MUST_USE nsresult GetOriginalHeader(nsHttpAtom aHeader, 80 nsIHttpHeaderVisitor *aVisitor); 81 void ClearHeader(nsHttpAtom h); 82 83 // Find the location of the given header value, or null if none exists. FindHeaderValue(nsHttpAtom header,const char * value)84 const char *FindHeaderValue(nsHttpAtom header, const char *value) const { 85 return nsHttp::FindToken(PeekHeader(header), value, HTTP_HEADER_VALUE_SEPS); 86 } 87 88 // Determine if the given header value exists. HasHeaderValue(nsHttpAtom header,const char * value)89 bool HasHeaderValue(nsHttpAtom header, const char *value) const { 90 return FindHeaderValue(header, value) != nullptr; 91 } 92 93 bool HasHeader(nsHttpAtom header) const; 94 95 enum VisitorFilter { 96 eFilterAll, 97 eFilterSkipDefault, 98 eFilterResponse, 99 eFilterResponseOriginal 100 }; 101 102 MOZ_MUST_USE nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor, 103 VisitorFilter filter = eFilterAll); 104 105 // parse a header line, return the header atom and a pointer to the 106 // header value (the substring of the header line -- do not free). 107 static MOZ_MUST_USE nsresult ParseHeaderLine( 108 const nsACString &line, nsHttpAtom *header = nullptr, 109 nsACString *headerNameOriginal = nullptr, nsACString *value = nullptr); 110 111 void Flatten(nsACString &, bool pruneProxyHeaders, bool pruneTransients); 112 void FlattenOriginalHeader(nsACString &); 113 Count()114 uint32_t Count() const { return mHeaders.Length(); } 115 116 const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header, 117 nsACString &headerNameOriginal) const; 118 119 void Clear(); 120 121 // Must be copy-constructable and assignable 122 struct nsEntry { 123 nsHttpAtom header; 124 nsCString headerNameOriginal; 125 nsCString value; 126 HeaderVariety variety = eVarietyUnknown; 127 128 struct MatchHeader { EqualsnsEntry::MatchHeader129 bool Equals(const nsEntry &aEntry, const nsHttpAtom &aHeader) const { 130 return aEntry.header == aHeader; 131 } 132 }; 133 134 bool operator==(const nsEntry &aOther) const { 135 return header == aOther.header && value == aOther.value; 136 } 137 }; 138 139 bool operator==(const nsHttpHeaderArray &aOther) const { 140 return mHeaders == aOther.mHeaders; 141 } 142 143 private: 144 // LookupEntry function will never return eVarietyResponseNetOriginal. 145 // It will ignore original headers from the network. 146 int32_t LookupEntry(nsHttpAtom header, const nsEntry **) const; 147 int32_t LookupEntry(nsHttpAtom header, nsEntry **); 148 MOZ_MUST_USE nsresult MergeHeader(nsHttpAtom header, nsEntry *entry, 149 const nsACString &value, 150 HeaderVariety variety); 151 MOZ_MUST_USE nsresult SetHeader_internal(nsHttpAtom header, 152 const nsACString &headeName, 153 const nsACString &value, 154 HeaderVariety variety); 155 156 // Header cannot be merged: only one value possible 157 bool IsSingletonHeader(nsHttpAtom header); 158 // Header cannot be merged, and subsequent values should be ignored 159 bool IsIgnoreMultipleHeader(nsHttpAtom header); 160 161 // Subset of singleton headers: should never see multiple, different 162 // instances of these, else something fishy may be going on (like CLRF 163 // injection) 164 bool IsSuspectDuplicateHeader(nsHttpAtom header); 165 166 // All members must be copy-constructable and assignable 167 nsTArray<nsEntry> mHeaders; 168 169 friend struct IPC::ParamTraits<nsHttpHeaderArray>; 170 friend class nsHttpRequestHead; 171 }; 172 173 //----------------------------------------------------------------------------- 174 // nsHttpHeaderArray <private>: inline functions 175 //----------------------------------------------------------------------------- 176 177 inline int32_t nsHttpHeaderArray::LookupEntry(nsHttpAtom header, 178 const nsEntry **entry) const { 179 uint32_t index = 0; 180 while (index != UINT32_MAX) { 181 index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader()); 182 if (index != UINT32_MAX) { 183 if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) { 184 *entry = &mHeaders[index]; 185 return index; 186 } 187 index++; 188 } 189 } 190 191 return index; 192 } 193 194 inline int32_t nsHttpHeaderArray::LookupEntry(nsHttpAtom header, 195 nsEntry **entry) { 196 uint32_t index = 0; 197 while (index != UINT32_MAX) { 198 index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader()); 199 if (index != UINT32_MAX) { 200 if ((&mHeaders[index])->variety != eVarietyResponseNetOriginal) { 201 *entry = &mHeaders[index]; 202 return index; 203 } 204 index++; 205 } 206 } 207 return index; 208 } 209 210 inline bool nsHttpHeaderArray::IsSingletonHeader(nsHttpAtom header) { 211 return header == nsHttp::Content_Type || 212 header == nsHttp::Content_Disposition || 213 header == nsHttp::Content_Length || header == nsHttp::User_Agent || 214 header == nsHttp::Referer || header == nsHttp::Host || 215 header == nsHttp::Authorization || 216 header == nsHttp::Proxy_Authorization || 217 header == nsHttp::If_Modified_Since || 218 header == nsHttp::If_Unmodified_Since || header == nsHttp::From || 219 header == nsHttp::Location || header == nsHttp::Max_Forwards || 220 // Ignore-multiple-headers are singletons in the sense that they 221 // shouldn't be merged. 222 IsIgnoreMultipleHeader(header); 223 } 224 225 // These are headers for which, in the presence of multiple values, we only 226 // consider the first. 227 inline bool nsHttpHeaderArray::IsIgnoreMultipleHeader(nsHttpAtom header) { 228 // https://tools.ietf.org/html/rfc6797#section-8: 229 // 230 // If a UA receives more than one STS header field in an HTTP 231 // response message over secure transport, then the UA MUST process 232 // only the first such header field. 233 return header == nsHttp::Strict_Transport_Security; 234 } 235 236 inline MOZ_MUST_USE nsresult nsHttpHeaderArray::MergeHeader( 237 nsHttpAtom header, nsEntry *entry, const nsACString &value, 238 nsHttpHeaderArray::HeaderVariety variety) { 239 if (value.IsEmpty()) return NS_OK; // merge of empty header = no-op 240 241 nsCString newValue = entry->value; 242 if (!newValue.IsEmpty()) { 243 // Append the new value to the existing value 244 if (header == nsHttp::Set_Cookie || header == nsHttp::WWW_Authenticate || 245 header == nsHttp::Proxy_Authenticate) { 246 // Special case these headers and use a newline delimiter to 247 // delimit the values from one another as commas may appear 248 // in the values of these headers contrary to what the spec says. 249 newValue.Append('\n'); 250 } else { 251 // Delimit each value from the others using a comma (per HTTP spec) 252 newValue.AppendLiteral(", "); 253 } 254 } 255 256 newValue.Append(value); 257 if (entry->variety == eVarietyResponseNetOriginalAndResponse) { 258 MOZ_ASSERT(variety == eVarietyResponse); 259 entry->variety = eVarietyResponseNetOriginal; 260 // Copy entry->headerNameOriginal because in SetHeader_internal we are going 261 // to a new one and a realocation can happen. 262 nsCString headerNameOriginal = entry->headerNameOriginal; 263 nsresult rv = SetHeader_internal(header, headerNameOriginal, newValue, 264 eVarietyResponse); 265 if (NS_FAILED(rv)) { 266 return rv; 267 } 268 } else { 269 entry->value = newValue; 270 entry->variety = variety; 271 } 272 return NS_OK; 273 } 274 275 inline bool nsHttpHeaderArray::IsSuspectDuplicateHeader(nsHttpAtom header) { 276 bool retval = header == nsHttp::Content_Length || 277 header == nsHttp::Content_Disposition || 278 header == nsHttp::Location; 279 280 MOZ_ASSERT(!retval || IsSingletonHeader(header), 281 "Only non-mergeable headers should be in this list\n"); 282 283 return retval; 284 } 285 286 } // namespace net 287 } // namespace mozilla 288 289 #endif 290