/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et tw=80 : */ /* 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 "nsHttpHandler.h" #include "Http3StreamTunnel.h" #include "Http3Session.h" #include "nsQueryObject.h" namespace mozilla::net { //----------------------------------------------------------------------------- // Http3TransportLayer::InputStreamTunnel impl //----------------------------------------------------------------------------- NS_IMPL_QUERY_INTERFACE(Http3TransportLayer::InputStreamTunnel, nsIInputStream, nsIAsyncInputStream) NS_IMETHODIMP_(MozExternalRefCountType) Http3TransportLayer::InputStreamTunnel::AddRef() { return mTransport->AddRef(); } NS_IMETHODIMP_(MozExternalRefCountType) Http3TransportLayer::InputStreamTunnel::Release() { return mTransport->Release(); } Http3TransportLayer::InputStreamTunnel::InputStreamTunnel( Http3TransportLayer* aTransport) : mTransport(aTransport) {} NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::Close() { LOG(("Http3TransportLayer::InputStreamTunnel::Close [this=%p]\n", this)); return CloseWithStatus(NS_BASE_STREAM_CLOSED); } NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::Available( uint64_t* avail) { LOG(("Http3TransportLayer::InputStreamTunnel::Available [this=%p]\n", this)); if (NS_FAILED(mCondition)) { return mCondition; } return NS_ERROR_FAILURE; } NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::StreamStatus() { LOG(("Http3TransportLayer::InputStreamTunnel::StreamStatus [this=%p]\n", this)); return mCondition; } NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::Read(char* buf, uint32_t count, uint32_t* countRead) { LOG(("Http3TransportLayer::InputStreamTunnel::Read [this=%p]\n", this)); *countRead = 0; if (NS_FAILED(mCondition)) { return mCondition; } RefPtr tunnel = mTransport->GetStream(); if (!tunnel) { return NS_ERROR_UNEXPECTED; } return tunnel->OnWriteSegment(buf, count, countRead); } NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::ReadSegments(nsWriteSegmentFun writer, void* closure, uint32_t count, uint32_t* countRead) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::IsNonBlocking(bool* nonblocking) { *nonblocking = true; return NS_OK; } NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::CloseWithStatus(nsresult reason) { LOG( ("Http3TransportLayer::InputStreamTunnel::CloseWithStatus [this=%p " "reason=%" PRIx32 "]\n", this, static_cast(reason))); mCondition = reason; RefPtr tunnel = mTransport->GetStream(); if (!tunnel) { return NS_OK; } tunnel->CleanupStream(reason); return NS_OK; } nsresult Http3TransportLayer::InputStreamTunnel::OnSocketReady( nsresult condition) { LOG(("InputStreamTunnel::OnSocketReady [this=%p cond=%" PRIx32 "]\n", this, static_cast(condition))); nsCOMPtr callback; // update condition, but be careful not to erase an already // existing error condition. if (NS_SUCCEEDED(mCondition)) { mCondition = condition; } callback = std::move(mCallback); return callback ? callback->OnInputStreamReady(this) : NS_OK; } NS_IMETHODIMP Http3TransportLayer::InputStreamTunnel::AsyncWait( nsIInputStreamCallback* callback, uint32_t flags, uint32_t amount, nsIEventTarget* target) { LOG( ("Http3TransportLayer::InputStreamTunnel::AsyncWait [this=%p, " "callback=%p]\n", this, callback)); // The following parameters are not used: MOZ_ASSERT(!flags); MOZ_ASSERT(!amount); Unused << target; RefPtr self(this); if (NS_FAILED(mCondition)) { Unused << NS_DispatchToCurrentThread(NS_NewRunnableFunction( "InputStreamTunnel::CallOnSocketReady", [self{std::move(self)}]() { self->OnSocketReady(self->mCondition); })); } else if (callback) { RefPtr tunnel = mTransport->GetStream(); if (!tunnel) { return NS_ERROR_UNEXPECTED; } tunnel->HasDataToRead(); } mCallback = callback; return NS_OK; } //----------------------------------------------------------------------------- // Http3TransportLayer::OutputStreamTunnel impl //----------------------------------------------------------------------------- NS_IMPL_QUERY_INTERFACE(Http3TransportLayer::OutputStreamTunnel, nsIOutputStream, nsIAsyncOutputStream) NS_IMETHODIMP_(MozExternalRefCountType) Http3TransportLayer::OutputStreamTunnel::AddRef() { return mTransport->AddRef(); } NS_IMETHODIMP_(MozExternalRefCountType) Http3TransportLayer::OutputStreamTunnel::Release() { return mTransport->Release(); } Http3TransportLayer::OutputStreamTunnel::OutputStreamTunnel( Http3TransportLayer* aTransport) : mTransport(aTransport) {} NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::Close() { LOG(("Http3TransportLayer::OutputStreamTunnel::Close [this=%p]\n", this)); return CloseWithStatus(NS_BASE_STREAM_CLOSED); } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::Flush() { LOG(("Http3TransportLayer::OutputStreamTunnel::Flush [this=%p]\n", this)); return NS_OK; } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::StreamStatus() { LOG(("TLSTransportLayerOutputStream::StreamStatus [this=%p]\n", this)); return mCondition; } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::Write(const char* buf, uint32_t count, uint32_t* countWritten) { LOG(("Http3TransportLayer::OutputStreamTunnel::Write [this=%p count=%u]\n", this, count)); *countWritten = 0; if (NS_FAILED(mCondition)) { return mCondition; } RefPtr tunnel = mTransport->GetStream(); if (!tunnel) { return NS_ERROR_UNEXPECTED; } tunnel->HasDataToWrite(); return tunnel->OnReadSegment(buf, count, countWritten); } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::WriteSegments(nsReadSegmentFun reader, void* closure, uint32_t count, uint32_t* countRead) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::WriteFrom(nsIInputStream* stream, uint32_t count, uint32_t* countRead) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::IsNonBlocking(bool* nonblocking) { *nonblocking = true; return NS_OK; } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::CloseWithStatus(nsresult reason) { LOG(("OutputStreamTunnel::CloseWithStatus [this=%p reason=%" PRIx32 "]\n", this, static_cast(reason))); mCondition = reason; RefPtr tunnel = mTransport->GetStream(); if (!tunnel) { return NS_OK; } tunnel->CleanupStream(reason); return NS_OK; } nsresult Http3TransportLayer::OutputStreamTunnel::OnSocketReady( nsresult condition) { LOG(("OutputStreamTunnel::OnSocketReady [this=%p cond=%" PRIx32 " callback=%p]\n", this, static_cast(condition), mCallback.get())); nsCOMPtr callback; // update condition, but be careful not to erase an already // existing error condition. if (NS_SUCCEEDED(mCondition)) { mCondition = condition; } callback = std::move(mCallback); nsresult rv = NS_OK; if (callback) { rv = callback->OnOutputStreamReady(this); MaybeSetRequestDone(callback); } return rv; } void Http3TransportLayer::OutputStreamTunnel::MaybeSetRequestDone( nsIOutputStreamCallback* aCallback) { RefPtr conn = do_QueryObject(aCallback); if (!conn) { return; } RefPtr tunnel = mTransport->GetStream(); if (!tunnel) { return; } if (conn->RequestDone()) { tunnel->SetRequestDone(); } } NS_IMETHODIMP Http3TransportLayer::OutputStreamTunnel::AsyncWait( nsIOutputStreamCallback* callback, uint32_t flags, uint32_t amount, nsIEventTarget* target) { LOG(("OutputStreamTunnel::AsyncWait [this=%p]\n", this)); // The following parameters are not used: MOZ_ASSERT(!flags); MOZ_ASSERT(!amount); Unused << target; RefPtr self(this); if (NS_FAILED(mCondition)) { Unused << NS_DispatchToCurrentThread(NS_NewRunnableFunction( "OutputStreamTunnel::CallOnSocketReady", [self{std::move(self)}]() { self->OnSocketReady(self->mCondition); })); } else if (callback) { // Inform the proxy connection that the inner connetion wants to // read data. RefPtr tunnel = mTransport->GetStream(); if (!tunnel) { return NS_ERROR_UNEXPECTED; } tunnel->HasDataToWrite(); } mCallback = callback; return NS_OK; } //----------------------------------------------------------------------------- // Http3TransportLayer impl //----------------------------------------------------------------------------- NS_IMPL_ISUPPORTS(Http3TransportLayer, nsISocketTransport, nsITransport, nsIInputStreamCallback, nsIOutputStreamCallback) Http3TransportLayer::Http3TransportLayer(Http3StreamTunnel* aStream) : mStream(aStream), mInput(this), mOutput(this) { LOG(("Http3TransportLayer ctor %p", this)); } Http3TransportLayer::~Http3TransportLayer() { LOG(("Http3TransportLayer dtor %p", this)); } already_AddRefed Http3TransportLayer::GetStream() { RefPtr stream = mStream; return stream.forget(); } nsIAsyncInputStream* Http3TransportLayer::GetInput() { return &mInput; } nsIAsyncOutputStream* Http3TransportLayer::GetOutput() { return &mOutput; } nsresult Http3TransportLayer::CallToReadData() { LOG(("Http3TransportLayer::CallToReadData this=%p", this)); return mOutput.OnSocketReady(NS_OK); } nsresult Http3TransportLayer::CallToWriteData() { LOG(("Http3TransportLayer::CallToWriteData this=%p", this)); if (!mInput.HasCallback()) { return NS_BASE_STREAM_WOULD_BLOCK; } return mInput.OnSocketReady(NS_OK); } NS_IMETHODIMP Http3TransportLayer::OnInputStreamReady(nsIAsyncInputStream* in) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::OnOutputStreamReady(nsIAsyncOutputStream* out) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::SetKeepaliveEnabled(bool aKeepaliveEnabled) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::SetKeepaliveVals(int32_t keepaliveIdleTime, int32_t keepaliveRetryInterval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::GetSecurityCallbacks( nsIInterfaceRequestor** aSecurityCallbacks) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::SetSecurityCallbacks( nsIInterfaceRequestor* aSecurityCallbacks) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize, uint32_t aSegmentCount, nsIInputStream** _retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::OpenOutputStream(uint32_t aFlags, uint32_t aSegmentSize, uint32_t aSegmentCount, nsIOutputStream** _retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::Close(nsresult aReason) { LOG(("Http3TransportLayer::Close [this=%p reason=%" PRIx32 "]\n", this, static_cast(aReason))); if (NS_SUCCEEDED(mCondition)) { if (NS_SUCCEEDED(aReason)) { aReason = NS_BASE_STREAM_CLOSED; } mOutput.CloseWithStatus(aReason); mInput.CloseWithStatus(aReason); // Let the session pickup that the stream has been closed. mCondition = aReason; } return NS_OK; } void Http3TransportLayer::OnStreamClosed(nsresult aReason) { LOG(("Http3TransportLayer::OnStreamClosed this=%p", this)); if (NS_SUCCEEDED(mCondition)) { if (NS_SUCCEEDED(aReason)) { aReason = NS_BASE_STREAM_CLOSED; } mOutput.OnSocketReady(aReason); mInput.OnSocketReady(aReason); mCondition = aReason; } } NS_IMETHODIMP Http3TransportLayer::SetEventSink(nsITransportEventSink* aSink, nsIEventTarget* aEventTarget) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::Bind(NetAddr* aLocalAddr) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::GetEchConfigUsed(bool* aEchConfigUsed) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::SetEchConfig(const nsACString& aEchConfig) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::ResolvedByTRR(bool* aResolvedByTRR) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::GetEffectiveTRRMode( nsIRequest::TRRMode* aEffectiveTRRMode) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP Http3TransportLayer::GetTrrSkipReason( nsITRRSkipReason::value* aTrrSkipReason) { return NS_ERROR_NOT_IMPLEMENTED; } #define FWD_H3ST_PTR(fx, ts) \ NS_IMETHODIMP \ Http3TransportLayer::fx(ts* arg) { return NS_OK; } #define FWD_H3ST_ADDREF(fx, ts) \ NS_IMETHODIMP \ Http3TransportLayer::fx(ts** arg) { return NS_OK; } #define FWD_H3ST(fx, ts) \ NS_IMETHODIMP \ Http3TransportLayer::fx(ts arg) { return NS_OK; } FWD_H3ST_PTR(GetKeepaliveEnabled, bool); FWD_H3ST_PTR(GetSendBufferSize, uint32_t); FWD_H3ST(SetSendBufferSize, uint32_t); FWD_H3ST_PTR(GetPort, int32_t); FWD_H3ST_PTR(GetSelfAddr, mozilla::net::NetAddr); FWD_H3ST_ADDREF(GetScriptablePeerAddr, nsINetAddr); FWD_H3ST_ADDREF(GetScriptableSelfAddr, nsINetAddr); FWD_H3ST_PTR(GetConnectionFlags, uint32_t); FWD_H3ST(SetConnectionFlags, uint32_t); FWD_H3ST(SetIsPrivate, bool); FWD_H3ST_PTR(GetTlsFlags, uint32_t); FWD_H3ST(SetTlsFlags, uint32_t); FWD_H3ST_PTR(GetRecvBufferSize, uint32_t); FWD_H3ST(SetRecvBufferSize, uint32_t); FWD_H3ST_PTR(GetResetIPFamilyPreference, bool); nsresult Http3TransportLayer::IsAlive(bool* aAlive) { *aAlive = true; return NS_OK; } nsresult Http3TransportLayer::GetPeerAddr(NetAddr* addr) { // TODO: what address we should use? NetAddr peerAddr; peerAddr.InitFromString("127.0.0.1"_ns); *addr = peerAddr; return NS_OK; } nsresult Http3TransportLayer::GetTlsSocketControl( nsITLSSocketControl** tlsSocketControl) { return NS_OK; } nsresult Http3TransportLayer::GetOriginAttributes( mozilla::OriginAttributes* aOriginAttributes) { return NS_OK; } nsresult Http3TransportLayer::SetOriginAttributes( const mozilla::OriginAttributes& aOriginAttributes) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::GetScriptableOriginAttributes( JSContext* aCx, JS::MutableHandle aOriginAttributes) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::SetScriptableOriginAttributes( JSContext* aCx, JS::Handle aOriginAttributes) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::GetHost(nsACString& aHost) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::GetTimeout(uint32_t aType, uint32_t* _retval) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::SetTimeout(uint32_t aType, uint32_t aValue) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::SetReuseAddrPort(bool aReuseAddrPort) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::SetLinger(bool aPolarity, int16_t aTimeout) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::GetQoSBits(uint8_t* aQoSBits) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::SetQoSBits(uint8_t aQoSBits) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::GetRetryDnsIfPossible(bool* aRetry) { return NS_OK; } NS_IMETHODIMP Http3TransportLayer::GetStatus(nsresult* aStatus) { *aStatus = mCondition; return NS_OK; } //----------------------------------------------------------------------------- // Http3StreamTunnel impl //----------------------------------------------------------------------------- Http3StreamTunnel::Http3StreamTunnel(nsAHttpTransaction* aTrans, Http3Session* aSession, uint64_t aCurrentBrowserId) : Http3Stream(aTrans, aSession, ClassOfService(), aCurrentBrowserId) { LOG(("Http3StreamTunnel ctor %p", this)); } Http3StreamTunnel::~Http3StreamTunnel() { LOG(("Http3StreamTunnel dtor %p", this)); } nsresult Http3StreamTunnel::TryActivating() { nsProxyInfo* info = mTransaction->ConnectionInfo()->ProxyInfo(); if (!info) { return NS_ERROR_UNEXPECTED; } nsAutoCString host; DebugOnly rv{}; rv = nsHttpHandler::GenerateHostPort( nsDependentCString(mTransaction->ConnectionInfo()->Origin()), mTransaction->ConnectionInfo()->OriginPort(), host); MOZ_ASSERT(NS_SUCCEEDED(rv)); LOG(("Http3StreamTunnel::TryActivating [auth=%s]", host.get())); return mSession->TryActivating("CONNECT"_ns, ""_ns, host, ""_ns, mFlatHttpRequestHeaders, &mStreamId, this); } void Http3StreamTunnel::Close(nsresult aResult) { LOG(("Http3StreamTunnel::Close %p", this)); if (mClosed) { return; } mClosed = true; mRecvState = RECV_DONE; mSendState = SEND_DONE; mSession = nullptr; if (mTransaction) { mTransaction->Close(aResult); } if (mTransport) { mTransport->OnStreamClosed(aResult); mTransport = nullptr; } } nsresult Http3StreamTunnel::ReadSegments() { LOG(("Http3StreamTunnel::ReadSegments %p mSendState=%d mRecvState=%d", this, mSendState, mRecvState)); if (mRecvState == RECV_DONE) { // Don't transmit any request frames if the peer cannot respond or respone // is already done. LOG3( ("Http3StreamTunnel %p ReadSegments request stream aborted due to" " response side closure\n", this)); return NS_ERROR_ABORT; } if (mSendState == SEND_DONE) { return NS_OK; } if (!mTransport) { return NS_ERROR_UNEXPECTED; } nsresult rv = mTransport->CallToReadData(); if (mSendState == WAITING_TO_ACTIVATE && (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK)) { LOG3(("Http3StreamTunnel %p ReadSegments forcing OnReadSegment call\n", this)); uint32_t wasted = 0; nsresult rv2 = OnReadSegment("", 0, &wasted); LOG3((" OnReadSegment returned 0x%08" PRIx32, static_cast(rv2))); } return rv; } nsresult Http3StreamTunnel::WriteSegments() { LOG(("Http3StreamTunnel::WriteSegments [this=%p]", this)); if (mRecvState == RECV_DONE) { return NS_OK; } if (!mTransport) { return NS_ERROR_UNEXPECTED; } return mTransport->CallToWriteData(); } void Http3StreamTunnel::SetRequestDone() { LOG(("Http3StreamTunnel::SetRequestDone %p", this)); } void Http3StreamTunnel::HasDataToWrite() { mSession->StreamHasDataToWrite(this); } void Http3StreamTunnel::HasDataToRead() { mSession->ConnectSlowConsumer(this); } already_AddRefed Http3StreamTunnel::CreateHttpConnection( nsIInterfaceRequestor* aCallbacks, PRIntervalTime aRtt, bool aIsExtendedCONNECT) { mTransport = new Http3TransportLayer(this); RefPtr conn = new nsHttpConnection(); conn->SetTransactionCaps(mTransaction->Caps()); nsresult rv = conn->Init(mTransaction->ConnectionInfo(), gHttpHandler->ConnMgr()->MaxRequestDelay(), mTransport, mTransport->GetInput(), mTransport->GetOutput(), true, NS_OK, aCallbacks, aRtt, aIsExtendedCONNECT); MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); return conn.forget(); } void Http3StreamTunnel::CleanupStream(nsresult aReason) { if (mSession) { LOG(("Http3StreamTunnel::CleanupStream %p", this)); mSession->CloseStream(this, aReason); } } } // namespace mozilla::net