commit 58afdd9c2861da7e473997e06310620daa32de64
parent f6e3ccb5853c46f917578697a488d48c3bc09ac8
Author: Kershaw Chang <kershaw@mozilla.com>
Date: Thu, 27 Nov 2025 13:32:40 +0000
Bug 2002687 - Don't retry transaction when it's cancelled, r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D274258
Diffstat:
4 files changed, 25 insertions(+), 24 deletions(-)
diff --git a/netwerk/protocol/http/nsHttp.h b/netwerk/protocol/http/nsHttp.h
@@ -485,11 +485,18 @@ nsresult HttpProxyResponseToErrorCode(uint32_t aStatusCode);
// Convert an alpn string to SupportedAlpnType.
SupportedAlpnRank IsAlpnSupported(const nsACString& aAlpn);
-static inline bool AllowedErrorForHTTPSRRFallback(nsresult aError) {
+// Keep this list in sync with the error mapping in
+// neqo_glue/src/lib.rs::into_nsresult(). These are the network/NSS errors for
+// which we allow a transaction to retry.
+static inline bool AllowedErrorForTransactionRetry(nsresult aError) {
return psm::IsNSSErrorCode(-1 * NS_ERROR_GET_CODE(aError)) ||
aError == NS_ERROR_NET_RESET ||
aError == NS_ERROR_CONNECTION_REFUSED ||
- aError == NS_ERROR_UNKNOWN_HOST || aError == NS_ERROR_NET_TIMEOUT;
+ aError == NS_ERROR_UNKNOWN_HOST || aError == NS_ERROR_NET_TIMEOUT ||
+ aError == NS_ERROR_NOT_CONNECTED ||
+ aError == NS_ERROR_SOCKET_ADDRESS_IN_USE ||
+ aError == NS_ERROR_FILE_ALREADY_EXISTS ||
+ aError == NS_ERROR_NET_INTERRUPT;
}
[[nodiscard]] nsresult MakeOriginURL(const nsACString& origin,
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6967,7 +6967,7 @@ nsHttpChannel::Cancel(nsresult status) {
static_cast<uint32_t>(status), mCanceledReason.get()));
MOZ_ASSERT_IF(!(mConnectionInfo && mConnectionInfo->UsingConnect()) &&
NS_SUCCEEDED(mStatus),
- !AllowedErrorForHTTPSRRFallback(status));
+ !AllowedErrorForTransactionRetry(status));
mEarlyHintObserver = nullptr;
mWebTransportSessionEventListener = nullptr;
diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -1337,7 +1337,8 @@ void nsHttpTransaction::Close(nsresult reason) {
// treat it as NS_ERROR_NET_RESET so the transaction will retry once.
// NOTE: This is a temporary workaround; the proper fix belongs in
// the Happy Eyeballs project.
- if (NS_FAILED(reason) && mHttp3BackupTimerCreated && mHttp3BackupTimer) {
+ if (NS_FAILED(reason) && AllowedErrorForTransactionRetry(reason) &&
+ mHttp3BackupTimerCreated && mHttp3BackupTimer) {
reason = NS_ERROR_NET_RESET;
}
@@ -1392,7 +1393,7 @@ void nsHttpTransaction::Close(nsresult reason) {
// to make sure this transaction can be restarted with the same conncetion
// info.
bool shouldRestartTransactionForHTTPSRR =
- mOrigConnInfo && AllowedErrorForHTTPSRRFallback(reason) &&
+ mOrigConnInfo && AllowedErrorForTransactionRetry(reason) &&
!mDoNotRemoveAltSvc;
//
diff --git a/netwerk/socket/neqo_glue/src/lib.rs b/netwerk/socket/neqo_glue/src/lib.rs
@@ -41,11 +41,11 @@ use neqo_transport::{
Error as TransportError, Output, OutputBatch, RandomConnectionIdGenerator, StreamId, Version,
};
use nserror::{
- nsresult, NS_BASE_STREAM_WOULD_BLOCK, NS_ERROR_CONNECTION_REFUSED, NS_ERROR_FAILURE,
- NS_ERROR_FILE_ALREADY_EXISTS, NS_ERROR_ILLEGAL_VALUE, NS_ERROR_INVALID_ARG,
- NS_ERROR_NET_HTTP3_PROTOCOL_ERROR, NS_ERROR_NET_INTERRUPT, NS_ERROR_NET_RESET,
- NS_ERROR_NET_TIMEOUT, NS_ERROR_NOT_AVAILABLE, NS_ERROR_NOT_CONNECTED, NS_ERROR_OUT_OF_MEMORY,
- NS_ERROR_SOCKET_ADDRESS_IN_USE, NS_ERROR_UNEXPECTED, NS_OK, NS_ERROR_DOM_INVALID_HEADER_NAME,
+ nsresult, NS_BASE_STREAM_WOULD_BLOCK, NS_ERROR_CONNECTION_REFUSED,
+ NS_ERROR_DOM_INVALID_HEADER_NAME, NS_ERROR_FILE_ALREADY_EXISTS, NS_ERROR_ILLEGAL_VALUE,
+ NS_ERROR_INVALID_ARG, NS_ERROR_NET_HTTP3_PROTOCOL_ERROR, NS_ERROR_NET_INTERRUPT,
+ NS_ERROR_NET_RESET, NS_ERROR_NET_TIMEOUT, NS_ERROR_NOT_AVAILABLE, NS_ERROR_NOT_CONNECTED,
+ NS_ERROR_OUT_OF_MEMORY, NS_ERROR_SOCKET_ADDRESS_IN_USE, NS_ERROR_UNEXPECTED, NS_OK,
};
use nsstring::{nsACString, nsCString};
use thin_vec::ThinVec;
@@ -2398,6 +2398,7 @@ pub unsafe extern "C" fn neqo_http3conn_webtransport_set_sendorder(
///
/// Note that this conversion is specific to `neqo_glue`, i.e. does not aim to
/// implement a general-purpose conversion.
+/// Treat NS_ERROR_NET_RESET as a generic retryable error for the upper layer.
///
/// Modeled after
/// [`ErrorAccordingToNSPR`](https://searchfox.org/mozilla-central/rev/a965e3c683ecc035dee1de72bd33a8d91b1203ed/netwerk/base/nsSocketTransport2.cpp#164-168).
@@ -2448,16 +2449,6 @@ fn into_nsresult(e: &io::Error) -> nsresult {
// TODO: nightly-only for now <https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.FilesystemLoop>.
// io::ErrorKind::FilesystemLoop => NS_ERROR_FILE_UNRESOLVABLE_SYMLINK,
-
- // > NSPR's socket code can return these, but they're not worth breaking out
- // > into their own error codes, distinct from NS_ERROR_FAILURE:
- // >
- // > PR_BAD_DESCRIPTOR_ERROR
- // > PR_INVALID_ARGUMENT_ERROR
- //
- // <https://searchfox.org/mozilla-central/rev/a965e3c683ecc035dee1de72bd33a8d91b1203ed/netwerk/base/nsSocketTransport2.cpp#231>
- io::ErrorKind::InvalidInput => NS_ERROR_FAILURE,
-
io::ErrorKind::TimedOut => NS_ERROR_NET_TIMEOUT,
io::ErrorKind::Interrupted => NS_ERROR_NET_INTERRUPT,
@@ -2477,7 +2468,7 @@ fn into_nsresult(e: &io::Error) -> nsresult {
| io::ErrorKind::InvalidData
| io::ErrorKind::WriteZero
| io::ErrorKind::Unsupported
- | io::ErrorKind::Other => NS_ERROR_FAILURE,
+ | io::ErrorKind::Other => NS_ERROR_NET_RESET,
// TODO: available since Rust v1.83.0 only
// <https://doc.rust-lang.org/std/io/enum.ErrorKind.html>.
@@ -2491,11 +2482,13 @@ fn into_nsresult(e: &io::Error) -> nsresult {
// | io::ErrorKind::ArgumentListTooLong
// | io::ErrorKind::NetworkDown
// | io::ErrorKind::StaleNetworkFileHandle
- // | io::ErrorKind::StorageFull => NS_ERROR_FAILURE,
+ // | io::ErrorKind::StorageFull => NS_ERROR_NET_RESET,
// TODO: nightly-only for now <https://doc.rust-lang.org/std/io/enum.ErrorKind.html>.
- // io::ErrorKind::CrossesDevices | io::ErrorKind::InvalidFilename => NS_ERROR_FAILURE,
- _ => NS_ERROR_FAILURE,
+ // io::ErrorKind::CrossesDevices
+ // | io::ErrorKind::InvalidFilename
+ // | io::ErrorKind::InvalidInput => NS_ERROR_NET_RESET,
+ _ => NS_ERROR_NET_RESET,
}
}