зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1825538 - Retry TRR request on on main thread when proxy connection failed, r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D176621
This commit is contained in:
Родитель
e6702068c2
Коммит
0ad6584639
|
@ -9,6 +9,7 @@
|
|||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsHttpHandler.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
|
@ -1072,7 +1073,9 @@ void TRR::Cancel(nsresult aStatus) {
|
|||
isTRRServiceChannel = false;
|
||||
}
|
||||
}
|
||||
if (isTRRServiceChannel && !XRE_IsSocketProcess()) {
|
||||
// nsHttpChannel can be only canceled on the main thread.
|
||||
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
|
||||
if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) {
|
||||
if (TRRService::Get()) {
|
||||
nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
|
||||
if (thread && !thread->IsOnCurrentThread()) {
|
||||
|
|
|
@ -545,7 +545,7 @@ nsresult TRRService::DispatchTRRRequestInternal(TRR* aTrrRequest,
|
|||
|
||||
already_AddRefed<nsIThread> TRRService::MainThreadOrTRRThread(bool aWithLock) {
|
||||
if (!StaticPrefs::network_trr_fetch_off_main_thread() ||
|
||||
XRE_IsSocketProcess()) {
|
||||
XRE_IsSocketProcess() || mDontUseTRRThread) {
|
||||
return do_GetMainThread();
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ class TRRService : public TRRServiceBase,
|
|||
|
||||
void InitTRRConnectionInfo() override;
|
||||
|
||||
void DontUseTRRThread() { mDontUseTRRThread = true; }
|
||||
|
||||
private:
|
||||
virtual ~TRRService();
|
||||
|
||||
|
@ -149,6 +151,7 @@ class TRRService : public TRRServiceBase,
|
|||
false}; // set when captive portal check is passed
|
||||
Atomic<bool, Relaxed> mDisableIPv6; // don't even try
|
||||
Atomic<bool, Relaxed> mShutdown{false};
|
||||
Atomic<bool, Relaxed> mDontUseTRRThread{false};
|
||||
|
||||
// TRR Blocklist storage
|
||||
// mTRRBLStorage is only modified on the main thread, but we query whether it
|
||||
|
|
|
@ -1367,7 +1367,17 @@ void nsHostResolver::AddToEvictionQ(nsHostRecord* rec,
|
|||
// Returns true if we retried with either TRR or Native.
|
||||
bool nsHostResolver::MaybeRetryTRRLookup(
|
||||
AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
|
||||
TRRSkippedReason aFirstAttemptSkipReason, const MutexAutoLock& aLock) {
|
||||
TRRSkippedReason aFirstAttemptSkipReason, nsresult aChannelStatus,
|
||||
const MutexAutoLock& aLock) {
|
||||
if (NS_FAILED(aFirstAttemptStatus) &&
|
||||
(aChannelStatus == NS_ERROR_PROXY_UNAUTHORIZED ||
|
||||
aChannelStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED) &&
|
||||
aAddrRec->mEffectiveTRRMode == nsIRequest::TRR_ONLY_MODE) {
|
||||
LOG(("MaybeRetryTRRLookup retry because of proxy connect failed"));
|
||||
TRRService::Get()->DontUseTRRThread();
|
||||
return DoRetryTRR(aAddrRec, aLock);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(aFirstAttemptStatus) ||
|
||||
aAddrRec->mEffectiveTRRMode != nsIRequest::TRR_FIRST_MODE ||
|
||||
aFirstAttemptStatus == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
|
||||
|
@ -1415,6 +1425,11 @@ bool nsHostResolver::MaybeRetryTRRLookup(
|
|||
static_cast<uint32_t>(aFirstAttemptSkipReason)));
|
||||
TRRService::Get()->RetryTRRConfirm();
|
||||
|
||||
return DoRetryTRR(aAddrRec, aLock);
|
||||
}
|
||||
|
||||
bool nsHostResolver::DoRetryTRR(AddrHostRecord* aAddrRec,
|
||||
const mozilla::MutexAutoLock& aLock) {
|
||||
{
|
||||
// Clear out the old query
|
||||
auto trrQuery = aAddrRec->mTRRQuery.Lock();
|
||||
|
@ -1498,7 +1513,8 @@ nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
|
|||
addrRec->RecordReason(TRRSkippedReason::TRR_OK);
|
||||
}
|
||||
|
||||
if (MaybeRetryTRRLookup(addrRec, status, aReason, aLock)) {
|
||||
nsresult channelStatus = aTRRRequest->ChannelStatus();
|
||||
if (MaybeRetryTRRLookup(addrRec, status, aReason, channelStatus, aLock)) {
|
||||
MOZ_ASSERT(addrRec->mResolving);
|
||||
return LOOKUP_OK;
|
||||
}
|
||||
|
|
|
@ -227,10 +227,12 @@ class nsHostResolver : public nsISupports, public AHostResolver {
|
|||
uint32_t defaultGracePeriod);
|
||||
virtual ~nsHostResolver();
|
||||
|
||||
bool DoRetryTRR(AddrHostRecord* aAddrRec,
|
||||
const mozilla::MutexAutoLock& aLock);
|
||||
bool MaybeRetryTRRLookup(
|
||||
AddrHostRecord* aAddrRec, nsresult aFirstAttemptStatus,
|
||||
mozilla::net::TRRSkippedReason aFirstAttemptSkipReason,
|
||||
const mozilla::MutexAutoLock& aLock);
|
||||
nsresult aChannelStatus, const mozilla::MutexAutoLock& aLock);
|
||||
|
||||
LookupStatus CompleteLookupLocked(nsHostRecord*, nsresult,
|
||||
mozilla::net::AddrInfo*, bool pb,
|
||||
|
|
|
@ -1028,6 +1028,17 @@ TRRServiceChannel::OnStartRequest(nsIRequest* request) {
|
|||
mResponseHead = mTransaction->TakeResponseHead();
|
||||
if (mResponseHead) {
|
||||
uint32_t httpStatus = mResponseHead->Status();
|
||||
if (mTransaction->ProxyConnectFailed()) {
|
||||
LOG(("TRRServiceChannel proxy connect failed httpStatus: %d",
|
||||
httpStatus));
|
||||
MOZ_ASSERT(mConnectionInfo->UsingConnect(),
|
||||
"proxy connect failed but not using CONNECT?");
|
||||
nsresult rv = HttpProxyResponseToErrorCode(httpStatus);
|
||||
mTransaction->DontReuseConnection();
|
||||
Cancel(rv);
|
||||
return CallOnStartRequest();
|
||||
}
|
||||
|
||||
if ((httpStatus < 500) && (httpStatus != 421) && (httpStatus != 407)) {
|
||||
ProcessAltService();
|
||||
}
|
||||
|
|
|
@ -448,7 +448,7 @@ class NodeHTTPSProxyServer extends BaseHTTPProxy {
|
|||
// HTTP2 proxy
|
||||
|
||||
class HTTP2ProxyCode {
|
||||
static async startServer(port) {
|
||||
static async startServer(port, auth) {
|
||||
const fs = require("fs");
|
||||
const options = {
|
||||
key: fs.readFileSync(__dirname + "/proxy-cert.key"),
|
||||
|
@ -457,7 +457,7 @@ class HTTP2ProxyCode {
|
|||
const http2 = require("http2");
|
||||
global.proxy = http2.createSecureServer(options);
|
||||
global.socketCounts = {};
|
||||
this.setupProxy();
|
||||
this.setupProxy(auth);
|
||||
|
||||
await global.proxy.listen(port);
|
||||
let proxyPort = global.proxy.address().port;
|
||||
|
@ -465,7 +465,7 @@ class HTTP2ProxyCode {
|
|||
return proxyPort;
|
||||
}
|
||||
|
||||
static setupProxy() {
|
||||
static setupProxy(auth) {
|
||||
if (!global.proxy) {
|
||||
throw new Error("proxy is null");
|
||||
}
|
||||
|
@ -539,6 +539,16 @@ class HTTP2ProxyCode {
|
|||
return;
|
||||
}
|
||||
|
||||
const authorization_token = headers["proxy-authorization"];
|
||||
if (auth && !authorization_token) {
|
||||
stream.respond({
|
||||
":status": 407,
|
||||
"proxy-authenticate": "Basic realm='foo'",
|
||||
});
|
||||
stream.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const target = headers[":authority"];
|
||||
const { port } = new URL(`https://${target}`);
|
||||
const net = require("net");
|
||||
|
@ -596,14 +606,16 @@ class NodeHTTP2ProxyServer extends BaseHTTPProxy {
|
|||
/// Starts the server
|
||||
/// @port - default 0
|
||||
/// when provided, will attempt to listen on that port.
|
||||
async start(port = 0) {
|
||||
async start(port = 0, auth) {
|
||||
this.processId = await NodeServer.fork();
|
||||
|
||||
await this.execute(BaseProxyCode);
|
||||
await this.execute(HTTP2ProxyCode);
|
||||
await this.execute(ADB);
|
||||
await this.execute(`global.connect_handler = null;`);
|
||||
this._port = await this.execute(`HTTP2ProxyCode.startServer(${port})`);
|
||||
this._port = await this.execute(
|
||||
`HTTP2ProxyCode.startServer(${port}, ${auth})`
|
||||
);
|
||||
|
||||
this.registerFilter();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from head_cache.js */
|
||||
/* import-globals-from head_cookies.js */
|
||||
/* import-globals-from head_channels.js */
|
||||
/* import-globals-from head_servers.js */
|
||||
|
||||
function setup() {
|
||||
trr_test_setup();
|
||||
Services.prefs.setBoolPref("network.trr.fetch_off_main_thread", true);
|
||||
}
|
||||
|
||||
setup();
|
||||
registerCleanupFunction(async () => {
|
||||
trr_clear_prefs();
|
||||
});
|
||||
|
||||
function AuthPrompt() {}
|
||||
|
||||
AuthPrompt.prototype = {
|
||||
user: "guest",
|
||||
pass: "guest",
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]),
|
||||
|
||||
promptAuth: function ap_promptAuth(channel, level, authInfo) {
|
||||
authInfo.username = this.user;
|
||||
authInfo.password = this.pass;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) {
|
||||
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
},
|
||||
};
|
||||
|
||||
function Requestor() {}
|
||||
|
||||
Requestor.prototype = {
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
|
||||
|
||||
getInterface: function requestor_gi(iid) {
|
||||
if (iid.equals(Ci.nsIAuthPrompt2)) {
|
||||
// Allow the prompt to store state by caching it here
|
||||
if (!this.prompt) {
|
||||
this.prompt = new AuthPrompt();
|
||||
}
|
||||
return this.prompt;
|
||||
}
|
||||
|
||||
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
prompt: null,
|
||||
};
|
||||
|
||||
// Test if we successfully retry TRR request on main thread.
|
||||
add_task(async function test_trr_proxy_auth() {
|
||||
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
|
||||
Ci.nsIX509CertDB
|
||||
);
|
||||
addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
|
||||
addCertFromFile(certdb, "proxy-ca.pem", "CTu,u,u");
|
||||
|
||||
let trrServer = new TRRServer();
|
||||
await trrServer.start();
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
"network.trr.uri",
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
);
|
||||
|
||||
await trrServer.registerDoHAnswers("test.proxy.com", "A", {
|
||||
answers: [
|
||||
{
|
||||
name: "test.proxy.com",
|
||||
ttl: 55,
|
||||
type: "A",
|
||||
flush: false,
|
||||
data: "3.3.3.3",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await new TRRDNSListener("test.proxy.com", "3.3.3.3");
|
||||
|
||||
let proxy = new NodeHTTP2ProxyServer();
|
||||
await proxy.start(0, true);
|
||||
registerCleanupFunction(async () => {
|
||||
await proxy.stop();
|
||||
await trrServer.stop();
|
||||
});
|
||||
|
||||
let authTriggered = false;
|
||||
let observer = {
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
|
||||
observe(aSubject, aTopic, aData) {
|
||||
if (aTopic == "http-on-examine-response") {
|
||||
Services.obs.removeObserver(observer, "http-on-examine-response");
|
||||
let channel = aSubject.QueryInterface(Ci.nsIChannel);
|
||||
channel.notificationCallbacks = new Requestor();
|
||||
if (
|
||||
channel.URI.spec.startsWith(
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
)
|
||||
) {
|
||||
authTriggered = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
Services.obs.addObserver(observer, "http-on-examine-response");
|
||||
|
||||
Services.dns.clearCache(true);
|
||||
await new TRRDNSListener("test.proxy.com", "3.3.3.3");
|
||||
Assert.ok(authTriggered);
|
||||
});
|
|
@ -772,3 +772,7 @@ head = head_channels.js head_cache.js head_cookies.js head_trr.js head_http3.js
|
|||
skip-if =
|
||||
os == 'android'
|
||||
socketprocess_networking
|
||||
[test_trr_proxy_auth.js]
|
||||
skip-if =
|
||||
os == 'android'
|
||||
socketprocess_networking
|
||||
|
|
Загрузка…
Ссылка в новой задаче