summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/Http3Session.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /netwerk/protocol/http/Http3Session.h
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk/protocol/http/Http3Session.h')
-rw-r--r--netwerk/protocol/http/Http3Session.h381
1 files changed, 381 insertions, 0 deletions
diff --git a/netwerk/protocol/http/Http3Session.h b/netwerk/protocol/http/Http3Session.h
new file mode 100644
index 0000000000..fa15c7df8f
--- /dev/null
+++ b/netwerk/protocol/http/Http3Session.h
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 et cindent: */
+/* 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/. */
+
+#ifndef Http3Session_H__
+#define Http3Session_H__
+
+#include "HttpTrafficAnalyzer.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/net/NeqoHttp3Conn.h"
+#include "nsAHttpConnection.h"
+#include "nsDeque.h"
+#include "nsISupportsImpl.h"
+#include "nsITimer.h"
+#include "nsIUDPSocket.h"
+#include "nsRefPtrHashtable.h"
+#include "nsTHashMap.h"
+#include "nsWeakReference.h"
+
+/*
+ * WebTransport
+ *
+ * Http3Session and the underlying neqo code support multiplexing of multiple
+ * WebTransport and multiplexing WebTransport sessions with regular HTTP
+ * traffic. Whether WebTransport sessions are polled, will be controlled by the
+ * nsHttpConnectionMgr.
+ *
+ * WebTransport support is negotiated using HTTP/3 setting. Before the settings
+ * are available all WebTransport transactions are queued in
+ * mWaitingForWebTransportNegotiation. The information on whether WebTransport
+ * is supported is received via an HTTP/3 event. The event is
+ * Http3Event::Tag::WebTransport with the value
+ * WebTransportEventExternal::Tag::Negotiated that can be true or false. If
+ * the WebTransport feature has been negotiated, queued transactions will be
+ * activated otherwise they will be canceled(i.e. WebTransportNegotiationDone).
+ *
+ * The lifetime of a WebTransport session
+ *
+ * A WebTransport lifetime consists of 2 parts:
+ * - WebTransport session setup
+ * - WebTransport session active time
+ *
+ * WebTransport session setup:
+ * A WebTransport session uses a regular HTTP request for negotiation.
+ * Therefore when a new WebTransport is started a nsHttpChannel and the
+ * corresponding nsHttpTransaction are created. The nsHttpTransaction is
+ * dispatched to a Http3Session and after the
+ * WebTransportEventExternal::Tag::Negotiated event it behaves almost the same
+ * as a regular transaction, e.g. it is added to mStreamTransactionHash and
+ * mStreamIdHash. For activating the session NeqoHttp3Conn::CreateWebTransport
+ * is called instead of NeqoHttp3Conn::Fetch(this is called for the regular
+ * HTTP requests). In this phase, the WebTransport session is canceled in the
+ * same way a regular request is canceled, by canceling the corresponding
+ * nsHttpChannel. If HTTP/3 connection is closed in this phase the
+ * corresponding nsHttpTransaction is canceled and this may cause the
+ * transaction to be restarted (this is the existing restart logic) or the
+ * error is propagated to the nsHttpChannel and its listener(via OnStartRequest
+ * and OnStopRequest as the regular HTTP request).
+ * The phase ends when a connection breaks or when the event
+ * Http3Event::Tag::WebTransport with the value
+ * WebTransportEventExternal::Tag::Session is received. The parameter
+ * aData(from NeqoHttp3Conn::GetEvent) contain the HTTP head of the response.
+ * The headers may be:
+ * - failed code, i.e. anything except 200. In this case, the nsHttpTransaction
+ * behaves the same as a normal HTTP request and the nsHttpChannel listener
+ * will be informed via OnStartRequest and OnStopRequest calls.
+ * - success code, i.e. 200. The code will be propagated to the
+ * nsHttpTransaction. The transaction will parse the header and call
+ * Http3Session::GetWebTransportSession. The function transfers WebTransport
+ * session into the next phase:
+ * - Removes nsHttpTransaction from mStreamTransactionHash.
+ * - Adds the stream to mWebTransportSessions
+ * - The nsHttpTransaction supplies Http3WebTransportSession to the
+ * WebTransportSessionProxy and vice versa.
+ * TODO remove this circular referencing.
+ *
+ * WebTransport session active time:
+ * During this phase the following actions are possible:
+ * - Cancelling a WebTransport session by the application:
+ * The application calls Http3WebTransportSession::CloseSession. This
+ * transfers Http3WebTransportSession into the CLOSE_PENDING state and calls
+ * Http3Session::ConnectSlowConsumer to add itself to the “ready for reading
+ * queue”. Consequently, the Http3Session will call
+ * Http3WebTransportSession::WriteSegments and
+ * Http3Session::CloseWebTransport will be called to send the closing signal
+ * to the peer. After this, the Http3WebTransportSession is in the state DONE
+ * and it will be removed from the Http3Session(the CloseStream function
+ * takes care of this).
+ * - The peer sending a session closing signal:
+ * Http3Session will receive a Http3Event::Tag::WebTransport event with value
+ * WebTransportEventExternal::Tag::SessionClosed. The
+ * Http3WebTransportSession::OnSessionClosed function for the corresponding
+ * stream wil be called. The function will inform the corresponding
+ * WebTransportSessionProxy by calling OnSessionClosed function. The
+ * Http3WebTransportSession is in the state DONE and will be removed from the
+ * Http3Session(the CloseStream function takes care of this).
+ */
+
+namespace mozilla::net {
+
+class HttpConnectionUDP;
+class Http3StreamBase;
+class QuicSocketControl;
+
+// IID for the Http3Session interface
+#define NS_HTTP3SESSION_IID \
+ { \
+ 0x8fc82aaf, 0xc4ef, 0x46ed, { \
+ 0x89, 0x41, 0x93, 0x95, 0x8f, 0xac, 0x4f, 0x21 \
+ } \
+ }
+
+enum class EchExtensionStatus {
+ kNotPresent, // No ECH Extension was sent
+ kGREASE, // A GREASE ECH Extension was sent
+ kReal // A 'real' ECH Extension was sent
+};
+
+class Http3Session final : public nsAHttpTransaction, public nsAHttpConnection {
+ public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTTP3SESSION_IID)
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSAHTTPTRANSACTION
+ NS_DECL_NSAHTTPCONNECTION(mConnection)
+
+ Http3Session();
+ nsresult Init(const nsHttpConnectionInfo* aConnInfo, nsINetAddr* selfAddr,
+ nsINetAddr* peerAddr, HttpConnectionUDP* udpConn,
+ uint32_t controlFlags, nsIInterfaceRequestor* callbacks);
+
+ bool IsConnected() const { return mState == CONNECTED; }
+ bool CanSandData() const {
+ return (mState == CONNECTED) || (mState == ZERORTT);
+ }
+ bool IsClosing() const { return (mState == CLOSING || mState == CLOSED); }
+ bool IsClosed() const { return mState == CLOSED; }
+
+ bool AddStream(nsAHttpTransaction* aHttpTransaction, int32_t aPriority,
+ nsIInterfaceRequestor* aCallbacks);
+
+ bool CanReuse();
+
+ // The following functions are used by Http3Stream and
+ // Http3WebTransportSession:
+ nsresult TryActivating(const nsACString& aMethod, const nsACString& aScheme,
+ const nsACString& aAuthorityHeader,
+ const nsACString& aPath, const nsACString& aHeaders,
+ uint64_t* aStreamId, Http3StreamBase* aStream);
+ // The folowing functions are used by Http3Stream:
+ void CloseSendingSide(uint64_t aStreamId);
+ nsresult SendRequestBody(uint64_t aStreamId, const char* buf, uint32_t count,
+ uint32_t* countRead);
+ nsresult ReadResponseHeaders(uint64_t aStreamId,
+ nsTArray<uint8_t>& aResponseHeaders, bool* aFin);
+ nsresult ReadResponseData(uint64_t aStreamId, char* aBuf, uint32_t aCount,
+ uint32_t* aCountWritten, bool* aFin);
+
+ // The folowing functions are used by Http3WebTransportSession:
+ nsresult CloseWebTransport(uint64_t aSessionId, uint32_t aError,
+ const nsACString& aMessage);
+ nsresult CreateWebTransportStream(uint64_t aSessionId,
+ WebTransportStreamType aStreamType,
+ uint64_t* aStreamId);
+ void CloseStream(Http3StreamBase* aStream, nsresult aResult);
+ void CloseStreamInternal(Http3StreamBase* aStream, nsresult aResult);
+
+ void SetCleanShutdown(bool aCleanShutdown) {
+ mCleanShutdown = aCleanShutdown;
+ }
+
+ bool TestJoinConnection(const nsACString& hostname, int32_t port);
+ bool JoinConnection(const nsACString& hostname, int32_t port);
+
+ void TransactionHasDataToWrite(nsAHttpTransaction* caller) override;
+ void TransactionHasDataToRecv(nsAHttpTransaction* caller) override;
+ [[nodiscard]] nsresult GetTransactionTLSSocketControl(
+ nsITLSSocketControl**) override;
+
+ // This function will be called by QuicSocketControl when the certificate
+ // verification is done.
+ void Authenticated(int32_t aError);
+
+ nsresult ProcessOutputAndEvents(nsIUDPSocket* socket);
+
+ void ReportHttp3Connection();
+
+ int64_t GetBytesWritten() { return mTotalBytesWritten; }
+ int64_t BytesRead() { return mTotalBytesRead; }
+
+ nsresult SendData(nsIUDPSocket* socket);
+ nsresult RecvData(nsIUDPSocket* socket);
+
+ void DoSetEchConfig(const nsACString& aEchConfig);
+
+ nsresult SendPriorityUpdateFrame(uint64_t aStreamId, uint8_t aPriorityUrgency,
+ bool aPriorityIncremental);
+
+ void ConnectSlowConsumer(Http3StreamBase* stream);
+
+ nsresult TryActivatingWebTransportStream(uint64_t* aStreamId,
+ Http3StreamBase* aStream);
+ void CloseWebTransportStream(Http3WebTransportStream* aStream,
+ nsresult aResult);
+ void StreamHasDataToWrite(Http3StreamBase* aStream);
+ void ResetWebTransportStream(Http3WebTransportStream* aStream,
+ uint64_t aErrorCode);
+ void StreamStopSending(Http3WebTransportStream* aStream, uint8_t aErrorCode);
+
+ void SendDatagram(Http3WebTransportSession* aSession,
+ nsTArray<uint8_t>& aData, uint64_t aTrackingId);
+
+ uint64_t MaxDatagramSize(uint64_t aSessionId);
+
+ void CloseWebTransportConn();
+
+ private:
+ ~Http3Session();
+
+ void CloseInternal(bool aCallNeqoClose);
+ void Shutdown();
+
+ bool RealJoinConnection(const nsACString& hostname, int32_t port,
+ bool justKidding);
+
+ nsresult ProcessOutput(nsIUDPSocket* socket);
+ void ProcessInput(nsIUDPSocket* socket);
+ nsresult ProcessEvents();
+
+ nsresult ProcessTransactionRead(uint64_t stream_id);
+ nsresult ProcessTransactionRead(Http3StreamBase* stream);
+ nsresult ProcessSlowConsumers();
+
+ void SetupTimer(uint64_t aTimeout);
+
+ enum ResetType {
+ RESET,
+ STOP_SENDING,
+ };
+ void ResetOrStopSendingRecvd(uint64_t aStreamId, uint64_t aError,
+ ResetType aType);
+
+ void QueueStream(Http3StreamBase* stream);
+ void RemoveStreamFromQueues(Http3StreamBase*);
+ void ProcessPending();
+
+ void CallCertVerification(Maybe<nsCString> aEchPublicName);
+ void SetSecInfo();
+
+ void EchOutcomeTelemetry();
+
+ void StreamReadyToWrite(Http3StreamBase* aStream);
+ void MaybeResumeSend();
+
+ void CloseConnectionTelemetry(CloseError& aError, bool aClosing);
+ void Finish0Rtt(bool aRestart);
+
+ enum ZeroRttOutcome {
+ NOT_USED,
+ USED_SUCCEEDED,
+ USED_REJECTED,
+ USED_CONN_ERROR,
+ USED_CONN_CLOSED_BY_NECKO
+ };
+ void ZeroRttTelemetry(ZeroRttOutcome aOutcome);
+
+ RefPtr<NeqoHttp3Conn> mHttp3Connection;
+ RefPtr<nsAHttpConnection> mConnection;
+ // We need an extra map to store the mapping of WebTransportSession and
+ // WebTransportStreams to handle the case that a stream is already removed
+ // from mStreamIdHash and we still need the WebTransportSession.
+ nsTHashMap<nsUint64HashKey, uint64_t> mWebTransportStreamToSessionMap;
+ nsRefPtrHashtable<nsUint64HashKey, Http3StreamBase> mStreamIdHash;
+ nsRefPtrHashtable<nsPtrHashKey<nsAHttpTransaction>, Http3StreamBase>
+ mStreamTransactionHash;
+
+ nsRefPtrDeque<Http3StreamBase> mReadyForWrite;
+ nsTArray<RefPtr<Http3StreamBase>> mSlowConsumersReadyForRead;
+ nsRefPtrDeque<Http3StreamBase> mQueuedStreams;
+
+ enum State {
+ INITIALIZING,
+ ZERORTT,
+ CONNECTED,
+ CLOSING,
+ CLOSED
+ } mState{INITIALIZING};
+
+ bool mAuthenticationStarted{false};
+ bool mCleanShutdown{false};
+ bool mGoawayReceived{false};
+ bool mShouldClose{false};
+ bool mIsClosedByNeqo{false};
+ bool mHttp3ConnectionReported = false;
+ // mError is neqo error (a protocol error) and that may mean that we will
+ // send some packets after that.
+ nsresult mError{NS_OK};
+ // This is a socket error, there is no poioint in sending anything on that
+ // socket.
+ nsresult mSocketError{NS_OK};
+ bool mBeforeConnectedError{false};
+ uint64_t mCurrentBrowserId;
+
+ // True if the mTimer is inited and waiting for firing.
+ bool mTimerActive{false};
+
+ RefPtr<HttpConnectionUDP> mUdpConn;
+
+ // The underlying socket transport object is needed to propogate some events
+ RefPtr<nsISocketTransport> mSocketTransport;
+
+ nsCOMPtr<nsITimer> mTimer;
+
+ nsTHashMap<nsCStringHashKey, bool> mJoinConnectionCache;
+
+ RefPtr<QuicSocketControl> mSocketControl;
+
+ uint64_t mTransactionCount = 0;
+
+ // The stream(s) that we are getting 0RTT data from.
+ nsTArray<WeakPtr<Http3StreamBase>> m0RTTStreams;
+ // The stream(s) that are not able to send 0RTT data. We need to
+ // remember them put them into mReadyForWrite queue when 0RTT finishes.
+ nsTArray<WeakPtr<Http3StreamBase>> mCannotDo0RTTStreams;
+
+ // The following variables are needed for telemetry.
+ TimeStamp mConnectionIdleStart;
+ TimeStamp mConnectionIdleEnd;
+ Maybe<uint64_t> mFirstStreamIdReuseIdleConnection;
+ TimeStamp mTimerShouldTrigger;
+ TimeStamp mZeroRttStarted;
+ uint64_t mBlockedByStreamLimitCount = 0;
+ uint64_t mTransactionsBlockedByStreamLimitCount = 0;
+ uint64_t mTransactionsSenderBlockedByFlowControlCount = 0;
+
+ // NS_NET_STATUS_CONNECTED_TO event will be created by the Http3Session.
+ // We want to propagate it to the first transaction.
+ RefPtr<nsHttpTransaction> mFirstHttpTransaction;
+
+ RefPtr<nsHttpConnectionInfo> mConnInfo;
+
+ bool mThroughCaptivePortal = false;
+ int64_t mTotalBytesRead = 0; // total data read
+ int64_t mTotalBytesWritten = 0; // total data read
+ PRIntervalTime mLastWriteTime = 0;
+
+ // Records whether we sent an ECH Extension and whether it was a GREASE Xtn
+ EchExtensionStatus mEchExtensionStatus = EchExtensionStatus::kNotPresent;
+
+ // Records whether the handshake finished successfully and we established a
+ // a connection.
+ bool mHandshakeSucceeded = false;
+
+ nsCOMPtr<nsINetAddr> mNetAddr;
+
+ enum WebTransportNegotiation { DISABLED, NEGOTIATING, FAILED, SUCCEEDED };
+ WebTransportNegotiation mWebTransportNegotiationStatus{
+ WebTransportNegotiation::DISABLED};
+
+ nsTArray<WeakPtr<Http3StreamBase>> mWaitingForWebTransportNegotiation;
+ // 1795854 implement the case when WebTransport is not supported.
+ // Also, implement the case when the HTTP/3 session fails before settings
+ // are exchanged.
+ void WebTransportNegotiationDone();
+
+ nsTArray<RefPtr<Http3StreamBase>> mWebTransportSessions;
+ nsTArray<RefPtr<Http3StreamBase>> mWebTransportStreams;
+
+ bool mHasWebTransportSession = false;
+ // When true, we don't add this connection info into the Http/3 excluded list.
+ bool mDontExclude = false;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(Http3Session, NS_HTTP3SESSION_IID);
+
+} // namespace mozilla::net
+
+#endif // Http3Session_H__