nsMultiMixedConv.cpp (31997B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsMultiMixedConv.h" 7 #include "nsIHttpChannel.h" 8 #include "nsIThreadRetargetableStreamListener.h" 9 #include "nsNetCID.h" 10 #include "nsMimeTypes.h" 11 #include "nsIStringStream.h" 12 #include "nsCRT.h" 13 #include "nsIHttpChannelInternal.h" 14 #include "nsURLHelper.h" 15 #include "nsIStreamConverterService.h" 16 #include "nsContentSecurityManager.h" 17 #include "nsHttp.h" 18 #include "nsNetUtil.h" 19 #include "nsIURI.h" 20 #include "nsHttpHeaderArray.h" 21 #include "mozilla/AutoRestore.h" 22 #include "mozilla/Components.h" 23 #include "mozilla/Tokenizer.h" 24 #include "nsComponentManagerUtils.h" 25 #include "mozilla/StaticPrefs_network.h" 26 27 using namespace mozilla; 28 29 nsPartChannel::nsPartChannel(nsIChannel* aMultipartChannel, uint32_t aPartID, 30 bool aIsFirstPart, nsIStreamListener* aListener) 31 : mMultipartChannel(aMultipartChannel), 32 mListener(aListener), 33 mPartID(aPartID), 34 mIsFirstPart(aIsFirstPart) { 35 // Inherit the load flags from the original channel... 36 mMultipartChannel->GetLoadFlags(&mLoadFlags); 37 38 mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup)); 39 } 40 41 void nsPartChannel::InitializeByteRange(int64_t aStart, int64_t aEnd) { 42 mIsByteRangeRequest = true; 43 44 mByteRangeStart = aStart; 45 mByteRangeEnd = aEnd; 46 } 47 48 nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext) { 49 return mListener->OnStartRequest(this); 50 } 51 52 nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext, 53 nsIInputStream* aStream, 54 uint64_t aOffset, uint32_t aLen) { 55 return mListener->OnDataAvailable(this, aStream, aOffset, aLen); 56 } 57 58 nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext, 59 nsresult aStatus) { 60 // Drop the listener 61 nsCOMPtr<nsIStreamListener> listener; 62 listener.swap(mListener); 63 return listener->OnStopRequest(this, aStatus); 64 } 65 66 void nsPartChannel::SetContentDisposition( 67 const nsACString& aContentDispositionHeader) { 68 mContentDispositionHeader = aContentDispositionHeader; 69 nsCOMPtr<nsIURI> uri; 70 GetURI(getter_AddRefs(uri)); 71 NS_GetFilenameFromDisposition(mContentDispositionFilename, 72 mContentDispositionHeader); 73 mContentDisposition = 74 NS_GetContentDispositionFromHeader(mContentDispositionHeader, this); 75 } 76 77 // 78 // nsISupports implementation... 79 // 80 81 NS_IMPL_ADDREF(nsPartChannel) 82 NS_IMPL_RELEASE(nsPartChannel) 83 84 NS_INTERFACE_MAP_BEGIN(nsPartChannel) 85 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) 86 NS_INTERFACE_MAP_ENTRY(nsIRequest) 87 NS_INTERFACE_MAP_ENTRY(nsIChannel) 88 NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest) 89 NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel) 90 NS_INTERFACE_MAP_END 91 92 // 93 // nsIRequest implementation... 94 // 95 96 NS_IMETHODIMP 97 nsPartChannel::GetName(nsACString& aResult) { 98 return mMultipartChannel->GetName(aResult); 99 } 100 101 NS_IMETHODIMP 102 nsPartChannel::IsPending(bool* aResult) { 103 // For now, consider the active lifetime of each part the same as 104 // the underlying multipart channel... This is not exactly right, 105 // but it is good enough :-) 106 return mMultipartChannel->IsPending(aResult); 107 } 108 109 NS_IMETHODIMP 110 nsPartChannel::GetStatus(nsresult* aResult) { 111 nsresult rv = NS_OK; 112 113 if (NS_FAILED(mStatus)) { 114 *aResult = mStatus; 115 } else { 116 rv = mMultipartChannel->GetStatus(aResult); 117 } 118 119 return rv; 120 } 121 122 NS_IMETHODIMP nsPartChannel::SetCanceledReason(const nsACString& aReason) { 123 return SetCanceledReasonImpl(aReason); 124 } 125 126 NS_IMETHODIMP nsPartChannel::GetCanceledReason(nsACString& aReason) { 127 return GetCanceledReasonImpl(aReason); 128 } 129 130 NS_IMETHODIMP nsPartChannel::CancelWithReason(nsresult aStatus, 131 const nsACString& aReason) { 132 return CancelWithReasonImpl(aStatus, aReason); 133 } 134 135 NS_IMETHODIMP 136 nsPartChannel::Cancel(nsresult aStatus) { 137 // Cancelling an individual part must not cancel the underlying 138 // multipart channel... 139 // XXX but we should stop sending data for _this_ part channel! 140 mStatus = aStatus; 141 return NS_OK; 142 } 143 144 NS_IMETHODIMP 145 nsPartChannel::GetCanceled(bool* aCanceled) { 146 *aCanceled = NS_FAILED(mStatus); 147 return NS_OK; 148 } 149 150 NS_IMETHODIMP 151 nsPartChannel::Suspend(void) { 152 // Suspending an individual part must not suspend the underlying 153 // multipart channel... 154 // XXX why not? 155 return NS_OK; 156 } 157 158 NS_IMETHODIMP 159 nsPartChannel::Resume(void) { 160 // Resuming an individual part must not resume the underlying 161 // multipart channel... 162 // XXX why not? 163 return NS_OK; 164 } 165 166 // 167 // nsIChannel implementation 168 // 169 170 NS_IMETHODIMP 171 nsPartChannel::GetOriginalURI(nsIURI** aURI) { 172 return mMultipartChannel->GetOriginalURI(aURI); 173 } 174 175 NS_IMETHODIMP 176 nsPartChannel::SetOriginalURI(nsIURI* aURI) { 177 return mMultipartChannel->SetOriginalURI(aURI); 178 } 179 180 NS_IMETHODIMP 181 nsPartChannel::GetURI(nsIURI** aURI) { return mMultipartChannel->GetURI(aURI); } 182 183 NS_IMETHODIMP 184 nsPartChannel::Open(nsIInputStream** aStream) { 185 nsCOMPtr<nsIStreamListener> listener; 186 nsresult rv = 187 nsContentSecurityManager::doContentSecurityCheck(this, listener); 188 NS_ENSURE_SUCCESS(rv, rv); 189 190 // This channel cannot be opened! 191 return NS_ERROR_FAILURE; 192 } 193 194 NS_IMETHODIMP 195 nsPartChannel::AsyncOpen(nsIStreamListener* aListener) { 196 nsCOMPtr<nsIStreamListener> listener = aListener; 197 nsresult rv = 198 nsContentSecurityManager::doContentSecurityCheck(this, listener); 199 NS_ENSURE_SUCCESS(rv, rv); 200 201 // This channel cannot be opened! 202 return NS_ERROR_FAILURE; 203 } 204 205 NS_IMETHODIMP 206 nsPartChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) { 207 *aLoadFlags = mLoadFlags; 208 return NS_OK; 209 } 210 211 NS_IMETHODIMP 212 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags) { 213 mLoadFlags = aLoadFlags; 214 return NS_OK; 215 } 216 217 NS_IMETHODIMP 218 nsPartChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { 219 return GetTRRModeImpl(aTRRMode); 220 } 221 222 NS_IMETHODIMP 223 nsPartChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) { 224 return SetTRRModeImpl(aTRRMode); 225 } 226 227 NS_IMETHODIMP 228 nsPartChannel::GetIsDocument(bool* aIsDocument) { 229 return NS_GetIsDocumentChannel(this, aIsDocument); 230 } 231 232 NS_IMETHODIMP 233 nsPartChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { 234 *aLoadGroup = do_AddRef(mLoadGroup).take(); 235 return NS_OK; 236 } 237 238 NS_IMETHODIMP 239 nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { 240 mLoadGroup = aLoadGroup; 241 242 return NS_OK; 243 } 244 245 NS_IMETHODIMP 246 nsPartChannel::GetOwner(nsISupports** aOwner) { 247 return mMultipartChannel->GetOwner(aOwner); 248 } 249 250 NS_IMETHODIMP 251 nsPartChannel::SetOwner(nsISupports* aOwner) { 252 return mMultipartChannel->SetOwner(aOwner); 253 } 254 255 NS_IMETHODIMP 256 nsPartChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { 257 return mMultipartChannel->GetLoadInfo(aLoadInfo); 258 } 259 260 NS_IMETHODIMP 261 nsPartChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) { 262 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null"); 263 return mMultipartChannel->SetLoadInfo(aLoadInfo); 264 } 265 266 NS_IMETHODIMP 267 nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { 268 return mMultipartChannel->GetNotificationCallbacks(aCallbacks); 269 } 270 271 NS_IMETHODIMP 272 nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) { 273 return mMultipartChannel->SetNotificationCallbacks(aCallbacks); 274 } 275 276 NS_IMETHODIMP 277 nsPartChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) { 278 return mMultipartChannel->GetSecurityInfo(aSecurityInfo); 279 } 280 281 NS_IMETHODIMP 282 nsPartChannel::GetContentType(nsACString& aContentType) { 283 aContentType = mContentType; 284 return NS_OK; 285 } 286 287 NS_IMETHODIMP 288 nsPartChannel::SetContentType(const nsACString& aContentType) { 289 bool dummy; 290 net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy); 291 return NS_OK; 292 } 293 294 NS_IMETHODIMP 295 nsPartChannel::GetContentCharset(nsACString& aContentCharset) { 296 aContentCharset = mContentCharset; 297 return NS_OK; 298 } 299 300 NS_IMETHODIMP 301 nsPartChannel::SetContentCharset(const nsACString& aContentCharset) { 302 mContentCharset = aContentCharset; 303 return NS_OK; 304 } 305 306 NS_IMETHODIMP 307 nsPartChannel::GetContentLength(int64_t* aContentLength) { 308 *aContentLength = mContentLength; 309 return NS_OK; 310 } 311 312 NS_IMETHODIMP 313 nsPartChannel::SetContentLength(int64_t aContentLength) { 314 mContentLength = aContentLength; 315 return NS_OK; 316 } 317 318 NS_IMETHODIMP 319 nsPartChannel::GetContentDisposition(uint32_t* aContentDisposition) { 320 if (mContentDispositionHeader.IsEmpty()) return NS_ERROR_NOT_AVAILABLE; 321 322 *aContentDisposition = mContentDisposition; 323 return NS_OK; 324 } 325 326 NS_IMETHODIMP 327 nsPartChannel::SetContentDisposition(uint32_t aContentDisposition) { 328 return NS_ERROR_NOT_AVAILABLE; 329 } 330 331 NS_IMETHODIMP 332 nsPartChannel::GetContentDispositionFilename( 333 nsAString& aContentDispositionFilename) { 334 if (mContentDispositionFilename.IsEmpty()) return NS_ERROR_NOT_AVAILABLE; 335 336 aContentDispositionFilename = mContentDispositionFilename; 337 return NS_OK; 338 } 339 340 NS_IMETHODIMP 341 nsPartChannel::SetContentDispositionFilename( 342 const nsAString& aContentDispositionFilename) { 343 return NS_ERROR_NOT_AVAILABLE; 344 } 345 346 NS_IMETHODIMP 347 nsPartChannel::GetContentDispositionHeader( 348 nsACString& aContentDispositionHeader) { 349 if (mContentDispositionHeader.IsEmpty()) return NS_ERROR_NOT_AVAILABLE; 350 351 aContentDispositionHeader = mContentDispositionHeader; 352 return NS_OK; 353 } 354 355 NS_IMETHODIMP 356 nsPartChannel::GetPartID(uint32_t* aPartID) { 357 *aPartID = mPartID; 358 return NS_OK; 359 } 360 361 NS_IMETHODIMP 362 nsPartChannel::GetIsFirstPart(bool* aIsFirstPart) { 363 *aIsFirstPart = mIsFirstPart; 364 return NS_OK; 365 } 366 367 NS_IMETHODIMP 368 nsPartChannel::GetIsLastPart(bool* aIsLastPart) { 369 *aIsLastPart = mIsLastPart; 370 return NS_OK; 371 } 372 373 // 374 // nsIByteRangeRequest implementation... 375 // 376 377 NS_IMETHODIMP 378 nsPartChannel::GetIsByteRangeRequest(bool* aIsByteRangeRequest) { 379 *aIsByteRangeRequest = mIsByteRangeRequest; 380 381 return NS_OK; 382 } 383 384 NS_IMETHODIMP 385 nsPartChannel::GetStartRange(int64_t* aStartRange) { 386 *aStartRange = mByteRangeStart; 387 388 return NS_OK; 389 } 390 391 NS_IMETHODIMP 392 nsPartChannel::GetEndRange(int64_t* aEndRange) { 393 *aEndRange = mByteRangeEnd; 394 return NS_OK; 395 } 396 397 NS_IMETHODIMP 398 nsPartChannel::GetBaseChannel(nsIChannel** aReturn) { 399 NS_ENSURE_ARG_POINTER(aReturn); 400 401 *aReturn = do_AddRef(mMultipartChannel).take(); 402 return NS_OK; 403 } 404 405 // nsISupports implementation 406 NS_IMPL_ISUPPORTS(nsMultiMixedConv, nsIStreamConverter, nsIStreamListener, 407 nsIThreadRetargetableStreamListener, nsIRequestObserver) 408 409 // nsIStreamConverter implementation 410 411 // No syncronous conversion at this time. 412 NS_IMETHODIMP 413 nsMultiMixedConv::Convert(nsIInputStream* aFromStream, const char* aFromType, 414 const char* aToType, nsISupports* aCtxt, 415 nsIInputStream** _retval) { 416 return NS_ERROR_NOT_IMPLEMENTED; 417 } 418 419 // Stream converter service calls this to initialize the actual stream converter 420 // (us). 421 NS_IMETHODIMP 422 nsMultiMixedConv::AsyncConvertData(const char* aFromType, const char* aToType, 423 nsIStreamListener* aListener, 424 nsISupports* aCtxt) { 425 NS_ASSERTION(aListener && aFromType && aToType, 426 "null pointer passed into multi mixed converter"); 427 428 // hook up our final listener. this guy gets the various On*() calls we want 429 // to throw at him. 430 // 431 // WARNING: this listener must be able to handle multiple OnStartRequest, 432 // OnDataAvail() and OnStopRequest() call combinations. We call of series 433 // of these for each sub-part in the raw stream. 434 mFinalListener = aListener; 435 436 return NS_OK; 437 } 438 439 NS_IMETHODIMP 440 nsMultiMixedConv::GetConvertedType(const nsACString& aFromType, 441 nsIChannel* aChannel, nsACString& aToType) { 442 return NS_ERROR_NOT_IMPLEMENTED; 443 } 444 445 NS_IMETHODIMP 446 nsMultiMixedConv::MaybeRetarget(nsIRequest* request) { 447 return NS_ERROR_NOT_IMPLEMENTED; 448 } 449 450 // nsIRequestObserver implementation 451 NS_IMETHODIMP 452 nsMultiMixedConv::OnStartRequest(nsIRequest* request) { 453 // we're assuming the content-type is available at this stage 454 NS_ASSERTION(mBoundary.IsEmpty(), "a second on start???"); 455 456 nsresult rv; 457 458 mTotalSent = 0; 459 mChannel = do_QueryInterface(request, &rv); 460 if (NS_FAILED(rv)) return rv; 461 462 nsAutoCString contentType; 463 464 // ask the HTTP channel for the content-type and extract the boundary from it. 465 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv); 466 if (NS_SUCCEEDED(rv)) { 467 rv = httpChannel->GetResponseHeader("content-type"_ns, contentType); 468 if (NS_FAILED(rv)) { 469 return rv; 470 } 471 nsCString csp; 472 rv = httpChannel->GetResponseHeader("content-security-policy"_ns, csp); 473 if (NS_SUCCEEDED(rv)) { 474 mRootContentSecurityPolicy = csp; 475 } 476 nsCString contentDisposition; 477 rv = httpChannel->GetResponseHeader("content-disposition"_ns, 478 contentDisposition); 479 if (NS_SUCCEEDED(rv)) { 480 mRootContentDisposition = contentDisposition; 481 } 482 } else { 483 // try asking the channel directly 484 rv = mChannel->GetContentType(contentType); 485 if (NS_FAILED(rv)) { 486 return NS_ERROR_FAILURE; 487 } 488 } 489 490 Tokenizer p(contentType); 491 p.SkipUntil(Token::Char(';')); 492 if (!p.CheckChar(';')) { 493 return NS_ERROR_CORRUPTED_CONTENT; 494 } 495 p.SkipWhites(); 496 if (!p.CheckWord("boundary")) { 497 return NS_ERROR_CORRUPTED_CONTENT; 498 } 499 p.SkipWhites(); 500 if (!p.CheckChar('=')) { 501 return NS_ERROR_CORRUPTED_CONTENT; 502 } 503 p.SkipWhites(); 504 (void)p.ReadUntil(Token::Char(';'), mBoundary); 505 mBoundary.Trim( 506 " \""); // ignoring potential quoted string formatting violations 507 if (mBoundary.IsEmpty()) { 508 return NS_ERROR_CORRUPTED_CONTENT; 509 } 510 511 mHeaderTokens[HEADER_CONTENT_TYPE] = mTokenizer.AddCustomToken( 512 "content-type", mTokenizer.CASE_INSENSITIVE, false); 513 mHeaderTokens[HEADER_CONTENT_LENGTH] = mTokenizer.AddCustomToken( 514 "content-length", mTokenizer.CASE_INSENSITIVE, false); 515 mHeaderTokens[HEADER_CONTENT_DISPOSITION] = mTokenizer.AddCustomToken( 516 "content-disposition", mTokenizer.CASE_INSENSITIVE, false); 517 mHeaderTokens[HEADER_SET_COOKIE] = mTokenizer.AddCustomToken( 518 "set-cookie", mTokenizer.CASE_INSENSITIVE, false); 519 mHeaderTokens[HEADER_CONTENT_RANGE] = mTokenizer.AddCustomToken( 520 "content-range", mTokenizer.CASE_INSENSITIVE, false); 521 mHeaderTokens[HEADER_RANGE] = 522 mTokenizer.AddCustomToken("range", mTokenizer.CASE_INSENSITIVE, false); 523 mHeaderTokens[HEADER_CONTENT_SECURITY_POLICY] = mTokenizer.AddCustomToken( 524 "content-security-policy", mTokenizer.CASE_INSENSITIVE, false); 525 526 mLFToken = mTokenizer.AddCustomToken("\n", mTokenizer.CASE_SENSITIVE, false); 527 mCRLFToken = 528 mTokenizer.AddCustomToken("\r\n", mTokenizer.CASE_SENSITIVE, false); 529 530 SwitchToControlParsing(); 531 532 mBoundaryToken = 533 mTokenizer.AddCustomToken(mBoundary, mTokenizer.CASE_SENSITIVE); 534 mBoundaryTokenWithDashes = 535 mTokenizer.AddCustomToken("--"_ns + mBoundary, mTokenizer.CASE_SENSITIVE); 536 537 return NS_OK; 538 } 539 540 // nsIStreamListener implementation 541 NS_IMETHODIMP 542 nsMultiMixedConv::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr, 543 uint64_t sourceOffset, uint32_t count) { 544 // Failing these assertions may indicate that some of the target listeners of 545 // this converter is looping the thead queue, which is harmful to how we 546 // collect the raw (content) data. 547 MOZ_DIAGNOSTIC_ASSERT(!mInOnDataAvailable, 548 "nsMultiMixedConv::OnDataAvailable reentered!"); 549 MOZ_DIAGNOSTIC_ASSERT( 550 !mRawData, "There are unsent data from the previous tokenizer feed!"); 551 552 if (mInOnDataAvailable) { 553 // The multipart logic is incapable of being reentered. 554 return NS_ERROR_UNEXPECTED; 555 } 556 557 mozilla::AutoRestore<bool> restore(mInOnDataAvailable); 558 mInOnDataAvailable = true; 559 560 nsresult rv_feed = mTokenizer.FeedInput(inStr, count); 561 // We must do this every time. Regardless if something has failed during the 562 // parsing process. Otherwise the raw data reference would not be thrown 563 // away. 564 nsresult rv_send = SendData(); 565 566 return NS_FAILED(rv_send) ? rv_send : rv_feed; 567 } 568 569 NS_IMETHODIMP 570 nsMultiMixedConv::OnDataFinished(nsresult aStatus) { return NS_OK; } 571 572 NS_IMETHODIMP 573 nsMultiMixedConv::CheckListenerChain() { return NS_ERROR_NOT_IMPLEMENTED; } 574 575 NS_IMETHODIMP 576 nsMultiMixedConv::OnStopRequest(nsIRequest* request, nsresult aStatus) { 577 nsresult rv; 578 579 if (mPartChannel) { 580 mPartChannel->SetIsLastPart(); 581 582 MOZ_DIAGNOSTIC_ASSERT( 583 !mRawData, "There are unsent data from the previous tokenizer feed!"); 584 585 rv = mTokenizer.FinishInput(); 586 if (NS_SUCCEEDED(aStatus)) { 587 aStatus = rv; 588 } 589 rv = SendData(); 590 if (NS_SUCCEEDED(aStatus)) { 591 aStatus = rv; 592 } 593 594 (void)SendStop(aStatus); 595 } else if (NS_FAILED(aStatus) && !mRequestListenerNotified) { 596 // underlying data production problem. we should not be in 597 // the middle of sending data. if we were, mPartChannel, 598 // above, would have been non-null. 599 600 (void)mFinalListener->OnStartRequest(request); 601 (void)mFinalListener->OnStopRequest(request, aStatus); 602 } 603 604 nsCOMPtr<nsIMultiPartChannelListener> multiListener = 605 do_QueryInterface(mFinalListener); 606 if (multiListener) { 607 multiListener->OnAfterLastPart(aStatus); 608 } 609 610 return NS_OK; 611 } 612 613 nsresult nsMultiMixedConv::ConsumeToken(Token const& token) { 614 nsresult rv; 615 616 switch (mParserState) { 617 case PREAMBLE: 618 if (token.Equals(mBoundaryTokenWithDashes)) { 619 // The server first used boundary '--boundary'. Hence, we no longer 620 // accept plain 'boundary' token as a delimiter. 621 mTokenizer.RemoveCustomToken(mBoundaryToken); 622 mParserState = BOUNDARY_CRLF; 623 break; 624 } 625 if (token.Equals(mBoundaryToken)) { 626 // And here the opposite from the just above block... 627 mTokenizer.RemoveCustomToken(mBoundaryTokenWithDashes); 628 mParserState = BOUNDARY_CRLF; 629 break; 630 } 631 632 // This is a preamble, just ignore it and wait for the boundary. 633 break; 634 635 case BOUNDARY_CRLF: 636 if (token.Equals(Token::NewLine())) { 637 mParserState = HEADER_NAME; 638 mResponseHeader = HEADER_UNKNOWN; 639 HeadersToDefault(); 640 SetHeaderTokensEnabled(true); 641 break; 642 } 643 return NS_ERROR_CORRUPTED_CONTENT; 644 645 case HEADER_NAME: 646 SetHeaderTokensEnabled(false); 647 if (token.Equals(Token::NewLine())) { 648 mParserState = BODY_INIT; 649 SwitchToBodyParsing(); 650 break; 651 } 652 for (uint32_t h = HEADER_CONTENT_TYPE; h < HEADER_UNKNOWN; ++h) { 653 if (token.Equals(mHeaderTokens[h])) { 654 mResponseHeader = static_cast<EHeader>(h); 655 break; 656 } 657 } 658 mParserState = HEADER_SEP; 659 break; 660 661 case HEADER_SEP: 662 if (token.Equals(Token::Char(':'))) { 663 mParserState = HEADER_VALUE; 664 mResponseHeaderValue.Truncate(); 665 break; 666 } 667 if (mResponseHeader == HEADER_UNKNOWN) { 668 // If the header is not of any we understand, just pass everything till 669 // ':' 670 break; 671 } 672 if (token.Equals(Token::Whitespace())) { 673 // Accept only header-name traling whitespaces after known headers 674 break; 675 } 676 return NS_ERROR_CORRUPTED_CONTENT; 677 678 case HEADER_VALUE: 679 if (token.Equals(Token::Whitespace()) && mResponseHeaderValue.IsEmpty()) { 680 // Eat leading whitespaces 681 break; 682 } 683 if (token.Equals(Token::NewLine())) { 684 nsresult rv = ProcessHeader(); 685 if (NS_FAILED(rv)) { 686 return rv; 687 } 688 mParserState = HEADER_NAME; 689 mResponseHeader = HEADER_UNKNOWN; 690 SetHeaderTokensEnabled(true); 691 } else { 692 mResponseHeaderValue.Append(token.Fragment()); 693 } 694 break; 695 696 case BODY_INIT: 697 rv = SendStart(); 698 if (NS_FAILED(rv)) { 699 return rv; 700 } 701 mParserState = BODY; 702 [[fallthrough]]; 703 704 case BODY: { 705 if (!token.Equals(mLFToken) && !token.Equals(mCRLFToken)) { 706 if (token.Equals(mBoundaryTokenWithDashes) || 707 token.Equals(mBoundaryToken)) { 708 // Allow CRLF to NOT be part of the boundary as well 709 SwitchToControlParsing(); 710 mParserState = TRAIL_DASH1; 711 break; 712 } 713 AccumulateData(token); 714 break; 715 } 716 717 // After CRLF we must explicitly check for boundary. If found, 718 // that CRLF is part of the boundary and must not be send to the 719 // data listener. 720 Token token2; 721 if (!mTokenizer.Next(token2)) { 722 // Note: this will give us the CRLF token again when more data 723 // or OnStopRequest arrive. I.e. we will enter BODY case in 724 // the very same state as we are now and start this block over. 725 mTokenizer.NeedMoreInput(); 726 break; 727 } 728 if (token2.Equals(mBoundaryTokenWithDashes) || 729 token2.Equals(mBoundaryToken)) { 730 SwitchToControlParsing(); 731 mParserState = TRAIL_DASH1; 732 break; 733 } 734 735 AccumulateData(token); 736 AccumulateData(token2); 737 break; 738 } 739 740 case TRAIL_DASH1: 741 if (token.Equals(Token::NewLine())) { 742 rv = SendStop(NS_OK); 743 if (NS_FAILED(rv)) { 744 return rv; 745 } 746 mParserState = BOUNDARY_CRLF; 747 mTokenizer.Rollback(); 748 break; 749 } 750 if (token.Equals(Token::Char('-'))) { 751 mParserState = TRAIL_DASH2; 752 break; 753 } 754 return NS_ERROR_CORRUPTED_CONTENT; 755 756 case TRAIL_DASH2: 757 if (token.Equals(Token::Char('-'))) { 758 mPartChannel->SetIsLastPart(); 759 // SendStop calls SendData first. 760 rv = SendStop(NS_OK); 761 if (NS_FAILED(rv)) { 762 return rv; 763 } 764 mParserState = EPILOGUE; 765 break; 766 } 767 return NS_ERROR_CORRUPTED_CONTENT; 768 769 case EPILOGUE: 770 // Just ignore 771 break; 772 773 default: 774 MOZ_ASSERT(false, "Missing parser state handling branch"); 775 break; 776 } // switch 777 778 return NS_OK; 779 } 780 781 void nsMultiMixedConv::SetHeaderTokensEnabled(bool aEnable) { 782 for (uint32_t h = HEADER_FIRST; h < HEADER_UNKNOWN; ++h) { 783 mTokenizer.EnableCustomToken(mHeaderTokens[h], aEnable); 784 } 785 } 786 787 void nsMultiMixedConv::SwitchToBodyParsing() { 788 mTokenizer.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY); 789 mTokenizer.EnableCustomToken(mLFToken, true); 790 mTokenizer.EnableCustomToken(mCRLFToken, true); 791 mTokenizer.EnableCustomToken(mBoundaryTokenWithDashes, true); 792 mTokenizer.EnableCustomToken(mBoundaryToken, true); 793 } 794 795 void nsMultiMixedConv::SwitchToControlParsing() { 796 mTokenizer.SetTokenizingMode(Tokenizer::Mode::FULL); 797 mTokenizer.EnableCustomToken(mLFToken, false); 798 mTokenizer.EnableCustomToken(mCRLFToken, false); 799 mTokenizer.EnableCustomToken(mBoundaryTokenWithDashes, false); 800 mTokenizer.EnableCustomToken(mBoundaryToken, false); 801 } 802 803 // nsMultiMixedConv methods 804 nsMultiMixedConv::nsMultiMixedConv() 805 // XXX: This is a hack to bypass the raw pointer to refcounted object in 806 // lambda analysis. It should be removed and replaced when the 807 // IncrementalTokenizer API is improved to avoid the need for such 808 // workarounds. 809 // 810 // This is safe because `mTokenizer` will not outlive `this`, meaning 811 // that this std::bind object will be destroyed before `this` dies. 812 : mTokenizer(std::bind(&nsMultiMixedConv::ConsumeToken, this, 813 std::placeholders::_1)) {} 814 815 nsresult nsMultiMixedConv::SendStart() { 816 nsresult rv = NS_OK; 817 818 nsCOMPtr<nsIStreamListener> partListener(mFinalListener); 819 if (mContentType.IsEmpty()) { 820 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); 821 nsCOMPtr<nsIStreamConverterService> serv; 822 serv = mozilla::components::StreamConverter::Service(&rv); 823 if (NS_SUCCEEDED(rv)) { 824 nsCOMPtr<nsIStreamListener> converter; 825 rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, "*/*", mFinalListener, 826 mContext, getter_AddRefs(converter)); 827 if (NS_SUCCEEDED(rv)) { 828 partListener = converter; 829 } 830 } 831 } 832 833 // if we already have an mPartChannel, that means we never sent a Stop() 834 // before starting up another "part." that would be bad. 835 MOZ_ASSERT(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel"); 836 837 nsPartChannel* newChannel; 838 newChannel = new nsPartChannel(mChannel, mCurrentPartID, mCurrentPartID == 0, 839 partListener); 840 841 ++mCurrentPartID; 842 843 if (mIsByteRangeRequest) { 844 newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd); 845 } 846 847 mTotalSent = 0; 848 849 // Set up the new part channel... 850 mPartChannel = newChannel; 851 852 rv = mPartChannel->SetContentType(mContentType); 853 if (NS_FAILED(rv)) return rv; 854 855 rv = mPartChannel->SetContentLength(mContentLength); 856 if (NS_FAILED(rv)) return rv; 857 858 if (!mRootContentDisposition.IsEmpty()) { 859 mPartChannel->SetContentDisposition(mRootContentDisposition); 860 } else { 861 mPartChannel->SetContentDisposition(mContentDisposition); 862 } 863 864 // Each part of a multipart/replace response can be used 865 // for the top level document. We must inform upper layers 866 // about this by setting the LOAD_REPLACE flag so that certain 867 // state assertions are evaluated as positive. 868 nsLoadFlags loadFlags = 0; 869 mPartChannel->GetLoadFlags(&loadFlags); 870 loadFlags |= nsIChannel::LOAD_REPLACE; 871 mPartChannel->SetLoadFlags(loadFlags); 872 873 nsCOMPtr<nsILoadGroup> loadGroup; 874 (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 875 876 // Add the new channel to the load group (if any) 877 if (loadGroup) { 878 rv = loadGroup->AddRequest(mPartChannel, nullptr); 879 if (NS_FAILED(rv)) return rv; 880 } 881 882 // This prevents artificial call to OnStart/StopRequest when the root 883 // channel fails. Since now it's ensured to keep with the nsIStreamListener 884 // contract every time. 885 mRequestListenerNotified = true; 886 887 // Let's start off the load. NOTE: we don't forward on the channel passed 888 // into our OnDataAvailable() as it's the root channel for the raw stream. 889 return mPartChannel->SendOnStartRequest(mContext); 890 } 891 892 nsresult nsMultiMixedConv::SendStop(nsresult aStatus) { 893 // Make sure we send out all accumulcated data prior call to OnStopRequest. 894 // If there is no data, this is a no-op. 895 nsresult rv = SendData(); 896 if (NS_SUCCEEDED(aStatus)) { 897 aStatus = rv; 898 } 899 if (mPartChannel) { 900 rv = mPartChannel->SendOnStopRequest(mContext, aStatus); 901 // don't check for failure here, we need to remove the channel from 902 // the loadgroup. 903 904 // Remove the channel from its load group (if any) 905 nsCOMPtr<nsILoadGroup> loadGroup; 906 (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 907 if (loadGroup) { 908 (void)loadGroup->RemoveRequest(mPartChannel, mContext, aStatus); 909 } 910 } 911 912 mPartChannel = nullptr; 913 return rv; 914 } 915 916 void nsMultiMixedConv::AccumulateData(Token const& aToken) { 917 if (!mRawData) { 918 // This is the first read of raw data during this FeedInput loop 919 // of the incremental tokenizer. All 'raw' tokens are coming from 920 // the same linear buffer, hence begining of this loop raw data 921 // is begining of the first raw token. Length of this loop raw 922 // data is just sum of all 'raw' tokens we collect during this loop. 923 // 924 // It's ensured we flush (send to to the listener via OnDataAvailable) 925 // and nullify the collected raw data right after FeedInput call. 926 // Hence, the reference can't outlive the actual buffer. 927 mRawData = aToken.Fragment().BeginReading(); 928 mRawDataLength = 0; 929 } 930 931 mRawDataLength += aToken.Fragment().Length(); 932 } 933 934 nsresult nsMultiMixedConv::SendData() { 935 nsresult rv; 936 937 if (!mRawData) { 938 return NS_OK; 939 } 940 941 nsACString::const_char_iterator rawData = mRawData; 942 mRawData = nullptr; 943 944 if (!mPartChannel) { 945 return NS_ERROR_FAILURE; // something went wrong w/ processing 946 } 947 948 if (mContentLength != UINT64_MAX) { 949 // make sure that we don't send more than the mContentLength 950 // XXX why? perhaps the Content-Length header was actually wrong!! 951 if ((uint64_t(mRawDataLength) + mTotalSent) > mContentLength) { 952 mRawDataLength = static_cast<uint32_t>(mContentLength - mTotalSent); 953 } 954 955 if (mRawDataLength == 0) return NS_OK; 956 } 957 958 uint64_t offset = mTotalSent; 959 mTotalSent += mRawDataLength; 960 961 nsCOMPtr<nsIStringInputStream> ss( 962 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv)); 963 if (NS_FAILED(rv)) return rv; 964 965 rv = ss->ShareData(rawData, mRawDataLength); 966 mRawData = nullptr; 967 if (NS_FAILED(rv)) return rv; 968 969 return mPartChannel->SendOnDataAvailable(mContext, ss, offset, 970 mRawDataLength); 971 } 972 973 void nsMultiMixedConv::HeadersToDefault() { 974 mContentLength = UINT64_MAX; 975 mContentType.Truncate(); 976 mContentDisposition.Truncate(); 977 mContentSecurityPolicy.Truncate(); 978 mIsByteRangeRequest = false; 979 } 980 981 nsresult nsMultiMixedConv::ProcessHeader() { 982 mozilla::Tokenizer p(mResponseHeaderValue); 983 984 switch (mResponseHeader) { 985 case HEADER_CONTENT_TYPE: 986 mContentType = mResponseHeaderValue; 987 mContentType.CompressWhitespace(); 988 break; 989 case HEADER_CONTENT_LENGTH: 990 p.SkipWhites(); 991 if (!p.ReadInteger(&mContentLength)) { 992 return NS_ERROR_CORRUPTED_CONTENT; 993 } 994 break; 995 case HEADER_CONTENT_DISPOSITION: 996 mContentDisposition = mResponseHeaderValue; 997 mContentDisposition.CompressWhitespace(); 998 break; 999 case HEADER_SET_COOKIE: { 1000 nsCOMPtr<nsIHttpChannelInternal> httpInternal = 1001 do_QueryInterface(mChannel); 1002 mResponseHeaderValue.CompressWhitespace(); 1003 if (!StaticPrefs::network_cookie_prevent_set_cookie_from_multipart() && 1004 httpInternal) { 1005 AutoTArray<nsCString, 1> cookieHeaderArray; 1006 cookieHeaderArray.AppendElement(mResponseHeaderValue); 1007 DebugOnly<nsresult> rv = 1008 httpInternal->SetCookieHeaders(cookieHeaderArray); 1009 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1010 } 1011 break; 1012 } 1013 case HEADER_RANGE: 1014 case HEADER_CONTENT_RANGE: { 1015 if (!p.CheckWord("bytes") || !p.CheckWhite()) { 1016 return NS_ERROR_CORRUPTED_CONTENT; 1017 } 1018 p.SkipWhites(); 1019 if (p.CheckChar('*')) { 1020 mByteRangeStart = mByteRangeEnd = 0; 1021 } else if (!p.ReadInteger(&mByteRangeStart) || !p.CheckChar('-') || 1022 !p.ReadInteger(&mByteRangeEnd)) { 1023 return NS_ERROR_CORRUPTED_CONTENT; 1024 } 1025 mIsByteRangeRequest = true; 1026 if (mContentLength == UINT64_MAX) { 1027 mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1); 1028 } 1029 break; 1030 } 1031 case HEADER_CONTENT_SECURITY_POLICY: { 1032 mContentSecurityPolicy = mResponseHeaderValue; 1033 mContentSecurityPolicy.CompressWhitespace(); 1034 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); 1035 if (httpChannel) { 1036 nsCString resultCSP = mRootContentSecurityPolicy; 1037 if (!mContentSecurityPolicy.IsEmpty()) { 1038 // We are updating the root channel CSP header respectively for 1039 // each part as: CSP-root + CSP-partN, where N is the part number. 1040 // Here we append current part's CSP to root CSP and reset CSP 1041 // header for each part. 1042 if (!resultCSP.IsEmpty()) { 1043 resultCSP.Append(";"); 1044 } 1045 resultCSP.Append(mContentSecurityPolicy); 1046 } 1047 nsresult rv = httpChannel->SetResponseHeader( 1048 "Content-Security-Policy"_ns, resultCSP, false); 1049 if (NS_FAILED(rv)) { 1050 return NS_ERROR_CORRUPTED_CONTENT; 1051 } 1052 } 1053 break; 1054 } 1055 case HEADER_UNKNOWN: 1056 // We ignore anything else... 1057 break; 1058 } 1059 1060 return NS_OK; 1061 } 1062 1063 nsresult NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv) { 1064 MOZ_ASSERT(aMultiMixedConv != nullptr, "null ptr"); 1065 1066 RefPtr<nsMultiMixedConv> conv = new nsMultiMixedConv(); 1067 conv.forget(aMultiMixedConv); 1068 return NS_OK; 1069 }