Bug 974018: Implement nsIRedirectHistory (r=mayhemer)

This commit is contained in:
Monica Chew 2014-05-22 12:58:23 -07:00
parent 4a93b644f6
commit 3f0f699d9c
11 changed files with 208 additions and 33 deletions

View File

@ -78,6 +78,7 @@ XPIDL_SOURCES += [
'nsIProxyInfo.idl',
'nsIRandomGenerator.idl',
'nsIRedirectChannelRegistrar.idl',
'nsIRedirectHistory.idl',
'nsIRedirectResultListener.idl',
'nsIRequest.idl',
'nsIRequestObserver.idl',

View File

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
/**
* Allows keeping track of channel redirects. Currently nsHttpChannel is the
* only implementor.
*/
#include "nsISupports.idl"
interface nsIArray;
interface nsIPrincipal;
[scriptable, uuid(ab87eabf-d0c4-40a9-b4b2-a1191108d4c0)]
interface nsIRedirectHistory : nsISupports
{
/**
* An array of nsIPrincipal that store the redirects associated with this
* channel. This array is filled whether or not the channel has ever been
* opened. The last element of the array is associated with the most recent
* channel.
*/
readonly attribute nsIArray redirects;
};

View File

@ -19,6 +19,7 @@
#include "nsITimedChannel.h"
#include "nsIEncodedChannel.h"
#include "nsIApplicationCacheChannel.h"
#include "nsIMutableArray.h"
#include "nsEscape.h"
#include "nsStreamListenerWrapper.h"
#include "nsISecurityConsoleMessage.h"
@ -160,6 +161,7 @@ NS_INTERFACE_MAP_BEGIN(HttpBaseChannel)
NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
NS_INTERFACE_MAP_ENTRY(nsIRedirectHistory)
NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
@ -1588,6 +1590,13 @@ HttpBaseChannel::SetResponseTimeoutEnabled(bool aEnable)
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::AddRedirect(nsIPrincipal *aRedirect)
{
mRedirects.AppendObject(aRedirect);
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsISupportsPriority
//-----------------------------------------------------------------------------
@ -1654,6 +1663,60 @@ HttpBaseChannel::GetEntityID(nsACString& aEntityID)
return NS_OK;
}
nsIPrincipal *
HttpBaseChannel::GetPrincipal(bool requireAppId)
{
if (mPrincipal) {
if (requireAppId && mPrincipal->GetUnknownAppId()) {
LOG(("HttpBaseChannel::GetPrincipal: No app id [this=%p]", this));
return nullptr;
}
return mPrincipal;
}
nsIScriptSecurityManager *securityManager =
nsContentUtils::GetSecurityManager();
if (!securityManager) {
LOG(("HttpBaseChannel::GetPrincipal: No security manager [this=%p]",
this));
return nullptr;
}
securityManager->GetChannelPrincipal(this, getter_AddRefs(mPrincipal));
if (!mPrincipal) {
LOG(("HttpBaseChannel::GetPrincipal: No channel principal [this=%p]",
this));
return nullptr;
}
// principals with unknown app ids do not work with the permission manager
if (requireAppId && mPrincipal->GetUnknownAppId()) {
LOG(("HttpBaseChannel::GetPrincipal: No app id [this=%p]", this));
return nullptr;
}
return mPrincipal;
}
// nsIRedirectHistory
NS_IMETHODIMP
HttpBaseChannel::GetRedirects(nsIArray * *aRedirects)
{
nsresult rv;
nsCOMPtr<nsIMutableArray> redirects =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
for (int i = 0; i < mRedirects.Count(); ++i) {
rv = redirects->AppendElement(mRedirects[i], false);
NS_ENSURE_SUCCESS(rv, rv);
}
*aRedirects = redirects;
NS_IF_ADDREF(*aRedirects);
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsITraceableChannel
//-----------------------------------------------------------------------------
@ -1899,6 +1962,25 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
"[this=%p] transferring chain of redirect cache-keys", this));
httpInternal->SetCacheKeysRedirectChain(mRedirectedCachekeys.forget());
}
// Transfer existing redirect information. Add all of our existing
// redirects to the new channel.
for (int32_t i = 0; i < mRedirects.Count(); ++i) {
#ifdef PR_LOGGING
nsCOMPtr<nsIURI> uri;
mRedirects[i]->GetURI(getter_AddRefs(uri));
nsCString spec;
uri->GetSpec(spec);
LOG(("HttpBaseChannel::SetupReplacementChannel adding redirect %s "
"[this=%p]", spec.get(), this));
#endif
httpInternal->AddRedirect(mRedirects[i]);
}
// Add our own principal to the redirect information on the new channel. If
// the redirect is vetoed, then newChannel->AsyncOpen won't be called.
// However, the new channel's redirect chain will still be complete.
nsCOMPtr<nsIPrincipal> principal = GetPrincipal(false);
httpInternal->AddRedirect(principal);
}
// transfer application cache information

