Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Bug 464954: Update Access-Control implementation to latest draft and …
…fix some bugs. r/sr=bz
  • Loading branch information
sicking committed Jan 21, 2009
1 parent f204f72 commit 2c8b4e0
Show file tree
Hide file tree
Showing 15 changed files with 627 additions and 154 deletions.
20 changes: 20 additions & 0 deletions content/base/public/nsContentUtils.h
Expand Up @@ -1359,6 +1359,26 @@ class nsContentUtils
{
return sThreadJSContextStack;
}


/**
* Get the Origin of the passed in nsIPrincipal or nsIURI. If the passed in
* nsIURI or the URI of the passed in nsIPrincipal does not have a host, the
* origin is set to 'null'.
*
* The ASCII versions return a ASCII strings that are puny-code encoded,
* suitable for for example header values. The UTF versions return strings
* containing international characters.
*
* aPrincipal/aOrigin must not be null.
*/
static nsresult GetASCIIOrigin(nsIPrincipal* aPrincipal,
nsCString& aOrigin);
static nsresult GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin);
static nsresult GetUTFOrigin(nsIPrincipal* aPrincipal,
nsString& aOrigin);
static nsresult GetUTFOrigin(nsIURI* aURI, nsString& aOrigin);

private:

static PRBool InitializeEventTable();
Expand Down
119 changes: 119 additions & 0 deletions content/base/src/nsContentUtils.cpp
Expand Up @@ -4525,6 +4525,125 @@ nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
return QueryInterface(aIID, aResult);
}

/* static */
nsresult
nsContentUtils::GetASCIIOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin)
{
NS_PRECONDITION(aPrincipal, "missing principal");

aOrigin.Truncate();

nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);

if (uri) {
return GetASCIIOrigin(uri, aOrigin);
}

aOrigin.AssignLiteral("null");

return NS_OK;
}

/* static */
nsresult
nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin)
{
NS_PRECONDITION(aURI, "missing uri");

aOrigin.Truncate();

nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);

nsCString host;
nsresult rv = uri->GetAsciiHost(host);

if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);

aOrigin = scheme + NS_LITERAL_CSTRING("://") + host;

// If needed, append the port
PRInt32 port;
uri->GetPort(&port);
if (port != -1) {
PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
if (port != defaultPort) {
aOrigin.Append(':');
aOrigin.AppendInt(port);
}
}
}
else {
aOrigin.AssignLiteral("null");
}

return NS_OK;
}

/* static */
nsresult
nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin)
{
NS_PRECONDITION(aPrincipal, "missing principal");

aOrigin.Truncate();

nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);

if (uri) {
return GetUTFOrigin(uri, aOrigin);
}

aOrigin.AssignLiteral("null");

return NS_OK;
}

/* static */
nsresult
nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsString& aOrigin)
{
NS_PRECONDITION(aURI, "missing uri");

aOrigin.Truncate();

nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);

nsCString host;
nsresult rv = uri->GetHost(host);

if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);

aOrigin = NS_ConvertUTF8toUTF16(scheme + NS_LITERAL_CSTRING("://") + host);

// If needed, append the port
PRInt32 port;
uri->GetPort(&port);
if (port != -1) {
PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
if (port != defaultPort) {
aOrigin.Append(':');
aOrigin.AppendInt(port);
}
}
}
else {
aOrigin.AssignLiteral("null");
}

return NS_OK;
}
nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
: mString(aString), mService(nsnull)
{
Expand Down
70 changes: 21 additions & 49 deletions content/base/src/nsCrossSiteListenerProxy.cpp
Expand Up @@ -105,7 +105,7 @@ NS_IMETHODIMP
nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest));
mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest, PR_FALSE));
if (!mRequestApproved) {
aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
mOuterListener->OnStartRequest(aRequest, aContext);
Expand Down Expand Up @@ -157,45 +157,8 @@ IsValidHTTPToken(const nsCSubstring& aToken)
}

nsresult
GetOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin)
{
aOrigin.AssignLiteral("null");

nsCString host;
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);

rv = uri->GetAsciiHost(host);
NS_ENSURE_SUCCESS(rv, rv);

if (!host.IsEmpty()) {
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);

aOrigin = scheme + NS_LITERAL_CSTRING("://") + host;

// If needed, append the port
PRInt32 port;
uri->GetPort(&port);
if (port != -1) {
PRInt32 defaultPort = NS_GetDefaultPort(scheme.get());
if (port != defaultPort) {
aOrigin.Append(":");
aOrigin.AppendInt(port);
}
}
}
else {
aOrigin.AssignLiteral("null");
}

return NS_OK;
}

nsresult
nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest,
PRBool aIsRedirect)
{
// Check if this was actually a cross domain request
if (!mHasBeenCrossSite) {
Expand All @@ -212,10 +175,14 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI);

PRBool succeeded;
rv = http->GetRequestSucceeded(&succeeded);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(succeeded, NS_ERROR_DOM_BAD_URI);
// Redirects aren't success-codes. But necko already checked that it was a
// valid redirect.
if (!aIsRedirect) {
PRBool succeeded;
rv = http->GetRequestSucceeded(&succeeded);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(succeeded, NS_ERROR_DOM_BAD_URI);
}

// Check the Access-Control-Allow-Origin header
nsCAutoString allowedOriginHeader;
Expand All @@ -225,10 +192,11 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest)

