mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset: 1a8fd714a794
Break out Access-Control code from nsXMLHttpRequest.cpp. r/sr=mrbkap b=459770 which leaks 280 kB.
This commit is contained in:
parent
284444879a
commit
c0f5d14cd9
@ -53,110 +53,8 @@
|
||||
#include "nsWhitespaceTokenizer.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsCommaSeparatedTokenizer.h"
|
||||
#include "prclist.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "prtime.h"
|
||||
|
||||
// =========================================================================
|
||||
// Support classes
|
||||
|
||||
// Class implementing the preflight request cache
|
||||
#define ACCESS_CONTROL_CACHE_SIZE 100
|
||||
|
||||
class nsACPreflightCache
|
||||
{
|
||||
public:
|
||||
static void Shutdown()
|
||||
{
|
||||
PR_INIT_CLIST(&mList);
|
||||
delete mTable;
|
||||
mTable = nsnull;
|
||||
}
|
||||
|
||||
struct TokenTime
|
||||
{
|
||||
nsCString token;
|
||||
PRTime expirationTime;
|
||||
};
|
||||
|
||||
class CacheEntry : public PRCList
|
||||
{
|
||||
public:
|
||||
CacheEntry(nsCString& aKey)
|
||||
: mKey(aKey)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsACPreflightCache::CacheEntry);
|
||||
}
|
||||
|
||||
~CacheEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsACPreflightCache::CacheEntry);
|
||||
}
|
||||
|
||||
void PurgeExpired(PRTime now);
|
||||
PRBool CheckRequest(const nsCString& aMethod,
|
||||
const nsTArray<nsCString>& aCustomHeaders);
|
||||
|
||||
nsCString mKey;
|
||||
nsTArray<TokenTime> mMethods;
|
||||
nsTArray<TokenTime> mHeaders;
|
||||
};
|
||||
|
||||
|
||||
static CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, PRBool aCreate);
|
||||
|
||||
private:
|
||||
PR_STATIC_CALLBACK(PLDHashOperator)
|
||||
RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
|
||||
void* aUserData);
|
||||
|
||||
static PRBool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, nsACString& _retval);
|
||||
|
||||
static nsClassHashtable<nsCStringHashKey, CacheEntry>* mTable;
|
||||
static PRCList mList;
|
||||
};
|
||||
|
||||
|
||||
// Class used as streamlistener and notification callback when
|
||||
// doing the initial GET request for an access-control check
|
||||
|
||||
class nsACPreflightListener : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink
|
||||
{
|
||||
public:
|
||||
nsACPreflightListener(nsIChannel* aOuterChannel,
|
||||
nsIStreamListener* aOuterListener,
|
||||
nsISupports* aOuterContext,
|
||||
nsIPrincipal* aReferrerPrincipal,
|
||||
PRBool aWithCredentials)
|
||||
: mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
|
||||
mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
|
||||
mWithCredentials(aWithCredentials)
|
||||
{ }
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
|
||||
private:
|
||||
void AddResultToCache(nsIRequest* aRequest);
|
||||
|
||||
nsCOMPtr<nsIChannel> mOuterChannel;
|
||||
nsCOMPtr<nsIStreamListener> mOuterListener;
|
||||
nsCOMPtr<nsISupports> mOuterContext;
|
||||
nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
|
||||
PRBool mWithCredentials;
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// nsCrossSiteListenerProxy
|
||||
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
|
||||
|
||||
NS_IMPL_ISUPPORTS4(nsCrossSiteListenerProxy, nsIStreamListener,
|
||||
nsIRequestObserver, nsIChannelEventSink,
|
||||
@ -203,128 +101,6 @@ nsCrossSiteListenerProxy::nsCrossSiteListenerProxy(nsIStreamListener* aOuter,
|
||||
*aResult = UpdateChannel(aChannel);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCrossSiteListenerProxy::CheckPreflight(nsIHttpChannel* aRequestChannel,
|
||||
nsIStreamListener* aRequestListener,
|
||||
nsISupports* aRequestContext,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
PRBool aForcePreflight,
|
||||
nsTArray<nsCString>& aUnsafeHeaders,
|
||||
PRBool aWithCredentials,
|
||||
PRBool* aPreflighted,
|
||||
nsIChannel** aPreflightChannel,
|
||||
nsIStreamListener** aPreflightListener)
|
||||
{
|
||||
*aPreflighted = PR_FALSE;
|
||||
|
||||
nsCAutoString method;
|
||||
aRequestChannel->GetRequestMethod(method);
|
||||
|
||||
if (!aUnsafeHeaders.IsEmpty() || aForcePreflight) {
|
||||
*aPreflighted = PR_TRUE;
|
||||
}
|
||||
else if (method.LowerCaseEqualsLiteral("post")) {
|
||||
nsCAutoString contentTypeHeader;
|
||||
aRequestChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
|
||||
contentTypeHeader);
|
||||
|
||||
nsCAutoString contentType, charset;
|
||||
NS_ParseContentType(contentTypeHeader, contentType, charset);
|
||||
if (!contentType.LowerCaseEqualsLiteral("text/plain")) {
|
||||
*aPreflighted = PR_TRUE;
|
||||
}
|
||||
}
|
||||
else if (!method.LowerCaseEqualsLiteral("get")) {
|
||||
*aPreflighted = PR_TRUE;
|
||||
}
|
||||
|
||||
if (!*aPreflighted) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If so, set up the preflight
|
||||
|
||||
// Check to see if this initial OPTIONS request has already been cached
|
||||
// in our special Access Control Cache.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aRequestChannel->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsACPreflightCache::CacheEntry* entry =
|
||||
nsACPreflightCache::GetEntry(uri, aRequestingPrincipal,
|
||||
aWithCredentials, PR_FALSE);
|
||||
|
||||
if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Either it wasn't cached or the cached result has expired. Build a
|
||||
// channel for the OPTIONS request.
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsLoadFlags loadFlags;
|
||||
rv = aRequestChannel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIChannel> preflightChannel;
|
||||
rv = NS_NewChannel(getter_AddRefs(preflightChannel), uri, nsnull,
|
||||
loadGroup, nsnull, loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> preflightHttp = do_QueryInterface(preflightChannel);
|
||||
NS_ASSERTION(preflightHttp, "Failed to QI to nsIHttpChannel!");
|
||||
|
||||
rv = preflightHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Add channel headers
|
||||
rv = preflightHttp->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
|
||||
method, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!aUnsafeHeaders.IsEmpty()) {
|
||||
nsCAutoString headers;
|
||||
for (PRUint32 i = 0; i < aUnsafeHeaders.Length(); ++i) {
|
||||
if (i != 0) {
|
||||
headers += ',';
|
||||
}
|
||||
headers += aUnsafeHeaders[i];
|
||||
}
|
||||
rv = preflightHttp->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
|
||||
headers, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Set up listener
|
||||
nsCOMPtr<nsIStreamListener> preflightListener =
|
||||
new nsACPreflightListener(aRequestChannel, aRequestListener,
|
||||
aRequestContext, aRequestingPrincipal,
|
||||
aWithCredentials);
|
||||
NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
preflightListener =
|
||||
new nsCrossSiteListenerProxy(preflightListener, aRequestingPrincipal,
|
||||
preflightChannel, aWithCredentials, method,
|
||||
aUnsafeHeaders, &rv);
|
||||
NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
preflightChannel.swap(*aPreflightChannel);
|
||||
preflightListener.swap(*aPreflightListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsCrossSiteListenerProxy::ShutdownPreflightCache()
|
||||
{
|
||||
nsACPreflightCache::Shutdown();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest,
|
||||
nsISupports* aContext)
|
||||
@ -608,6 +384,28 @@ nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)
|
||||
rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Add preflight headers if this is a preflight request
|
||||
if (mIsPreflight) {
|
||||
rv = http->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
|
||||
mPreflightMethod, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!mPreflightHeaders.IsEmpty()) {
|
||||
nsCAutoString headers;
|
||||
for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) {
|
||||
if (i != 0) {
|
||||
headers += ',';
|
||||
}
|
||||
headers += mPreflightHeaders[i];
|
||||
}
|
||||
rv = http->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
|
||||
headers, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
// Make cookie-less if needed
|
||||
if (mIsPreflight || !mWithCredentials) {
|
||||
nsLoadFlags flags;
|
||||
@ -621,387 +419,3 @@ nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// nsACPreflightListener
|
||||
|
||||
NS_IMPL_ISUPPORTS4(nsACPreflightListener, nsIStreamListener, nsIRequestObserver,
|
||||
nsIInterfaceRequestor, nsIChannelEventSink)
|
||||
|
||||
void
|
||||
nsACPreflightListener::AddResultToCache(nsIRequest *aRequest)
|
||||
{
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
NS_ASSERTION(http, "Request was not http");
|
||||
|
||||
// The "Access-Control-Max-Age" header should return an age in seconds.
|
||||
nsCAutoString headerVal;
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"),
|
||||
headerVal);
|
||||
if (headerVal.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanitize the string. We only allow 'delta-seconds' as specified by
|
||||
// http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or
|
||||
// trailing non-whitespace characters).
|
||||
PRUint32 age = 0;
|
||||
nsCSubstring::const_char_iterator iter, end;
|
||||
headerVal.BeginReading(iter);
|
||||
headerVal.EndReading(end);
|
||||
while (iter != end) {
|
||||
if (*iter < '0' || *iter > '9') {
|
||||
return;
|
||||
}
|
||||
age = age * 10 + (*iter - '0');
|
||||
// Cap at 24 hours. This also avoids overflow
|
||||
age = PR_MIN(age, 86400);
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (!age) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// String seems fine, go ahead and cache.
|
||||
// Note that we have already checked that these headers follow the correct
|
||||
// syntax.
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
http->GetURI(getter_AddRefs(uri));
|
||||
|
||||
// PR_Now gives microseconds
|
||||
PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC;
|
||||
|
||||
nsACPreflightCache::CacheEntry* entry =
|
||||
nsACPreflightCache::GetEntry(uri, mReferrerPrincipal, mWithCredentials,
|
||||
PR_TRUE);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Methods" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
|
||||
headerVal);
|
||||
|
||||
nsCCommaSeparatedTokenizer methods(headerVal);
|
||||
while(methods.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& method = methods.nextToken();
|
||||
if (method.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
PRUint32 i;
|
||||
for (i = 0; i < entry->mMethods.Length(); ++i) {
|
||||
if (entry->mMethods[i].token.Equals(method)) {
|
||||
entry->mMethods[i].expirationTime = expirationTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == entry->mMethods.Length()) {
|
||||
nsACPreflightCache::TokenTime* newMethod =
|
||||
entry->mMethods.AppendElement();
|
||||
if (!newMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
newMethod->token = method;
|
||||
newMethod->expirationTime = expirationTime;
|
||||
}
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Headers" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
|
||||
headerVal);
|
||||
|
||||
nsCCommaSeparatedTokenizer headers(headerVal);
|
||||
while(headers.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& header = headers.nextToken();
|
||||
if (header.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
PRUint32 i;
|
||||
for (i = 0; i < entry->mHeaders.Length(); ++i) {
|
||||
if (entry->mHeaders[i].token.Equals(header)) {
|
||||
entry->mHeaders[i].expirationTime = expirationTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == entry->mHeaders.Length()) {
|
||||
nsACPreflightCache::TokenTime* newHeader =
|
||||
entry->mHeaders.AppendElement();
|
||||
if (!newHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
newHeader->token = header;
|
||||
newHeader->expirationTime = expirationTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACPreflightListener::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = status;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Everything worked, try to cache and then fire off the actual request.
|
||||
AddResultToCache(aRequest);
|
||||
|
||||
rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mOuterChannel->Cancel(rv);
|
||||
mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
|
||||
mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACPreflightListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACPreflightListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *ctxt,
|
||||
nsIInputStream *inStr,
|
||||
PRUint32 sourceOffset,
|
||||
PRUint32 count)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACPreflightListener::OnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
// No redirects allowed for now.
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsACPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// nsACPreflightCache
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, nsACPreflightCache::CacheEntry>*
|
||||
nsACPreflightCache::mTable;
|
||||
PRCList nsACPreflightCache::mList;
|
||||
|
||||
void
|
||||
nsACPreflightCache::CacheEntry::PurgeExpired(PRTime now)
|
||||
{
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mMethods.Length(); ++i) {
|
||||
if (now >= mMethods[i].expirationTime) {
|
||||
mMethods.RemoveElementAt(i--);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < mHeaders.Length(); ++i) {
|
||||
if (now >= mHeaders[i].expirationTime) {
|
||||
mHeaders.RemoveElementAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsACPreflightCache::CacheEntry::CheckRequest(const nsCString& aMethod,
|
||||
const nsTArray<nsCString>& aHeaders)
|
||||
{
|
||||
PurgeExpired(PR_Now());
|
||||
|
||||
if (!aMethod.EqualsLiteral("GET") && !aMethod.EqualsLiteral("POST")) {
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mMethods.Length(); ++i) {
|
||||
if (aMethod.Equals(mMethods[i].token))
|
||||
break;
|
||||
}
|
||||
if (i == mMethods.Length()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
for (PRUint32 i = 0; i < aHeaders.Length(); ++i) {
|
||||
PRUint32 j;
|
||||
for (j = 0; j < mHeaders.Length(); ++j) {
|
||||
if (aHeaders[i].Equals(mHeaders[j].token,
|
||||
nsCaseInsensitiveCStringComparator())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == mHeaders.Length()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
nsACPreflightCache::CacheEntry*
|
||||
nsACPreflightCache::GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, PRBool aCreate)
|
||||
{
|
||||
if (!mTable) {
|
||||
if (!aCreate) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PR_INIT_CLIST(&mList);
|
||||
|
||||
mTable = new nsClassHashtable<nsCStringHashKey, CacheEntry>;
|
||||
NS_ENSURE_TRUE(mTable, nsnull);
|
||||
|
||||
if (!mTable->Init()) {
|
||||
delete mTable;
|
||||
mTable = nsnull;
|
||||
return nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
nsCString key;
|
||||
if (!GetCacheKey(aURI, aPrincipal, aWithCredentials, key)) {
|
||||
NS_WARNING("Invalid cache key!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
CacheEntry* entry;
|
||||
|
||||
if (mTable->Get(key, &entry)) {
|
||||
// Entry already existed so just return it. Also update the LRU list.
|
||||
|
||||
// Move to the head of the list.
|
||||
PR_REMOVE_LINK(entry);
|
||||
PR_INSERT_LINK(entry, &mList);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (!aCreate) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// This is a new entry, allocate and insert into the table now so that any
|
||||
// failures don't cause items to be removed from a full cache.
|
||||
entry = new CacheEntry(key);
|
||||
if (!entry) {
|
||||
NS_WARNING("Failed to allocate new cache entry!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (!mTable->Put(key, entry)) {
|
||||
// Failed, clean up the new entry.
|
||||
delete entry;
|
||||
|
||||
NS_WARNING("Failed to add entry to the access control cache!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PR_INSERT_LINK(entry, &mList);
|
||||
|
||||
NS_ASSERTION(mTable->Count() <= ACCESS_CONTROL_CACHE_SIZE + 1,
|
||||
"Something is borked, too many entries in the cache!");
|
||||
|
||||
// Now enforce the max count.
|
||||
if (mTable->Count() > ACCESS_CONTROL_CACHE_SIZE) {
|
||||
// Try to kick out all the expired entries.
|
||||
PRTime now = PR_Now();
|
||||
mTable->Enumerate(RemoveExpiredEntries, &now);
|
||||
|
||||
// If that didn't remove anything then kick out the least recently used
|
||||
// entry.
|
||||
if (mTable->Count() > ACCESS_CONTROL_CACHE_SIZE) {
|
||||
CacheEntry* lruEntry = static_cast<CacheEntry*>(PR_LIST_TAIL(&mList));
|
||||
PR_REMOVE_LINK(lruEntry);
|
||||
|
||||
// This will delete 'lruEntry'.
|
||||
mTable->Remove(lruEntry->mKey);
|
||||
|
||||
NS_ASSERTION(mTable->Count() >= ACCESS_CONTROL_CACHE_SIZE,
|
||||
"Somehow tried to remove an entry that was never added!");
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* static */ PR_CALLBACK PLDHashOperator
|
||||
nsACPreflightCache::RemoveExpiredEntries(const nsACString& aKey,
|
||||
nsAutoPtr<CacheEntry>& aValue,
|
||||
void* aUserData)
|
||||
{
|
||||
PRTime* now = static_cast<PRTime*>(aUserData);
|
||||
|
||||
aValue->PurgeExpired(*now);
|
||||
|
||||
if (aValue->mHeaders.IsEmpty() &&
|
||||
aValue->mHeaders.IsEmpty()) {
|
||||
// Expired, remove from the list as well as the hash table.
|
||||
PR_REMOVE_LINK(aValue);
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
/* static */ PRBool
|
||||
nsACPreflightCache::GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, nsACString& _retval)
|
||||
{
|
||||
NS_ASSERTION(aURI, "Null uri!");
|
||||
NS_ASSERTION(aPrincipal, "Null principal!");
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(space, " ");
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
nsCAutoString scheme, host, port;
|
||||
if (uri) {
|
||||
uri->GetScheme(scheme);
|
||||
uri->GetHost(host);
|
||||
port.AppendInt(NS_GetRealPort(uri));
|
||||
}
|
||||
|
||||
nsCAutoString cred;
|
||||
if (aWithCredentials) {
|
||||
_retval.AssignLiteral("cred");
|
||||
}
|
||||
else {
|
||||
_retval.AssignLiteral("nocred");
|
||||
}
|
||||
|
||||
nsCAutoString spec;
|
||||
rv = aURI->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
_retval.Assign(cred + space + scheme + space + host + space + port + space +
|
||||
spec);
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
@ -39,13 +39,17 @@
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIContentSink.h"
|
||||
#include "nsIXMLContentSink.h"
|
||||
#include "nsIExpatSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
|
||||
class nsIURI;
|
||||
class nsIParser;
|
||||
class nsIPrincipal;
|
||||
class nsIHttpChannel;
|
||||
|
||||
extern PRBool
|
||||
IsValidHTTPToken(const nsCSubstring& aToken);
|
||||
@ -68,19 +72,6 @@ public:
|
||||
const nsTArray<nsCString>& aPreflightHeaders,
|
||||
nsresult* aResult);
|
||||
|
||||
static nsresult CheckPreflight(nsIHttpChannel* aRequestChannel,
|
||||
nsIStreamListener* aRequestListener,
|
||||
nsISupports* aRequestContext,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
PRBool aForcePreflight,
|
||||
nsTArray<nsCString>& aUnsafeHeaders,
|
||||
PRBool aWithCredentials,
|
||||
PRBool* aPreflighted,
|
||||
nsIChannel** aPreflightChannel,
|
||||
nsIStreamListener** aPreflightListener);
|
||||
|
||||
static void ShutdownPreflightCache();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -64,6 +64,9 @@
|
||||
#include "nsIJSNativeInitializer.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDOMLSProgressEvent.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "prclist.h"
|
||||
#include "prtime.h"
|
||||
#include "nsIEventListenerManager.h"
|
||||
#include "nsIDOMNSEvent.h"
|
||||
@ -72,6 +75,71 @@
|
||||
|
||||
class nsILoadGroup;
|
||||
|
||||
class nsAccessControlLRUCache
|
||||
{
|
||||
public:
|
||||
struct TokenTime
|
||||
{
|
||||
nsCString token;
|
||||
PRTime expirationTime;
|
||||
};
|
||||
|
||||
struct CacheEntry : public PRCList
|
||||
{
|
||||
CacheEntry(nsCString& aKey)
|
||||
: mKey(aKey)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsAccessControlLRUCache::CacheEntry);
|
||||
}
|
||||
|
||||
~CacheEntry()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsAccessControlLRUCache::CacheEntry);
|
||||
}
|
||||
|
||||
void PurgeExpired(PRTime now);
|
||||
PRBool CheckRequest(const nsCString& aMethod,
|
||||
const nsTArray<nsCString>& aCustomHeaders);
|
||||
|
||||
nsCString mKey;
|
||||
nsTArray<TokenTime> mMethods;
|
||||
nsTArray<TokenTime> mHeaders;
|
||||
};
|
||||
|
||||
nsAccessControlLRUCache()
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsAccessControlLRUCache);
|
||||
PR_INIT_CLIST(&mList);
|
||||
}
|
||||
|
||||
~nsAccessControlLRUCache()
|
||||
{
|
||||
Clear();
|
||||
MOZ_COUNT_DTOR(nsAccessControlLRUCache);
|
||||
}
|
||||
|
||||
PRBool Initialize()
|
||||
{
|
||||
return mTable.Init();
|
||||
}
|
||||
|
||||
CacheEntry* GetEntry(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, PRBool aCreate);
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
PR_STATIC_CALLBACK(PLDHashOperator)
|
||||
RemoveExpiredEntries(const nsACString& aKey, nsAutoPtr<CacheEntry>& aValue,
|
||||
void* aUserData);
|
||||
|
||||
static PRBool GetCacheKey(nsIURI* aURI, nsIPrincipal* aPrincipal,
|
||||
PRBool aWithCredentials, nsACString& _retval);
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
|
||||
PRCList mList;
|
||||
};
|
||||
|
||||
class nsDOMEventListenerWrapper : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
@ -265,12 +333,35 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXMLHttpRequest,
|
||||
nsXHREventTarget)
|
||||
|
||||
static PRBool EnsureACCache()
|
||||
{
|
||||
if (sAccessControlCache)
|
||||
return PR_TRUE;
|
||||
|
||||
nsAutoPtr<nsAccessControlLRUCache> newCache(new nsAccessControlLRUCache());
|
||||
NS_ENSURE_TRUE(newCache, PR_FALSE);
|
||||
|
||||
if (newCache->Initialize()) {
|
||||
sAccessControlCache = newCache.forget();
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
static void ShutdownACCache()
|
||||
{
|
||||
delete sAccessControlCache;
|
||||
sAccessControlCache = nsnull;
|
||||
}
|
||||
|
||||
PRBool AllowUploadProgress();
|
||||
|
||||
static nsAccessControlLRUCache* sAccessControlCache;
|
||||
|
||||
protected:
|
||||
friend class nsMultipartProxyListener;
|
||||
|
||||
nsresult SetUploadDataAndType(nsIVariant* aBody);
|
||||
nsresult DetectCharset(nsACString& aCharset);
|
||||
nsresult ConvertBodyToText(nsAString& aOutBuffer);
|
||||
static NS_METHOD StreamReaderFunc(nsIInputStream* in,
|
||||
@ -283,6 +374,8 @@ protected:
|
||||
// determines if the onreadystatechange listener should be called.
|
||||
nsresult ChangeState(PRUint32 aState, PRBool aBroadcast = PR_TRUE);
|
||||
nsresult RequestCompleted();
|
||||
nsresult GetLoadGroup(nsILoadGroup **aLoadGroup);
|
||||
nsIURI *GetBaseURI();
|
||||
|
||||
nsresult RemoveAddEventListener(const nsAString& aType,
|
||||
nsRefPtr<nsDOMEventListenerWrapper>& aCurrent,
|
||||
@ -307,7 +400,7 @@ protected:
|
||||
// mReadRequest is different from mChannel for multipart requests
|
||||
nsCOMPtr<nsIRequest> mReadRequest;
|
||||
nsCOMPtr<nsIDOMDocument> mResponseXML;
|
||||
nsCOMPtr<nsIChannel> mACPreflightChannel;
|
||||
nsCOMPtr<nsIChannel> mACGetChannel;
|
||||
nsTArray<nsCString> mACUnsafeHeaders;
|
||||
|
||||
nsRefPtr<nsDOMEventListenerWrapper> mOnUploadProgressListener;
|
||||
|
@ -83,7 +83,6 @@
|
||||
#include "nsXMLHttpRequest.h"
|
||||
#include "nsIFocusEventSuppressor.h"
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsCrossSiteListenerProxy.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsXULPopupManager.h"
|
||||
@ -347,7 +346,7 @@ nsLayoutStatics::Shutdown()
|
||||
nsAudioStream::ShutdownLibrary();
|
||||
#endif
|
||||
|
||||
nsCrossSiteListenerProxy::ShutdownPreflightCache();
|
||||
nsXMLHttpRequest::ShutdownACCache();
|
||||
}
|
||||
|
||||
void
|
||||
|
Loading…
Reference in New Issue
Block a user