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