Move the TLS version interference probe to HttpNetworkTransaction.
Having the logic in the SSLConnectJob, while easier on the socket pools,
interacts badly with the proxy tunneling logic, which bypasses the
SSLConnectJob layer and so we lose the state on whether we were
retrying. The end result is, if the origin always resets the connection,
we retry without bound. This doesn't cause connections that would
otherwise succeeded to fail, but it's still not great.
Instead, go back to our tried and true mechanism of trying at
HttpNetworkTransaction, at the cost of more socket pool bifurcation.
Note this comes with a slight change in metrics, due to things shifting
around: Net.SSL_Connection_Error will report the original error code
(e.g. ERR_CONNECTION_RESET) rather than ERR_SSL_VERSION_INTERFERENCE.
Net.ErrorCodesForMainFrame and user-visible errors remain the same.
Additionally, rather than do a lot of work to plumb
Net.SSLVersionInterferenceDetails's information up, I've just removed
that metric. It was useful when we were developing the protocol
workarounds for the various non-compliant enterprise middleboxes, but
now that the workarounds are applied, we don't need it anymore.
On the test side of things, the existing ERR_SSL_VERSION_INTERFERENCE
tests were sufficiently high-level that they cover this new
implementation too. I've additionally added a regression test to cover
this particular issue.
Bug: 823387
Change-Id: Ia0b5b18c4cfa65fed55f7c1d937623ab37ff741d
Reviewed-on: https://chromium-review.googlesource.com/990812
Reviewed-by: Ilya Sherman <[email protected]>
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Steven Valdez <[email protected]>
Commit-Queue: David Benjamin <[email protected]>
Cr-Commit-Position: refs/heads/master@{#548691}
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 299fe75..9d96d1a8 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -15,6 +15,7 @@
#include "base/compiler_specific.h"
#include "base/format_macros.h"
#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/stl_util.h"
@@ -112,7 +113,8 @@
websocket_handshake_stream_base_create_helper_(NULL),
net_error_details_(),
retry_attempts_(0),
- num_restarts_(0) {}
+ num_restarts_(0),
+ ssl_version_interference_error_(OK) {}
HttpNetworkTransaction::~HttpNetworkTransaction() {
if (stream_.get()) {
@@ -844,6 +846,9 @@
}
int HttpNetworkTransaction::DoCreateStreamComplete(int result) {
+ // Version interference probes should not result in success.
+ DCHECK(!server_ssl_config_.version_interference_probe || result != OK);
+
// If |result| is ERR_HTTPS_PROXY_TUNNEL_RESPONSE, then
// DoCreateStreamComplete is being called from OnHttpsProxyTunnelResponse,
// which resets the stream request first. Therefore, we have to grab the
@@ -865,6 +870,41 @@
return HandleHttp11Required(result);
}
+ // Perform a TLS 1.3 version interference probe on various connection
+ // errors. The retry will never produce a successful connection but may map
+ // errors to ERR_SSL_VERSION_INTERFERENCE, which signals a probable
+ // version-interfering middlebox.
+ if (IsSecureRequest() && !HasExceededMaxRetries() &&
+ server_ssl_config_.version_max == SSL_PROTOCOL_VERSION_TLS1_3 &&
+ !server_ssl_config_.version_interference_probe) {
+ if (result == ERR_CONNECTION_CLOSED || result == ERR_SSL_PROTOCOL_ERROR ||
+ result == ERR_SSL_VERSION_OR_CIPHER_MISMATCH ||
+ result == ERR_CONNECTION_RESET ||
+ result == ERR_SSL_BAD_RECORD_MAC_ALERT) {
+ // Report the error code for each time a version interference probe is
+ // triggered.
+ base::UmaHistogramSparse("Net.SSLVersionInterferenceProbeTrigger",
+ std::abs(result));
+ net_log_.AddEventWithNetErrorCode(
+ NetLogEventType::SSL_VERSION_INTERFERENCE_PROBE, result);
+
+ retry_attempts_++;
+ server_ssl_config_.version_interference_probe = true;
+ server_ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1_2;
+ ssl_version_interference_error_ = result;
+ ResetConnectionAndRequestForResend();
+ return OK;
+ }
+ }
+
+ if (result == ERR_SSL_VERSION_INTERFERENCE) {
+ // Record the error code version interference was detected at.
+ DCHECK(server_ssl_config_.version_interference_probe);
+ DCHECK_NE(OK, ssl_version_interference_error_);
+ base::UmaHistogramSparse("Net.SSLVersionInterferenceError",
+ std::abs(ssl_version_interference_error_));
+ }
+
// Handle possible client certificate errors that may have occurred if the
// stream used SSL for one or more of the layers.
result = HandleSSLClientAuthError(result);