if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) {
nsCAutoString origin;
rv = GetOrigin(mRequestingPrincipal, origin);
rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin);
NS_ENSURE_SUCCESS(rv, rv);

if (!allowedOriginHeader.Equals(origin)) {
if (!allowedOriginHeader.Equals(origin) ||
origin.EqualsLiteral("null")) {
return NS_ERROR_DOM_BAD_URI;
}
}
Expand Down Expand Up @@ -268,6 +236,7 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest)

// The "Access-Control-Allow-Headers" header contains a comma separated
// list of header names.
headerVal.Truncate();
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
headerVal);
nsTArray<nsCString> headers;
Expand Down Expand Up @@ -335,8 +304,11 @@ nsCrossSiteListenerProxy::OnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags)
{
nsresult rv = CheckRequestApproved(aOldChannel);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv;
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
rv = CheckRequestApproved(aOldChannel, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
}

nsCOMPtr<nsIChannelEventSink> outer =
do_GetInterface(mOuterNotificationCallbacks);
Expand Down Expand Up @@ -387,7 +359,7 @@ nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel)

// Add the Origin header
nsCAutoString origin;
rv = GetOrigin(mRequestingPrincipal, origin);
rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin);
NS_ENSURE_SUCCESS(rv, rv);

nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
Expand Down
2 changes: 1 addition & 1 deletion content/base/src/nsCrossSiteListenerProxy.h
Expand Up @@ -77,7 +77,7 @@ class nsCrossSiteListenerProxy : public nsIStreamListener,

private:
nsresult UpdateChannel(nsIChannel* aChannel);
nsresult CheckRequestApproved(nsIRequest* aRequest);
nsresult CheckRequestApproved(nsIRequest* aRequest, PRBool aIsRedirect);

nsCOMPtr<nsIStreamListener> mOuterListener;
nsCOMPtr<nsIPrincipal> mRequestingPrincipal;
Expand Down
24 changes: 14 additions & 10 deletions content/base/src/nsXMLHttpRequest.cpp
Expand Up @@ -489,8 +489,9 @@ nsACProxyListener::OnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags)
{
// No redirects allowed for now.
return NS_ERROR_DOM_BAD_URI;
// Only internal redirects allowed for now.
return NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) ?
NS_OK : NS_ERROR_DOM_BAD_URI;
}

NS_IMETHODIMP
Expand Down Expand Up @@ -2634,7 +2635,8 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
}
}
else if (!method.LowerCaseEqualsLiteral("get")) {
else if (!method.LowerCaseEqualsLiteral("get") &&
!method.LowerCaseEqualsLiteral("head")) {
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
}

Expand Down Expand Up @@ -3111,14 +3113,16 @@ nsXMLHttpRequest::OnChannelRedirect(nsIChannel *aOldChannel,

nsresult rv;

rv = CheckChannelForCrossSiteRequest(aNewChannel);
NS_ENSURE_SUCCESS(rv, rv);
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
rv = CheckChannelForCrossSiteRequest(aNewChannel);
NS_ENSURE_SUCCESS(rv, rv);

// Disable redirects for preflighted cross-site requests entirely for now
// Note, do this after the call to CheckChannelForCrossSiteRequest
// to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
return NS_ERROR_DOM_BAD_URI;
// Disable redirects for preflighted cross-site requests entirely for now
// Note, do this after the call to CheckChannelForCrossSiteRequest
// to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
return NS_ERROR_DOM_BAD_URI;
}
}

if (mChannelEventSink) {
Expand Down
2 changes: 2 additions & 0 deletions content/base/test/Makefile.in
Expand Up @@ -246,6 +246,8 @@ _TEST_FILES = test_bug5141.html \
bug457746.sjs \
test_CrossSiteXHR.html \
file_CrossSiteXHR_inner.html \
file_CrossSiteXHR_inner_data.sjs \
file_CrossSiteXHR_inner.jar \
file_CrossSiteXHR_server.sjs \
test_CrossSiteXHR_cache.html \
file_CrossSiteXHR_cache_server.sjs \
Expand Down
8 changes: 7 additions & 1 deletion content/base/test/file_CrossSiteXHR_inner.html
@@ -1,8 +1,14 @@
<!DOCTYPE HTML>
<!--
NOTE! The content of this file is duplicated in file_CrossSiteXHR_inner.jar
and file_CrossSiteXHR_inner_data.sjs
Please update those files if you update this one.
-->

<html>
<head>
<script>
window.addEventListener('message', function(e) {
window.addEventListener("message", function(e) {

sendData = null;

Expand Down
Binary file added content/base/test/file_CrossSiteXHR_inner.jar
Binary file not shown.

0 comments on commit 2c8b4e0

Please sign in to comment.