Bug 1368527 add new on-before-connect notification to httpChannel, r?dragana
MozReview-Commit-ID: 8hiAgY4KzDB
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -427,22 +427,20 @@ nsHttpChannel::LogBlockedCORSRequest(con
return NS_ERROR_UNEXPECTED;
}
//-----------------------------------------------------------------------------
// nsHttpChannel <private>
//-----------------------------------------------------------------------------
nsresult
-nsHttpChannel::Connect()
+nsHttpChannel::OnBeforeConnect()
{
nsresult rv;
- LOG(("nsHttpChannel::Connect [this=%p]\n", this));
-
// Note that we are only setting the "Upgrade-Insecure-Requests" request
// header for *all* navigational requests instead of all requests as
// defined in the spec, see:
// https://www.w3.org/TR/upgrade-insecure-requests/#preference
nsContentPolicyType type = mLoadInfo ?
mLoadInfo->GetExternalContentPolicyType() :
nsIContentPolicy::TYPE_OTHER;
@@ -493,26 +491,74 @@ nsHttpChannel::Connect()
}
// Finalize ConnectionInfo flags before SpeculativeConnect
mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
mConnectionInfo->SetPrivate(mPrivateBrowsing);
mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) || mBeConservative);
+ // notify "http-on-before-connect" observers
+ gHttpHandler->OnBeforeConnect(this);
+
+ // Check if request was cancelled during http-on-before-connect.
+ if (mCanceled) {
+ return mStatus;
+ }
+
+ if (mSuspendCount) {
+ // We abandon the connection here if there was one.
+ LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
+ MOZ_ASSERT(!mCallOnResume);
+ mCallOnResume = &nsHttpChannel::OnBeforeConnectContinue;
+ return NS_OK;
+ }
+
+ return Connect();
+}
+
+void
+nsHttpChannel::OnBeforeConnectContinue()
+{
+ NS_PRECONDITION(!mCallOnResume, "How did that happen?");
+ nsresult rv;
+
+ if (mSuspendCount) {
+ LOG(("Waiting until resume OnBeforeConnect [this=%p]\n", this));
+ mCallOnResume = &nsHttpChannel::OnBeforeConnectContinue;
+ return;
+ }
+
+ LOG(("nsHttpChannel::OnBeforeConnectContinue [this=%p]\n", this));
+ rv = Connect();
+ if (NS_FAILED(rv)) {
+ CloseCacheEntry(false);
+ Unused << AsyncAbort(rv);
+ }
+}
+
+nsresult
+nsHttpChannel::Connect()
+{
+ LOG(("nsHttpChannel::Connect [this=%p]\n", this));
+
// Consider opening a TCP connection right away.
SpeculativeConnect();
// Don't allow resuming when cache must be used
if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
LOG(("Resuming from cache is not supported yet"));
return NS_ERROR_DOCUMENT_NOT_CACHED;
}
// open a cache entry for this channel...
+ nsresult rv;
+ bool isHttps = false;
+ rv = mURI->SchemeIs("https", &isHttps);
+ NS_ENSURE_SUCCESS(rv,rv);
rv = OpenCacheEntry(isHttps);
// do not continue if asyncOpenCacheEntry is in progress
if (AwaitingCacheCallbacks()) {
LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n", this));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
if (mNetworkTriggered && mWaitingForProxy) {
@@ -6688,17 +6734,17 @@ nsHttpChannel::ContinueBeginConnectWithR
LOG(("Waiting until resume to do async connect [this=%p]\n", this));
mCallOnResume = &nsHttpChannel::ContinueBeginConnect;
rv = NS_OK;
} else if (mCanceled) {
// We may have been cancelled already, by nsChannelClassifier in that
// case, we should not send the request to the server
rv = mStatus;
} else {
- rv = Connect();
+ rv = OnBeforeConnect();
}
LOG(("nsHttpChannel::ContinueBeginConnectWithResult result [this=%p rv=%" PRIx32
" mCanceled=%u]\n",
this, static_cast<uint32_t>(rv), static_cast<bool>(mCanceled)));
return rv;
}
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -315,16 +315,18 @@ private:
// whether an async tracker lookup is required. If the tracker lookup
// is required, this funciton will just return NS_OK and BeginConnectActual()
// will be called when callback. See Bug 1325054 for more information.
nsresult BeginConnect();
void HandleBeginConnectContinue();
MOZ_MUST_USE nsresult BeginConnectContinue();
MOZ_MUST_USE nsresult ContinueBeginConnectWithResult();
void ContinueBeginConnect();
+ MOZ_MUST_USE nsresult OnBeforeConnect();
+ void OnBeforeConnectContinue();
MOZ_MUST_USE nsresult Connect();
void SpeculativeConnect();
MOZ_MUST_USE nsresult SetupTransaction();
void SetupTransactionRequestContext();
MOZ_MUST_USE nsresult CallOnStartRequest();
MOZ_MUST_USE nsresult ProcessResponse();
void AsyncContinueProcessResponse();
MOZ_MUST_USE nsresult ContinueProcessResponse1();
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -313,16 +313,22 @@ public:
}
// Called by the channel and cached in the loadGroup
void OnUserAgentRequest(nsIHttpChannel *chan)
{
NotifyObservers(chan, NS_HTTP_ON_USERAGENT_REQUEST_TOPIC);
}
+ // Called by the channel before setting up the transaction
+ void OnBeforeConnect(nsIHttpChannel *chan)
+ {
+ NotifyObservers(chan, NS_HTTP_ON_BEFORE_CONNECT_TOPIC);
+ }
+
// Called by the channel once headers are available
void OnExamineResponse(nsIHttpChannel *chan)
{
NotifyObservers(chan, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
}
// Called by the channel once headers have been merged with cached headers
void OnExamineMergedResponse(nsIHttpChannel *chan)
--- a/netwerk/protocol/http/nsIHttpProtocolHandler.idl
+++ b/netwerk/protocol/http/nsIHttpProtocolHandler.idl
@@ -88,16 +88,26 @@ interface nsIHttpProtocolHandler : nsIPr
* Before an HTTP request is sent to the server, this observer topic is
* notified. The observer of this topic can then choose to set any additional
* headers for this request before the request is actually sent to the server.
* The "subject" of the notification is the nsIHttpChannel instance.
*/
#define NS_HTTP_ON_MODIFY_REQUEST_TOPIC "http-on-modify-request"
/**
+ * Before an HTTP connection to the server is created, this observer topic is
+ * notified. This observer happens after HSTS upgrades, etc. are set, providing
+ * access to the full set of request headers. The observer of this topic can
+ * choose to set any additional headers for this request before the request is
+ * actually sent to the server. The "subject" of the notification is the
+ * nsIHttpChannel instance.
+ */
+#define NS_HTTP_ON_BEFORE_CONNECT_TOPIC "http-on-before-connect"
+
+/**
* After an HTTP server response is received, this observer topic is notified.
* The observer of this topic can interrogate the response. The "subject" of
* the notification is the nsIHttpChannel instance.
*/
#define NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC "http-on-examine-response"
/**
* The observer of this topic is notified after the received HTTP response
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -527,16 +527,17 @@ class AuthRequestor {
channelData.authPromptForward = null;
channelData.authPromptCallback = null;
},
};
}
}
HttpObserverManager = {
+ openingInitialized: false,
modifyInitialized: false,
examineInitialized: false,
redirectInitialized: false,
activityInitialized: false,
needTracing: false,
listeners: {
opening: new Map(),
@@ -550,23 +551,31 @@ HttpObserverManager = {
onStop: new Map(),
},
get activityDistributor() {
return Cc["@mozilla.org/network/http-activity-distributor;1"].getService(Ci.nsIHttpActivityDistributor);
},
addOrRemove() {
- let needModify = this.listeners.opening.size || this.listeners.modify.size || this.listeners.afterModify.size;
+ let needOpening = this.listeners.opening.size;
+ let needModify = this.listeners.modify.size || this.listeners.afterModify.size;
+ if (needOpening && !this.openingInitialized) {
+ this.openingInitialized = true;
+ Services.obs.addObserver(this, "http-on-modify-request");
+ } else if (!needOpening && this.openingInitialized) {
+ this.openingInitialized = false;
+ Services.obs.removeObserver(this, "http-on-modify-request");
+ }
if (needModify && !this.modifyInitialized) {
this.modifyInitialized = true;
- Services.obs.addObserver(this, "http-on-modify-request");
+ Services.obs.addObserver(this, "http-on-before-connect");
} else if (!needModify && this.modifyInitialized) {
this.modifyInitialized = false;
- Services.obs.removeObserver(this, "http-on-modify-request");
+ Services.obs.removeObserver(this, "http-on-before-connect");
}
this.needTracing = this.listeners.onStart.size ||
this.listeners.onError.size ||
this.listeners.onStop.size;
let needExamine = this.needTracing ||
this.listeners.headersReceived.size ||
this.listeners.authRequired.size;
@@ -627,19 +636,20 @@ HttpObserverManager = {
}
}
},
observe(subject, topic, data) {
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
switch (topic) {
case "http-on-modify-request":
- let loadContext = this.getLoadContext(channel);
-
- this.runChannelListener(channel, loadContext, "opening");
+ this.runChannelListener(channel, this.getLoadContext(channel), "opening");
+ break;
+ case "http-on-before-connect":
+ this.runChannelListener(channel, this.getLoadContext(channel), "modify");
break;
case "http-on-examine-cached-response":
case "http-on-examine-merged-response":
getData(channel).fromCache = true;
// falls through
case "http-on-examine-response":
this.examine(channel, topic, data);
break;
@@ -997,19 +1007,17 @@ HttpObserverManager = {
// forward the auth request to the base handler.
if (kind === "authRequired") {
let channelData = getData(channel);
if (channelData.authPromptForward) {
channelData.authPromptForward();
}
}
- if (kind === "opening") {
- await this.runChannelListener(channel, loadContext, "modify");
- } else if (kind === "modify") {
+ if (kind === "modify") {
await this.runChannelListener(channel, loadContext, "afterModify");
}
} catch (e) {
Cu.reportError(e);
}
// Only resume the channel if it was suspended by this call.
if (shouldResume) {