nsHttpHeaderArray.cpp (16331B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=4 sw=2 sts=2 ci et: */ 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 // HttpLog.h should generally be included first 8 #include "HttpLog.h" 9 10 #include "nsHttpHeaderArray.h" 11 #include "nsURLHelper.h" 12 #include "nsIHttpHeaderVisitor.h" 13 #include "nsHttpHandler.h" 14 15 namespace mozilla { 16 namespace net { 17 18 //----------------------------------------------------------------------------- 19 // nsHttpHeaderArray <public> 20 //----------------------------------------------------------------------------- 21 22 nsresult nsHttpHeaderArray::SetHeader( 23 const nsACString& headerName, const nsACString& value, bool merge, 24 nsHttpHeaderArray::HeaderVariety variety) { 25 nsHttpAtom header = nsHttp::ResolveAtom(headerName); 26 if (!header) { 27 NS_WARNING("failed to resolve atom"); 28 return NS_ERROR_NOT_AVAILABLE; 29 } 30 return SetHeader(header, headerName, value, merge, variety); 31 } 32 33 nsresult nsHttpHeaderArray::SetHeader( 34 const nsHttpAtom& header, const nsACString& value, bool merge, 35 nsHttpHeaderArray::HeaderVariety variety) { 36 return SetHeader(header, ""_ns, value, merge, variety); 37 } 38 39 nsresult nsHttpHeaderArray::SetHeader( 40 const nsHttpAtom& header, const nsACString& headerName, 41 const nsACString& value, bool merge, 42 nsHttpHeaderArray::HeaderVariety variety) { 43 MOZ_ASSERT( 44 (variety == eVarietyResponse) || (variety == eVarietyRequestDefault) || 45 (variety == eVarietyRequestOverride) || 46 (variety == eVarietyResponseOverride) || 47 (variety == eVarietyRequestEnforceDefault), 48 "Net original headers can only be set using SetHeader_internal()."); 49 50 nsEntry* entry = nullptr; 51 int32_t index = LookupEntry(header, &entry); 52 53 // If an empty value is received and we aren't merging headers discard it, 54 // unless we're overriding 55 if (value.IsEmpty() && header != nsHttp::X_Frame_Options && 56 variety != eVarietyResponseOverride) { 57 if (!merge && entry) { 58 if (entry->variety == eVarietyResponseNetOriginalAndResponse) { 59 MOZ_ASSERT(variety == eVarietyResponse); 60 entry->variety = eVarietyResponseNetOriginal; 61 } else { 62 mHeaders.RemoveElementAt(index); 63 } 64 } 65 return NS_OK; 66 } 67 68 MOZ_ASSERT((variety == eVarietyRequestEnforceDefault) || 69 (!entry || variety != eVarietyRequestDefault), 70 "Cannot set default entry which overrides existing entry!"); 71 72 // Set the variety to default if we are enforcing it. 73 if (variety == eVarietyRequestEnforceDefault) { 74 variety = eVarietyRequestDefault; 75 } 76 if (!entry) { 77 return SetHeader_internal(header, headerName, value, variety); 78 } 79 if (merge && !IsSingletonHeader(header)) { 80 return MergeHeader(header, entry, value, variety); 81 } 82 if (!IsIgnoreMultipleHeader(header)) { 83 // Replace the existing string with the new value 84 if (entry->variety == eVarietyResponseNetOriginalAndResponse) { 85 MOZ_ASSERT(variety == eVarietyResponse || 86 variety == eVarietyResponseOverride); 87 entry->variety = eVarietyResponseNetOriginal; 88 return SetHeader_internal(header, headerName, value, variety); 89 } 90 entry->value = value; 91 entry->variety = variety; 92 } 93 94 return NS_OK; 95 } 96 97 nsresult nsHttpHeaderArray::SetHeader_internal( 98 const nsHttpAtom& header, const nsACString& headerName, 99 const nsACString& value, nsHttpHeaderArray::HeaderVariety variety) { 100 nsEntry* entry = mHeaders.AppendElement(); 101 if (!entry) { 102 return NS_ERROR_OUT_OF_MEMORY; 103 } 104 entry->header = header; 105 // Only save original form of a header if it is different than the header 106 // atom string. 107 if (!headerName.Equals(header.get())) { 108 entry->headerNameOriginal = headerName; 109 } 110 entry->value = value; 111 entry->variety = variety; 112 return NS_OK; 113 } 114 115 nsresult nsHttpHeaderArray::SetEmptyHeader(const nsACString& headerName, 116 HeaderVariety variety) { 117 nsHttpAtom header = nsHttp::ResolveAtom(headerName); 118 if (!header) { 119 NS_WARNING("failed to resolve atom"); 120 return NS_ERROR_NOT_AVAILABLE; 121 } 122 123 MOZ_ASSERT((variety == eVarietyResponse) || 124 (variety == eVarietyRequestDefault) || 125 (variety == eVarietyRequestOverride), 126 "Original headers can only be set using SetHeader_internal()."); 127 nsEntry* entry = nullptr; 128 129 LookupEntry(header, &entry); 130 131 if (entry && entry->variety != eVarietyResponseNetOriginalAndResponse) { 132 entry->value.Truncate(); 133 return NS_OK; 134 } 135 if (entry) { 136 MOZ_ASSERT(variety == eVarietyResponse); 137 entry->variety = eVarietyResponseNetOriginal; 138 } 139 140 return SetHeader_internal(header, headerName, ""_ns, variety); 141 } 142 143 nsresult nsHttpHeaderArray::SetHeaderFromNet( 144 const nsHttpAtom& header, const nsACString& headerNameOriginal, 145 const nsACString& value, bool response) { 146 // mHeader holds the consolidated (merged or updated) headers. 147 // mHeader for response header will keep the original heades as well. 148 nsEntry* entry = nullptr; 149 150 LookupEntry(header, &entry); 151 152 if (!entry) { 153 HeaderVariety variety = eVarietyRequestOverride; 154 if (response) { 155 variety = eVarietyResponseNetOriginalAndResponse; 156 } 157 return SetHeader_internal(header, headerNameOriginal, value, variety); 158 } 159 if (!IsSingletonHeader(header)) { 160 HeaderVariety variety = eVarietyRequestOverride; 161 if (response) { 162 variety = eVarietyResponse; 163 } 164 nsresult rv = MergeHeader(header, entry, value, variety); 165 if (NS_FAILED(rv)) { 166 return rv; 167 } 168 if (response) { 169 rv = SetHeader_internal(header, headerNameOriginal, value, 170 eVarietyResponseNetOriginal); 171 } 172 return rv; 173 } 174 if (!IsIgnoreMultipleHeader(header)) { 175 // Multiple instances of non-mergeable header received from network 176 // - ignore if same value 177 if (header == nsHttp::Content_Length) { 178 // Content length header needs special handling. 179 // For e.g. for CL all the below headers evaluates to value of X 180 // Content-Length: X 181 // Content-Length: X, X, X 182 // Content-Length: X \n\r Content-Length: X 183 // remove duplicate values from the header-values for comparison 184 185 nsAutoCString headerValue; 186 RemoveDuplicateHeaderValues(value, headerValue); 187 188 nsAutoCString entryValue; 189 RemoveDuplicateHeaderValues(entry->value, entryValue); 190 if (entryValue != headerValue) { 191 // reply may be corrupt/hacked (ex: CLRF injection attacks) 192 return NS_ERROR_CORRUPTED_CONTENT; 193 } 194 } else if (!entry->value.Equals(value)) { // compare remaining headers 195 if (IsSuspectDuplicateHeader(header)) { 196 // reply may be corrupt/hacked (ex: CLRF injection attacks) 197 return NS_ERROR_CORRUPTED_CONTENT; 198 } // else silently drop value: keep value from 1st header seen 199 LOG(("Header %s silently dropped as non mergeable header\n", 200 header.get())); 201 } 202 203 if (response) { 204 return SetHeader_internal(header, headerNameOriginal, value, 205 eVarietyResponseNetOriginal); 206 } 207 } 208 209 return NS_OK; 210 } 211 212 nsresult nsHttpHeaderArray::SetResponseHeaderFromCache( 213 const nsHttpAtom& header, const nsACString& headerNameOriginal, 214 const nsACString& value, nsHttpHeaderArray::HeaderVariety variety) { 215 MOZ_ASSERT( 216 (variety == eVarietyResponse) || (variety == eVarietyResponseNetOriginal), 217 "Headers from cache can only be eVarietyResponse and " 218 "eVarietyResponseNetOriginal"); 219 220 if (variety == eVarietyResponseNetOriginal) { 221 return SetHeader_internal(header, headerNameOriginal, value, 222 eVarietyResponseNetOriginal); 223 } 224 nsTArray<nsEntry>::index_type index = 0; 225 do { 226 index = mHeaders.IndexOf(header, index, nsEntry::MatchHeader()); 227 if (index != 228 CopyableTArray<mozilla::net::nsHttpHeaderArray::nsEntry>::NoIndex) { 229 nsEntry& entry = mHeaders[index]; 230 if (value.Equals(entry.value)) { 231 MOZ_ASSERT( 232 (entry.variety == eVarietyResponseNetOriginal) || 233 (entry.variety == eVarietyResponseNetOriginalAndResponse), 234 "This array must contain only eVarietyResponseNetOriginal" 235 " and eVarietyResponseNetOriginalAndRespons headers!"); 236 entry.variety = eVarietyResponseNetOriginalAndResponse; 237 return NS_OK; 238 } 239 index++; 240 } 241 } while (index != 242 CopyableTArray<mozilla::net::nsHttpHeaderArray::nsEntry>::NoIndex); 243 // If we are here, we have not found an entry so add a new one. 244 return SetHeader_internal(header, headerNameOriginal, value, 245 eVarietyResponse); 246 } 247 248 void nsHttpHeaderArray::ClearHeader(const nsHttpAtom& header) { 249 nsEntry* entry = nullptr; 250 int32_t index = LookupEntry(header, &entry); 251 if (entry) { 252 if (entry->variety == eVarietyResponseNetOriginalAndResponse) { 253 entry->variety = eVarietyResponseNetOriginal; 254 } else { 255 mHeaders.RemoveElementAt(index); 256 } 257 } 258 } 259 260 const char* nsHttpHeaderArray::PeekHeader(const nsHttpAtom& header) const { 261 const nsEntry* entry = nullptr; 262 LookupEntry(header, &entry); 263 return entry ? entry->value.get() : nullptr; 264 } 265 266 nsresult nsHttpHeaderArray::GetHeader(const nsHttpAtom& header, 267 nsACString& result) const { 268 const nsEntry* entry = nullptr; 269 LookupEntry(header, &entry); 270 if (!entry) return NS_ERROR_NOT_AVAILABLE; 271 result = entry->value; 272 return NS_OK; 273 } 274 275 nsresult nsHttpHeaderArray::GetOriginalHeader(const nsHttpAtom& aHeader, 276 nsIHttpHeaderVisitor* aVisitor) { 277 NS_ENSURE_ARG_POINTER(aVisitor); 278 uint32_t index = 0; 279 nsresult rv = NS_ERROR_NOT_AVAILABLE; 280 while (true) { 281 index = mHeaders.IndexOf(aHeader, index, nsEntry::MatchHeader()); 282 if (index != UINT32_MAX) { 283 const nsEntry& entry = mHeaders[index]; 284 285 MOZ_ASSERT((entry.variety == eVarietyResponseNetOriginalAndResponse) || 286 (entry.variety == eVarietyResponseNetOriginal) || 287 (entry.variety == eVarietyResponse) || 288 (entry.variety == eVarietyResponseOverride), 289 "This must be a response header."); 290 index++; 291 if (entry.variety == eVarietyResponse || 292 entry.variety == eVarietyResponseOverride) { 293 continue; 294 } 295 296 const nsCString& hdr = entry.headerNameOriginal.IsEmpty() 297 ? entry.header.val() 298 : entry.headerNameOriginal; 299 300 rv = NS_OK; 301 if (NS_FAILED(aVisitor->VisitHeader(hdr, entry.value))) { 302 break; 303 } 304 } else { 305 // if there is no such a header, it will return 306 // NS_ERROR_NOT_AVAILABLE or NS_OK otherwise. 307 return rv; 308 } 309 } 310 return NS_OK; 311 } 312 313 bool nsHttpHeaderArray::HasHeader(const nsHttpAtom& header) const { 314 const nsEntry* entry = nullptr; 315 LookupEntry(header, &entry); 316 return entry; 317 } 318 319 nsresult nsHttpHeaderArray::VisitHeaders( 320 nsIHttpHeaderVisitor* visitor, nsHttpHeaderArray::VisitorFilter filter) { 321 NS_ENSURE_ARG_POINTER(visitor); 322 nsresult rv; 323 324 uint32_t i, count = mHeaders.Length(); 325 for (i = 0; i < count; ++i) { 326 const nsEntry& entry = mHeaders[i]; 327 if (filter == eFilterSkipDefault && 328 entry.variety == eVarietyRequestDefault) { 329 continue; 330 } 331 if (filter == eFilterResponse && 332 entry.variety == eVarietyResponseNetOriginal) { 333 continue; 334 } 335 if (filter == eFilterResponseOriginal && 336 entry.variety == eVarietyResponse) { 337 continue; 338 } 339 340 const nsCString& hdr = entry.headerNameOriginal.IsEmpty() 341 ? entry.header.val() 342 : entry.headerNameOriginal; 343 rv = visitor->VisitHeader(hdr, entry.value); 344 if (NS_FAILED(rv)) { 345 return rv; 346 } 347 } 348 return NS_OK; 349 } 350 351 /*static*/ 352 nsresult nsHttpHeaderArray::ParseHeaderLine(const nsACString& line, 353 nsHttpAtom* hdr, 354 nsACString* headerName, 355 nsACString* val) { 356 // 357 // BNF from section 4.2 of RFC 2616: 358 // 359 // message-header = field-name ":" [ field-value ] 360 // field-name = token 361 // field-value = *( field-content | LWS ) 362 // field-content = <the OCTETs making up the field-value 363 // and consisting of either *TEXT or combinations 364 // of token, separators, and quoted-string> 365 // 366 367 // We skip over mal-formed headers in the hope that we'll still be able to 368 // do something useful with the response. 369 int32_t split = line.FindChar(':'); 370 371 if (split == kNotFound) { 372 LOG(("malformed header [%s]: no colon\n", PromiseFlatCString(line).get())); 373 return NS_ERROR_FAILURE; 374 } 375 376 const nsACString& sub = Substring(line, 0, split); 377 const nsACString& sub2 = 378 Substring(line, split + 1, line.Length() - split - 1); 379 380 // make sure we have a valid token for the field-name 381 if (!nsHttp::IsValidToken(sub)) { 382 LOG(("malformed header [%s]: field-name not a token\n", 383 PromiseFlatCString(line).get())); 384 return NS_ERROR_FAILURE; 385 } 386 387 nsHttpAtom atom = nsHttp::ResolveAtom(sub); 388 if (!atom) { 389 LOG(("failed to resolve atom [%s]\n", PromiseFlatCString(line).get())); 390 return NS_ERROR_FAILURE; 391 } 392 393 // skip over whitespace 394 char* p = 395 net_FindCharNotInSet(sub2.BeginReading(), sub2.EndReading(), HTTP_LWS); 396 397 // trim trailing whitespace - bug 86608 398 char* p2 = net_RFindCharNotInSet(p, sub2.EndReading(), HTTP_LWS); 399 400 // assign return values 401 if (hdr) *hdr = atom; 402 if (val) val->Assign(p, p2 - p + 1); 403 if (headerName) headerName->Assign(sub); 404 405 return NS_OK; 406 } 407 408 void nsHttpHeaderArray::Flatten(nsACString& buf, bool pruneProxyHeaders, 409 bool pruneTransients) { 410 uint32_t i, count = mHeaders.Length(); 411 for (i = 0; i < count; ++i) { 412 const nsEntry& entry = mHeaders[i]; 413 // Skip original header. 414 if (entry.variety == eVarietyResponseNetOriginal) { 415 continue; 416 } 417 // prune proxy headers if requested 418 if (pruneProxyHeaders && ((entry.header == nsHttp::Proxy_Authorization) || 419 (entry.header == nsHttp::Proxy_Connection))) { 420 continue; 421 } 422 if (pruneTransients && 423 (entry.value.IsEmpty() || entry.header == nsHttp::Connection || 424 entry.header == nsHttp::Proxy_Connection || 425 entry.header == nsHttp::Keep_Alive || 426 entry.header == nsHttp::WWW_Authenticate || 427 entry.header == nsHttp::Proxy_Authenticate || 428 entry.header == nsHttp::Trailer || 429 entry.header == nsHttp::Transfer_Encoding || 430 entry.header == nsHttp::Upgrade || 431 // XXX this will cause problems when we start honoring 432 // Cache-Control: no-cache="set-cookie", what to do? 433 entry.header == nsHttp::Set_Cookie)) { 434 continue; 435 } 436 437 if (entry.headerNameOriginal.IsEmpty()) { 438 buf.Append(entry.header.val()); 439 } else { 440 buf.Append(entry.headerNameOriginal); 441 } 442 buf.AppendLiteral(": "); 443 buf.Append(entry.value); 444 buf.AppendLiteral("\r\n"); 445 } 446 } 447 448 void nsHttpHeaderArray::FlattenOriginalHeader(nsACString& buf) { 449 uint32_t i, count = mHeaders.Length(); 450 for (i = 0; i < count; ++i) { 451 const nsEntry& entry = mHeaders[i]; 452 // Skip changed header. 453 if (entry.variety == eVarietyResponse) { 454 continue; 455 } 456 457 if (entry.headerNameOriginal.IsEmpty()) { 458 buf.Append(entry.header.val()); 459 } else { 460 buf.Append(entry.headerNameOriginal); 461 } 462 463 buf.AppendLiteral(": "); 464 buf.Append(entry.value); 465 buf.AppendLiteral("\r\n"); 466 } 467 } 468 469 const char* nsHttpHeaderArray::PeekHeaderAt( 470 uint32_t index, nsHttpAtom& header, nsACString& headerNameOriginal) const { 471 const nsEntry& entry = mHeaders[index]; 472 473 header = entry.header; 474 headerNameOriginal = entry.headerNameOriginal; 475 return entry.value.get(); 476 } 477 478 void nsHttpHeaderArray::Clear() { mHeaders.Clear(); } 479 480 } // namespace net 481 } // namespace mozilla