tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Http3Session.h (20077B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=4 sw=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef Http3Session_H__
      8 #define Http3Session_H__
      9 
     10 #include "HttpTrafficAnalyzer.h"
     11 #include "mozilla/Array.h"
     12 #include "mozilla/WeakPtr.h"
     13 #include "mozilla/net/NeqoHttp3Conn.h"
     14 #include "nsAHttpConnection.h"
     15 #include "nsDeque.h"
     16 #include "nsISupportsImpl.h"
     17 #include "nsITimer.h"
     18 #include "nsIUDPSocket.h"
     19 #include "nsRefPtrHashtable.h"
     20 #include "nsTHashMap.h"
     21 #include "nsWeakReference.h"
     22 
     23 /*
     24 * WebTransport
     25 *
     26 * Http3Session and the underlying neqo code support multiplexing of multiple
     27 * WebTransport and multiplexing WebTransport sessions with regular HTTP
     28 * traffic. Whether WebTransport sessions are polled, will be controlled by the
     29 * nsHttpConnectionMgr.
     30 *
     31 * WebTransport support is negotiated using HTTP/3 setting. Before the settings
     32 * are available all WebTransport transactions are queued in
     33 * mWaitingForWebTransportNegotiation. The information on whether WebTransport
     34 * is supported is received via an HTTP/3 event. The event is
     35 * Http3Event::Tag::WebTransport  with the value
     36 * WebTransportEventExternal::Tag::Negotiated  that can be true or false. If
     37 * the WebTransport feature has been negotiated, queued transactions will be
     38 * activated otherwise they will be canceled(i.e. WebTransportNegotiationDone).
     39 *
     40 * The lifetime of a WebTransport session
     41 *
     42 * A WebTransport lifetime consists of 2 parts:
     43 * - WebTransport session setup
     44 * - WebTransport session active time
     45 *
     46 * WebTransport session setup:
     47 * A WebTransport session uses a regular HTTP request for negotiation.
     48 * Therefore when a new WebTransport is started a nsHttpChannel and the
     49 * corresponding nsHttpTransaction are created. The nsHttpTransaction is
     50 * dispatched to a Http3Session and after the
     51 * WebTransportEventExternal::Tag::Negotiated event it behaves almost the same
     52 * as a regular transaction, e.g. it is added to mStreamTransactionHash and
     53 * mStreamIdHash. For activating the session NeqoHttp3Conn::CreateWebTransport
     54 * is  called instead of NeqoHttp3Conn::Fetch(this is called for the regular
     55 * HTTP requests). In this phase, the WebTransport session is canceled in the
     56 * same way a regular request is canceled, by canceling the corresponding
     57 * nsHttpChannel. If HTTP/3 connection is closed in this phase the
     58 * corresponding nsHttpTransaction is canceled and this may cause the
     59 * transaction to be restarted (this is the existing restart logic) or the
     60 * error is propagated to the nsHttpChannel and its listener(via OnStartRequest
     61 * and OnStopRequest as the regular HTTP request).
     62 * The phase ends when a connection breaks or when the event
     63 * Http3Event::Tag::WebTransport with the value
     64 * WebTransportEventExternal::Tag::Session is received. The parameter
     65 * aData(from NeqoHttp3Conn::GetEvent) contain the HTTP head of the response.
     66 * The headers may be:
     67 * - failed code, i.e. anything except 200. In this case, the nsHttpTransaction
     68 *   behaves the same as a normal HTTP request and the nsHttpChannel listener
     69 *   will be informed via OnStartRequest and OnStopRequest calls.
     70 * - success code, i.e. 200. The code will be propagated to the
     71 *   nsHttpTransaction. The transaction will parse the header and call
     72 *   Http3Session::GetWebTransportSession. The function transfers WebTransport
     73 *   session into the next phase:
     74 *   - Removes nsHttpTransaction from mStreamTransactionHash.
     75 *   - Adds the stream to mWebTransportSessions
     76 *   - The nsHttpTransaction supplies Http3WebTransportSession to the
     77 *     WebTransportSessionProxy and vice versa.
     78 *     TODO remove this circular referencing.
     79 *
     80 * WebTransport session active time:
     81 * During this phase the following actions are possible:
     82 * - Cancelling a WebTransport session by the application:
     83 *   The application calls Http3WebTransportSession::CloseSession. This
     84 *   transfers Http3WebTransportSession into the CLOSE_PENDING state and calls
     85 *   Http3Session::ConnectSlowConsumer to add itself to the “ready for reading
     86 *   queue”. Consequently, the Http3Session will call
     87 *   Http3WebTransportSession::WriteSegments and
     88 *   Http3Session::CloseWebTransport will be called to send the closing signal
     89 *   to the peer. After this, the Http3WebTransportSession is in the state DONE
     90 *   and it will be removed from the Http3Session(the CloseStream function
     91 *   takes care of this).
     92 * - The peer sending a session closing signal:
     93 *   Http3Session will receive a Http3Event::Tag::WebTransport event with value
     94 *   WebTransportEventExternal::Tag::SessionClosed. The
     95 *   Http3WebTransportSession::OnSessionClosed function for the corresponding
     96 *   stream wil be called. The function will inform the corresponding
     97 *   WebTransportSessionProxy by calling OnSessionClosed function. The
     98 *   Http3WebTransportSession is in the state DONE and will be removed from the
     99 *   Http3Session(the CloseStream function takes care of this).
    100 */
    101 
    102 namespace mozilla::net {
    103 
    104 class HttpConnectionUDP;
    105 class Http3StreamBase;
    106 class QuicSocketControl;
    107 class Http3WebTransportSession;
    108 class Http3WebTransportStream;
    109 class nsHttpConnection;
    110 
    111 // IID for the Http3Session interface
    112 #define NS_HTTP3SESSION_IID \
    113  {0x8fc82aaf, 0xc4ef, 0x46ed, {0x89, 0x41, 0x93, 0x95, 0x8f, 0xac, 0x4f, 0x21}}
    114 
    115 enum class EchExtensionStatus {
    116  kNotPresent,  // No ECH Extension was sent
    117  kGREASE,      // A GREASE ECH Extension was sent
    118  kReal         // A 'real' ECH Extension was sent
    119 };
    120 
    121 // An abstract layer for testing.
    122 class Http3SessionBase {
    123 public:
    124  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
    125 
    126  virtual nsresult TryActivating(const nsACString& aMethod,
    127                                 const nsACString& aScheme,
    128                                 const nsACString& aAuthorityHeader,
    129                                 const nsACString& aPath,
    130                                 const nsACString& aHeaders,
    131                                 uint64_t* aStreamId,
    132                                 Http3StreamBase* aStream) = 0;
    133  virtual void CloseSendingSide(uint64_t aStreamId) = 0;
    134  virtual void SendHTTPDatagram(uint64_t aStreamId, nsTArray<uint8_t>& aData,
    135                                uint64_t aTrackingId) = 0;
    136  virtual nsresult SendPriorityUpdateFrame(uint64_t aStreamId,
    137                                           uint8_t aPriorityUrgency,
    138                                           bool aPriorityIncremental) = 0;
    139  virtual void ConnectSlowConsumer(Http3StreamBase* stream) = 0;
    140 
    141  virtual nsresult SendRequestBody(uint64_t aStreamId, const char* buf,
    142                                   uint32_t count, uint32_t* countRead) = 0;
    143  virtual nsresult ReadResponseData(uint64_t aStreamId, char* aBuf,
    144                                    uint32_t aCount, uint32_t* aCountWritten,
    145                                    bool* aFin) = 0;
    146  virtual void FinishTunnelSetup(nsAHttpTransaction* aTransaction) = 0;
    147 
    148  virtual void CloseStream(Http3StreamBase* aStream, nsresult aResult) {}
    149 
    150  // For WebTransport
    151  virtual void CloseWebTransportConn() = 0;
    152  virtual void StreamHasDataToWrite(Http3StreamBase* aStream) = 0;
    153  virtual nsresult CloseWebTransport(uint64_t aSessionId, uint32_t aError,
    154                                     const nsACString& aMessage) = 0;
    155  virtual void SendDatagram(Http3WebTransportSession* aSession,
    156                            nsTArray<uint8_t>& aData, uint64_t aTrackingId) = 0;
    157  virtual uint64_t MaxDatagramSize(uint64_t aSessionId) = 0;
    158  virtual nsresult TryActivatingWebTransportStream(
    159      uint64_t* aStreamId, Http3StreamBase* aStream) = 0;
    160  virtual void ResetWebTransportStream(Http3WebTransportStream* aStream,
    161                                       uint64_t aErrorCode) = 0;
    162  virtual void StreamStopSending(Http3WebTransportStream* aStream,
    163                                 uint8_t aErrorCode) = 0;
    164  virtual void SetSendOrder(Http3StreamBase* aStream,
    165                            Maybe<int64_t> aSendOrder) = 0;
    166 };
    167 
    168 class Http3Session final : public Http3SessionBase,
    169                           public nsAHttpTransaction,
    170                           public nsAHttpConnection {
    171 public:
    172  NS_INLINE_DECL_STATIC_IID(NS_HTTP3SESSION_IID)
    173 
    174  NS_DECL_ISUPPORTS_INHERITED
    175  NS_DECL_NSAHTTPTRANSACTION
    176  NS_DECL_NSAHTTPCONNECTION(mConnection)
    177 
    178  class OnQuicTimeout final : public nsITimerCallback, public nsINamed {
    179   public:
    180    NS_DECL_THREADSAFE_ISUPPORTS
    181    NS_DECL_NSITIMERCALLBACK
    182    NS_DECL_NSINAMED
    183 
    184    explicit OnQuicTimeout(HttpConnectionUDP* aConnection);
    185 
    186   private:
    187    ~OnQuicTimeout() = default;
    188    RefPtr<HttpConnectionUDP> mConnection;
    189  };
    190 
    191  Http3Session();
    192  nsresult Init(const nsHttpConnectionInfo* aConnInfo, nsINetAddr* selfAddr,
    193                nsINetAddr* peerAddr, HttpConnectionUDP* udpConn,
    194                uint32_t aProviderFlags, nsIInterfaceRequestor* callbacks,
    195                nsIUDPSocket* socket, bool aIsTunnel = false);
    196 
    197  bool IsConnected() const { return mState == CONNECTED; }
    198  bool CanSendData() const {
    199    return (mState == CONNECTED) || (mState == ZERORTT);
    200  }
    201  bool IsClosing() const { return (mState == CLOSING || mState == CLOSED); }
    202  bool IsClosed() const { return mState == CLOSED; }
    203 
    204  bool AddStream(nsAHttpTransaction* aHttpTransaction, int32_t aPriority,
    205                 nsIInterfaceRequestor* aCallbacks);
    206 
    207  bool CanReuse();
    208 
    209  // The following functions are used by Http3Stream and
    210  // Http3WebTransportSession:
    211  nsresult TryActivating(const nsACString& aMethod, const nsACString& aScheme,
    212                         const nsACString& aAuthorityHeader,
    213                         const nsACString& aPath, const nsACString& aHeaders,
    214                         uint64_t* aStreamId,
    215                         Http3StreamBase* aStream) override;
    216  // The folowing functions are used by Http3Stream:
    217  void CloseSendingSide(uint64_t aStreamId) override;
    218  nsresult SendRequestBody(uint64_t aStreamId, const char* buf, uint32_t count,
    219                           uint32_t* countRead) override;
    220  nsresult ReadResponseHeaders(uint64_t aStreamId,
    221                               nsTArray<uint8_t>& aResponseHeaders, bool* aFin);
    222  nsresult ReadResponseData(uint64_t aStreamId, char* aBuf, uint32_t aCount,
    223                            uint32_t* aCountWritten, bool* aFin) override;
    224 
    225  // The folowing functions are used by Http3WebTransportSession:
    226  nsresult CloseWebTransport(uint64_t aSessionId, uint32_t aError,
    227                             const nsACString& aMessage) override;
    228  nsresult CreateWebTransportStream(uint64_t aSessionId,
    229                                    WebTransportStreamType aStreamType,
    230                                    uint64_t* aStreamId);
    231  void CloseStream(Http3StreamBase* aStream, nsresult aResult) override;
    232  void CloseStreamInternal(Http3StreamBase* aStream, nsresult aResult);
    233 
    234  void SetCleanShutdown(bool aCleanShutdown) {
    235    mCleanShutdown = aCleanShutdown;
    236  }
    237 
    238  bool TestJoinConnection(const nsACString& hostname, int32_t port);
    239  bool JoinConnection(const nsACString& hostname, int32_t port);
    240 
    241  void TransactionHasDataToWrite(nsAHttpTransaction* caller) override;
    242  void TransactionHasDataToRecv(nsAHttpTransaction* caller) override;
    243  [[nodiscard]] nsresult GetTransactionTLSSocketControl(
    244      nsITLSSocketControl**) override;
    245 
    246  // This function will be called by QuicSocketControl when the certificate
    247  // verification is done.
    248  void Authenticated(int32_t aError, bool aServCertHashesSucceeded = false);
    249 
    250  nsresult ProcessOutputAndEvents(nsIUDPSocket* socket);
    251 
    252  void ReportHttp3Connection();
    253 
    254  int64_t GetBytesWritten() { return mTotalBytesWritten; }
    255  int64_t BytesRead() { return mTotalBytesRead; }
    256 
    257  nsresult SendData(nsIUDPSocket* socket);
    258  nsresult RecvData(nsIUDPSocket* socket);
    259 
    260  void DoSetEchConfig(const nsACString& aEchConfig);
    261 
    262  nsresult SendPriorityUpdateFrame(uint64_t aStreamId, uint8_t aPriorityUrgency,
    263                                   bool aPriorityIncremental) override;
    264 
    265  void ConnectSlowConsumer(Http3StreamBase* stream) override;
    266 
    267  nsresult TryActivatingWebTransportStream(uint64_t* aStreamId,
    268                                           Http3StreamBase* aStream) override;
    269  void CloseWebTransportStream(Http3WebTransportStream* aStream,
    270                               nsresult aResult);
    271  void StreamHasDataToWrite(Http3StreamBase* aStream) override;
    272  void ResetWebTransportStream(Http3WebTransportStream* aStream,
    273                               uint64_t aErrorCode) override;
    274  void StreamStopSending(Http3WebTransportStream* aStream,
    275                         uint8_t aErrorCode) override;
    276 
    277  void SendDatagram(Http3WebTransportSession* aSession,
    278                    nsTArray<uint8_t>& aData, uint64_t aTrackingId) override;
    279  void SendHTTPDatagram(uint64_t aStreamId, nsTArray<uint8_t>& aData,
    280                        uint64_t aTrackingId) override;
    281 
    282  uint64_t MaxDatagramSize(uint64_t aSessionId) override;
    283 
    284  void SetSendOrder(Http3StreamBase* aStream,
    285                    Maybe<int64_t> aSendOrder) override;
    286 
    287  void CloseWebTransportConn() override;
    288 
    289  void FinishTunnelSetup(nsAHttpTransaction* aTransaction) override;
    290 
    291  Http3Stats GetStats();
    292 
    293  // For connect-udp
    294  already_AddRefed<HttpConnectionUDP> CreateTunnelStream(
    295      nsAHttpTransaction* aHttpTransaction, nsIInterfaceRequestor* aCallbacks);
    296  // For HTTP CONNECT
    297  already_AddRefed<nsHttpConnection> CreateTunnelStream(
    298      nsAHttpTransaction* aHttpTransaction, nsIInterfaceRequestor* aCallbacks,
    299      PRIntervalTime aRtt, bool aIsExtendedCONNECT);
    300  void SetIsInTunnel() { mIsInTunnel = true; }
    301 
    302 private:
    303  ~Http3Session();
    304 
    305  void CloseInternal(bool aCallNeqoClose);
    306  void Shutdown();
    307 
    308  bool RealJoinConnection(const nsACString& hostname, int32_t port,
    309                          bool justKidding);
    310 
    311  nsresult ProcessOutput(nsIUDPSocket* socket);
    312  nsresult ProcessInput(nsIUDPSocket* socket);
    313  nsresult ProcessEvents();
    314 
    315  nsresult ProcessTransactionRead(uint64_t stream_id);
    316  nsresult ProcessTransactionRead(Http3StreamBase* stream);
    317  nsresult ProcessSlowConsumers();
    318 
    319  void SetupTimer(uint64_t aTimeout);
    320 
    321  enum ResetType {
    322    RESET,
    323    STOP_SENDING,
    324  };
    325  void ResetOrStopSendingRecvd(uint64_t aStreamId, uint64_t aError,
    326                               ResetType aType);
    327 
    328  void QueueStream(Http3StreamBase* stream);
    329  void RemoveStreamFromQueues(Http3StreamBase*);
    330  void ProcessPending();
    331 
    332  void CallCertVerification(Maybe<nsCString> aEchPublicName);
    333  void SetSecInfo();
    334 
    335 #ifndef ANDROID
    336  void EchOutcomeTelemetry();
    337 #endif
    338 
    339  void StreamReadyToWrite(Http3StreamBase* aStream);
    340  void MaybeResumeSend();
    341 
    342  void CloseConnectionTelemetry(CloseError& aError, bool aClosing);
    343  void Finish0Rtt(bool aRestart);
    344 
    345 #ifndef ANDROID
    346  enum ZeroRttOutcome {
    347    NOT_USED,
    348    USED_SUCCEEDED,
    349    USED_REJECTED,
    350    USED_CONN_ERROR,
    351    USED_CONN_CLOSED_BY_NECKO
    352  };
    353  void ZeroRttTelemetry(ZeroRttOutcome aOutcome);
    354 #endif
    355 
    356  RefPtr<NeqoHttp3Conn> mHttp3Connection;
    357  RefPtr<nsAHttpConnection> mConnection;
    358  // We need an extra map to store the mapping of WebTransportSession and
    359  // WebTransportStreams to handle the case that a stream is already removed
    360  // from mStreamIdHash and we still need the WebTransportSession.
    361  nsTHashMap<nsUint64HashKey, uint64_t> mWebTransportStreamToSessionMap;
    362  nsRefPtrHashtable<nsUint64HashKey, Http3StreamBase> mStreamIdHash;
    363  nsRefPtrHashtable<nsPtrHashKey<nsAHttpTransaction>, Http3StreamBase>
    364      mStreamTransactionHash;
    365 
    366  nsRefPtrDeque<Http3StreamBase> mReadyForWrite;
    367 
    368  nsTArray<RefPtr<Http3StreamBase>> mSlowConsumersReadyForRead;
    369  nsRefPtrDeque<Http3StreamBase> mQueuedStreams;
    370 
    371  enum State {
    372    INITIALIZING,
    373    ZERORTT,
    374    CONNECTED,
    375    CLOSING,
    376    CLOSED
    377  } mState{INITIALIZING};
    378 
    379  bool mAuthenticationStarted{false};
    380  bool mCleanShutdown{false};
    381  bool mGoawayReceived{false};
    382  bool mShouldClose{false};
    383  bool mIsClosedByNeqo{false};
    384  bool mHttp3ConnectionReported = false;
    385  // mError is neqo error (a protocol error) and that may mean that we will
    386  // send some packets after that.
    387  nsresult mError{NS_OK};
    388  // This is a socket error, there is no poioint in sending anything on that
    389  // socket.
    390  nsresult mSocketError{NS_OK};
    391  bool mBeforeConnectedError{false};
    392  uint64_t mCurrentBrowserId;
    393 
    394  // True if this http3 session uses NSPR for UDP IO.
    395  bool mUseNSPRForIO{true};
    396 
    397  RefPtr<HttpConnectionUDP> mUdpConn;
    398 
    399  nsCOMPtr<nsITimer> mTimer;
    400  RefPtr<OnQuicTimeout> mTimerCallback;
    401 
    402  nsTHashMap<nsCStringHashKey, bool> mJoinConnectionCache;
    403 
    404  RefPtr<QuicSocketControl> mSocketControl;
    405 
    406  uint64_t mTransactionCount = 0;
    407 
    408  // The stream(s) that we are getting 0RTT data from.
    409  nsTArray<WeakPtr<Http3StreamBase>> m0RTTStreams;
    410  // The stream(s) that are not able to send 0RTT data. We need to
    411  // remember them put them into mReadyForWrite queue when 0RTT finishes.
    412  nsTArray<WeakPtr<Http3StreamBase>> mCannotDo0RTTStreams;
    413 
    414  // The following variables are needed for telemetry.
    415  TimeStamp mConnectionIdleStart;
    416  TimeStamp mConnectionIdleEnd;
    417  Maybe<uint64_t> mFirstStreamIdReuseIdleConnection;
    418  TimeStamp mTimerShouldTrigger;
    419  TimeStamp mZeroRttStarted;
    420  TimeStamp mLastTRRResponseTime;  // Time of the last successful TRR response
    421  uint64_t mBlockedByStreamLimitCount = 0;
    422  uint64_t mTransactionsBlockedByStreamLimitCount = 0;
    423  uint64_t mTransactionsSenderBlockedByFlowControlCount = 0;
    424 
    425  // NS_NET_STATUS_CONNECTED_TO event will be created by the Http3Session.
    426  // We want to  propagate it to the first transaction.
    427  RefPtr<nsHttpTransaction> mFirstHttpTransaction;
    428 
    429  RefPtr<nsHttpConnectionInfo> mConnInfo;
    430 
    431  int64_t mTotalBytesRead = 0;     // total data read
    432  int64_t mTotalBytesWritten = 0;  // total data read
    433  PRIntervalTime mLastWriteTime = 0;
    434  nsCString mServer;
    435 
    436  // Records whether we sent an ECH Extension and whether it was a GREASE Xtn
    437  EchExtensionStatus mEchExtensionStatus = EchExtensionStatus::kNotPresent;
    438 
    439  // Records whether the handshake finished successfully and we established a
    440  // a connection.
    441  bool mHandshakeSucceeded = false;
    442 
    443  nsCOMPtr<nsINetAddr> mNetAddr;
    444 
    445  enum class ExtendedConnectKind : uint8_t {
    446    WebTransport = 0,
    447    ConnectUDP,
    448    // add more extended CONNECT protocols here
    449  };
    450 
    451  enum ExtendedConnectNegotiation { DISABLED, NEGOTIATING, FAILED, SUCCEEDED };
    452 
    453  struct ExtendedConnectState {
    454    ExtendedConnectNegotiation mStatus = DISABLED;
    455    nsTArray<WeakPtr<Http3StreamBase>> mWaiters;
    456  };
    457 
    458  Array<ExtendedConnectState, 2> mExtConnect{ExtendedConnectState{},
    459                                             ExtendedConnectState{}};
    460 
    461  // convenience accessors
    462  ExtendedConnectState& ExtState(ExtendedConnectKind aKind) {
    463    return mExtConnect[static_cast<size_t>(aKind)];
    464  }
    465 
    466  bool DeferIfNegotiating(ExtendedConnectKind aKind, Http3StreamBase* aStream);
    467  // 1795854 implement the case when WebTransport is not supported.
    468  // Also, implement the case when the  HTTP/3 session fails before settings
    469  // are exchanged.
    470  void FinishNegotiation(ExtendedConnectKind aKind, bool aSuccess);
    471 
    472  inline bool HasNoActiveStreams() const {
    473    return mStreamTransactionHash.Count() == 0 &&
    474           mWebTransportSessions.IsEmpty() && mWebTransportStreams.IsEmpty() &&
    475           mTunnelStreams.IsEmpty();
    476  }
    477 
    478  nsTArray<RefPtr<Http3StreamBase>> mWebTransportSessions;
    479  nsTArray<RefPtr<Http3StreamBase>> mWebTransportStreams;
    480  nsTArray<RefPtr<Http3StreamBase>> mTunnelStreams;
    481 
    482  bool mHasWebTransportSession = false;
    483  // When true, we don't add this connection info into the Http/3 excluded list.
    484  bool mDontExclude = false;
    485  // The lifetime of the UDP socket is managed by the HttpConnectionUDP. This
    486  // is only used in Http3Session::ProcessOutput. Using raw pointer here to
    487  // improve performance.
    488  nsIUDPSocket* mSocket;
    489  uint32_t mTrrStreams = 0;
    490  bool mIsInTunnel = false;
    491 };
    492 
    493 }  // namespace mozilla::net
    494 
    495 #endif  // Http3Session_H__