author | Kershaw Chang <kershaw@mozilla.com> |
Thu, 30 Jan 2020 13:56:57 +0000 | |
changeset 512157 | 1d2e243f15816f932d86999b548c73512b49ca1d |
parent 512156 | 0925f61718bbfde97fcd4c3052696be917b73d3c |
child 512158 | 391ad84d82516e086b13535995d20c0b31d4c049 |
push id | 106219 |
push user | kjang@mozilla.com |
push date | Thu, 30 Jan 2020 13:59:40 +0000 |
treeherder | autoland@655675bbf178 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dragana |
bugs | 1600254 |
milestone | 74.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/netwerk/protocol/http/HttpTransactionChild.cpp +++ b/netwerk/protocol/http/HttpTransactionChild.cpp @@ -327,16 +327,26 @@ static TimingStructArgs ToTimingStructAr args.secureConnectionStart() = aTiming.secureConnectionStart; args.connectEnd() = aTiming.connectEnd; args.requestStart() = aTiming.requestStart; args.responseStart() = aTiming.responseStart; args.responseEnd() = aTiming.responseEnd; return args; } +// The maximum number of bytes to consider when attempting to sniff. +// See https://mimesniff.spec.whatwg.org/#reading-the-resource-header. +static const uint32_t MAX_BYTES_SNIFFED = 1445; + +static void GetDataForSniffer(void* aClosure, const uint8_t* aData, + uint32_t aCount) { + nsTArray<uint8_t>* outData = static_cast<nsTArray<uint8_t>*>(aClosure); + outData->AppendElements(aData, std::min(aCount, MAX_BYTES_SNIFFED)); +} + NS_IMETHODIMP HttpTransactionChild::OnStartRequest(nsIRequest* aRequest) { LOG(("HttpTransactionChild::OnStartRequest start [this=%p] mTransaction=%p\n", this, mTransaction.get())); // Don't bother sending IPC to parent process if already canceled. if (mCanceled) { return mStatus; @@ -357,27 +367,36 @@ HttpTransactionChild::OnStartRequest(nsI nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfoSupp); if (secInfoSer) { NS_SerializeToString(secInfoSer, serializedSecurityInfoOut); } } nsAutoPtr<nsHttpResponseHead> head(mTransaction->TakeResponseHead()); Maybe<nsHttpResponseHead> optionalHead; + nsTArray<uint8_t> dataForSniffer; if (head) { optionalHead = Some(*head); + if (mTransaction->Caps() & NS_HTTP_CALL_CONTENT_SNIFFER) { + nsAutoCString contentTypeOptionsHeader; + if (head->GetContentTypeOptionsHeader(contentTypeOptionsHeader) && + contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) { + RefPtr<nsInputStreamPump> pump = do_QueryObject(mTransactionPump); + pump->PeekStream(GetDataForSniffer, &dataForSniffer); + } + } } int32_t proxyConnectResponseCode = mTransaction->GetProxyConnectResponseCode(); Unused << SendOnStartRequest(status, optionalHead, serializedSecurityInfoOut, mTransaction->ProxyConnectFailed(), ToTimingStructArgs(mTransaction->Timings()), - proxyConnectResponseCode); + proxyConnectResponseCode, dataForSniffer); LOG(("HttpTransactionChild::OnStartRequest end [this=%p]\n", this)); return NS_OK; } NS_IMETHODIMP HttpTransactionChild::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) { LOG(("HttpTransactionChild::OnStopRequest [this=%p]\n", this));
--- a/netwerk/protocol/http/HttpTransactionParent.cpp +++ b/netwerk/protocol/http/HttpTransactionParent.cpp @@ -13,18 +13,22 @@ #include "mozilla/net/SocketProcessParent.h" #include "nsHttpHandler.h" #include "nsStreamUtils.h" namespace mozilla { namespace net { NS_IMPL_ADDREF(HttpTransactionParent) -NS_IMPL_QUERY_INTERFACE(HttpTransactionParent, nsIRequest, - nsIThreadRetargetableRequest) +NS_INTERFACE_MAP_BEGIN(HttpTransactionParent) + NS_INTERFACE_MAP_ENTRY(nsIRequest) + NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest) + NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpTransactionParent) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequest) +NS_INTERFACE_MAP_END NS_IMETHODIMP_(MozExternalRefCountType) HttpTransactionParent::Release(void) { MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); if (!mRefCnt.isThreadSafe) { NS_ASSERT_OWNINGTHREAD(HttpTransactionParent); } @@ -225,21 +229,22 @@ nsHttpResponseHead* HttpTransactionParen nsHttpHeaderArray* HttpTransactionParent::TakeResponseTrailers() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mResponseTrailersTaken, "TakeResponseTrailers called 2x"); mResponseTrailersTaken = true; return mResponseTrailers.forget(); } -nsresult HttpTransactionParent::SetSniffedTypeToChannel( - nsIRequest* aPump, nsIChannel* aChannel, - nsInputStreamPump::PeekSegmentFun aCallTypeSniffers) { - // TODO: will be implemented later in bug 1600254. - return NS_ERROR_NOT_IMPLEMENTED; +void HttpTransactionParent::SetSniffedTypeToChannel( + nsInputStreamPump::PeekSegmentFun aCallTypeSniffers, nsIChannel* aChannel) { + if (!mDataForSniffer.IsEmpty()) { + aCallTypeSniffers(aChannel, mDataForSniffer.Elements(), + mDataForSniffer.Length()); + } } NS_IMETHODIMP HttpTransactionParent::GetDeliveryTarget(nsIEventTarget** aNewTarget) { nsCOMPtr<nsIEventTarget> target = mTargetThread; target.forget(aNewTarget); return NS_OK; } @@ -364,24 +369,27 @@ already_AddRefed<nsIEventTarget> HttpTra nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget(); return target.forget(); } mozilla::ipc::IPCResult HttpTransactionParent::RecvOnStartRequest( const nsresult& aStatus, const Maybe<nsHttpResponseHead>& aResponseHead, const nsCString& aSecurityInfoSerialization, const bool& aProxyConnectFailed, const TimingStructArgs& aTimings, - const int32_t& aProxyConnectResponseCode) { + const int32_t& aProxyConnectResponseCode, + nsTArray<uint8_t>&& aDataForSniffer) { mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent( this, [self = UnsafePtr<HttpTransactionParent>(this), aStatus, aResponseHead, aSecurityInfoSerialization, aProxyConnectFailed, - aTimings, aProxyConnectResponseCode]() { + aTimings, aProxyConnectResponseCode, + aDataForSniffer{std::move(aDataForSniffer)}]() mutable { self->DoOnStartRequest(aStatus, aResponseHead, aSecurityInfoSerialization, aProxyConnectFailed, - aTimings, aProxyConnectResponseCode); + aTimings, aProxyConnectResponseCode, + std::move(aDataForSniffer)); })); return IPC_OK(); } static void TimingStructArgsToTimingsStruct(const TimingStructArgs& aArgs, TimingStruct& aTimings) { // If domainLookupStart/End was set by the channel before, we use these // timestamps instead the ones from the transaction. @@ -398,17 +406,18 @@ static void TimingStructArgsToTimingsStr aTimings.responseStart = aArgs.responseStart(); aTimings.responseEnd = aArgs.responseEnd(); } void HttpTransactionParent::DoOnStartRequest( const nsresult& aStatus, const Maybe<nsHttpResponseHead>& aResponseHead, const nsCString& aSecurityInfoSerialization, const bool& aProxyConnectFailed, const TimingStructArgs& aTimings, - const int32_t& aProxyConnectResponseCode) { + const int32_t& aProxyConnectResponseCode, + nsTArray<uint8_t>&& aDataForSniffer) { LOG(("HttpTransactionParent::DoOnStartRequest [this=%p aStatus=%" PRIx32 "]\n", this, static_cast<uint32_t>(aStatus))); if (mCanceled) { return; } @@ -423,16 +432,17 @@ void HttpTransactionParent::DoOnStartReq if (aResponseHead.isSome()) { mResponseHead = new nsHttpResponseHead(aResponseHead.ref()); } mProxyConnectFailed = aProxyConnectFailed; TimingStructArgsToTimingsStruct(aTimings, mTimings); mProxyConnectResponseCode = aProxyConnectResponseCode; + mDataForSniffer = std::move(aDataForSniffer); AutoEventEnqueuer ensureSerialDispatch(mEventQ); nsresult rv = mChannel->OnStartRequest(this); mOnStartRequestCalled = true; if (NS_FAILED(rv)) { Cancel(rv); } }
--- a/netwerk/protocol/http/HttpTransactionParent.h +++ b/netwerk/protocol/http/HttpTransactionParent.h @@ -16,37 +16,46 @@ #include "nsIRequest.h" namespace mozilla { namespace net { class ChannelEventQueue; class nsHttpConnectionInfo; +#define HTTP_TRANSACTION_PARENT_IID \ + { \ + 0xb83695cb, 0xc24b, 0x4c53, { \ + 0x85, 0x9b, 0x77, 0x77, 0x3e, 0xc5, 0x44, 0xe5 \ + } \ + } + // HttpTransactionParent plays the role of nsHttpTransaction and delegates the // work to the nsHttpTransaction in socket process. class HttpTransactionParent final : public PHttpTransactionParent, public HttpTransactionShell, public nsIRequest, public nsIThreadRetargetableRequest { public: NS_DECL_ISUPPORTS NS_DECL_HTTPTRANSACTIONSHELL NS_DECL_NSIREQUEST NS_DECL_NSITHREADRETARGETABLEREQUEST + NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_TRANSACTION_PARENT_IID) explicit HttpTransactionParent(); void ActorDestroy(ActorDestroyReason aWhy) override; mozilla::ipc::IPCResult RecvOnStartRequest( const nsresult& aStatus, const Maybe<nsHttpResponseHead>& aResponseHead, const nsCString& aSecurityInfoSerialization, const bool& aProxyConnectFailed, const TimingStructArgs& aTimings, - const int32_t& aProxyConnectResponseCode); + const int32_t& aProxyConnectResponseCode, + nsTArray<uint8_t>&& aDataForSniffer); mozilla::ipc::IPCResult RecvOnTransportStatus(const nsresult& aStatus, const int64_t& aProgress, const int64_t& aProgressMax); mozilla::ipc::IPCResult RecvOnDataAvailable(const nsCString& aData, const uint64_t& aOffset, const uint32_t& aCount); mozilla::ipc::IPCResult RecvOnStopRequest( const nsresult& aStatus, const bool& aResponseIsComplete, @@ -60,27 +69,32 @@ class HttpTransactionParent final : publ mozilla::ipc::IPCResult RecvOnInitFailed(const nsresult& aStatus); mozilla::ipc::IPCResult RecvOnH2PushStream(const uint32_t& aPushedStreamId, const nsCString& aResourceUrl, const nsCString& aRequestString); already_AddRefed<nsIEventTarget> GetNeckoTarget(); + void SetSniffedTypeToChannel( + nsInputStreamPump::PeekSegmentFun aCallTypeSniffers, + nsIChannel* aChannel); + private: virtual ~HttpTransactionParent(); void GetStructFromInfo(nsHttpConnectionInfo* aInfo, HttpConnectionInfoCloneArgs& aArgs); void DoOnStartRequest(const nsresult& aStatus, const Maybe<nsHttpResponseHead>& aResponseHead, const nsCString& aSecurityInfoSerialization, const bool& aProxyConnectFailed, const TimingStructArgs& aTimings, - const int32_t& aProxyConnectResponseCode); + const int32_t& aProxyConnectResponseCode, + nsTArray<uint8_t>&& aDataForSniffer); void DoOnTransportStatus(const nsresult& aStatus, const int64_t& aProgress, const int64_t& aProgressMax); void DoOnDataAvailable(const nsCString& aData, const uint64_t& aOffset, const uint32_t& aCount); void DoOnStopRequest( const nsresult& aStatus, const bool& aResponseIsComplete, const int64_t& aTransferSize, const TimingStructArgs& aTimings, const Maybe<nsHttpHeaderArray>& responseTrailers, @@ -115,14 +129,18 @@ class HttpTransactionParent final : publ NetAddr mSelfAddr; NetAddr mPeerAddr; TimingStruct mTimings; TimeStamp mDomainLookupStart; TimeStamp mDomainLookupEnd; TransactionObserverFunc mTransactionObserver; TransactionObserverResult mTransactionObserverResult; OnPushCallback mOnPushCallback; + nsTArray<uint8_t> mDataForSniffer; }; +NS_DEFINE_STATIC_IID_ACCESSOR(HttpTransactionParent, + HTTP_TRANSACTION_PARENT_IID) + } // namespace net } // namespace mozilla #endif // nsHttpTransactionParent_h__
--- a/netwerk/protocol/http/HttpTransactionShell.h +++ b/netwerk/protocol/http/HttpTransactionShell.h @@ -138,20 +138,16 @@ class HttpTransactionShell : public nsIS virtual void DontReuseConnection() = 0; virtual bool HasStickyConnection() const = 0; virtual void SetH2WSConnRefTaken() = 0; virtual bool ProxyConnectFailed() = 0; virtual int32_t GetProxyConnectResponseCode() = 0; - virtual nsresult SetSniffedTypeToChannel( - nsIRequest* aPump, nsIChannel* aChannel, - nsInputStreamPump::PeekSegmentFun aCallTypeSniffers) = 0; - virtual void GetTransactionObserverResult( TransactionObserverResult& aResult) = 0; virtual nsHttpTransaction* AsHttpTransaction() = 0; virtual HttpTransactionParent* AsHttpTransactionParent() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(HttpTransactionShell, HTTPTRANSACTIONSHELL_IID) @@ -198,19 +194,16 @@ NS_DEFINE_STATIC_IID_ACCESSOR(HttpTransa virtual int64_t GetTransferSize() override; \ virtual int64_t GetRequestSize() override; \ virtual void SetDNSWasRefreshed() override; \ virtual void DontReuseConnection() override; \ virtual bool HasStickyConnection() const override; \ virtual void SetH2WSConnRefTaken() override; \ virtual bool ProxyConnectFailed() override; \ virtual int32_t GetProxyConnectResponseCode() override; \ - virtual nsresult SetSniffedTypeToChannel( \ - nsIRequest* aPump, nsIChannel* aChannel, \ - nsInputStreamPump::PeekSegmentFun aCallTypeSniffers) override; \ virtual void GetTransactionObserverResult( \ TransactionObserverResult& aResult) override; \ virtual nsHttpTransaction* AsHttpTransaction() override; \ virtual HttpTransactionParent* AsHttpTransactionParent() override; } // namespace net } // namespace mozilla #endif // HttpTransactionShell_h__
--- a/netwerk/protocol/http/PHttpTransaction.ipdl +++ b/netwerk/protocol/http/PHttpTransaction.ipdl @@ -30,17 +30,18 @@ refcounted protocol PHttpTransaction manager PSocketProcess; parent: async OnStartRequest(nsresult status, nsHttpResponseHead? responseHead, nsCString securityInfoSerialization, bool proxyConnectFailed, TimingStructArgs timings, - int32_t proxyConnectResponseCode); + int32_t proxyConnectResponseCode, + uint8_t[] dataForSniffer); async OnTransportStatus(nsresult status, int64_t progress, int64_t progressMax); async OnDataAvailable(nsCString data, uint64_t offset, uint32_t count); async OnStopRequest(nsresult status, bool responseIsComplete,
--- a/netwerk/protocol/http/nsHttp.h +++ b/netwerk/protocol/http/nsHttp.h @@ -125,16 +125,19 @@ const char kHttp3VersionHEX[] = "ff00000 #define NS_HTTP_DISABLE_IPV4 (1 << 17) // The connection should not use IPv6 #define NS_HTTP_DISABLE_IPV6 (1 << 18) // Encodes the TRR mode. #define NS_HTTP_TRR_MODE_MASK ((1 << 19) | (1 << 20)) +// The connection could bring the peeked data for sniffing +#define NS_HTTP_CALL_CONTENT_SNIFFER (1 << 21) + #define NS_HTTP_TRR_FLAGS_FROM_MODE(x) ((static_cast<uint32_t>(x) & 3) << 19) #define NS_HTTP_TRR_MODE_FROM_FLAGS(x) \ (static_cast<nsIRequest::TRRMode>((((x)&NS_HTTP_TRR_MODE_MASK) >> 19) & 3)) //----------------------------------------------------------------------------- // some default values //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -1301,16 +1301,20 @@ nsresult nsHttpChannel::SetupTransaction // Save the mapping of channel id and the channel. We need this mapping for // nsIHttpActivityObserver. gHttpHandler->AddHttpChannel(mChannelId, ToSupports(this)); // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS; + if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) { + mCaps |= NS_HTTP_CALL_CONTENT_SNIFFER; + } + if (mTimingEnabled) mCaps |= NS_HTTP_TIMING_ENABLED; if (mUpgradeProtocolCallback) { rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false); MOZ_ASSERT(NS_SUCCEEDED(rv)); rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(), true); MOZ_ASSERT(NS_SUCCEEDED(rv)); @@ -1436,36 +1440,24 @@ nsresult ProcessXCTO(nsHttpChannel* aCha if (!aURI || !aResponseHead || !aLoadInfo) { // if there is no uri, no response head or no loadInfo, then there is // nothing to do return NS_OK; } // 1) Query the XCTO header and check if 'nosniff' is the first value. nsAutoCString contentTypeOptionsHeader; - Unused << aResponseHead->GetHeader(nsHttp::X_Content_Type_Options, - contentTypeOptionsHeader); - if (contentTypeOptionsHeader.IsEmpty()) { - // if there is no XCTO header, then there is nothing to do. - return NS_OK; - } - // XCTO header might contain multiple values which are comma separated, so: - // a) let's skip all subsequent values - // e.g. " NoSniFF , foo " will be " NoSniFF " - int32_t idx = contentTypeOptionsHeader.Find(","); - if (idx > 0) { - contentTypeOptionsHeader = Substring(contentTypeOptionsHeader, 0, idx); - } - // b) let's trim all surrounding whitespace - // e.g. " NoSniFF " -> "NoSniFF" - nsHttp::TrimHTTPWhitespace(contentTypeOptionsHeader, - contentTypeOptionsHeader); - // c) let's compare the header (ignoring case) - // e.g. "NoSniFF" -> "nosniff" - // if it's not 'nosniff' then there is nothing to do here + if (!aResponseHead->GetContentTypeOptionsHeader(contentTypeOptionsHeader)) { + // if failed to get XCTO header, then there is nothing to do. + return NS_OK; + } + + // let's compare the header (ignoring case) + // e.g. "NoSniFF" -> "nosniff" + // if it's not 'nosniff' then there is nothing to do here if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) { // since we are getting here, the XCTO header was sent; // a non matching value most likely means a mistake happenend; // e.g. sending 'nosnif' instead of 'nosniff', let's log a warning. AutoTArray<nsString, 1> params; CopyUTF8toUTF16(contentTypeOptionsHeader, *params.AppendElement()); RefPtr<Document> doc; aLoadInfo->GetLoadingDocument(getter_AddRefs(doc)); @@ -1848,16 +1840,21 @@ nsresult nsHttpChannel::CallOnStartReque typeSniffersCalled = NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel)); } if (!typeSniffersCalled && mTransactionPump) { RefPtr<nsInputStreamPump> pump = do_QueryObject(mTransactionPump); if (pump) { pump->PeekStream(CallTypeSniffers, thisChannel); + } else { + MOZ_ASSERT(gIOService->UseSocketProcess()); + RefPtr<HttpTransactionParent> trans = do_QueryObject(mTransactionPump); + MOZ_ASSERT(trans); + trans->SetSniffedTypeToChannel(CallTypeSniffers, thisChannel); } } } bool unknownDecoderStarted = false; if (mResponseHead && !mResponseHead->HasContentType()) { MOZ_ASSERT(mConnectionInfo, "Should have connection info here"); if (!mContentTypeHint.IsEmpty())
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -1199,10 +1199,36 @@ bool nsHttpResponseHead::HasContentType( return !mContentType.IsEmpty(); } bool nsHttpResponseHead::HasContentCharset() { RecursiveMutexAutoLock monitor(mRecursiveMutex); return !mContentCharset.IsEmpty(); } +bool nsHttpResponseHead::GetContentTypeOptionsHeader(nsACString& aOutput) { + aOutput.Truncate(); + + nsAutoCString contentTypeOptionsHeader; + Unused << GetHeader(nsHttp::X_Content_Type_Options, contentTypeOptionsHeader); + if (contentTypeOptionsHeader.IsEmpty()) { + // if there is no XCTO header, then there is nothing to do. + return false; + } + + // XCTO header might contain multiple values which are comma separated, so: + // a) let's skip all subsequent values + // e.g. " NoSniFF , foo " will be " NoSniFF " + int32_t idx = contentTypeOptionsHeader.Find(","); + if (idx > 0) { + contentTypeOptionsHeader = Substring(contentTypeOptionsHeader, 0, idx); + } + // b) let's trim all surrounding whitespace + // e.g. " NoSniFF " -> "NoSniFF" + nsHttp::TrimHTTPWhitespace(contentTypeOptionsHeader, + contentTypeOptionsHeader); + + aOutput.Assign(contentTypeOptionsHeader); + return true; +} + } // namespace net } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpResponseHead.h +++ b/netwerk/protocol/http/nsHttpResponseHead.h @@ -150,16 +150,17 @@ class nsHttpResponseHead { // automatically under one lock. MOZ_MUST_USE nsresult VisitHeaders(nsIHttpHeaderVisitor* visitor, nsHttpHeaderArray::VisitorFilter filter); MOZ_MUST_USE nsresult GetOriginalHeader(nsHttpAtom aHeader, nsIHttpHeaderVisitor* aVisitor); bool HasContentType(); bool HasContentCharset(); + bool GetContentTypeOptionsHeader(nsACString& aOutput); private: MOZ_MUST_USE nsresult SetHeader_locked(nsHttpAtom atom, const nsACString& h, const nsACString& v, bool m = false); void AssignDefaultStatusText(); void ParseVersion(const char*); void ParseCacheControl(const char*); void ParsePragma(const char*);
--- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -510,23 +510,16 @@ nsHttpHeaderArray* nsHttpTransaction::Ta // Lock TakeResponseTrailers() against main thread MutexAutoLock lock(*nsHttp::GetLock()); mResponseTrailersTaken = true; return mForTakeResponseTrailers.forget(); } -nsresult nsHttpTransaction::SetSniffedTypeToChannel( - nsIRequest* aPump, nsIChannel* aChannel, - nsInputStreamPump::PeekSegmentFun aCallTypeSniffers) { - // TODO: will be implemented later in bug 1600254. - return NS_ERROR_NOT_IMPLEMENTED; -} - void nsHttpTransaction::SetProxyConnectFailed() { mProxyConnectFailed = true; } nsHttpRequestHead* nsHttpTransaction::RequestHead() { return mRequestHead; } uint32_t nsHttpTransaction::Http1xTransactionCount() { return 1; } nsresult nsHttpTransaction::TakeSubTransactions( nsTArray<RefPtr<nsAHttpTransaction> >& outTransactions) {
--- a/netwerk/test/unit/test_content_sniffer.js +++ b/netwerk/test/unit/test_content_sniffer.js @@ -6,16 +6,18 @@ const unknownType = "application/x-unkno const sniffedType = "application/x-sniffed"; const snifferCID = Components.ID("{4c93d2db-8a56-48d7-b261-9cf2a8d998eb}"); const snifferContract = "@mozilla.org/network/unittest/contentsniffer;1"; const categoryName = "net-content-sniffers"; var sniffing_enabled = true; +var isNosniff = false; + /** * This object is both a factory and an nsIContentSniffer implementation (so, it * is de-facto a service) */ var sniffer = { QueryInterface: ChromeUtils.generateQI(["nsIFactory", "nsIContentSniffer"]), createInstance: function sniffer_ci(outer, iid) { if (outer) { @@ -34,17 +36,21 @@ var sniffer = { var listener = { onStartRequest: function test_onStartR(request) { try { var chan = request.QueryInterface(Ci.nsIChannel); if (chan.contentType == unknownType) { do_throw("Type should not be unknown!"); } - if ( + if (isNosniff) { + if (chan.contentType == sniffedType) { + do_throw("Sniffer called for X-Content-Type-Options:nosniff"); + } + } else if ( sniffing_enabled && this._iteration > 2 && chan.contentType != sniffedType ) { do_throw( "Expecting <" + sniffedType + "> but got <" + @@ -85,37 +91,43 @@ function makeChan(url) { return chan; } var httpserv = null; var urls = null; function run_test() { httpserv = new HttpServer(); + httpserv.registerPathHandler("/nosniff", nosniffHandler); httpserv.start(-1); urls = [ // NOTE: First URL here runs without our content sniffer "data:" + unknownType + ", Some text", "data:" + unknownType + ", Text", // Make sure sniffing works even if we // used the unknown content sniffer too "data:text/plain, Some more text", "http://localhost:" + httpserv.identity.primaryPort, + "http://localhost:" + httpserv.identity.primaryPort + "/nosniff", ]; Components.manager.nsIComponentRegistrar.registerFactory( snifferCID, "Unit test content sniffer", snifferContract, sniffer ); run_test_iteration(1); } +function nosniffHandler(request, response) { + response.setHeader("X-Content-Type-Options", "nosniff"); +} + function run_test_iteration(index) { if (index > urls.length) { if (sniffing_enabled) { sniffing_enabled = false; index = listener._iteration = 1; } else { do_test_pending(); httpserv.stop(do_test_finished); @@ -131,16 +143,18 @@ function run_test_iteration(index) { ); catMan.nsICategoryManager.addCategoryEntry( categoryName, "unit test", snifferContract, false, true ); + } else if (sniffing_enabled && index == 5) { + isNosniff = true; } var chan = makeChan(urls[index - 1]); listener._iteration++; chan.asyncOpen(listener); do_test_pending();