/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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 mozilla_net_WebTransportProxy_h #define mozilla_net_WebTransportProxy_h #include #include "nsIChannelEventSink.h" #include "nsIInterfaceRequestor.h" #include "nsIRedirectResultListener.h" #include "nsIStreamListener.h" #include "nsIWebTransport.h" /* * WebTransportSessionProxy is introduced to enable the creation of a * Http3WebTransportSession and coordination of actions that are performed on * the main thread and on the socket thread. * * mChannel, mRedirectChannel, and mListener are used only on the main thread. * * mWebTransportSession is used only on the socket thread. * * mState and mSessionId are used on both threads, socket and main thread and it * is only used with a lock. * * * WebTransportSessionProxyState: * - INIT: before AsyncConnect is called. * * - NEGOTIATING: It is set during AsyncConnect. During this state HttpChannel * is open but OnStartRequest has not been called yet. This state can * transfer into: * - NEGOTIATING_SUCCEEDED: when a Http3WebTransportSession has been * negotiated. * - DONE: when a WebTransport session has been canceled. * * - NEGOTIATING_SUCCEEDED: It is set during parsing of * Http3WebTransportSession response when the response has been successful. * mWebTransport is set to the Http3WebTransportSession at the same time the * session changes to this state. This state can transfer into: * - ACTIVE: during the OnStopRequest call if the WebTransport has not been * canceled or failed for other reason, e.g. a browser shutdown or content * blocking policies. * - SESSION_CLOSE_PENDING: if the WebTransport has been canceled via an API * call or content blocking policies. (the main thread initiated close). * - CLOSE_CALLBACK_PENDING: if Http3WebTransportSession has been canceled * due to a shutdown or a server closing a session. (the socket thread * initiated close). * * - ACTIVE: In this state the session is negotiated and ready to use. This * state can transfer into: * - SESSION_CLOSE_PENDING: if the WebTransport has been canceled via an API * call(nsIWebTransport::closeSession) or content blocking policies. (the * main thread initiated close). * - CLOSE_CALLBACK_PENDING: if Http3WebTransportSession has been canceled * due to a shutdown or a server closing a session. (the socket thread * initiated close). * * - CLOSE_CALLBACK_PENDING: This is the socket thread initiated close. In this * state, the Http3WebTransportSession has been closed and a * CallOnSessionClosed call is dispatched to the main thread to call the * appropriate listener. * * - SESSION_CLOSE_PENDING: This is the main thread initiated close. In this * state, the WebTransport has been closed via an API call * (nsIWebTransport::closeSession) and a CloseSessionInternal call is * dispatched to the socket thread to close the appropriate * Http3WebTransportSession. * * - DONE: everything has been cleaned up on both threads. * * * AsyncConnect creates mChannel on the main thread. Redirect callbacks are also * performed on the main thread (mRedirectChannel set and access only on the * main thread). Before this point, there are no activities on the socket thread * and Http3WebTransportSession is nullptr. mChannel is going to create a * nsHttpTransaction. The transaction will be dispatched on a nsAHttpConnection, * i.e. currently only the HTTP/3 version is implemented, therefore this will be * a HttpConnectionUDP and a Http3Session. The Http3Session creates a * Http3WebTransportSession. Until a response is received * Http3WebTransportSession is only accessed by Http3Session. During parsing of * a successful received from a server on the socket thread, * WebTransportSessionProxy::mWebTransportSession will take a reference to * Http3WebTransportSession and mState will be set to NEGOTIATING_SUCCEEDED. * From now on WebTransportSessionProxy is responsible for closing * Http3WebTransportSession if the closing of the session is initiated on the * main thread. OnStartRequest and OnStopRequest will be called on the main * thread. The session negotiation can have 2 outcomes: * - If both calls, i.e. OnStartRequest an OnStopRequest, indicate that the * request has succeeded and mState is NEGOTIATING_SUCCEEDED, the * mListener->OnSessionReady will be called during OnStopRequest. * - Otherwise, mListener->OnSessionClosed will be called, the state transferred * into SESSION_CLOSE_PENDING, and CloseSessionInternal will be dispatched to * the socket thread. * * CloseSession is called on the main thread. If the session is already closed * it returns an error. If the session is in state NEGOTIATING or * NEGOTIATING_SUCCEEDED mChannel will be canceled. If the session is in state * NEGOTIATING_SUCCEEDED or ACTIVE the state transferred into * SESSION_CLOSE_PENDING, and CloseSessionInternal will be dispatched to the * socket thread * * OnSessionReadyInternal is called on the socket thread. If mState is * NEGOTIATING the state will be set to NEGOTIATING_SUCCEEDED and mWebTransport * will be set to the newly negotiated Http3WebTransportSession. If mState is * DONE, the Http3WebTransportSession will be close. * * OnSessionClosed is called on the socket thread. mState will be set to * CLOSE_CALLBACK_PENDING and CallOnSessionClosed will be dispatched to the main * thread. * * mWebTransport is set during states NEGOTIATING_SUCCEEDED, ACTIVE and * SESSION_CLOSE_PENDING. */ namespace mozilla::net { class WebTransportStreamCallbackWrapper; class WebTransportSessionProxy final : public nsIWebTransport, public WebTransportSessionEventListener, public nsIStreamListener, public nsIChannelEventSink, public nsIRedirectResultListener, public nsIInterfaceRequestor { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIWEBTRANSPORT NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSICHANNELEVENTSINK NS_DECL_NSIREDIRECTRESULTLISTENER NS_DECL_NSIINTERFACEREQUESTOR WebTransportSessionProxy(); private: ~WebTransportSessionProxy(); void CloseSessionInternal(); void CallOnSessionClosed(); enum WebTransportSessionProxyState { INIT, NEGOTIATING, NEGOTIATING_SUCCEEDED, ACTIVE, CLOSE_CALLBACK_PENDING, SESSION_CLOSE_PENDING, DONE, }; mozilla::Mutex mMutex; WebTransportSessionProxyState mState MOZ_GUARDED_BY(mMutex) = WebTransportSessionProxyState::INIT; void ChangeState(WebTransportSessionProxyState newState); void CreateStreamInternal(WebTransportStreamCallbackWrapper* aCallback, bool aBidi); void SendDatagramInternal(const RefPtr& aSession, nsTArray&& aData, uint64_t aTrackingId); void NotifyDatagramReceived(nsTArray&& aData); void GetMaxDatagramSizeInternal( const RefPtr& aSession); void OnMaxDatagramSizeInternal(uint64_t aSize); void OnOutgoingDatagramOutComeInternal( uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome); nsCOMPtr mChannel; nsCOMPtr mRedirectChannel; nsCOMPtr mListener MOZ_GUARDED_BY(mMutex); RefPtr mWebTransportSession MOZ_GUARDED_BY(mMutex); uint64_t mSessionId MOZ_GUARDED_BY(mMutex) = UINT64_MAX; uint32_t mCloseStatus MOZ_GUARDED_BY(mMutex) = 0; nsCString mReason MOZ_GUARDED_BY(mMutex); // This is used to store events happened before OnSessionReady. nsTArray> mPendingEvents MOZ_GUARDED_BY(mMutex); }; } // namespace mozilla::net #endif // mozilla_net_WebTransportProxy_h