2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2001
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Akkana Peck <akkana@netscape.com> (original author)
|
|
|
|
* Darin Fisher <darin@meer.net>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsURIChecker.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIAuthPrompt.h"
|
|
|
|
#include "nsIHttpChannel.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsString.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static PRBool
|
|
|
|
ServerIsNES3x(nsIHttpChannel *httpChannel)
|
|
|
|
{
|
|
|
|
nsCAutoString server;
|
|
|
|
httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
|
|
|
|
// case sensitive string comparison is OK here. the server string
|
|
|
|
// is a well-known value, so we should not have to worry about it
|
|
|
|
// being case-smashed or otherwise case-mutated.
|
|
|
|
return StringBeginsWith(server,
|
|
|
|
NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2007-08-19 20:19:11 -07:00
|
|
|
NS_IMPL_ISUPPORTS6(nsURIChecker,
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIURIChecker,
|
|
|
|
nsIRequest,
|
2007-08-19 20:19:11 -07:00
|
|
|
nsIRequestObserver,
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIStreamListener,
|
|
|
|
nsIChannelEventSink,
|
|
|
|
nsIInterfaceRequestor)
|
|
|
|
|
|
|
|
nsURIChecker::nsURIChecker()
|
|
|
|
: mStatus(NS_OK)
|
|
|
|
, mIsPending(PR_FALSE)
|
|
|
|
, mAllowHead(PR_TRUE)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
|
|
|
|
{
|
|
|
|
mStatus = aStatus;
|
|
|
|
mIsPending = PR_FALSE;
|
|
|
|
|
|
|
|
if (mObserver) {
|
|
|
|
mObserver->OnStartRequest(this, mObserverContext);
|
|
|
|
mObserver->OnStopRequest(this, mObserverContext, mStatus);
|
|
|
|
mObserver = nsnull;
|
|
|
|
mObserverContext = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsURIChecker::CheckStatus()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mChannel, "no channel");
|
|
|
|
|
|
|
|
nsresult status;
|
|
|
|
nsresult rv = mChannel->GetStatus(&status);
|
|
|
|
// DNS errors and other obvious problems will return failure status
|
|
|
|
if (NS_FAILED(rv) || NS_FAILED(status))
|
|
|
|
return NS_BINDING_FAILED;
|
|
|
|
|
|
|
|
// If status is zero, it might still be an error if it's http:
|
|
|
|
// http has data even when there's an error like a 404.
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
|
|
|
|
if (!httpChannel)
|
|
|
|
return NS_BINDING_SUCCEEDED;
|
|
|
|
|
|
|
|
PRUint32 responseStatus;
|
|
|
|
rv = httpChannel->GetResponseStatus(&responseStatus);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return NS_BINDING_FAILED;
|
|
|
|
|
|
|
|
// If it's between 200-299, it's valid:
|
|
|
|
if (responseStatus / 100 == 2)
|
|
|
|
return NS_BINDING_SUCCEEDED;
|
|
|
|
|
|
|
|
// If we got a 404 (not found), we need some extra checking:
|
|
|
|
// toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
|
|
|
|
// hosted http://www.mozilla.org, generate a 404 and will have to be
|
|
|
|
// retried without the head.
|
|
|
|
if (responseStatus == 404) {
|
|
|
|
if (mAllowHead && ServerIsNES3x(httpChannel)) {
|
|
|
|
mAllowHead = PR_FALSE;
|
|
|
|
|
|
|
|
// save the current value of mChannel in case we can't issue
|
|
|
|
// the new request for some reason.
|
|
|
|
nsCOMPtr<nsIChannel> lastChannel = mChannel;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
PRUint32 loadFlags;
|
|
|
|
|
|
|
|
rv = lastChannel->GetOriginalURI(getter_AddRefs(uri));
|
|
|
|
rv |= lastChannel->GetLoadFlags(&loadFlags);
|
|
|
|
|
|
|
|
// XXX we are carrying over the load flags, but what about other
|
|
|
|
// parameters that may have been set on lastChannel??
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
rv = Init(uri);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
rv = mChannel->SetLoadFlags(loadFlags);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
rv = AsyncCheck(mObserver, mObserverContext);
|
|
|
|
// if we succeeded in loading the new channel, then we
|
|
|
|
// want to return without notifying our observer.
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// it is important to update this so our observer will be able
|
|
|
|
// to access our baseChannel attribute if they want.
|
|
|
|
mChannel = lastChannel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we get here, assume the resource does not exist.
|
|
|
|
return NS_BINDING_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsIURIChecker methods:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::Init(nsIURI *aURI)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
rv = ios->NewChannelFromURI(aURI, getter_AddRefs(mChannel));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
if (mAllowHead) {
|
|
|
|
mAllowHead = PR_FALSE;
|
|
|
|
// See if it's an http channel, which needs special treatment:
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
|
|
|
|
if (httpChannel) {
|
|
|
|
// We can have an HTTP channel that has a non-HTTP URL if
|
|
|
|
// we're doing FTP via an HTTP proxy, for example. See for
|
|
|
|
// example bug 148813
|
|
|
|
PRBool isReallyHTTP = PR_FALSE;
|
|
|
|
aURI->SchemeIs("http", &isReallyHTTP);
|
|
|
|
if (!isReallyHTTP)
|
|
|
|
aURI->SchemeIs("https", &isReallyHTTP);
|
|
|
|
if (isReallyHTTP) {
|
|
|
|
httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
|
|
|
|
// set back to true so we'll know that this request is issuing
|
|
|
|
// a HEAD request. this is used down in OnStartRequest to
|
|
|
|
// handle cases where we need to repeat the request as a normal
|
|
|
|
// GET to deal with server borkage.
|
|
|
|
mAllowHead = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver,
|
|
|
|
nsISupports *aObserverContext)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
|
|
|
|
// Hook us up to listen to redirects and the like (this creates a reference
|
|
|
|
// cycle!)
|
|
|
|
mChannel->SetNotificationCallbacks(this);
|
|
|
|
|
|
|
|
// and start the request:
|
|
|
|
nsresult rv = mChannel->AsyncOpen(this, nsnull);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
mChannel = nsnull;
|
|
|
|
else {
|
|
|
|
// ok, wait for OnStartRequest to fire.
|
|
|
|
mIsPending = PR_TRUE;
|
|
|
|
mObserver = aObserver;
|
|
|
|
mObserverContext = aObserverContext;
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
NS_ADDREF(*aChannel = mChannel);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsIRequest methods:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::GetName(nsACString &aName)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->GetName(aName);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::IsPending(PRBool *aPendingRet)
|
|
|
|
{
|
|
|
|
*aPendingRet = mIsPending;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::GetStatus(nsresult* aStatusRet)
|
|
|
|
{
|
|
|
|
*aStatusRet = mStatus;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::Cancel(nsresult status)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->Cancel(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::Suspend()
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->Suspend();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::Resume()
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->Resume();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->GetLoadGroup(aLoadGroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->SetLoadGroup(aLoadGroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->GetLoadFlags(aLoadFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
return mChannel->SetLoadFlags(aLoadFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2007-08-19 20:19:11 -07:00
|
|
|
// nsIRequestObserever methods:
|
2007-03-22 10:30:00 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aRequest == mChannel, "unexpected request");
|
|
|
|
|
|
|
|
nsresult rv = CheckStatus();
|
|
|
|
if (rv != NS_BASE_STREAM_WOULD_BLOCK)
|
|
|
|
SetStatusAndCallBack(rv);
|
|
|
|
|
|
|
|
// cancel the request (we don't care to look at the data).
|
|
|
|
return NS_BINDING_ABORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
|
|
|
|
nsresult statusCode)
|
|
|
|
{
|
|
|
|
// NOTE: we may have kicked off a subsequent request, so we should not do
|
|
|
|
// any cleanup unless this request matches the one we are currently using.
|
|
|
|
if (mChannel == request) {
|
|
|
|
// break reference cycle between us and the channel (see comment in
|
|
|
|
// AsyncCheckURI)
|
|
|
|
mChannel = nsnull;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-08-19 20:19:11 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsIStreamListener methods:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
|
|
|
|
nsIInputStream *aInput, PRUint32 aOffset,
|
|
|
|
PRUint32 aCount)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("nsURIChecker::OnDataAvailable");
|
|
|
|
return NS_BINDING_ABORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsIInterfaceRequestor methods:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
|
|
|
|
{
|
|
|
|
if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
|
|
|
|
nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
|
|
|
|
if (req)
|
|
|
|
return req->GetInterface(aIID, aResult);
|
|
|
|
}
|
|
|
|
return QueryInterface(aIID, aResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsIChannelEventSink methods:
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsURIChecker::OnChannelRedirect(nsIChannel *aOldChannel,
|
|
|
|
nsIChannel *aNewChannel,
|
|
|
|
PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
// We have a new channel
|
|
|
|
mChannel = aNewChannel;
|
|
|
|
return NS_OK;
|
|
|
|
}
|