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 #include "HttpLog.h"
8 #include "Http3Session.h"
9 #include "Http3Stream.h"
10 #include "mozilla/net/DNS.h"
11 #include "nsHttpHandler.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/Telemetry.h"
14 #include "ASpdySession.h"  // because of SoftStreamError()
15 #include "nsIOService.h"
16 #include "nsISSLSocketControl.h"
17 #include "ScopedNSSTypes.h"
18 #include "nsNetAddr.h"
19 #include "nsQueryObject.h"
20 #include "nsSocketTransportService2.h"
21 #include "nsThreadUtils.h"
22 #include "QuicSocketControl.h"
23 #include "SSLServerCertVerification.h"
24 #include "SSLTokensCache.h"
25 #include "HttpConnectionUDP.h"
26 #include "sslerr.h"
27 
28 namespace mozilla {
29 namespace net {
30 
31 const uint64_t HTTP3_APP_ERROR_NO_ERROR = 0x100;
32 const uint64_t HTTP3_APP_ERROR_GENERAL_PROTOCOL_ERROR = 0x101;
33 const uint64_t HTTP3_APP_ERROR_INTERNAL_ERROR = 0x102;
34 const uint64_t HTTP3_APP_ERROR_STREAM_CREATION_ERROR = 0x103;
35 const uint64_t HTTP3_APP_ERROR_CLOSED_CRITICAL_STREAM = 0x104;
36 const uint64_t HTTP3_APP_ERROR_FRAME_UNEXPECTED = 0x105;
37 const uint64_t HTTP3_APP_ERROR_FRAME_ERROR = 0x106;
38 const uint64_t HTTP3_APP_ERROR_EXCESSIVE_LOAD = 0x107;
39 const uint64_t HTTP3_APP_ERROR_ID_ERROR = 0x108;
40 const uint64_t HTTP3_APP_ERROR_SETTINGS_ERROR = 0x109;
41 const uint64_t HTTP3_APP_ERROR_MISSING_SETTINGS = 0x10a;
42 const uint64_t HTTP3_APP_ERROR_REQUEST_REJECTED = 0x10b;
43 const uint64_t HTTP3_APP_ERROR_REQUEST_CANCELLED = 0x10c;
44 const uint64_t HTTP3_APP_ERROR_REQUEST_INCOMPLETE = 0x10d;
45 const uint64_t HTTP3_APP_ERROR_EARLY_RESPONSE = 0x10e;
46 const uint64_t HTTP3_APP_ERROR_CONNECT_ERROR = 0x10f;
47 const uint64_t HTTP3_APP_ERROR_VERSION_FALLBACK = 0x110;
48 
49 const uint32_t UDP_MAX_PACKET_SIZE = 4096;
50 const uint32_t MAX_PTO_COUNTS = 16;
51 
52 const uint32_t TRANSPORT_ERROR_STATELESS_RESET = 20;
53 
54 NS_IMPL_ADDREF(Http3Session)
NS_IMPL_RELEASE(Http3Session)55 NS_IMPL_RELEASE(Http3Session)
56 NS_INTERFACE_MAP_BEGIN(Http3Session)
57   NS_INTERFACE_MAP_ENTRY(nsAHttpConnection)
58   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
59   NS_INTERFACE_MAP_ENTRY_CONCRETE(Http3Session)
60 NS_INTERFACE_MAP_END
61 
62 Http3Session::Http3Session() {
63   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
64   LOG(("Http3Session::Http3Session [this=%p]", this));
65 
66   mCurrentTopBrowsingContextId =
67       gHttpHandler->ConnMgr()->CurrentTopBrowsingContextId();
68   mThroughCaptivePortal = gHttpHandler->GetThroughCaptivePortal();
69 }
70 
AddrToString(nsINetAddr * netAddr,nsACString & addrStr)71 static void AddrToString(nsINetAddr* netAddr, nsACString& addrStr) {
72   nsAutoCString address;
73   netAddr->GetAddress(address);
74   uint16_t family = nsINetAddr::FAMILY_INET;
75   netAddr->GetFamily(&family);
76   uint16_t port = 0;
77   netAddr->GetPort(&port);
78 
79   if (family == nsINetAddr::FAMILY_INET6) {
80     // Append '[' and ']'
81     addrStr.Append("[");
82     addrStr.Append(address);
83     addrStr.Append("]:");
84     addrStr.AppendInt(port);
85   } else {
86     addrStr.Append(address);
87     addrStr.Append(":");
88     addrStr.AppendInt(port);
89   }
90 }
91 
AddrToString(NetAddr & netAddr,nsACString & addrStr)92 static void AddrToString(NetAddr& netAddr, nsACString& addrStr) {
93   char buf[kIPv6CStrBufSize];
94   netAddr.ToStringBuffer(buf, kIPv6CStrBufSize);
95 
96   if (netAddr.raw.family == AF_INET6) {
97     // Append '[' and ']'
98     addrStr.Append("[");
99     addrStr.Append(buf, strlen(buf));
100     addrStr.Append("]:");
101     addrStr.AppendInt(ntohs(netAddr.inet6.port));
102   } else {
103     addrStr.Append(buf, strlen(buf));
104     addrStr.Append(":");
105     addrStr.AppendInt(ntohs(netAddr.inet.port));
106   }
107 }
108 
StringAndPortToNetAddr(nsACString & remoteAddrStr,uint16_t remotePort,NetAddr * netAddr)109 static nsresult StringAndPortToNetAddr(nsACString& remoteAddrStr,
110                                        uint16_t remotePort, NetAddr* netAddr) {
111   if (NS_FAILED(netAddr->InitFromString(remoteAddrStr, remotePort))) {
112     return NS_ERROR_FAILURE;
113   }
114 
115   return NS_OK;
116 }
117 
Init(const nsHttpConnectionInfo * aConnInfo,nsINetAddr * selfAddr,nsINetAddr * peerAddr,HttpConnectionUDP * udpConn,uint32_t controlFlags,nsIInterfaceRequestor * callbacks)118 nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo,
119                             nsINetAddr* selfAddr, nsINetAddr* peerAddr,
120                             HttpConnectionUDP* udpConn, uint32_t controlFlags,
121                             nsIInterfaceRequestor* callbacks) {
122   LOG3(("Http3Session::Init %p", this));
123 
124   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
125   MOZ_ASSERT(udpConn);
126 
127   mConnInfo = aConnInfo->Clone();
128   mNetAddr = peerAddr;
129 
130   bool httpsProxy =
131       aConnInfo->ProxyInfo() ? aConnInfo->ProxyInfo()->IsHTTPS() : false;
132 
133   // Create security control and info object for quic.
134   mSocketControl = new QuicSocketControl(controlFlags);
135   mSocketControl->SetHostName(httpsProxy ? aConnInfo->ProxyInfo()->Host().get()
136                                          : aConnInfo->GetOrigin().get());
137   mSocketControl->SetPort(httpsProxy ? aConnInfo->ProxyInfo()->Port()
138                                      : aConnInfo->OriginPort());
139 
140   // don't call into PSM while holding mLock!!
141   mSocketControl->SetNotificationCallbacks(callbacks);
142 
143   nsAutoCString selfAddrStr;
144   AddrToString(selfAddr, selfAddrStr);
145   nsAutoCString peerAddrStr;
146   AddrToString(peerAddr, peerAddrStr);
147 
148   LOG3(
149       ("Http3Session::Init origin=%s, alpn=%s, selfAddr=%s, peerAddr=%s,"
150        " qpack table size=%u, max blocked streams=%u [this=%p]",
151        PromiseFlatCString(mConnInfo->GetOrigin()).get(),
152        PromiseFlatCString(mConnInfo->GetNPNToken()).get(), selfAddrStr.get(),
153        peerAddrStr.get(), gHttpHandler->DefaultQpackTableSize(),
154        gHttpHandler->DefaultHttp3MaxBlockedStreams(), this));
155 
156   nsresult rv = NeqoHttp3Conn::Init(
157       mConnInfo->GetOrigin(), mConnInfo->GetNPNToken(), selfAddrStr,
158       peerAddrStr, gHttpHandler->DefaultQpackTableSize(),
159       gHttpHandler->DefaultHttp3MaxBlockedStreams(),
160       gHttpHandler->Http3QlogDir(), getter_AddRefs(mHttp3Connection));
161   if (NS_FAILED(rv)) {
162     return rv;
163   }
164 
165   nsAutoCString peerId;
166   mSocketControl->GetPeerId(peerId);
167   nsTArray<uint8_t> token;
168   if (StaticPrefs::network_http_http3_enable_0rtt() &&
169       NS_SUCCEEDED(SSLTokensCache::Get(peerId, token))) {
170     LOG(("Found a resumption token in the cache."));
171     mHttp3Connection->SetResumptionToken(token);
172     if (mHttp3Connection->IsZeroRtt()) {
173       LOG(("Can send ZeroRtt data"));
174       RefPtr<Http3Session> self(this);
175       mState = ZERORTT;
176       mZeroRttStarted = TimeStamp::Now();
177       // Let the nsHttpConnectionMgr know that the connection can accept
178       // transactions.
179       // We need to dispatch the following function to this thread so that
180       // it is executed after the current function. At this point a
181       // Http3Session is still being initialized and ReportHttp3Connection
182       // will try to dispatch transaction on this session therefore it
183       // needs to be executed after the initializationg is done.
184       DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(
185           NS_NewRunnableFunction("Http3Session::ReportHttp3Connection",
186                                  [self]() { self->ReportHttp3Connection(); }));
187       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
188                            "NS_DispatchToCurrentThread failed");
189     }
190   }
191 
192   if (mState != ZERORTT) {
193     ZeroRttTelemetry(ZeroRttOutcome::NOT_USED);
194   }
195 
196   // After this line, Http3Session and HttpConnectionUDP become a cycle. We put
197   // this line in the end of Http3Session::Init to make sure Http3Session can be
198   // released when Http3Session::Init early returned.
199   mUdpConn = udpConn;
200   return NS_OK;
201 }
202 
203 // Shutdown the http3session and close all transactions.
Shutdown()204 void Http3Session::Shutdown() {
205   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
206 
207   if ((mBeforeConnectedError ||
208        (mError == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR)) &&
209       (mError !=
210        mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN))) {
211     gHttpHandler->ExcludeHttp3(mConnInfo);
212   }
213 
214   for (const auto& stream : mStreamTransactionHash.Values()) {
215     if (mBeforeConnectedError) {
216       // We have an error before we were connected, just restart transactions.
217       // The transaction restart code path will remove AltSvc mapping and the
218       // direct path will be used.
219       MOZ_ASSERT(NS_FAILED(mError));
220       stream->Close(NS_ERROR_NET_RESET);
221     } else if (!stream->HasStreamId()) {
222       if (NS_SUCCEEDED(mError)) {
223         // Connection has not been started yet. We can restart it.
224         stream->Transaction()->DoNotRemoveAltSvc();
225       }
226       stream->Close(NS_ERROR_NET_RESET);
227     } else if (stream->RecvdData()) {
228       stream->Close(NS_ERROR_NET_PARTIAL_TRANSFER);
229     } else if (mError == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR) {
230       stream->Close(NS_ERROR_NET_HTTP3_PROTOCOL_ERROR);
231     } else {
232       stream->Close(NS_ERROR_ABORT);
233     }
234     RemoveStreamFromQueues(stream);
235     if (stream->HasStreamId()) {
236       mStreamIdHash.Remove(stream->StreamId());
237     }
238   }
239 
240   mStreamTransactionHash.Clear();
241 }
242 
~Http3Session()243 Http3Session::~Http3Session() {
244   LOG3(("Http3Session::~Http3Session %p", this));
245 
246   Telemetry::Accumulate(Telemetry::HTTP3_REQUEST_PER_CONN, mTransactionCount);
247   Telemetry::Accumulate(Telemetry::HTTP3_BLOCKED_BY_STREAM_LIMIT_PER_CONN,
248                         mBlockedByStreamLimitCount);
249   Telemetry::Accumulate(Telemetry::HTTP3_TRANS_BLOCKED_BY_STREAM_LIMIT_PER_CONN,
250                         mTransactionsBlockedByStreamLimitCount);
251 
252   Telemetry::Accumulate(
253       Telemetry::HTTP3_TRANS_SENDING_BLOCKED_BY_FLOW_CONTROL_PER_CONN,
254       mTransactionsSenderBlockedByFlowControlCount);
255 
256   if (mThroughCaptivePortal) {
257     if (mTotalBytesRead || mTotalBytesWritten) {
258       auto total =
259           Clamp<uint32_t>((mTotalBytesRead >> 10) + (mTotalBytesWritten >> 10),
260                           0, std::numeric_limits<uint32_t>::max());
261       Telemetry::ScalarAdd(
262           Telemetry::ScalarID::NETWORKING_DATA_TRANSFERRED_CAPTIVE_PORTAL,
263           total);
264     }
265 
266     Telemetry::ScalarAdd(
267         Telemetry::ScalarID::NETWORKING_HTTP_CONNECTIONS_CAPTIVE_PORTAL, 1);
268   }
269 
270   Shutdown();
271 }
272 
273 // This function may return a socket error.
274 // It will not return an error if socket error is
275 // NS_BASE_STREAM_WOULD_BLOCK.
276 // A caller of this function will close the Http3 connection
277 // in case of a error.
278 // The only callers is:
279 //   HttpConnectionUDP::RecvData ->
280 //   Http3Session::RecvData
ProcessInput(nsIUDPSocket * socket)281 void Http3Session::ProcessInput(nsIUDPSocket* socket) {
282   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
283   MOZ_ASSERT(mUdpConn);
284 
285   LOG(("Http3Session::ProcessInput writer=%p [this=%p state=%d]",
286        mUdpConn.get(), this, mState));
287 
288   while (true) {
289     nsTArray<uint8_t> data;
290     NetAddr addr{};
291     // RecvWithAddr actually does not return an error.
292     nsresult rv = socket->RecvWithAddr(&addr, data);
293     MOZ_ALWAYS_SUCCEEDS(rv);
294     if (NS_FAILED(rv) || data.IsEmpty()) {
295       break;
296     }
297     nsAutoCString remoteAddrStr;
298     AddrToString(addr, remoteAddrStr);
299     rv = mHttp3Connection->ProcessInput(&remoteAddrStr, data);
300     MOZ_ALWAYS_SUCCEEDS(rv);
301     if (NS_FAILED(rv)) {
302       break;
303     }
304 
305     LOG(("Http3Session::ProcessInput received=%zu", data.Length()));
306     mTotalBytesRead += data.Length();
307   }
308 }
309 
ProcessTransactionRead(uint64_t stream_id,uint32_t * countWritten)310 nsresult Http3Session::ProcessTransactionRead(uint64_t stream_id,
311                                               uint32_t* countWritten) {
312   RefPtr<Http3Stream> stream = mStreamIdHash.Get(stream_id);
313   if (!stream) {
314     LOG(
315         ("Http3Session::ProcessTransactionRead - stream not found "
316          "stream_id=0x%" PRIx64 " [this=%p].",
317          stream_id, this));
318     return NS_OK;
319   }
320 
321   return ProcessTransactionRead(stream, countWritten);
322 }
323 
ProcessTransactionRead(Http3Stream * stream,uint32_t * countWritten)324 nsresult Http3Session::ProcessTransactionRead(Http3Stream* stream,
325                                               uint32_t* countWritten) {
326   nsresult rv = stream->WriteSegments(stream, nsIOService::gDefaultSegmentSize,
327                                       countWritten);
328 
329   if (ASpdySession::SoftStreamError(rv) || stream->Done()) {
330     LOG3(
331         ("Http3Session::ProcessSingleTransactionRead session=%p stream=%p "
332          "0x%" PRIx64 " cleanup stream rv=0x%" PRIx32 " done=%d.\n",
333          this, stream, stream->StreamId(), static_cast<uint32_t>(rv),
334          stream->Done()));
335     CloseStream(stream,
336                 (rv == NS_BINDING_RETARGETED) ? NS_BINDING_RETARGETED : NS_OK);
337     return NS_OK;
338   }
339 
340   if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
341     return rv;
342   }
343   return NS_OK;
344 }
345 
ProcessEvents()346 nsresult Http3Session::ProcessEvents() {
347   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
348 
349   LOG(("Http3Session::ProcessEvents [this=%p]", this));
350 
351   // We need an array to pick up header data or a resumption token.
352   nsTArray<uint8_t> data;
353   Http3Event event{};
354   event.tag = Http3Event::Tag::NoEvent;
355 
356   nsresult rv = mHttp3Connection->GetEvent(&event, data);
357   if (NS_FAILED(rv)) {
358     LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
359          static_cast<uint32_t>(rv)));
360     return rv;
361   }
362 
363   while (event.tag != Http3Event::Tag::NoEvent) {
364     switch (event.tag) {
365       case Http3Event::Tag::HeaderReady: {
366         MOZ_ASSERT(mState == CONNECTED);
367         LOG(("Http3Session::ProcessEvents - HeaderReady"));
368         uint64_t id = event.header_ready.stream_id;
369 
370         RefPtr<Http3Stream> stream = mStreamIdHash.Get(id);
371         if (!stream) {
372           LOG(
373               ("Http3Session::ProcessEvents - HeaderReady - stream not found "
374                "stream_id=0x%" PRIx64 " [this=%p].",
375                id, this));
376           break;
377         }
378 
379         stream->SetResponseHeaders(data, event.header_ready.fin);
380 
381         uint32_t read = 0;
382         rv = ProcessTransactionRead(stream, &read);
383 
384         if (NS_FAILED(rv)) {
385           LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
386                static_cast<uint32_t>(rv)));
387           return rv;
388         }
389         break;
390       }
391       case Http3Event::Tag::DataReadable: {
392         MOZ_ASSERT(mState == CONNECTED);
393         LOG(("Http3Session::ProcessEvents - DataReadable"));
394         uint64_t id = event.data_readable.stream_id;
395 
396         uint32_t read = 0;
397         nsresult rv = ProcessTransactionRead(id, &read);
398 
399         if (NS_FAILED(rv)) {
400           LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
401                static_cast<uint32_t>(rv)));
402           return rv;
403         }
404         break;
405       }
406       case Http3Event::Tag::DataWritable: {
407         MOZ_ASSERT(CanSandData());
408         LOG(("Http3Session::ProcessEvents - DataWritable"));
409 
410         RefPtr<Http3Stream> stream =
411             mStreamIdHash.Get(event.data_writable.stream_id);
412         if (stream) {
413           StreamReadyToWrite(stream);
414         }
415       } break;
416       case Http3Event::Tag::Reset:
417         LOG(("Http3Session::ProcessEvents - Reset"));
418         ResetRecvd(event.reset.stream_id, event.reset.error);
419         break;
420       case Http3Event::Tag::StopSending:
421         LOG(("Http3Session::ProcessEvents - StopSeniding with error 0x%" PRIx64,
422              event.stop_sending.error));
423         if (event.stop_sending.error == HTTP3_APP_ERROR_NO_ERROR) {
424           RefPtr<Http3Stream> stream =
425               mStreamIdHash.Get(event.data_writable.stream_id);
426           if (stream) {
427             stream->StopSending();
428           }
429         } else {
430           ResetRecvd(event.reset.stream_id, event.reset.error);
431         }
432         break;
433       case Http3Event::Tag::PushPromise:
434         LOG(("Http3Session::ProcessEvents - PushPromise"));
435         break;
436       case Http3Event::Tag::PushHeaderReady:
437         LOG(("Http3Session::ProcessEvents - PushHeaderReady"));
438         break;
439       case Http3Event::Tag::PushDataReadable:
440         LOG(("Http3Session::ProcessEvents - PushDataReadable"));
441         break;
442       case Http3Event::Tag::PushCanceled:
443         LOG(("Http3Session::ProcessEvents - PushCanceled"));
444         break;
445       case Http3Event::Tag::RequestsCreatable:
446         LOG(("Http3Session::ProcessEvents - StreamCreatable"));
447         ProcessPending();
448         break;
449       case Http3Event::Tag::AuthenticationNeeded:
450         LOG(("Http3Session::ProcessEvents - AuthenticationNeeded %d",
451              mAuthenticationStarted));
452         if (!mAuthenticationStarted) {
453           mAuthenticationStarted = true;
454           LOG(("Http3Session::ProcessEvents - AuthenticationNeeded called"));
455           CallCertVerification();
456         }
457         break;
458       case Http3Event::Tag::ZeroRttRejected:
459         LOG(("Http3Session::ProcessEvents - ZeroRttRejected"));
460         if (mState == ZERORTT) {
461           mState = INITIALIZING;
462           mTransactionCount = 0;
463           Finish0Rtt(true);
464           ZeroRttTelemetry(ZeroRttOutcome::USED_REJECTED);
465         }
466         break;
467       case Http3Event::Tag::ResumptionToken: {
468         LOG(("Http3Session::ProcessEvents - ResumptionToken"));
469         if (StaticPrefs::network_http_http3_enable_0rtt() && !data.IsEmpty()) {
470           LOG(("Got a resumption token"));
471           nsAutoCString peerId;
472           mSocketControl->GetPeerId(peerId);
473           if (NS_FAILED(SSLTokensCache::Put(
474                   peerId, data.Elements(), data.Length(), mSocketControl,
475                   PR_Now() + event.resumption_token.expire_in))) {
476             LOG(("Adding resumption token failed"));
477           }
478         }
479       } break;
480       case Http3Event::Tag::ConnectionConnected: {
481         LOG(("Http3Session::ProcessEvents - ConnectionConnected"));
482         bool was0RTT = mState == ZERORTT;
483         mState = CONNECTED;
484         SetSecInfo();
485         mSocketControl->HandshakeCompleted();
486         if (was0RTT) {
487           Finish0Rtt(false);
488           ZeroRttTelemetry(ZeroRttOutcome::USED_SUCCEEDED);
489         }
490 
491         OnTransportStatus(mSocketTransport, NS_NET_STATUS_CONNECTED_TO, 0);
492         // Also send the NS_NET_STATUS_TLS_HANDSHAKE_ENDED event.
493         OnTransportStatus(mSocketTransport, NS_NET_STATUS_TLS_HANDSHAKE_ENDED,
494                           0);
495 
496         ReportHttp3Connection();
497         // Maybe call ResumeSend:
498         // In case ZeroRtt has been used and it has been rejected, 2 events will
499         // be received: ZeroRttRejected and ConnectionConnected. ZeroRttRejected
500         // that will put all transaction into mReadyForWrite queue and it will
501         // call MaybeResumeSend, but that will not have an effect because the
502         // connection is ont in CONNECTED state. When ConnectionConnected event
503         // is received call MaybeResumeSend to trigger reads for the
504         // zero-rtt-rejected transactions.
505         MaybeResumeSend();
506       } break;
507       case Http3Event::Tag::GoawayReceived:
508         LOG(("Http3Session::ProcessEvents - GoawayReceived"));
509         mGoawayReceived = true;
510         break;
511       case Http3Event::Tag::ConnectionClosing:
512         LOG(("Http3Session::ProcessEvents - ConnectionClosing"));
513         if (NS_SUCCEEDED(mError) && !IsClosing()) {
514           mError = NS_ERROR_NET_HTTP3_PROTOCOL_ERROR;
515           CloseConnectionTelemetry(event.connection_closing.error, true);
516 
517           auto isStatelessResetOrNoError = [](CloseError& aError) -> bool {
518             if (aError.tag == CloseError::Tag::TransportInternalErrorOther &&
519                 aError.transport_internal_error_other._0 ==
520                     TRANSPORT_ERROR_STATELESS_RESET) {
521               return true;
522             }
523             if (aError.tag == CloseError::Tag::TransportError &&
524                 aError.transport_error._0 == 0) {
525               return true;
526             }
527             if (aError.tag == CloseError::Tag::PeerError &&
528                 aError.peer_error._0 == 0) {
529               return true;
530             }
531             if (aError.tag == CloseError::Tag::AppError &&
532                 aError.app_error._0 == HTTP3_APP_ERROR_NO_ERROR) {
533               return true;
534             }
535             if (aError.tag == CloseError::Tag::PeerAppError &&
536                 aError.peer_app_error._0 == HTTP3_APP_ERROR_NO_ERROR) {
537               return true;
538             }
539             return false;
540           };
541           if (isStatelessResetOrNoError(event.connection_closing.error)) {
542             mError = NS_ERROR_NET_RESET;
543           }
544         }
545         return mError;
546         break;
547       case Http3Event::Tag::ConnectionClosed:
548         LOG(("Http3Session::ProcessEvents - ConnectionClosed"));
549         if (NS_SUCCEEDED(mError)) {
550           mError = NS_ERROR_NET_TIMEOUT;
551           CloseConnectionTelemetry(event.connection_closed.error, false);
552         }
553         mIsClosedByNeqo = true;
554         LOG(("Http3Session::ProcessEvents - ConnectionClosed error=%" PRIx32,
555              static_cast<uint32_t>(mError)));
556         // We need to return here and let HttpConnectionUDP close the session.
557         return mError;
558         break;
559       default:
560         break;
561     }
562     // Delete previous content of data
563     data.TruncateLength(0);
564     rv = mHttp3Connection->GetEvent(&event, data);
565     if (NS_FAILED(rv)) {
566       LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
567            static_cast<uint32_t>(rv)));
568       return rv;
569     }
570   }
571 
572   return NS_OK;
573 }  // namespace net
574 
575 // This function may return a socket error.
576 // It will not return an error if socket error is
577 // NS_BASE_STREAM_WOULD_BLOCK.
578 // A Caller of this function will close the Http3 connection
579 // if this function returns an error.
580 // Callers are:
581 //   1) HttpConnectionUDP::OnQuicTimeoutExpired
582 //   2) HttpConnectionUDP::SendData ->
583 //      Http3Session::SendData
ProcessOutput(nsIUDPSocket * socket)584 nsresult Http3Session::ProcessOutput(nsIUDPSocket* socket) {
585   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
586   MOZ_ASSERT(mUdpConn);
587 
588   LOG(("Http3Session::ProcessOutput reader=%p, [this=%p]", mUdpConn.get(),
589        this));
590 
591   // Check if we have a packet that could not have been sent in a previous
592   // iteration or maybe get new packets to send.
593   while (true) {
594     nsTArray<uint8_t> packetToSend;
595     nsAutoCString remoteAddrStr;
596     uint16_t port = 0;
597     uint64_t timeout = 0;
598     if (!mHttp3Connection->ProcessOutput(&remoteAddrStr, &port, packetToSend,
599                                          &timeout)) {
600       SetupTimer(timeout);
601       break;
602     }
603     MOZ_ASSERT(packetToSend.Length());
604     LOG(
605         ("Http3Session::ProcessOutput sending packet with %u bytes to %s "
606          "port=%d [this=%p].",
607          (uint32_t)packetToSend.Length(),
608          PromiseFlatCString(remoteAddrStr).get(), port, this));
609 
610     uint32_t written = 0;
611     NetAddr addr;
612     if (NS_FAILED(StringAndPortToNetAddr(remoteAddrStr, port, &addr))) {
613       continue;
614     }
615     nsresult rv = socket->SendWithAddress(&addr, packetToSend, &written);
616     LOG(("Http3Session::ProcessOutput sending packet rv=%d",
617          static_cast<int32_t>(rv)));
618     if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) {
619       mSocketError = rv;
620       // If there was an error that is not NS_BASE_STREAM_WOULD_BLOCK
621       // return from here. We do not need to set a timer, because we
622       // will close the connection.
623       return rv;
624     }
625     mTotalBytesWritten += packetToSend.Length();
626     mLastWriteTime = PR_IntervalNow();
627   }
628 
629   return NS_OK;
630 }
631 
632 // This is only called when timer expires.
633 // It is called by HttpConnectionUDP::OnQuicTimeout.
634 // If tihs function returns an error OnQuicTimeout will handle the error
635 // properly and close the connection.
ProcessOutputAndEvents(nsIUDPSocket * socket)636 nsresult Http3Session::ProcessOutputAndEvents(nsIUDPSocket* socket) {
637   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
638   // ProcessOutput could fire another timer. Need to unset the flag before that.
639   mTimerActive = false;
640 
641   MOZ_ASSERT(mTimerShouldTrigger);
642 
643   Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_TIMER_DELAYED,
644                                  mTimerShouldTrigger, TimeStamp::Now());
645 
646   mTimerShouldTrigger = TimeStamp();
647 
648   nsresult rv = ProcessOutput(socket);
649   if (NS_FAILED(rv)) {
650     return rv;
651   }
652   return ProcessEvents();
653 }
654 
SetupTimer(uint64_t aTimeout)655 void Http3Session::SetupTimer(uint64_t aTimeout) {
656   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
657   // UINT64_MAX indicated a no-op from neqo, which only happens when a
658   // connection is in or going to be Closed state.
659   if (aTimeout == UINT64_MAX) {
660     return;
661   }
662 
663   LOG(("Http3Session::SetupTimer to %" PRIu64 "ms [this=%p].", aTimeout, this));
664 
665   // Remember the time when the timer should trigger.
666   mTimerShouldTrigger =
667       TimeStamp::Now() + TimeDuration::FromMilliseconds(aTimeout);
668 
669   if (mTimerActive && mTimer) {
670     LOG(
671         ("  -- Previous timer has not fired. Update the delay instead of "
672          "re-initializing the timer"));
673     mTimer->SetDelay(aTimeout);
674     return;
675   }
676 
677   if (!mTimer) {
678     mTimer = NS_NewTimer();
679   }
680 
681   mTimerActive = true;
682 
683   if (!mTimer ||
684       NS_FAILED(mTimer->InitWithNamedFuncCallback(
685           &HttpConnectionUDP::OnQuicTimeout, mUdpConn, aTimeout,
686           nsITimer::TYPE_ONE_SHOT, "net::HttpConnectionUDP::OnQuicTimeout"))) {
687     NS_DispatchToCurrentThread(
688         NewRunnableMethod("net::HttpConnectionUDP::OnQuicTimeoutExpired",
689                           mUdpConn, &HttpConnectionUDP::OnQuicTimeoutExpired));
690   }
691 }
692 
AddStream(nsAHttpTransaction * aHttpTransaction,int32_t aPriority,nsIInterfaceRequestor * aCallbacks)693 bool Http3Session::AddStream(nsAHttpTransaction* aHttpTransaction,
694                              int32_t aPriority,
695                              nsIInterfaceRequestor* aCallbacks) {
696   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
697 
698   nsHttpTransaction* trans = aHttpTransaction->QueryHttpTransaction();
699 
700   if (!mConnection) {
701     // Get the connection from the first transaction.
702     mConnection = aHttpTransaction->Connection();
703   }
704 
705   if (IsClosing()) {
706     LOG3(
707         ("Http3Session::AddStream %p atrans=%p trans=%p session unusable - "
708          "resched.\n",
709          this, aHttpTransaction, trans));
710     aHttpTransaction->SetConnection(nullptr);
711     nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
712     if (NS_FAILED(rv)) {
713       LOG3(
714           ("Http3Session::AddStream %p atrans=%p trans=%p failed to initiate "
715            "transaction (0x%" PRIx32 ").\n",
716            this, aHttpTransaction, trans, static_cast<uint32_t>(rv)));
717     }
718     return true;
719   }
720 
721   aHttpTransaction->SetConnection(this);
722   aHttpTransaction->OnActivated();
723   // reset the read timers to wash away any idle time
724   mLastWriteTime = PR_IntervalNow();
725 
726   LOG3(("Http3Session::AddStream %p atrans=%p.\n", this, aHttpTransaction));
727   Http3Stream* stream = new Http3Stream(aHttpTransaction, this);
728   mStreamTransactionHash.InsertOrUpdate(aHttpTransaction, RefPtr{stream});
729 
730   if (mState == ZERORTT) {
731     if (!stream->Do0RTT()) {
732       LOG(("Http3Session %p will not get early data from Http3Stream %p", this,
733            stream));
734       if (!mCannotDo0RTTStreams.Contains(stream)) {
735         mCannotDo0RTTStreams.AppendElement(stream);
736       }
737       return true;
738     }
739     m0RTTStreams.AppendElement(stream);
740   }
741 
742   if (!mFirstHttpTransaction && !IsConnected()) {
743     mFirstHttpTransaction = aHttpTransaction->QueryHttpTransaction();
744     LOG3(("Http3Session::AddStream first session=%p trans=%p ", this,
745           mFirstHttpTransaction.get()));
746   }
747 
748   StreamReadyToWrite(stream);
749 
750   return true;
751 }
752 
CanReuse()753 bool Http3Session::CanReuse() {
754   return CanSandData() && !(mGoawayReceived || mShouldClose);
755 }
756 
QueueStream(Http3Stream * stream)757 void Http3Session::QueueStream(Http3Stream* stream) {
758   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
759   MOZ_ASSERT(!stream->Queued());
760 
761   LOG3(("Http3Session::QueueStream %p stream %p queued.", this, stream));
762 
763   stream->SetQueued(true);
764   mQueuedStreams.Push(stream);
765 }
766 
ProcessPending()767 void Http3Session::ProcessPending() {
768   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
769 
770   Http3Stream* stream;
771   while ((stream = mQueuedStreams.PopFront())) {
772     LOG3(("Http3Session::ProcessPending %p stream %p woken from queue.", this,
773           stream));
774     MOZ_ASSERT(stream->Queued());
775     stream->SetQueued(false);
776     mReadyForWrite.Push(stream);
777   }
778   MaybeResumeSend();
779 }
780 
RemoveStreamFromQueue(Http3Stream * aStream,nsDeque<Http3Stream> & queue)781 static void RemoveStreamFromQueue(Http3Stream* aStream,
782                                   nsDeque<Http3Stream>& queue) {
783   size_t size = queue.GetSize();
784   for (size_t count = 0; count < size; ++count) {
785     Http3Stream* stream = queue.PopFront();
786     if (stream != aStream) {
787       queue.Push(stream);
788     }
789   }
790 }
791 
RemoveStreamFromQueues(Http3Stream * aStream)792 void Http3Session::RemoveStreamFromQueues(Http3Stream* aStream) {
793   RemoveStreamFromQueue(aStream, mReadyForWrite);
794   RemoveStreamFromQueue(aStream, mQueuedStreams);
795   mSlowConsumersReadyForRead.RemoveElement(aStream);
796 }
797 
798 // This is called by Http3Stream::OnReadSegment.
799 // ProcessOutput will be called in Http3Session::ReadSegment that
800 // calls Http3Stream::OnReadSegment.
TryActivating(const nsACString & aMethod,const nsACString & aScheme,const nsACString & aAuthorityHeader,const nsACString & aPath,const nsACString & aHeaders,uint64_t * aStreamId,Http3Stream * aStream)801 nsresult Http3Session::TryActivating(
802     const nsACString& aMethod, const nsACString& aScheme,
803     const nsACString& aAuthorityHeader, const nsACString& aPath,
804     const nsACString& aHeaders, uint64_t* aStreamId, Http3Stream* aStream) {
805   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
806   MOZ_ASSERT(*aStreamId == UINT64_MAX);
807 
808   LOG(("Http3Session::TryActivating [stream=%p, this=%p state=%d]", aStream,
809        this, mState));
810 
811   if (IsClosing()) {
812     if (NS_FAILED(mError)) {
813       return mError;
814     }
815     return NS_ERROR_FAILURE;
816   }
817 
818   if (aStream->Queued()) {
819     LOG3(("Http3Session::TryActivating %p stream=%p already queued.\n", this,
820           aStream));
821     return NS_BASE_STREAM_WOULD_BLOCK;
822   }
823 
824   if (mState == ZERORTT) {
825     if (!aStream->Do0RTT()) {
826       MOZ_ASSERT(!mCannotDo0RTTStreams.Contains(aStream));
827       return NS_BASE_STREAM_WOULD_BLOCK;
828     }
829   }
830 
831   nsresult rv = mHttp3Connection->Fetch(aMethod, aScheme, aAuthorityHeader,
832                                         aPath, aHeaders, aStreamId);
833   if (NS_FAILED(rv)) {
834     LOG(("Http3Session::TryActivating returns error=0x%" PRIx32 "[stream=%p, "
835          "this=%p]",
836          static_cast<uint32_t>(rv), aStream, this));
837     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
838       LOG3(
839           ("Http3Session::TryActivating %p stream=%p no room for more "
840            "concurrent streams\n",
841            this, aStream));
842       mTransactionsBlockedByStreamLimitCount++;
843       if (mQueuedStreams.GetSize() == 0) {
844         mBlockedByStreamLimitCount++;
845       }
846       QueueStream(aStream);
847       return rv;
848     }
849     // Ignore this error. This may happen if some events are not handled yet.
850     // TODO we may try to add an assertion here.
851     return NS_OK;
852   }
853 
854   LOG(("Http3Session::TryActivating streamId=0x%" PRIx64
855        " for stream=%p [this=%p].",
856        *aStreamId, aStream, this));
857 
858   MOZ_ASSERT(*aStreamId != UINT64_MAX);
859 
860   if (mTransactionCount > 0 && mStreamIdHash.IsEmpty()) {
861     MOZ_ASSERT(mConnectionIdleStart);
862     MOZ_ASSERT(mFirstStreamIdReuseIdleConnection.isNothing());
863 
864     mConnectionIdleEnd = TimeStamp::Now();
865     mFirstStreamIdReuseIdleConnection = Some(*aStreamId);
866   }
867   mStreamIdHash.InsertOrUpdate(*aStreamId, RefPtr{aStream});
868   mTransactionCount++;
869 
870   return NS_OK;
871 }
872 
873 // This is only called by Http3Stream::OnReadSegment.
874 // ProcessOutput will be called in Http3Session::ReadSegment that
875 // calls Http3Stream::OnReadSegment.
CloseSendingSide(uint64_t aStreamId)876 void Http3Session::CloseSendingSide(uint64_t aStreamId) {
877   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
878   mHttp3Connection->CloseStream(aStreamId);
879 }
880 
881 // This is only called by Http3Stream::OnReadSegment.
882 // ProcessOutput will be called in Http3Session::ReadSegment that
883 // calls Http3Stream::OnReadSegment.
SendRequestBody(uint64_t aStreamId,const char * buf,uint32_t count,uint32_t * countRead)884 nsresult Http3Session::SendRequestBody(uint64_t aStreamId, const char* buf,
885                                        uint32_t count, uint32_t* countRead) {
886   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
887   nsresult rv = mHttp3Connection->SendRequestBody(
888       aStreamId, (const uint8_t*)buf, count, countRead);
889   if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
890     mTransactionsSenderBlockedByFlowControlCount++;
891   } else if (NS_FAILED(rv)) {
892     // Ignore this error. This may happen if some events are not handled yet.
893     // TODO we may try to add an assertion here.
894     // We will pretend that sender is blocked, that will cause the caller to
895     // stop trying.
896     *countRead = 0;
897     rv = NS_BASE_STREAM_WOULD_BLOCK;
898   }
899 
900   MOZ_ASSERT((*countRead != 0) || NS_FAILED(rv));
901   return rv;
902 }
903 
ResetRecvd(uint64_t aStreamId,uint64_t aError)904 void Http3Session::ResetRecvd(uint64_t aStreamId, uint64_t aError) {
905   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
906   RefPtr<Http3Stream> stream = mStreamIdHash.Get(aStreamId);
907   if (!stream) {
908     return;
909   }
910 
911   stream->SetRecvdReset();
912 
913   // We only handle some of Http3 error as epecial, the rest are just equivalent
914   // to cancel.
915   if (aError == HTTP3_APP_ERROR_VERSION_FALLBACK) {
916     // We will restart the request and the alt-svc will be removed
917     // automatically.
918     // Also disable http3 we want http1.1.
919     stream->Transaction()->DisableHttp3();
920     stream->Transaction()->DisableSpdy();
921     CloseStream(stream, NS_ERROR_NET_RESET);
922   } else if (aError == HTTP3_APP_ERROR_REQUEST_REJECTED) {
923     // This request was rejected because server is probably busy or going away.
924     // We can restart the request using alt-svc. Without calling
925     // DoNotRemoveAltSvc the alt-svc route will be removed.
926     stream->Transaction()->DoNotRemoveAltSvc();
927     CloseStream(stream, NS_ERROR_NET_RESET);
928   } else {
929     if (stream->RecvdData()) {
930       CloseStream(stream, NS_ERROR_NET_PARTIAL_TRANSFER);
931     } else {
932       CloseStream(stream, NS_ERROR_NET_INTERRUPT);
933     }
934   }
935 }
936 
SetConnection(nsAHttpConnection * aConn)937 void Http3Session::SetConnection(nsAHttpConnection* aConn) {
938   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
939   mConnection = aConn;
940 }
941 
GetSecurityCallbacks(nsIInterfaceRequestor ** aOut)942 void Http3Session::GetSecurityCallbacks(nsIInterfaceRequestor** aOut) {
943   *aOut = nullptr;
944 }
945 
946 // TODO
OnTransportStatus(nsITransport * aTransport,nsresult aStatus,int64_t aProgress)947 void Http3Session::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
948                                      int64_t aProgress) {
949   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
950 
951   if ((aStatus == NS_NET_STATUS_CONNECTED_TO) && !IsConnected()) {
952     // We should ignore the event. This is sent by the nsSocketTranpsort
953     // and it does not mean that HTTP3 session is connected.
954     // We will use this event to mark start of TLS handshake
955     aStatus = NS_NET_STATUS_TLS_HANDSHAKE_STARTING;
956   }
957 
958   switch (aStatus) {
959       // These should appear only once, deliver to the first
960       // transaction on the session.
961     case NS_NET_STATUS_RESOLVING_HOST:
962     case NS_NET_STATUS_RESOLVED_HOST:
963     case NS_NET_STATUS_CONNECTING_TO:
964     case NS_NET_STATUS_CONNECTED_TO:
965     case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
966     case NS_NET_STATUS_TLS_HANDSHAKE_ENDED: {
967       if (!mFirstHttpTransaction) {
968         // if we still do not have a HttpTransaction store timings info in
969         // a HttpConnection.
970         // If some error occur it can happen that we do not have a connection.
971         if (mConnection) {
972           RefPtr<HttpConnectionBase> conn = mConnection->HttpConnection();
973           conn->SetEvent(aStatus);
974         }
975       } else {
976         mFirstHttpTransaction->OnTransportStatus(aTransport, aStatus,
977                                                  aProgress);
978       }
979 
980       if (aStatus == NS_NET_STATUS_CONNECTED_TO) {
981         mFirstHttpTransaction = nullptr;
982       }
983       break;
984     }
985 
986     default:
987       // The other transport events are ignored here because there is no good
988       // way to map them to the right transaction in HTTP3. Instead, the events
989       // are generated again from the HTTP3 code and passed directly to the
990       // correct transaction.
991 
992       // NS_NET_STATUS_SENDING_TO:
993       // This is generated by the socket transport when (part) of
994       // a transaction is written out
995       //
996       // There is no good way to map it to the right transaction in HTTP3,
997       // so it is ignored here and generated separately when the request
998       // is sent from Http3Stream.
999 
1000       // NS_NET_STATUS_WAITING_FOR:
1001       // Created by nsHttpConnection when the request has been totally sent.
1002       // There is no good way to map it to the right transaction in HTTP3,
1003       // so it is ignored here and generated separately when the same
1004       // condition is complete in Http3Stream when there is no more
1005       // request body left to be transmitted.
1006 
1007       // NS_NET_STATUS_RECEIVING_FROM
1008       // Generated in Http3Stream whenever the stream reads data.
1009 
1010       break;
1011   }
1012 }
1013 
IsDone()1014 bool Http3Session::IsDone() { return mState == CLOSED; }
1015 
Status()1016 nsresult Http3Session::Status() {
1017   MOZ_ASSERT(false, "Http3Session::Status()");
1018   return NS_ERROR_UNEXPECTED;
1019 }
1020 
Caps()1021 uint32_t Http3Session::Caps() {
1022   MOZ_ASSERT(false, "Http3Session::Caps()");
1023   return 0;
1024 }
1025 
ReadSegments(nsAHttpSegmentReader * reader,uint32_t count,uint32_t * countRead)1026 nsresult Http3Session::ReadSegments(nsAHttpSegmentReader* reader,
1027                                     uint32_t count, uint32_t* countRead) {
1028   MOZ_ASSERT(false, "Http3Session::ReadSegments()");
1029   return NS_ERROR_UNEXPECTED;
1030 }
1031 
SendData(nsIUDPSocket * socket)1032 nsresult Http3Session::SendData(nsIUDPSocket* socket) {
1033   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1034 
1035   LOG(("Http3Session::SendData [this=%p]", this));
1036 
1037   //   1) go through all streams/transactions that are ready to write and
1038   //      write their data into quic streams (no network write yet).
1039   //   2) call ProcessOutput that will loop until all available packets are
1040   //      written to a socket or the socket returns an error code.
1041   //   3) if we still have streams ready to write call ResumeSend()(we may
1042   //      still have such streams because on an stream error we return earlier
1043   //      to let the error be handled).
1044 
1045   nsresult rv = NS_OK;
1046   Http3Stream* stream = nullptr;
1047 
1048   // Step 1)
1049   while (CanSandData() && (stream = mReadyForWrite.PopFront())) {
1050     LOG(("Http3Session::SendData call ReadSegments from stream=%p [this=%p]",
1051          stream, this));
1052 
1053     rv = stream->ReadSegments(nullptr);
1054 
1055     // on stream error we return earlier to let the error be handled.
1056     if (NS_FAILED(rv)) {
1057       LOG3(("Http3Session::SendData %p returns error code 0x%" PRIx32, this,
1058             static_cast<uint32_t>(rv)));
1059       MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK);
1060       if (rv == NS_BASE_STREAM_WOULD_BLOCK) {  // Just in case!
1061         rv = NS_OK;
1062       } else if (ASpdySession::SoftStreamError(rv)) {
1063         CloseStream(stream, rv);
1064         LOG3(("Http3Session::SendData %p soft error override\n", this));
1065         rv = NS_OK;
1066       } else {
1067         break;
1068       }
1069     }
1070   }
1071 
1072   if (NS_SUCCEEDED(rv)) {
1073     // Step 2:
1074     // Call actuall network write.
1075     rv = ProcessOutput(socket);
1076   }
1077 
1078   // Step 3:
1079   MaybeResumeSend();
1080 
1081   if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
1082     rv = NS_OK;
1083   }
1084 
1085   return rv;
1086 }
1087 
StreamReadyToWrite(Http3Stream * aStream)1088 void Http3Session::StreamReadyToWrite(Http3Stream* aStream) {
1089   MOZ_ASSERT(aStream);
1090   mReadyForWrite.Push(aStream);
1091   if (CanSandData() && mConnection) {
1092     Unused << mConnection->ResumeSend();
1093   }
1094 }
1095 
MaybeResumeSend()1096 void Http3Session::MaybeResumeSend() {
1097   if ((mReadyForWrite.GetSize() > 0) && CanSandData() && mConnection) {
1098     Unused << mConnection->ResumeSend();
1099   }
1100 }
1101 
ProcessSlowConsumers()1102 nsresult Http3Session::ProcessSlowConsumers() {
1103   if (mSlowConsumersReadyForRead.IsEmpty()) {
1104     return NS_OK;
1105   }
1106 
1107   RefPtr<Http3Stream> slowConsumer = mSlowConsumersReadyForRead.ElementAt(0);
1108   mSlowConsumersReadyForRead.RemoveElementAt(0);
1109 
1110   uint32_t countRead = 0;
1111   nsresult rv = ProcessTransactionRead(slowConsumer, &countRead);
1112 
1113   return rv;
1114 }
1115 
WriteSegments(nsAHttpSegmentWriter * writer,uint32_t count,uint32_t * countWritten)1116 nsresult Http3Session::WriteSegments(nsAHttpSegmentWriter* writer,
1117                                      uint32_t count, uint32_t* countWritten) {
1118   MOZ_ASSERT(false, "Http3Session::WriteSegments()");
1119   return NS_ERROR_UNEXPECTED;
1120 }
1121 
RecvData(nsIUDPSocket * socket)1122 nsresult Http3Session::RecvData(nsIUDPSocket* socket) {
1123   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1124 
1125   // Process slow consumers.
1126   nsresult rv = ProcessSlowConsumers();
1127   if (NS_FAILED(rv)) {
1128     LOG3(("Http3Session %p ProcessSlowConsumers returns 0x%" PRIx32 "\n", this,
1129           static_cast<uint32_t>(rv)));
1130     return rv;
1131   }
1132 
1133   ProcessInput(socket);
1134 
1135   rv = ProcessEvents();
1136   if (NS_FAILED(rv)) {
1137     return rv;
1138   }
1139 
1140   rv = ProcessOutput(socket);
1141   if (NS_FAILED(rv)) {
1142     return rv;
1143   }
1144 
1145   return NS_OK;
1146 }
1147 
1148 const uint32_t HTTP3_TELEMETRY_APP_NECKO = 42;
1149 
Close(nsresult aReason)1150 void Http3Session::Close(nsresult aReason) {
1151   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1152 
1153   LOG(("Http3Session::Close [this=%p]", this));
1154 
1155   if (NS_FAILED(mError)) {
1156     CloseInternal(false);
1157   } else {
1158     mError = aReason;
1159     // If necko closes connection, this will map to the "closing" key and the
1160     // value HTTP3_TELEMETRY_APP_NECKO.
1161     Telemetry::Accumulate(Telemetry::HTTP3_CONNECTION_CLOSE_CODE_3,
1162                           "app_closing"_ns, HTTP3_TELEMETRY_APP_NECKO);
1163     CloseInternal(true);
1164   }
1165 
1166   if (mCleanShutdown || mIsClosedByNeqo || NS_FAILED(mSocketError)) {
1167     // It is network-tear-down, a socker error or neqo is state CLOSED
1168     // (it does not need to send any more packets or wait for new packets).
1169     // We need to remove all references, so that
1170     // Http3Session will be destroyed.
1171     if (mTimer) {
1172       mTimer->Cancel();
1173     }
1174     mConnection = nullptr;
1175     mUdpConn = nullptr;
1176     mState = CLOSED;
1177   }
1178   if (mConnection) {
1179     // resume sending to send CLOSE_CONNECTION frame.
1180     Unused << mConnection->ResumeSend();
1181   }
1182 }
1183 
CloseInternal(bool aCallNeqoClose)1184 void Http3Session::CloseInternal(bool aCallNeqoClose) {
1185   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1186 
1187   if (IsClosing()) {
1188     return;
1189   }
1190 
1191   LOG(("Http3Session::Closing [this=%p]", this));
1192 
1193   if (mState != CONNECTED) {
1194     mBeforeConnectedError = true;
1195   }
1196 
1197   if (mState == ZERORTT) {
1198     ZeroRttTelemetry(aCallNeqoClose ? ZeroRttOutcome::USED_CONN_CLOSED_BY_NECKO
1199                                     : ZeroRttOutcome::USED_CONN_ERROR);
1200   }
1201 
1202   mState = CLOSING;
1203   Shutdown();
1204 
1205   if (aCallNeqoClose) {
1206     mHttp3Connection->Close(HTTP3_APP_ERROR_NO_ERROR);
1207   }
1208 
1209   mStreamIdHash.Clear();
1210   mStreamTransactionHash.Clear();
1211 }
1212 
ConnectionInfo()1213 nsHttpConnectionInfo* Http3Session::ConnectionInfo() {
1214   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1215   RefPtr<nsHttpConnectionInfo> ci;
1216   GetConnectionInfo(getter_AddRefs(ci));
1217   return ci.get();
1218 }
1219 
SetProxyConnectFailed()1220 void Http3Session::SetProxyConnectFailed() {
1221   MOZ_ASSERT(false, "Http3Session::SetProxyConnectFailed()");
1222 }
1223 
RequestHead()1224 nsHttpRequestHead* Http3Session::RequestHead() {
1225   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1226   MOZ_ASSERT(false,
1227              "Http3Session::RequestHead() "
1228              "should not be called after http/3 is setup");
1229   return nullptr;
1230 }
1231 
Http1xTransactionCount()1232 uint32_t Http3Session::Http1xTransactionCount() { return 0; }
1233 
TakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction>> & outTransactions)1234 nsresult Http3Session::TakeSubTransactions(
1235     nsTArray<RefPtr<nsAHttpTransaction>>& outTransactions) {
1236   return NS_OK;
1237 }
1238 
1239 //-----------------------------------------------------------------------------
1240 // Pass through methods of nsAHttpConnection
1241 //-----------------------------------------------------------------------------
1242 
Connection()1243 nsAHttpConnection* Http3Session::Connection() {
1244   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1245   return mConnection;
1246 }
1247 
OnHeadersAvailable(nsAHttpTransaction * transaction,nsHttpRequestHead * requestHead,nsHttpResponseHead * responseHead,bool * reset)1248 nsresult Http3Session::OnHeadersAvailable(nsAHttpTransaction* transaction,
1249                                           nsHttpRequestHead* requestHead,
1250                                           nsHttpResponseHead* responseHead,
1251                                           bool* reset) {
1252   MOZ_ASSERT(mConnection);
1253   if (mConnection) {
1254     return mConnection->OnHeadersAvailable(transaction, requestHead,
1255                                            responseHead, reset);
1256   }
1257   return NS_OK;
1258 }
1259 
IsReused()1260 bool Http3Session::IsReused() {
1261   if (mConnection) {
1262     return mConnection->IsReused();
1263   }
1264   return true;
1265 }
1266 
PushBack(const char * buf,uint32_t len)1267 nsresult Http3Session::PushBack(const char* buf, uint32_t len) {
1268   return NS_ERROR_UNEXPECTED;
1269 }
1270 
TakeHttpConnection()1271 already_AddRefed<HttpConnectionBase> Http3Session::TakeHttpConnection() {
1272   MOZ_ASSERT(false, "TakeHttpConnection of Http3Session");
1273   return nullptr;
1274 }
1275 
HttpConnection()1276 already_AddRefed<HttpConnectionBase> Http3Session::HttpConnection() {
1277   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1278   if (mConnection) {
1279     return mConnection->HttpConnection();
1280   }
1281   return nullptr;
1282 }
1283 
CloseTransaction(nsAHttpTransaction * aTransaction,nsresult aResult)1284 void Http3Session::CloseTransaction(nsAHttpTransaction* aTransaction,
1285                                     nsresult aResult) {
1286   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1287   LOG3(("Http3Session::CloseTransaction %p %p 0x%" PRIx32, this, aTransaction,
1288         static_cast<uint32_t>(aResult)));
1289 
1290   // Generally this arrives as a cancel event from the connection manager.
1291 
1292   // need to find the stream and call CloseStream() on it.
1293   RefPtr<Http3Stream> stream = mStreamTransactionHash.Get(aTransaction);
1294   if (!stream) {
1295     LOG3(("Http3Session::CloseTransaction %p %p 0x%" PRIx32 " - not found.",
1296           this, aTransaction, static_cast<uint32_t>(aResult)));
1297     return;
1298   }
1299   LOG3(
1300       ("Http3Session::CloseTransaction probably a cancel. this=%p, "
1301        "trans=%p, result=0x%" PRIx32 ", streamId=0x%" PRIx64 " stream=%p",
1302        this, aTransaction, static_cast<uint32_t>(aResult), stream->StreamId(),
1303        stream.get()));
1304   CloseStream(stream, aResult);
1305   if (mConnection) {
1306     Unused << mConnection->ResumeSend();
1307   }
1308 }
1309 
CloseStream(Http3Stream * aStream,nsresult aResult)1310 void Http3Session::CloseStream(Http3Stream* aStream, nsresult aResult) {
1311   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1312   if (!aStream->RecvdFin() && !aStream->RecvdReset() &&
1313       (aStream->HasStreamId())) {
1314     mHttp3Connection->ResetStream(aStream->StreamId(),
1315                                   HTTP3_APP_ERROR_REQUEST_CANCELLED);
1316   }
1317   aStream->Close(aResult);
1318   if (aStream->HasStreamId()) {
1319     // We know the transaction reusing an idle connection has succeeded or
1320     // failed.
1321     if (mFirstStreamIdReuseIdleConnection.isSome() &&
1322         aStream->StreamId() == *mFirstStreamIdReuseIdleConnection) {
1323       MOZ_ASSERT(mConnectionIdleStart);
1324       MOZ_ASSERT(mConnectionIdleEnd);
1325 
1326       if (mConnectionIdleStart) {
1327         Telemetry::AccumulateTimeDelta(
1328             Telemetry::HTTP3_TIME_TO_REUSE_IDLE_CONNECTTION_MS,
1329             NS_SUCCEEDED(aResult) ? "succeeded"_ns : "failed"_ns,
1330             mConnectionIdleStart, mConnectionIdleEnd);
1331       }
1332 
1333       mConnectionIdleStart = TimeStamp();
1334       mConnectionIdleEnd = TimeStamp();
1335       mFirstStreamIdReuseIdleConnection.reset();
1336     }
1337 
1338     mStreamIdHash.Remove(aStream->StreamId());
1339 
1340     // Start to idle when we remove the last stream.
1341     if (mStreamIdHash.IsEmpty()) {
1342       mConnectionIdleStart = TimeStamp::Now();
1343     }
1344   }
1345   RemoveStreamFromQueues(aStream);
1346   mStreamTransactionHash.Remove(aStream->Transaction());
1347   if ((mShouldClose || mGoawayReceived) && !mStreamTransactionHash.Count()) {
1348     MOZ_ASSERT(!IsClosing());
1349     Close(NS_OK);
1350   }
1351 }
1352 
TakeTransport(nsISocketTransport **,nsIAsyncInputStream **,nsIAsyncOutputStream **)1353 nsresult Http3Session::TakeTransport(nsISocketTransport**,
1354                                      nsIAsyncInputStream**,
1355                                      nsIAsyncOutputStream**) {
1356   MOZ_ASSERT(false, "TakeTransport of Http3Session");
1357   return NS_ERROR_UNEXPECTED;
1358 }
1359 
IsPersistent()1360 bool Http3Session::IsPersistent() { return true; }
1361 
DontReuse()1362 void Http3Session::DontReuse() {
1363   LOG3(("Http3Session::DontReuse %p\n", this));
1364   if (!OnSocketThread()) {
1365     LOG3(("Http3Session %p not on socket thread\n", this));
1366     nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
1367         "Http3Session::DontReuse", this, &Http3Session::DontReuse);
1368     gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
1369     return;
1370   }
1371 
1372   if (mGoawayReceived || IsClosing()) {
1373     return;
1374   }
1375 
1376   mShouldClose = true;
1377   if (!mStreamTransactionHash.Count()) {
1378     Close(NS_OK);
1379   }
1380 }
1381 
TopBrowsingContextIdChanged(uint64_t id)1382 void Http3Session::TopBrowsingContextIdChanged(uint64_t id) {
1383   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1384 
1385   mCurrentTopBrowsingContextId = id;
1386 
1387   for (const auto& stream : mStreamTransactionHash.Values()) {
1388     stream->TopBrowsingContextIdChanged(id);
1389   }
1390 }
1391 
1392 // This is called by Http3Stream::OnWriteSegment.
ReadResponseData(uint64_t aStreamId,char * aBuf,uint32_t aCount,uint32_t * aCountWritten,bool * aFin)1393 nsresult Http3Session::ReadResponseData(uint64_t aStreamId, char* aBuf,
1394                                         uint32_t aCount,
1395                                         uint32_t* aCountWritten, bool* aFin) {
1396   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1397 
1398   nsresult rv = mHttp3Connection->ReadResponseData(aStreamId, (uint8_t*)aBuf,
1399                                                    aCount, aCountWritten, aFin);
1400 
1401   // This should not happen, i.e. stream must be present in neqo and in necko at
1402   // the same time.
1403   MOZ_ASSERT(rv != NS_ERROR_INVALID_ARG);
1404   if (NS_FAILED(rv)) {
1405     LOG3(("Http3Session::ReadResponseData return an error %" PRIx32
1406           " [this=%p]",
1407           static_cast<uint32_t>(rv), this));
1408     // This error will be handled by neqo and the whole connection will be
1409     // closed. We will return NS_BASE_STREAM_WOULD_BLOCK here.
1410     *aCountWritten = 0;
1411     *aFin = false;
1412     rv = NS_BASE_STREAM_WOULD_BLOCK;
1413   }
1414 
1415   MOZ_ASSERT((*aCountWritten != 0) || aFin || NS_FAILED(rv));
1416   return rv;
1417 }
1418 
TransactionHasDataToWrite(nsAHttpTransaction * caller)1419 void Http3Session::TransactionHasDataToWrite(nsAHttpTransaction* caller) {
1420   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1421   LOG3(("Http3Session::TransactionHasDataToWrite %p trans=%p", this, caller));
1422 
1423   // a trapped signal from the http transaction to the connection that
1424   // it is no longer blocked on read.
1425 
1426   RefPtr<Http3Stream> stream = mStreamTransactionHash.Get(caller);
1427   if (!stream) {
1428     LOG3(("Http3Session::TransactionHasDataToWrite %p caller %p not found",
1429           this, caller));
1430     return;
1431   }
1432 
1433   LOG3(("Http3Session::TransactionHasDataToWrite %p ID is 0x%" PRIx64, this,
1434         stream->StreamId()));
1435 
1436   if (!IsClosing()) {
1437     StreamReadyToWrite(stream);
1438   } else {
1439     LOG3(
1440         ("Http3Session::TransactionHasDataToWrite %p closed so not setting "
1441          "Ready4Write\n",
1442          this));
1443   }
1444 
1445   // NSPR poll will not poll the network if there are non system PR_FileDesc's
1446   // that are ready - so we can get into a deadlock waiting for the system IO
1447   // to come back here if we don't force the send loop manually.
1448   Unused << ForceSend();
1449 }
1450 
TransactionHasDataToRecv(nsAHttpTransaction * caller)1451 void Http3Session::TransactionHasDataToRecv(nsAHttpTransaction* caller) {
1452   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1453   LOG3(("Http3Session::TransactionHasDataToRecv %p trans=%p", this, caller));
1454 
1455   // a signal from the http transaction to the connection that it will consume
1456   // more
1457   RefPtr<Http3Stream> stream = mStreamTransactionHash.Get(caller);
1458   if (!stream) {
1459     LOG3(("Http3Session::TransactionHasDataToRecv %p caller %p not found", this,
1460           caller));
1461     return;
1462   }
1463 
1464   LOG3(("Http3Session::TransactionHasDataToRecv %p ID is 0x%" PRIx64 "\n", this,
1465         stream->StreamId()));
1466   ConnectSlowConsumer(stream);
1467 }
1468 
ConnectSlowConsumer(Http3Stream * stream)1469 void Http3Session::ConnectSlowConsumer(Http3Stream* stream) {
1470   LOG3(("Http3Session::ConnectSlowConsumer %p 0x%" PRIx64 "\n", this,
1471         stream->StreamId()));
1472   mSlowConsumersReadyForRead.AppendElement(stream);
1473   Unused << ForceRecv();
1474 }
1475 
TestJoinConnection(const nsACString & hostname,int32_t port)1476 bool Http3Session::TestJoinConnection(const nsACString& hostname,
1477                                       int32_t port) {
1478   return RealJoinConnection(hostname, port, true);
1479 }
1480 
JoinConnection(const nsACString & hostname,int32_t port)1481 bool Http3Session::JoinConnection(const nsACString& hostname, int32_t port) {
1482   return RealJoinConnection(hostname, port, false);
1483 }
1484 
1485 // TODO test
RealJoinConnection(const nsACString & hostname,int32_t port,bool justKidding)1486 bool Http3Session::RealJoinConnection(const nsACString& hostname, int32_t port,
1487                                       bool justKidding) {
1488   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1489   if (!mConnection || !CanSandData() || mShouldClose || mGoawayReceived) {
1490     return false;
1491   }
1492 
1493   nsHttpConnectionInfo* ci = ConnectionInfo();
1494   if (nsCString(hostname).EqualsIgnoreCase(ci->Origin()) &&
1495       (port == ci->OriginPort())) {
1496     return true;
1497   }
1498 
1499   nsAutoCString key(hostname);
1500   key.Append(':');
1501   key.Append(justKidding ? 'k' : '.');
1502   key.AppendInt(port);
1503   bool cachedResult;
1504   if (mJoinConnectionCache.Get(key, &cachedResult)) {
1505     LOG(("joinconnection [%p %s] %s result=%d cache\n", this,
1506          ConnectionInfo()->HashKey().get(), key.get(), cachedResult));
1507     return cachedResult;
1508   }
1509 
1510   nsresult rv;
1511   bool isJoined = false;
1512 
1513   nsCOMPtr<nsISupports> securityInfo;
1514   nsCOMPtr<nsISSLSocketControl> sslSocketControl;
1515 
1516   mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
1517   sslSocketControl = do_QueryInterface(securityInfo, &rv);
1518   if (NS_FAILED(rv) || !sslSocketControl) {
1519     return false;
1520   }
1521 
1522   bool joinedReturn = false;
1523   if (justKidding) {
1524     rv = sslSocketControl->TestJoinConnection(mConnInfo->GetNPNToken(),
1525                                               hostname, port, &isJoined);
1526   } else {
1527     rv = sslSocketControl->JoinConnection(mConnInfo->GetNPNToken(), hostname,
1528                                           port, &isJoined);
1529   }
1530   if (NS_SUCCEEDED(rv) && isJoined) {
1531     joinedReturn = true;
1532   }
1533 
1534   LOG(("joinconnection [%p %s] %s result=%d lookup\n", this,
1535        ConnectionInfo()->HashKey().get(), key.get(), joinedReturn));
1536   mJoinConnectionCache.InsertOrUpdate(key, joinedReturn);
1537   if (!justKidding) {
1538     // cache a kidding entry too as this one is good for both
1539     nsAutoCString key2(hostname);
1540     key2.Append(':');
1541     key2.Append('k');
1542     key2.AppendInt(port);
1543     if (!mJoinConnectionCache.Get(key2)) {
1544       mJoinConnectionCache.InsertOrUpdate(key2, joinedReturn);
1545     }
1546   }
1547   return joinedReturn;
1548 }
1549 
CallCertVerification()1550 void Http3Session::CallCertVerification() {
1551   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1552   LOG(("Http3Session::CallCertVerification [this=%p]", this));
1553 
1554   NeqoCertificateInfo certInfo;
1555   if (NS_FAILED(mHttp3Connection->PeerCertificateInfo(&certInfo))) {
1556     LOG(("Http3Session::CallCertVerification [this=%p] - no cert", this));
1557     mHttp3Connection->PeerAuthenticated(SSL_ERROR_BAD_CERTIFICATE);
1558     mError = psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERTIFICATE);
1559     return;
1560   }
1561 
1562   Maybe<nsTArray<nsTArray<uint8_t>>> stapledOCSPResponse;
1563   if (certInfo.stapled_ocsp_responses_present) {
1564     stapledOCSPResponse.emplace(std::move(certInfo.stapled_ocsp_responses));
1565   }
1566 
1567   Maybe<nsTArray<uint8_t>> sctsFromTLSExtension;
1568   if (certInfo.signed_cert_timestamp_present) {
1569     sctsFromTLSExtension.emplace(std::move(certInfo.signed_cert_timestamp));
1570   }
1571 
1572   mSocketControl->SetAuthenticationCallback(this);
1573   uint32_t providerFlags;
1574   // the return value is always NS_OK, just ignore it.
1575   Unused << mSocketControl->GetProviderFlags(&providerFlags);
1576 
1577   SECStatus rv = AuthCertificateHookWithInfo(
1578       mSocketControl, static_cast<const void*>(this), std::move(certInfo.certs),
1579       stapledOCSPResponse, sctsFromTLSExtension, providerFlags);
1580   if ((rv != SECSuccess) && (rv != SECWouldBlock)) {
1581     LOG(("Http3Session::CallCertVerification [this=%p] AuthCertificate failed",
1582          this));
1583     mHttp3Connection->PeerAuthenticated(SSL_ERROR_BAD_CERTIFICATE);
1584     mError = psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERTIFICATE);
1585   }
1586 }
1587 
Authenticated(int32_t aError)1588 void Http3Session::Authenticated(int32_t aError) {
1589   LOG(("Http3Session::Authenticated error=0x%" PRIx32 " [this=%p].", aError,
1590        this));
1591   if ((mState == INITIALIZING) || (mState == ZERORTT)) {
1592     if (psm::IsNSSErrorCode(aError)) {
1593       mError = psm::GetXPCOMFromNSSError(aError);
1594       LOG(("Http3Session::Authenticated psm-error=0x%" PRIx32 " [this=%p].",
1595            static_cast<uint32_t>(mError), this));
1596     }
1597     mHttp3Connection->PeerAuthenticated(aError);
1598 
1599     // Call OnQuicTimeoutExpired to properly process neqo events and outputs.
1600     // We call OnQuicTimeoutExpired instead of ProcessOutputAndEvents, because
1601     // HttpConnectionUDP must close this session in case of an error.
1602     NS_DispatchToCurrentThread(
1603         NewRunnableMethod("net::HttpConnectionUDP::OnQuicTimeoutExpired",
1604                           mUdpConn, &HttpConnectionUDP::OnQuicTimeoutExpired));
1605   }
1606 }
1607 
SetSecInfo()1608 void Http3Session::SetSecInfo() {
1609   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1610   NeqoSecretInfo secInfo;
1611   if (NS_SUCCEEDED(mHttp3Connection->GetSecInfo(&secInfo))) {
1612     mSocketControl->SetSSLVersionUsed(secInfo.version);
1613     mSocketControl->SetResumed(secInfo.resumed);
1614     mSocketControl->SetNegotiatedNPN(secInfo.alpn);
1615 
1616     mSocketControl->SetInfo(secInfo.cipher, secInfo.version, secInfo.group,
1617                             secInfo.signature_scheme);
1618   }
1619 
1620   if (!mSocketControl->HasServerCert()) {
1621     mSocketControl->RebuildCertificateInfoFromSSLTokenCache();
1622   }
1623 }
1624 
1625 // Transport error have values from 0x0 to 0x11.
1626 // (https://tools.ietf.org/html/draft-ietf-quic-transport-34#section-20.1)
1627 // We will map this error to 0-16.
1628 // 17 will capture error codes between and including 0x12 and 0x0ff. This
1629 // error codes are not define by the spec but who know peer may sent them.
1630 // CryptoAlerts have value 0x100 + alert code. The range of alert code is
1631 // 0x00-0xff. (https://tools.ietf.org/html/draft-ietf-quic-tls_34#section-4.8)
1632 // Since telemetry does not allow more than 100 bucket, we use three diffrent
1633 // keys to map all alert codes.
1634 const uint32_t HTTP3_TELEMETRY_TRANSPORT_END = 16;
1635 const uint32_t HTTP3_TELEMETRY_TRANSPORT_UNKNOWN = 17;
1636 const uint32_t HTTP3_TELEMETRY_TRANSPORT_CRYPTO_UNKNOWN = 18;
1637 // All errors from CloseError::Tag::CryptoError will be map to 19
1638 const uint32_t HTTP3_TELEMETRY_CRYPTO_ERROR = 19;
1639 
GetCryptoAlertCode(nsCString & key,uint64_t error)1640 uint64_t GetCryptoAlertCode(nsCString& key, uint64_t error) {
1641   if (error < 100) {
1642     key.Append("_a"_ns);
1643     return error;
1644   }
1645   if (error < 200) {
1646     error -= 100;
1647     key.Append("_b"_ns);
1648     return error;
1649   }
1650   if (error < 256) {
1651     error -= 200;
1652     key.Append("_c"_ns);
1653     return error;
1654   }
1655   return HTTP3_TELEMETRY_TRANSPORT_CRYPTO_UNKNOWN;
1656 }
1657 
GetTransportErrorCodeForTelemetry(nsCString & key,uint64_t error)1658 uint64_t GetTransportErrorCodeForTelemetry(nsCString& key, uint64_t error) {
1659   if (error <= HTTP3_TELEMETRY_TRANSPORT_END) {
1660     return error;
1661   }
1662   if (error < 0x100) {
1663     return HTTP3_TELEMETRY_TRANSPORT_UNKNOWN;
1664   }
1665 
1666   return GetCryptoAlertCode(key, error - 0x100);
1667 }
1668 
1669 // Http3 error codes are 0x100-0x110.
1670 // (https://tools.ietf.org/html/draft-ietf-quic-http-33#section-8.1)
1671 // The mapping is described below.
1672 // 0x00-0x10 mapped to 0-16
1673 // 0x11-0xff mapped to 17
1674 // 0x100-0x110 mapped to 18-36
1675 // 0x111-0x1ff mapped to 37
1676 // 0x200-0x202 mapped to 38-40
1677 // Others mapped to 41
1678 const uint32_t HTTP3_TELEMETRY_APP_UNKNOWN_1 = 17;
1679 const uint32_t HTTP3_TELEMETRY_APP_START = 18;
1680 // Values between 0x111 and 0x1ff are no definded and will be map to 18.
1681 const uint32_t HTTP3_TELEMETRY_APP_UNKNOWN_2 = 37;
1682 // Error codes between 0x200 and 0x202 are related to qpack.
1683 // (https://tools.ietf.org/html/draft-ietf-quic-qpack-20#section-6)
1684 // They will be mapped to 19-21
1685 const uint32_t HTTP3_TELEMETRY_APP_QPACK_START = 38;
1686 // Values greater or equal to 0x203 are no definded and will be map to 41.
1687 const uint32_t HTTP3_TELEMETRY_APP_UNKNOWN_3 = 41;
1688 
GetAppErrorCodeForTelemetry(uint64_t error)1689 uint64_t GetAppErrorCodeForTelemetry(uint64_t error) {
1690   if (error <= 0x10) {
1691     return error;
1692   }
1693   if (error <= 0xff) {
1694     return HTTP3_TELEMETRY_APP_UNKNOWN_1;
1695   }
1696   if (error <= 0x110) {
1697     return error - 0x100 + HTTP3_TELEMETRY_APP_START;
1698   }
1699   if (error < 0x200) {
1700     return HTTP3_TELEMETRY_APP_UNKNOWN_2;
1701   }
1702   if (error <= 0x202) {
1703     return error - 0x200 + HTTP3_TELEMETRY_APP_QPACK_START;
1704   }
1705   return HTTP3_TELEMETRY_APP_UNKNOWN_3;
1706 }
1707 
CloseConnectionTelemetry(CloseError & aError,bool aClosing)1708 void Http3Session::CloseConnectionTelemetry(CloseError& aError, bool aClosing) {
1709   uint64_t value = 0;
1710   nsCString key = EmptyCString();
1711 
1712   switch (aError.tag) {
1713     case CloseError::Tag::TransportInternalError:
1714       key = "transport_internal"_ns;
1715       value = aError.transport_internal_error._0;
1716       break;
1717     case CloseError::Tag::TransportInternalErrorOther:
1718       key = "transport_other"_ns;
1719       value = aError.transport_internal_error_other._0;
1720       break;
1721     case CloseError::Tag::TransportError:
1722       key = "transport"_ns;
1723       value = GetTransportErrorCodeForTelemetry(key, aError.transport_error._0);
1724       break;
1725     case CloseError::Tag::CryptoError:
1726       key = "transport"_ns;
1727       value = HTTP3_TELEMETRY_CRYPTO_ERROR;
1728       break;
1729     case CloseError::Tag::CryptoAlert:
1730       key = "transport_crypto_alert"_ns;
1731       value = GetCryptoAlertCode(key, aError.crypto_alert._0);
1732       break;
1733     case CloseError::Tag::PeerAppError:
1734       key = "peer_app"_ns;
1735       value = GetAppErrorCodeForTelemetry(aError.peer_app_error._0);
1736       break;
1737     case CloseError::Tag::PeerError:
1738       key = "peer_transport"_ns;
1739       value = GetTransportErrorCodeForTelemetry(key, aError.peer_error._0);
1740       break;
1741     case CloseError::Tag::AppError:
1742       key = "app"_ns;
1743       value = GetAppErrorCodeForTelemetry(aError.app_error._0);
1744       break;
1745     case CloseError::Tag::EchRetry:
1746       key = "transport_crypto_alert"_ns;
1747       value = 121;
1748   }
1749 
1750   key.Append(aClosing ? "_closing"_ns : "_closed"_ns);
1751 
1752   Telemetry::Accumulate(Telemetry::HTTP3_CONNECTION_CLOSE_CODE_3, key, value);
1753 
1754   Http3Stats stats{};
1755   mHttp3Connection->GetStats(&stats);
1756 
1757   if (stats.packets_tx > 0) {
1758     unsigned long loss = (stats.lost * 10000) / stats.packets_tx;
1759     Telemetry::Accumulate(Telemetry::HTTP3_LOSS_RATIO, loss);
1760 
1761     Telemetry::Accumulate(Telemetry::HTTP3_LATE_ACK, "ack"_ns, stats.late_ack);
1762     Telemetry::Accumulate(Telemetry::HTTP3_LATE_ACK, "pto"_ns, stats.pto_ack);
1763 
1764     unsigned long late_ack_ratio = (stats.late_ack * 10000) / stats.packets_tx;
1765     unsigned long pto_ack_ratio = (stats.pto_ack * 10000) / stats.packets_tx;
1766     Telemetry::Accumulate(Telemetry::HTTP3_LATE_ACK_RATIO, "ack"_ns,
1767                           late_ack_ratio);
1768     Telemetry::Accumulate(Telemetry::HTTP3_LATE_ACK_RATIO, "pto"_ns,
1769                           pto_ack_ratio);
1770 
1771     for (uint32_t i = 0; i < MAX_PTO_COUNTS; i++) {
1772       nsAutoCString key;
1773       key.AppendInt(i);
1774       Telemetry::Accumulate(Telemetry::HTTP3_COUNTS_PTO, key,
1775                             stats.pto_counts[i]);
1776     }
1777 
1778     Telemetry::Accumulate(Telemetry::HTTP3_DROP_DGRAMS, stats.dropped_rx);
1779     Telemetry::Accumulate(Telemetry::HTTP3_SAVED_DGRAMS, stats.saved_datagrams);
1780   }
1781 
1782   Telemetry::Accumulate(Telemetry::HTTP3_RECEIVED_SENT_DGRAMS, "received"_ns,
1783                         stats.packets_rx);
1784   Telemetry::Accumulate(Telemetry::HTTP3_RECEIVED_SENT_DGRAMS, "sent"_ns,
1785                         stats.packets_tx);
1786 }
1787 
Finish0Rtt(bool aRestart)1788 void Http3Session::Finish0Rtt(bool aRestart) {
1789   for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
1790     if (m0RTTStreams[i]) {
1791       if (aRestart) {
1792         // When we need to restart transactions remove them from all lists.
1793         if (m0RTTStreams[i]->HasStreamId()) {
1794           mStreamIdHash.Remove(m0RTTStreams[i]->StreamId());
1795         }
1796         RemoveStreamFromQueues(m0RTTStreams[i]);
1797         // The stream is ready to write again.
1798         mReadyForWrite.Push(m0RTTStreams[i]);
1799       }
1800       m0RTTStreams[i]->Finish0RTT(aRestart);
1801     }
1802   }
1803 
1804   for (size_t i = 0; i < mCannotDo0RTTStreams.Length(); ++i) {
1805     if (mCannotDo0RTTStreams[i]) {
1806       mReadyForWrite.Push(mCannotDo0RTTStreams[i]);
1807     }
1808   }
1809   m0RTTStreams.Clear();
1810   mCannotDo0RTTStreams.Clear();
1811   MaybeResumeSend();
1812 }
1813 
ReportHttp3Connection()1814 void Http3Session::ReportHttp3Connection() {
1815   if (CanSandData() && !mHttp3ConnectionReported) {
1816     mHttp3ConnectionReported = true;
1817     gHttpHandler->ConnMgr()->ReportHttp3Connection(mUdpConn);
1818     MaybeResumeSend();
1819   }
1820 }
1821 
ZeroRttTelemetry(ZeroRttOutcome aOutcome)1822 void Http3Session::ZeroRttTelemetry(ZeroRttOutcome aOutcome) {
1823   Telemetry::Accumulate(Telemetry::HTTP3_0RTT_STATE, aOutcome);
1824 
1825   nsAutoCString key;
1826 
1827   switch (aOutcome) {
1828     case USED_SUCCEEDED:
1829       key = "succeeded"_ns;
1830       break;
1831     case USED_REJECTED:
1832       key = "rejected"_ns;
1833       break;
1834     case USED_CONN_ERROR:
1835       key = "conn_error"_ns;
1836       break;
1837     case USED_CONN_CLOSED_BY_NECKO:
1838       key = "conn_closed_by_necko"_ns;
1839       break;
1840     default:
1841       break;
1842   }
1843 
1844   if (!key.IsEmpty()) {
1845     MOZ_ASSERT(mZeroRttStarted);
1846     Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_0RTT_STATE_DURATION, key,
1847                                    mZeroRttStarted, TimeStamp::Now());
1848   }
1849 }
1850 
GetTransactionSecurityInfo(nsISupports ** secinfo)1851 nsresult Http3Session::GetTransactionSecurityInfo(nsISupports** secinfo) {
1852   nsCOMPtr<nsISupports> info;
1853   mSocketControl->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(info));
1854   info.forget(secinfo);
1855   return NS_OK;
1856 }
1857 
1858 }  // namespace net
1859 }  // namespace mozilla
1860