View File

@ -19,6 +19,7 @@
#include "nsIHttpChannel.h"
#include "nsHttpHandler.h"
#include "nsIHttpChannelInternal.h"
#include "nsIRedirectHistory.h"
#include "nsIUploadChannel.h"
#include "nsIUploadChannel2.h"
#include "nsIProgressEventSink.h"
@ -53,6 +54,7 @@ class HttpBaseChannel : public nsHashPropertyBag
, public nsIEncodedChannel
, public nsIHttpChannel
, public nsIHttpChannelInternal
, public nsIRedirectHistory
, public nsIUploadChannel
, public nsIUploadChannel2
, public nsISupportsPriority
@ -67,6 +69,7 @@ public:
NS_DECL_NSIUPLOADCHANNEL2
NS_DECL_NSITRACEABLECHANNEL
NS_DECL_NSITIMEDCHANNEL
NS_DECL_NSIREDIRECTHISTORY
HttpBaseChannel();
virtual ~HttpBaseChannel();
@ -161,6 +164,7 @@ public:
NS_IMETHOD TakeAllSecurityMessages(nsCOMArray<nsISecurityConsoleMessage> &aMessages);
NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable);
NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable);
NS_IMETHOD AddRedirect(nsIPrincipal *aRedirect);
inline void CleanRedirectCacheChainIfNecessary()
{
@ -215,7 +219,7 @@ public: /* Necko internal use only... */
nsHttpRequestHead::ParsedMethodType method);
protected:
nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;
nsCOMArray<nsISecurityConsoleMessage> mSecurityConsoleMessages;
// Handle notifying listener, removing from loadgroup if request failed.
void DoNotifyListener();
@ -250,6 +254,11 @@ protected:
// Checks whether or not aURI and mOriginalURI share the same domain.
bool SameOriginWithOriginalUri(nsIURI *aURI);
// GetPrincipal
// Returns the channel principal. If requireAppId is true, then returns
// null if the principal has unknown appId.
nsIPrincipal *GetPrincipal(bool requireAppId);
friend class PrivateBrowsingChannel<HttpBaseChannel>;
nsCOMPtr<nsIURI> mURI;
@ -321,6 +330,8 @@ protected:
nsCOMPtr<nsIURI> mAPIRedirectToURI;
nsAutoPtr<nsTArray<nsCString> > mRedirectedCachekeys;
// Redirects added by previous channels.
nsCOMArray<nsIPrincipal> mRedirects;
uint32_t mProxyResolveFlags;
nsCOMPtr<nsIURI> mProxyURI;
@ -351,6 +362,8 @@ protected:
// copied from the transaction before we null out mTransaction
// so that the timing can still be queried from OnStopRequest
TimingStruct mTransactionTimings;
nsCOMPtr<nsIPrincipal> mPrincipal;
};
// Share some code while working around C++'s absurd inability to handle casting

View File

@ -613,7 +613,7 @@ nsHttpChannel::RetrieveSSLOptions()
if (!IsHTTPS() || mPrivateBrowsing)
return;
nsIPrincipal *principal = GetPrincipal();
nsIPrincipal *principal = GetPrincipal(true);
if (!principal)
return;
@ -1172,7 +1172,7 @@ nsHttpChannel::ProcessSSLInformation()
int16_t kea = ssl->GetKEAUsed();
nsIPrincipal *principal = GetPrincipal();
nsIPrincipal *principal = GetPrincipal(true);
if (!principal)
return;
@ -4235,7 +4235,8 @@ nsHttpChannel::ContinueProcessRedirection(nsresult rv)
{
AutoRedirectVetoNotifier notifier(this);
LOG(("ContinueProcessRedirection [rv=%x]\n", rv));
LOG(("nsHttpChannel::ContinueProcessRedirection [rv=%x,this=%p]\n", rv,
this));
if (NS_FAILED(rv))
return rv;
@ -6131,30 +6132,6 @@ nsHttpChannel::UpdateAggregateCallbacks()
mTransaction->SetSecurityCallbacks(callbacks);
}
nsIPrincipal *
nsHttpChannel::GetPrincipal()
{
if (mPrincipal)
return mPrincipal;
nsIScriptSecurityManager *securityManager =
nsContentUtils::GetSecurityManager();
if (!securityManager)
return nullptr;
securityManager->GetChannelPrincipal(this, getter_AddRefs(mPrincipal));
if (!mPrincipal)
return nullptr;
// principals with unknown app ids do not work with the permission manager
if (mPrincipal->GetUnknownAppId())
mPrincipal = nullptr;
return mPrincipal;
}
NS_IMETHODIMP
nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
{

View File

@ -424,8 +424,6 @@ private: // cache telemetry
bool mDidReval;
private:
nsIPrincipal *GetPrincipal();
nsCOMPtr<nsIPrincipal> mPrincipal;
bool mForcePending;
};

