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__