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/. */
6 #include "nsMultiMixedConv.h"
7 #include "nsIHttpChannel.h"
9 #include "nsMimeTypes.h"
10 #include "nsIStringStream.h"
12 #include "nsIHttpChannelInternal.h"
13 #include "nsURLHelper.h"
14 #include "nsIStreamConverterService.h"
16 #include "nsContentSecurityManager.h"
18 #include "nsNetUtil.h"
20 #include "nsHttpHeaderArray.h"
21 #include "mozilla/AutoRestore.h"
22 #include "mozilla/Tokenizer.h"
23 #include "nsComponentManagerUtils.h"
25 using namespace mozilla
;
27 nsPartChannel::nsPartChannel(nsIChannel
* aMultipartChannel
, uint32_t aPartID
,
28 bool aIsFirstPart
, nsIStreamListener
* aListener
)
29 : mMultipartChannel(aMultipartChannel
),
32 mIsFirstPart(aIsFirstPart
) {
33 // Inherit the load flags from the original channel...
34 mMultipartChannel
->GetLoadFlags(&mLoadFlags
);
36 mMultipartChannel
->GetLoadGroup(getter_AddRefs(mLoadGroup
));
39 void nsPartChannel::InitializeByteRange(int64_t aStart
, int64_t aEnd
) {
40 mIsByteRangeRequest
= true;
42 mByteRangeStart
= aStart
;
46 nsresult
nsPartChannel::SendOnStartRequest(nsISupports
* aContext
) {
47 return mListener
->OnStartRequest(this);
50 nsresult
nsPartChannel::SendOnDataAvailable(nsISupports
* aContext
,
51 nsIInputStream
* aStream
,
52 uint64_t aOffset
, uint32_t aLen
) {
53 return mListener
->OnDataAvailable(this, aStream
, aOffset
, aLen
);
56 nsresult
nsPartChannel::SendOnStopRequest(nsISupports
* aContext
,
59 nsCOMPtr
<nsIStreamListener
> listener
;
60 listener
.swap(mListener
);
61 return listener
->OnStopRequest(this, aStatus
);
64 void nsPartChannel::SetContentDisposition(
65 const nsACString
& aContentDispositionHeader
) {
66 mContentDispositionHeader
= aContentDispositionHeader
;
68 GetURI(getter_AddRefs(uri
));
69 NS_GetFilenameFromDisposition(mContentDispositionFilename
,
70 mContentDispositionHeader
);
72 NS_GetContentDispositionFromHeader(mContentDispositionHeader
, this);
76 // nsISupports implementation...
79 NS_IMPL_ADDREF(nsPartChannel
)
80 NS_IMPL_RELEASE(nsPartChannel
)
82 NS_INTERFACE_MAP_BEGIN(nsPartChannel
)
83 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIChannel
)
84 NS_INTERFACE_MAP_ENTRY(nsIRequest
)
85 NS_INTERFACE_MAP_ENTRY(nsIChannel
)
86 NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest
)
87 NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel
)
91 // nsIRequest implementation...
95 nsPartChannel::GetName(nsACString
& aResult
) {
96 return mMultipartChannel
->GetName(aResult
);
100 nsPartChannel::IsPending(bool* aResult
) {
101 // For now, consider the active lifetime of each part the same as
102 // the underlying multipart channel... This is not exactly right,
103 // but it is good enough :-)
104 return mMultipartChannel
->IsPending(aResult
);
108 nsPartChannel::GetStatus(nsresult
* aResult
) {
111 if (NS_FAILED(mStatus
)) {
114 rv
= mMultipartChannel
->GetStatus(aResult
);
120 NS_IMETHODIMP
nsPartChannel::SetCanceledReason(const nsACString
& aReason
) {
121 return SetCanceledReasonImpl(aReason
);
124 NS_IMETHODIMP
nsPartChannel::GetCanceledReason(nsACString
& aReason
) {
125 return GetCanceledReasonImpl(aReason
);
128 NS_IMETHODIMP
nsPartChannel::CancelWithReason(nsresult aStatus
,
129 const nsACString
& aReason
) {
130 return CancelWithReasonImpl(aStatus
, aReason
);
134 nsPartChannel::Cancel(nsresult aStatus
) {
135 // Cancelling an individual part must not cancel the underlying
136 // multipart channel...
137 // XXX but we should stop sending data for _this_ part channel!
143 nsPartChannel::GetCanceled(bool* aCanceled
) {
144 *aCanceled
= NS_FAILED(mStatus
);
149 nsPartChannel::Suspend(void) {
150 // Suspending an individual part must not suspend the underlying
151 // multipart channel...
157 nsPartChannel::Resume(void) {
158 // Resuming an individual part must not resume the underlying
159 // multipart channel...
165 // nsIChannel implementation
169 nsPartChannel::GetOriginalURI(nsIURI
** aURI
) {
170 return mMultipartChannel
->GetOriginalURI(aURI
);
174 nsPartChannel::SetOriginalURI(nsIURI
* aURI
) {
175 return mMultipartChannel
->SetOriginalURI(aURI
);
179 nsPartChannel::GetURI(nsIURI
** aURI
) { return mMultipartChannel
->GetURI(aURI
); }
182 nsPartChannel::Open(nsIInputStream
** aStream
) {
183 nsCOMPtr
<nsIStreamListener
> listener
;
185 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
186 NS_ENSURE_SUCCESS(rv
, rv
);
188 // This channel cannot be opened!
189 return NS_ERROR_FAILURE
;
193 nsPartChannel::AsyncOpen(nsIStreamListener
* aListener
) {
194 nsCOMPtr
<nsIStreamListener
> listener
= aListener
;
196 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
197 NS_ENSURE_SUCCESS(rv
, rv
);
199 // This channel cannot be opened!
200 return NS_ERROR_FAILURE
;
204 nsPartChannel::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
205 *aLoadFlags
= mLoadFlags
;
210 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags
) {
211 mLoadFlags
= aLoadFlags
;
216 nsPartChannel::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
217 return GetTRRModeImpl(aTRRMode
);
221 nsPartChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
222 return SetTRRModeImpl(aTRRMode
);
226 nsPartChannel::GetIsDocument(bool* aIsDocument
) {
227 return NS_GetIsDocumentChannel(this, aIsDocument
);
231 nsPartChannel::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
232 *aLoadGroup
= do_AddRef(mLoadGroup
).take();
237 nsPartChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
) {
238 mLoadGroup
= aLoadGroup
;
244 nsPartChannel::GetOwner(nsISupports
** aOwner
) {
245 return mMultipartChannel
->GetOwner(aOwner
);
249 nsPartChannel::SetOwner(nsISupports
* aOwner
) {
250 return mMultipartChannel
->SetOwner(aOwner
);
254 nsPartChannel::GetLoadInfo(nsILoadInfo
** aLoadInfo
) {
255 return mMultipartChannel
->GetLoadInfo(aLoadInfo
);
259 nsPartChannel::SetLoadInfo(nsILoadInfo
* aLoadInfo
) {
260 MOZ_RELEASE_ASSERT(aLoadInfo
, "loadinfo can't be null");
261 return mMultipartChannel
->SetLoadInfo(aLoadInfo
);
265 nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor
** aCallbacks
) {
266 return mMultipartChannel
->GetNotificationCallbacks(aCallbacks
);
270 nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor
* aCallbacks
) {
271 return mMultipartChannel
->SetNotificationCallbacks(aCallbacks
);
275 nsPartChannel::GetSecurityInfo(nsITransportSecurityInfo
** aSecurityInfo
) {
276 return mMultipartChannel
->GetSecurityInfo(aSecurityInfo
);
280 nsPartChannel::GetContentType(nsACString
& aContentType
) {
281 aContentType
= mContentType
;
286 nsPartChannel::SetContentType(const nsACString
& aContentType
) {
288 net_ParseContentType(aContentType
, mContentType
, mContentCharset
, &dummy
);
293 nsPartChannel::GetContentCharset(nsACString
& aContentCharset
) {
294 aContentCharset
= mContentCharset
;
299 nsPartChannel::SetContentCharset(const nsACString
& aContentCharset
) {
300 mContentCharset
= aContentCharset
;
305 nsPartChannel::GetContentLength(int64_t* aContentLength
) {
306 *aContentLength
= mContentLength
;
311 nsPartChannel::SetContentLength(int64_t aContentLength
) {
312 mContentLength
= aContentLength
;
317 nsPartChannel::GetContentDisposition(uint32_t* aContentDisposition
) {
318 if (mContentDispositionHeader
.IsEmpty()) return NS_ERROR_NOT_AVAILABLE
;
320 *aContentDisposition
= mContentDisposition
;
325 nsPartChannel::SetContentDisposition(uint32_t aContentDisposition
) {
326 return NS_ERROR_NOT_AVAILABLE
;
330 nsPartChannel::GetContentDispositionFilename(
331 nsAString
& aContentDispositionFilename
) {
332 if (mContentDispositionFilename
.IsEmpty()) return NS_ERROR_NOT_AVAILABLE
;
334 aContentDispositionFilename
= mContentDispositionFilename
;
339 nsPartChannel::SetContentDispositionFilename(
340 const nsAString
& aContentDispositionFilename
) {
341 return NS_ERROR_NOT_AVAILABLE
;
345 nsPartChannel::GetContentDispositionHeader(
346 nsACString
& aContentDispositionHeader
) {
347 if (mContentDispositionHeader
.IsEmpty()) return NS_ERROR_NOT_AVAILABLE
;
349 aContentDispositionHeader
= mContentDispositionHeader
;
354 nsPartChannel::GetPartID(uint32_t* aPartID
) {
360 nsPartChannel::GetIsFirstPart(bool* aIsFirstPart
) {
361 *aIsFirstPart
= mIsFirstPart
;
366 nsPartChannel::GetIsLastPart(bool* aIsLastPart
) {
367 *aIsLastPart
= mIsLastPart
;
372 // nsIByteRangeRequest implementation...
376 nsPartChannel::GetIsByteRangeRequest(bool* aIsByteRangeRequest
) {
377 *aIsByteRangeRequest
= mIsByteRangeRequest
;
383 nsPartChannel::GetStartRange(int64_t* aStartRange
) {
384 *aStartRange
= mByteRangeStart
;
390 nsPartChannel::GetEndRange(int64_t* aEndRange
) {
391 *aEndRange
= mByteRangeEnd
;
396 nsPartChannel::GetBaseChannel(nsIChannel
** aReturn
) {
397 NS_ENSURE_ARG_POINTER(aReturn
);
399 *aReturn
= do_AddRef(mMultipartChannel
).take();
403 // nsISupports implementation
404 NS_IMPL_ISUPPORTS(nsMultiMixedConv
, nsIStreamConverter
, nsIStreamListener
,
407 // nsIStreamConverter implementation
409 // No syncronous conversion at this time.
411 nsMultiMixedConv::Convert(nsIInputStream
* aFromStream
, const char* aFromType
,
412 const char* aToType
, nsISupports
* aCtxt
,
413 nsIInputStream
** _retval
) {
414 return NS_ERROR_NOT_IMPLEMENTED
;
417 // Stream converter service calls this to initialize the actual stream converter
420 nsMultiMixedConv::AsyncConvertData(const char* aFromType
, const char* aToType
,
421 nsIStreamListener
* aListener
,
422 nsISupports
* aCtxt
) {
423 NS_ASSERTION(aListener
&& aFromType
&& aToType
,
424 "null pointer passed into multi mixed converter");
426 // hook up our final listener. this guy gets the various On*() calls we want
429 // WARNING: this listener must be able to handle multiple OnStartRequest,
430 // OnDataAvail() and OnStopRequest() call combinations. We call of series
431 // of these for each sub-part in the raw stream.
432 mFinalListener
= aListener
;
438 nsMultiMixedConv::GetConvertedType(const nsACString
& aFromType
,
439 nsIChannel
* aChannel
, nsACString
& aToType
) {
440 return NS_ERROR_NOT_IMPLEMENTED
;
443 // nsIRequestObserver implementation
445 nsMultiMixedConv::OnStartRequest(nsIRequest
* request
) {
446 // we're assuming the content-type is available at this stage
447 NS_ASSERTION(mBoundary
.IsEmpty(), "a second on start???");
452 mChannel
= do_QueryInterface(request
, &rv
);
453 if (NS_FAILED(rv
)) return rv
;
455 nsAutoCString contentType
;
457 // ask the HTTP channel for the content-type and extract the boundary from it.
458 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
, &rv
);
459 if (NS_SUCCEEDED(rv
)) {
460 rv
= httpChannel
->GetResponseHeader("content-type"_ns
, contentType
);
465 rv
= httpChannel
->GetResponseHeader("content-security-policy"_ns
, csp
);
466 if (NS_SUCCEEDED(rv
)) {
467 mRootContentSecurityPolicy
= csp
;
470 // try asking the channel directly
471 rv
= mChannel
->GetContentType(contentType
);
473 return NS_ERROR_FAILURE
;
477 Tokenizer
p(contentType
);
478 p
.SkipUntil(Token::Char(';'));
479 if (!p
.CheckChar(';')) {
480 return NS_ERROR_CORRUPTED_CONTENT
;
483 if (!p
.CheckWord("boundary")) {
484 return NS_ERROR_CORRUPTED_CONTENT
;
487 if (!p
.CheckChar('=')) {
488 return NS_ERROR_CORRUPTED_CONTENT
;
491 Unused
<< p
.ReadUntil(Token::Char(';'), mBoundary
);
493 " \""); // ignoring potential quoted string formatting violations
494 if (mBoundary
.IsEmpty()) {
495 return NS_ERROR_CORRUPTED_CONTENT
;
498 mHeaderTokens
[HEADER_CONTENT_TYPE
] = mTokenizer
.AddCustomToken(
499 "content-type", mTokenizer
.CASE_INSENSITIVE
, false);
500 mHeaderTokens
[HEADER_CONTENT_LENGTH
] = mTokenizer
.AddCustomToken(
501 "content-length", mTokenizer
.CASE_INSENSITIVE
, false);
502 mHeaderTokens
[HEADER_CONTENT_DISPOSITION
] = mTokenizer
.AddCustomToken(
503 "content-disposition", mTokenizer
.CASE_INSENSITIVE
, false);
504 mHeaderTokens
[HEADER_SET_COOKIE
] = mTokenizer
.AddCustomToken(
505 "set-cookie", mTokenizer
.CASE_INSENSITIVE
, false);
506 mHeaderTokens
[HEADER_CONTENT_RANGE
] = mTokenizer
.AddCustomToken(
507 "content-range", mTokenizer
.CASE_INSENSITIVE
, false);
508 mHeaderTokens
[HEADER_RANGE
] =
509 mTokenizer
.AddCustomToken("range", mTokenizer
.CASE_INSENSITIVE
, false);
510 mHeaderTokens
[HEADER_CONTENT_SECURITY_POLICY
] = mTokenizer
.AddCustomToken(
511 "content-security-policy", mTokenizer
.CASE_INSENSITIVE
, false);
513 mLFToken
= mTokenizer
.AddCustomToken("\n", mTokenizer
.CASE_SENSITIVE
, false);
515 mTokenizer
.AddCustomToken("\r\n", mTokenizer
.CASE_SENSITIVE
, false);
517 SwitchToControlParsing();
520 mTokenizer
.AddCustomToken(mBoundary
, mTokenizer
.CASE_SENSITIVE
);
521 mBoundaryTokenWithDashes
=
522 mTokenizer
.AddCustomToken("--"_ns
+ mBoundary
, mTokenizer
.CASE_SENSITIVE
);
527 // nsIStreamListener implementation
529 nsMultiMixedConv::OnDataAvailable(nsIRequest
* request
, nsIInputStream
* inStr
,
530 uint64_t sourceOffset
, uint32_t count
) {
531 // Failing these assertions may indicate that some of the target listeners of
532 // this converter is looping the thead queue, which is harmful to how we
533 // collect the raw (content) data.
534 MOZ_DIAGNOSTIC_ASSERT(!mInOnDataAvailable
,
535 "nsMultiMixedConv::OnDataAvailable reentered!");
536 MOZ_DIAGNOSTIC_ASSERT(
537 !mRawData
, "There are unsent data from the previous tokenizer feed!");
539 if (mInOnDataAvailable
) {
540 // The multipart logic is incapable of being reentered.
541 return NS_ERROR_UNEXPECTED
;
544 mozilla::AutoRestore
<bool> restore(mInOnDataAvailable
);
545 mInOnDataAvailable
= true;
547 nsresult rv_feed
= mTokenizer
.FeedInput(inStr
, count
);
548 // We must do this every time. Regardless if something has failed during the
549 // parsing process. Otherwise the raw data reference would not be thrown
551 nsresult rv_send
= SendData();
553 return NS_FAILED(rv_send
) ? rv_send
: rv_feed
;
557 nsMultiMixedConv::OnStopRequest(nsIRequest
* request
, nsresult aStatus
) {
561 mPartChannel
->SetIsLastPart();
563 MOZ_DIAGNOSTIC_ASSERT(
564 !mRawData
, "There are unsent data from the previous tokenizer feed!");
566 rv
= mTokenizer
.FinishInput();
567 if (NS_SUCCEEDED(aStatus
)) {
571 if (NS_SUCCEEDED(aStatus
)) {
575 (void)SendStop(aStatus
);
576 } else if (NS_FAILED(aStatus
) && !mRequestListenerNotified
) {
577 // underlying data production problem. we should not be in
578 // the middle of sending data. if we were, mPartChannel,
579 // above, would have been non-null.
581 (void)mFinalListener
->OnStartRequest(request
);
582 (void)mFinalListener
->OnStopRequest(request
, aStatus
);
585 nsCOMPtr
<nsIMultiPartChannelListener
> multiListener
=
586 do_QueryInterface(mFinalListener
);
588 multiListener
->OnAfterLastPart(aStatus
);
594 nsresult
nsMultiMixedConv::ConsumeToken(Token
const& token
) {
597 switch (mParserState
) {
599 if (token
.Equals(mBoundaryTokenWithDashes
)) {
600 // The server first used boundary '--boundary'. Hence, we no longer
601 // accept plain 'boundary' token as a delimiter.
602 mTokenizer
.RemoveCustomToken(mBoundaryToken
);
603 mParserState
= BOUNDARY_CRLF
;
606 if (token
.Equals(mBoundaryToken
)) {
607 // And here the opposite from the just above block...
608 mTokenizer
.RemoveCustomToken(mBoundaryTokenWithDashes
);
609 mParserState
= BOUNDARY_CRLF
;
613 // This is a preamble, just ignore it and wait for the boundary.
617 if (token
.Equals(Token::NewLine())) {
618 mParserState
= HEADER_NAME
;
619 mResponseHeader
= HEADER_UNKNOWN
;
621 SetHeaderTokensEnabled(true);
624 return NS_ERROR_CORRUPTED_CONTENT
;
627 SetHeaderTokensEnabled(false);
628 if (token
.Equals(Token::NewLine())) {
629 mParserState
= BODY_INIT
;
630 SwitchToBodyParsing();
633 for (uint32_t h
= HEADER_CONTENT_TYPE
; h
< HEADER_UNKNOWN
; ++h
) {
634 if (token
.Equals(mHeaderTokens
[h
])) {
635 mResponseHeader
= static_cast<EHeader
>(h
);
639 mParserState
= HEADER_SEP
;
643 if (token
.Equals(Token::Char(':'))) {
644 mParserState
= HEADER_VALUE
;
645 mResponseHeaderValue
.Truncate();
648 if (mResponseHeader
== HEADER_UNKNOWN
) {
649 // If the header is not of any we understand, just pass everything till
653 if (token
.Equals(Token::Whitespace())) {
654 // Accept only header-name traling whitespaces after known headers
657 return NS_ERROR_CORRUPTED_CONTENT
;
660 if (token
.Equals(Token::Whitespace()) && mResponseHeaderValue
.IsEmpty()) {
661 // Eat leading whitespaces
664 if (token
.Equals(Token::NewLine())) {
665 nsresult rv
= ProcessHeader();
669 mParserState
= HEADER_NAME
;
670 mResponseHeader
= HEADER_UNKNOWN
;
671 SetHeaderTokensEnabled(true);
673 mResponseHeaderValue
.Append(token
.Fragment());
686 if (!token
.Equals(mLFToken
) && !token
.Equals(mCRLFToken
)) {
687 if (token
.Equals(mBoundaryTokenWithDashes
) ||
688 token
.Equals(mBoundaryToken
)) {
689 // Allow CRLF to NOT be part of the boundary as well
690 SwitchToControlParsing();
691 mParserState
= TRAIL_DASH1
;
694 AccumulateData(token
);
698 // After CRLF we must explicitly check for boundary. If found,
699 // that CRLF is part of the boundary and must not be send to the
702 if (!mTokenizer
.Next(token2
)) {
703 // Note: this will give us the CRLF token again when more data
704 // or OnStopRequest arrive. I.e. we will enter BODY case in
705 // the very same state as we are now and start this block over.
706 mTokenizer
.NeedMoreInput();
709 if (token2
.Equals(mBoundaryTokenWithDashes
) ||
710 token2
.Equals(mBoundaryToken
)) {
711 SwitchToControlParsing();
712 mParserState
= TRAIL_DASH1
;
716 AccumulateData(token
);
717 AccumulateData(token2
);
722 if (token
.Equals(Token::NewLine())) {
723 rv
= SendStop(NS_OK
);
727 mParserState
= BOUNDARY_CRLF
;
728 mTokenizer
.Rollback();
731 if (token
.Equals(Token::Char('-'))) {
732 mParserState
= TRAIL_DASH2
;
735 return NS_ERROR_CORRUPTED_CONTENT
;
738 if (token
.Equals(Token::Char('-'))) {
739 mPartChannel
->SetIsLastPart();
740 // SendStop calls SendData first.
741 rv
= SendStop(NS_OK
);
745 mParserState
= EPILOGUE
;
748 return NS_ERROR_CORRUPTED_CONTENT
;
755 MOZ_ASSERT(false, "Missing parser state handling branch");
762 void nsMultiMixedConv::SetHeaderTokensEnabled(bool aEnable
) {
763 for (uint32_t h
= HEADER_FIRST
; h
< HEADER_UNKNOWN
; ++h
) {
764 mTokenizer
.EnableCustomToken(mHeaderTokens
[h
], aEnable
);
768 void nsMultiMixedConv::SwitchToBodyParsing() {
769 mTokenizer
.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY
);
770 mTokenizer
.EnableCustomToken(mLFToken
, true);
771 mTokenizer
.EnableCustomToken(mCRLFToken
, true);
772 mTokenizer
.EnableCustomToken(mBoundaryTokenWithDashes
, true);
773 mTokenizer
.EnableCustomToken(mBoundaryToken
, true);
776 void nsMultiMixedConv::SwitchToControlParsing() {
777 mTokenizer
.SetTokenizingMode(Tokenizer::Mode::FULL
);
778 mTokenizer
.EnableCustomToken(mLFToken
, false);
779 mTokenizer
.EnableCustomToken(mCRLFToken
, false);
780 mTokenizer
.EnableCustomToken(mBoundaryTokenWithDashes
, false);
781 mTokenizer
.EnableCustomToken(mBoundaryToken
, false);
784 // nsMultiMixedConv methods
785 nsMultiMixedConv::nsMultiMixedConv()
786 // XXX: This is a hack to bypass the raw pointer to refcounted object in
787 // lambda analysis. It should be removed and replaced when the
788 // IncrementalTokenizer API is improved to avoid the need for such
791 // This is safe because `mTokenizer` will not outlive `this`, meaning
792 // that this std::bind object will be destroyed before `this` dies.
793 : mTokenizer(std::bind(&nsMultiMixedConv::ConsumeToken
, this,
794 std::placeholders::_1
)) {}
796 nsresult
nsMultiMixedConv::SendStart() {
799 nsCOMPtr
<nsIStreamListener
> partListener(mFinalListener
);
800 if (mContentType
.IsEmpty()) {
801 mContentType
.AssignLiteral(UNKNOWN_CONTENT_TYPE
);
802 nsCOMPtr
<nsIStreamConverterService
> serv
=
803 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID
, &rv
);
804 if (NS_SUCCEEDED(rv
)) {
805 nsCOMPtr
<nsIStreamListener
> converter
;
806 rv
= serv
->AsyncConvertData(UNKNOWN_CONTENT_TYPE
, "*/*", mFinalListener
,
807 mContext
, getter_AddRefs(converter
));
808 if (NS_SUCCEEDED(rv
)) {
809 partListener
= converter
;
814 // if we already have an mPartChannel, that means we never sent a Stop()
815 // before starting up another "part." that would be bad.
816 MOZ_ASSERT(!mPartChannel
, "tisk tisk, shouldn't be overwriting a channel");
818 nsPartChannel
* newChannel
;
819 newChannel
= new nsPartChannel(mChannel
, mCurrentPartID
, mCurrentPartID
== 0,
824 if (mIsByteRangeRequest
) {
825 newChannel
->InitializeByteRange(mByteRangeStart
, mByteRangeEnd
);
830 // Set up the new part channel...
831 mPartChannel
= newChannel
;
833 rv
= mPartChannel
->SetContentType(mContentType
);
834 if (NS_FAILED(rv
)) return rv
;
836 rv
= mPartChannel
->SetContentLength(mContentLength
);
837 if (NS_FAILED(rv
)) return rv
;
839 mPartChannel
->SetContentDisposition(mContentDisposition
);
841 // Each part of a multipart/replace response can be used
842 // for the top level document. We must inform upper layers
843 // about this by setting the LOAD_REPLACE flag so that certain
844 // state assertions are evaluated as positive.
845 nsLoadFlags loadFlags
= 0;
846 mPartChannel
->GetLoadFlags(&loadFlags
);
847 loadFlags
|= nsIChannel::LOAD_REPLACE
;
848 mPartChannel
->SetLoadFlags(loadFlags
);
850 nsCOMPtr
<nsILoadGroup
> loadGroup
;
851 (void)mPartChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
853 // Add the new channel to the load group (if any)
855 rv
= loadGroup
->AddRequest(mPartChannel
, nullptr);
856 if (NS_FAILED(rv
)) return rv
;
859 // This prevents artificial call to OnStart/StopRequest when the root
860 // channel fails. Since now it's ensured to keep with the nsIStreamListener
861 // contract every time.
862 mRequestListenerNotified
= true;
864 // Let's start off the load. NOTE: we don't forward on the channel passed
865 // into our OnDataAvailable() as it's the root channel for the raw stream.
866 return mPartChannel
->SendOnStartRequest(mContext
);
869 nsresult
nsMultiMixedConv::SendStop(nsresult aStatus
) {
870 // Make sure we send out all accumulcated data prior call to OnStopRequest.
871 // If there is no data, this is a no-op.
872 nsresult rv
= SendData();
873 if (NS_SUCCEEDED(aStatus
)) {
877 rv
= mPartChannel
->SendOnStopRequest(mContext
, aStatus
);
878 // don't check for failure here, we need to remove the channel from
881 // Remove the channel from its load group (if any)
882 nsCOMPtr
<nsILoadGroup
> loadGroup
;
883 (void)mPartChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
885 (void)loadGroup
->RemoveRequest(mPartChannel
, mContext
, aStatus
);
889 mPartChannel
= nullptr;
893 void nsMultiMixedConv::AccumulateData(Token
const& aToken
) {
895 // This is the first read of raw data during this FeedInput loop
896 // of the incremental tokenizer. All 'raw' tokens are coming from
897 // the same linear buffer, hence begining of this loop raw data
898 // is begining of the first raw token. Length of this loop raw
899 // data is just sum of all 'raw' tokens we collect during this loop.
901 // It's ensured we flush (send to to the listener via OnDataAvailable)
902 // and nullify the collected raw data right after FeedInput call.
903 // Hence, the reference can't outlive the actual buffer.
904 mRawData
= aToken
.Fragment().BeginReading();
908 mRawDataLength
+= aToken
.Fragment().Length();
911 nsresult
nsMultiMixedConv::SendData() {
918 nsACString::const_char_iterator rawData
= mRawData
;
922 return NS_ERROR_FAILURE
; // something went wrong w/ processing
925 if (mContentLength
!= UINT64_MAX
) {
926 // make sure that we don't send more than the mContentLength
927 // XXX why? perhaps the Content-Length header was actually wrong!!
928 if ((uint64_t(mRawDataLength
) + mTotalSent
) > mContentLength
) {
929 mRawDataLength
= static_cast<uint32_t>(mContentLength
- mTotalSent
);
932 if (mRawDataLength
== 0) return NS_OK
;
935 uint64_t offset
= mTotalSent
;
936 mTotalSent
+= mRawDataLength
;
938 nsCOMPtr
<nsIStringInputStream
> ss(
939 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv
));
940 if (NS_FAILED(rv
)) return rv
;
942 rv
= ss
->ShareData(rawData
, mRawDataLength
);
944 if (NS_FAILED(rv
)) return rv
;
946 return mPartChannel
->SendOnDataAvailable(mContext
, ss
, offset
,
950 void nsMultiMixedConv::HeadersToDefault() {
951 mContentLength
= UINT64_MAX
;
952 mContentType
.Truncate();
953 mContentDisposition
.Truncate();
954 mContentSecurityPolicy
.Truncate();
955 mIsByteRangeRequest
= false;
958 nsresult
nsMultiMixedConv::ProcessHeader() {
959 mozilla::Tokenizer
p(mResponseHeaderValue
);
961 switch (mResponseHeader
) {
962 case HEADER_CONTENT_TYPE
:
963 mContentType
= mResponseHeaderValue
;
964 mContentType
.CompressWhitespace();
966 case HEADER_CONTENT_LENGTH
:
968 if (!p
.ReadInteger(&mContentLength
)) {
969 return NS_ERROR_CORRUPTED_CONTENT
;
972 case HEADER_CONTENT_DISPOSITION
:
973 mContentDisposition
= mResponseHeaderValue
;
974 mContentDisposition
.CompressWhitespace();
976 case HEADER_SET_COOKIE
: {
977 nsCOMPtr
<nsIHttpChannelInternal
> httpInternal
=
978 do_QueryInterface(mChannel
);
979 mResponseHeaderValue
.CompressWhitespace();
981 DebugOnly
<nsresult
> rv
= httpInternal
->SetCookie(mResponseHeaderValue
);
982 MOZ_ASSERT(NS_SUCCEEDED(rv
));
987 case HEADER_CONTENT_RANGE
: {
988 if (!p
.CheckWord("bytes") || !p
.CheckWhite()) {
989 return NS_ERROR_CORRUPTED_CONTENT
;
992 if (p
.CheckChar('*')) {
993 mByteRangeStart
= mByteRangeEnd
= 0;
994 } else if (!p
.ReadInteger(&mByteRangeStart
) || !p
.CheckChar('-') ||
995 !p
.ReadInteger(&mByteRangeEnd
)) {
996 return NS_ERROR_CORRUPTED_CONTENT
;
998 mIsByteRangeRequest
= true;
999 if (mContentLength
== UINT64_MAX
) {
1000 mContentLength
= uint64_t(mByteRangeEnd
- mByteRangeStart
+ 1);
1004 case HEADER_CONTENT_SECURITY_POLICY
: {
1005 mContentSecurityPolicy
= mResponseHeaderValue
;
1006 mContentSecurityPolicy
.CompressWhitespace();
1007 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
1009 nsCString resultCSP
= mRootContentSecurityPolicy
;
1010 if (!mContentSecurityPolicy
.IsEmpty()) {
1011 // We are updating the root channel CSP header respectively for
1012 // each part as: CSP-root + CSP-partN, where N is the part number.
1013 // Here we append current part's CSP to root CSP and reset CSP
1014 // header for each part.
1015 if (!resultCSP
.IsEmpty()) {
1016 resultCSP
.Append(";");
1018 resultCSP
.Append(mContentSecurityPolicy
);
1020 nsresult rv
= httpChannel
->SetResponseHeader(
1021 "Content-Security-Policy"_ns
, resultCSP
, false);
1022 if (NS_FAILED(rv
)) {
1023 return NS_ERROR_CORRUPTED_CONTENT
;
1028 case HEADER_UNKNOWN
:
1029 // We ignore anything else...
1036 nsresult
NS_NewMultiMixedConv(nsMultiMixedConv
** aMultiMixedConv
) {
1037 MOZ_ASSERT(aMultiMixedConv
!= nullptr, "null ptr");
1039 RefPtr
<nsMultiMixedConv
> conv
= new nsMultiMixedConv();
1040 conv
.forget(aMultiMixedConv
);