View File

@ -13,12 +13,13 @@ class nsCString;
[ptr] native StringArray(nsTArray<nsCString>);
[ref] native securityMessagesArray(nsCOMArray<nsISecurityConsoleMessage>);
interface nsISocketTransport;
interface nsIAsyncInputStream;
interface nsIAsyncOutputStream;
interface nsIURI;
interface nsIPrincipal;
interface nsIProxyInfo;
interface nsISecurityConsoleMessage;
interface nsISocketTransport;
interface nsIURI;
/**
* The callback interface for nsIHttpChannelInternal::HTTPUpgrade()
@ -37,7 +38,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(b733194f-6751-4876-a444-bca4ba3f2fcb)]
[scriptable, uuid(a4bf4fc5-b5a9-4098-bd20-409d71bf18e6)]
interface nsIHttpChannelInternal : nsISupports
{
/**
@ -192,4 +193,10 @@ interface nsIHttpChannelInternal : nsISupports
* May return null when redirectTo() has not been called.
*/
readonly attribute nsIURI apiRedirectToURI;
/**
* Add a new nsIPrincipal to the redirect chain. This is the only way to
* write to nsIRedirectHistory.redirects.
*/
void addRedirect(in nsIPrincipal aPrincipal);
};

View File

@ -0,0 +1,63 @@
Cu.import("resource://testing-common/httpd.js");
XPCOMUtils.defineLazyGetter(this, "URL", function() {
return "http://localhost:" + httpServer.identity.primaryPort;
});
var httpServer = null;
var randomPath = "/redirect/" + Math.random();
var redirects = [];
const numRedirects = 10;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function contentHandler(request, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
let chan = request.QueryInterface(Ci.nsIRedirectHistory);
do_check_eq(numRedirects - 1, chan.redirects.length);
for (let i = 0; i < numRedirects - 1; ++i) {
let principal = chan.redirects.queryElementAt(i, Ci.nsIPrincipal);
do_check_eq(URL + redirects[i], principal.URI.spec);
}
httpServer.stop(do_test_finished);
}
function redirectHandler(index, request, response) {
response.setStatusLine(request.httpVersion, 301, "Moved");
let path = redirects[index + 1];
response.setHeader("Location", URL + path, false);
}
function run_test()
{
httpServer = new HttpServer();
for (let i = 0; i < numRedirects; ++i) {
var randomPath = "/redirect/" + Math.random();
redirects.push(randomPath);
if (i < numRedirects - 1) {
httpServer.registerPathHandler(randomPath, redirectHandler.bind(this, i));
} else {
// The last one doesn't redirect
httpServer.registerPathHandler(redirects[numRedirects - 1],
contentHandler);
}
}
httpServer.start(-1);
var chan = make_channel(URL + redirects[0]);
chan.asyncOpen(new ChannelListener(finish_test, null), null);
do_test_pending();
}

View File

@ -324,3 +324,4 @@ skip-if = os == "android"
[test_signature_extraction.js]
run-if = os == "win"
[test_udp_multicast.js]
[test_redirect_history.js]

View File

@ -0,0 +1,7 @@
//
// Run test script in content process instead of chrome (xpcshell's default)
//
function run_test() {
run_test_in_child("../unit/test_redirect_history.js");
}

View File

@ -32,3 +32,4 @@ skip-if = true
[test_simple_wrap.js]
[test_xmlhttprequest_wrap.js]
[test_XHR_redirects.js]
[test_redirect_history_wrap.js]