/* vim:set ts=4 sw=2 sts=2 et cin: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // HttpLog.h should generally be included first #include "HttpLog.h" #include "HappyEyeballsTransaction.h" #include "nsHttpHandler.h" #include "nsHttpTransaction.h" // Log on level :5, instead of default :4. #undef LOG #define LOG(args) LOG5(args) #undef LOG_ENABLED #define LOG_ENABLED() LOG5_ENABLED() namespace mozilla::net { NS_IMPL_ISUPPORTS(HappyEyeballsTransaction, nsISupportsWeakReference) HappyEyeballsTransaction::HappyEyeballsTransaction(nsHttpTransaction* aTrans) : mTransaction(aTrans) { MOZ_ASSERT(mTransaction); LOG(("HappyEyeballsTransaction ctor %p trans=%p", this, mTransaction.get())); } HappyEyeballsTransaction::~HappyEyeballsTransaction() { LOG(("HappyEyeballsTransaction dtor %p", this)); } void HappyEyeballsTransaction::SetConnection(nsAHttpConnection* conn) { if (mTransaction) { mTransaction->SetConnection(conn); } } nsAHttpConnection* HappyEyeballsTransaction::Connection() { return mTransaction ? mTransaction->Connection() : nullptr; } void HappyEyeballsTransaction::GetSecurityCallbacks( nsIInterfaceRequestor** cb) { if (mTransaction) { mTransaction->GetSecurityCallbacks(cb); } else { *cb = nullptr; } } void HappyEyeballsTransaction::OnTransportStatus(nsITransport* transport, nsresult status, int64_t progress) { if (mTransaction) { mTransaction->OnTransportStatus(transport, status, progress); } } bool HappyEyeballsTransaction::IsDone() { return mTransaction ? mTransaction->IsDone() : true; } nsresult HappyEyeballsTransaction::Status() { return mTransaction ? mTransaction->Status() : NS_ERROR_ABORT; } uint32_t HappyEyeballsTransaction::Caps() { return mTransaction ? mTransaction->Caps() : 0; } void HappyEyeballsTransaction::MaybeInvokeConnectedCallback(nsresult aStatus) { if (mConnectedCallbackInvoked || !mConnectedCallback) { return; } if (NS_FAILED(aStatus) || (mTransaction && mTransaction->Connected())) { LOG( ("HappyEyeballsTransaction::MaybeInvokeConnectedCallback %p " "status=%x", this, static_cast(aStatus))); mConnectedCallbackInvoked = true; auto cb = std::move(mConnectedCallback); cb(aStatus); } } nsresult HappyEyeballsTransaction::ReadSegments(nsAHttpSegmentReader* reader, uint32_t count, uint32_t* countRead) { if (!mTransaction) { return NS_BASE_STREAM_CLOSED; } nsresult rv = mTransaction->ReadSegments(reader, count, countRead); LOG(("HappyEyeballsTransaction::ReadSegments %p rv=%x connected=%d", this, static_cast(rv), mTransaction ? mTransaction->Connected() : 0)); MaybeInvokeConnectedCallback(NS_OK); return rv; } nsresult HappyEyeballsTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged) { LOG(("HappyEyeballsTransaction::Finish0RTT %p restart=%d alpnChanged=%d", this, aRestart, aAlpnChanged)); if (!mTransaction) { return NS_ERROR_NOT_AVAILABLE; } nsresult rv = mTransaction->Finish0RTT(aRestart, aAlpnChanged); if (!aRestart) { MaybeInvokeConnectedCallback(NS_OK); } return rv; } nsresult HappyEyeballsTransaction::WriteSegments(nsAHttpSegmentWriter* writer, uint32_t count, uint32_t* countWritten) { if (!mTransaction) { return NS_BASE_STREAM_CLOSED; } return mTransaction->WriteSegments(writer, count, countWritten); } void HappyEyeballsTransaction::Close(nsresult reason) { LOG(("HappyEyeballsTransaction::Close %p reason=%x trans=%p cancelled=%d", this, static_cast(reason), mTransaction.get(), mCancelled)); RefPtr trans = mTransaction; mTransaction = nullptr; if (!trans) { return; } trans->SetHappyEyeballsProxy(nullptr); if (NS_SUCCEEDED(reason) || mConnectedCallbackInvoked || reason == NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED) { trans->Close(mCancelled ? mCancelReason : reason); return; } // Clear the inner transaction's connection reference to avoid leaking the // Http3Session. The connection was set by Http3Stream to point to the // Http3Session, and if we don't clear it here, the transaction will keep // the session alive indefinitely. trans->SetConnection(nullptr); // Notify the ConnectionEstablisher about the failure. The // HappyEyeballsConnectionAttempt still holds a reference to the inner // transaction and will either retry on another connection or close it // when all attempts have failed. MaybeInvokeConnectedCallback(mCancelled ? mCancelReason : reason); } void HappyEyeballsTransaction::SetConnectedCallback( std::function&& aCallback) { LOG(("HappyEyeballsTransaction::SetConnectedCallback %p hasCallback=%d", this, !!aCallback)); mConnectedCallback = std::move(aCallback); } nsHttpConnectionInfo* HappyEyeballsTransaction::ConnectionInfo() { return mTransaction ? mTransaction->ConnectionInfo() : nullptr; } void HappyEyeballsTransaction::SetProxyConnectFailed() { if (mTransaction) { mTransaction->SetProxyConnectFailed(); } } nsHttpRequestHead* HappyEyeballsTransaction::RequestHead() { return mTransaction ? mTransaction->RequestHead() : nullptr; } uint32_t HappyEyeballsTransaction::Http1xTransactionCount() { return mTransaction ? mTransaction->Http1xTransactionCount() : 0; } nsresult HappyEyeballsTransaction::TakeSubTransactions( nsTArray>& outTransactions) { return NS_ERROR_NOT_IMPLEMENTED; } void HappyEyeballsTransaction::OnActivated() { LOG(("HappyEyeballsTransaction::OnActivated %p trans=%p", this, mTransaction.get())); if (mTransaction) { mTransaction->OnActivated(); } } uint64_t HappyEyeballsTransaction::BrowserId() { return mTransaction ? mTransaction->BrowserId() : 0; } nsIRequestContext* HappyEyeballsTransaction::RequestContext() { return mTransaction ? mTransaction->RequestContext() : nullptr; } bool HappyEyeballsTransaction::Do0RTT() { return mTransaction ? mTransaction->Do0RTT() : false; } void HappyEyeballsTransaction::DisableSpdy() { if (mTransaction) { mTransaction->DisableSpdy(); } } void HappyEyeballsTransaction::DisableHttp2ForProxy() { if (mTransaction) { mTransaction->DisableHttp2ForProxy(); } } void HappyEyeballsTransaction::DisableHttp3(bool aAllowRetryHTTPSRR) { if (mTransaction) { mTransaction->DisableHttp3(aAllowRetryHTTPSRR); } } void HappyEyeballsTransaction::MakeNonSticky() { if (mTransaction) { mTransaction->MakeNonSticky(); } } void HappyEyeballsTransaction::MakeRestartable() { if (mTransaction) { mTransaction->MakeRestartable(); } } void HappyEyeballsTransaction::ReuseConnectionOnRestartOK(bool reuseOk) { if (mTransaction) { static_cast(mTransaction.get()) ->ReuseConnectionOnRestartOK(reuseOk); } } void HappyEyeballsTransaction::SetIsHttp2Websocket(bool h2ws) { if (mTransaction) { mTransaction->SetIsHttp2Websocket(h2ws); } } bool HappyEyeballsTransaction::IsHttp2Websocket() { return mTransaction ? mTransaction->IsHttp2Websocket() : false; } void HappyEyeballsTransaction::SetTRRInfo(nsIRequest::TRRMode aMode, TRRSkippedReason aSkipReason) { if (mTransaction) { mTransaction->SetTRRInfo(aMode, aSkipReason); } } void HappyEyeballsTransaction::DoNotRemoveAltSvc() { if (mTransaction) { mTransaction->DoNotRemoveAltSvc(); } } void HappyEyeballsTransaction::DoNotResetIPFamilyPreference() { if (mTransaction) { mTransaction->DoNotResetIPFamilyPreference(); } } void HappyEyeballsTransaction::OnProxyConnectComplete(int32_t aResponseCode) { if (mTransaction) { mTransaction->OnProxyConnectComplete(aResponseCode); } } nsresult HappyEyeballsTransaction::OnHTTPSRRAvailable( nsIDNSHTTPSSVCRecord* aHTTPSSVCRecord, nsISVCBRecord* aHighestPriorityRecord, const nsACString& aCname) { MOZ_ASSERT_UNREACHABLE("Should not be called"); return NS_ERROR_NOT_IMPLEMENTED; } bool HappyEyeballsTransaction::IsForWebTransport() { return mTransaction ? mTransaction->IsForWebTransport() : false; } bool HappyEyeballsTransaction::IsResettingForTunnelConn() { return mTransaction ? mTransaction->IsResettingForTunnelConn() : false; } void HappyEyeballsTransaction::SetResettingForTunnelConn(bool aValue) { if (mTransaction) { mTransaction->SetResettingForTunnelConn(aValue); } } bool HappyEyeballsTransaction::AllowedToConnectToIpAddressSpace( nsILoadInfo::IPAddressSpace aTargetIpAddressSpace) { MOZ_ASSERT_UNREACHABLE("Should not be called"); return false; } void HappyEyeballsTransaction::Detach() { LOG(("HappyEyeballsTransaction::Detach %p trans=%p", this, mTransaction.get())); if (mTransaction) { mTransaction->SetHappyEyeballsProxy(nullptr); } mTransaction = nullptr; mConnectedCallback = nullptr; } } // namespace mozilla::net