Back to index

lightning-sunbird  0.9+nobinonly
nsURIChecker.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Akkana Peck <akkana@netscape.com> (original author)
00024  *   Darin Fisher <darin@meer.net>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsURIChecker.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsIAuthPrompt.h"
00043 #include "nsIHttpChannel.h"
00044 #include "nsNetUtil.h"
00045 #include "nsString.h"
00046 
00047 //-----------------------------------------------------------------------------
00048 
00049 static PRBool
00050 ServerIsNES3x(nsIHttpChannel *httpChannel)
00051 {
00052     nsCAutoString server;
00053     httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
00054     // case sensitive string comparison is OK here.  the server string
00055     // is a well-known value, so we should not have to worry about it
00056     // being case-smashed or otherwise case-mutated.
00057     return StringBeginsWith(server,
00058                             NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
00059 }
00060 
00061 //-----------------------------------------------------------------------------
00062 
00063 NS_IMPL_ISUPPORTS5(nsURIChecker,
00064                    nsIURIChecker,
00065                    nsIRequest,
00066                    nsIStreamListener,
00067                    nsIChannelEventSink,
00068                    nsIInterfaceRequestor)
00069 
00070 nsURIChecker::nsURIChecker()
00071     : mStatus(NS_OK)
00072     , mIsPending(PR_FALSE)
00073     , mAllowHead(PR_TRUE)
00074 {
00075 }
00076 
00077 void
00078 nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
00079 {
00080     mStatus = aStatus;
00081     mIsPending = PR_FALSE;
00082 
00083     if (mObserver) {
00084         mObserver->OnStartRequest(this, mObserverContext);
00085         mObserver->OnStopRequest(this, mObserverContext, mStatus);
00086         mObserver = nsnull;
00087         mObserverContext = nsnull;
00088     }
00089 }
00090 
00091 nsresult
00092 nsURIChecker::CheckStatus()
00093 {
00094     NS_ASSERTION(mChannel, "no channel");
00095 
00096     nsresult status;
00097     nsresult rv = mChannel->GetStatus(&status);
00098     // DNS errors and other obvious problems will return failure status
00099     if (NS_FAILED(rv) || NS_FAILED(status))
00100         return NS_BINDING_FAILED;
00101 
00102     // If status is zero, it might still be an error if it's http:
00103     // http has data even when there's an error like a 404.
00104     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
00105     if (!httpChannel)
00106         return NS_BINDING_SUCCEEDED;
00107 
00108     PRUint32 responseStatus;
00109     rv = httpChannel->GetResponseStatus(&responseStatus);
00110     if (NS_FAILED(rv))
00111         return NS_BINDING_FAILED;
00112 
00113     // If it's between 200-299, it's valid:
00114     if (responseStatus / 100 == 2)
00115         return NS_BINDING_SUCCEEDED;
00116 
00117     // If we got a 404 (not found), we need some extra checking:
00118     // toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
00119     // hosted http://www.mozilla.org, generate a 404 and will have to be
00120     // retried without the head.
00121     if (responseStatus == 404) {
00122         if (mAllowHead && ServerIsNES3x(httpChannel)) {
00123             mAllowHead = PR_FALSE;
00124 
00125             // save the current value of mChannel in case we can't issue
00126             // the new request for some reason.
00127             nsCOMPtr<nsIChannel> lastChannel = mChannel;
00128 
00129             nsCOMPtr<nsIURI> uri;
00130             PRUint32 loadFlags;
00131 
00132             rv  = lastChannel->GetOriginalURI(getter_AddRefs(uri));
00133             rv |= lastChannel->GetLoadFlags(&loadFlags);
00134 
00135             // XXX we are carrying over the load flags, but what about other
00136             // parameters that may have been set on lastChannel??
00137 
00138             if (NS_SUCCEEDED(rv)) {
00139                 rv = Init(uri);
00140                 if (NS_SUCCEEDED(rv)) {
00141                     rv = mChannel->SetLoadFlags(loadFlags);
00142                     if (NS_SUCCEEDED(rv)) {
00143                         rv = AsyncCheck(mObserver, mObserverContext);
00144                         // if we succeeded in loading the new channel, then we
00145                         // want to return without notifying our observer.
00146                         if (NS_SUCCEEDED(rv))
00147                             return NS_BASE_STREAM_WOULD_BLOCK;
00148                     }
00149                 }
00150             }
00151             // it is important to update this so our observer will be able
00152             // to access our baseChannel attribute if they want.
00153             mChannel = lastChannel;
00154         }
00155     }
00156 
00157     // If we get here, assume the resource does not exist.
00158     return NS_BINDING_FAILED;
00159 }
00160 
00161 //-----------------------------------------------------------------------------
00162 // nsIURIChecker methods:
00163 //-----------------------------------------------------------------------------
00164 
00165 NS_IMETHODIMP
00166 nsURIChecker::Init(nsIURI *aURI)
00167 {
00168     nsresult rv;
00169     nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
00170     if (NS_FAILED(rv)) return rv;
00171 
00172     rv = ios->NewChannelFromURI(aURI, getter_AddRefs(mChannel));
00173     if (NS_FAILED(rv)) return rv;
00174 
00175     if (mAllowHead) {
00176         mAllowHead = PR_FALSE;
00177         // See if it's an http channel, which needs special treatment:
00178         nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
00179         if (httpChannel) {
00180             // We can have an HTTP channel that has a non-HTTP URL if
00181             // we're doing FTP via an HTTP proxy, for example.  See for
00182             // example bug 148813
00183             PRBool isReallyHTTP = PR_FALSE;
00184             aURI->SchemeIs("http", &isReallyHTTP);
00185             if (!isReallyHTTP)
00186                 aURI->SchemeIs("https", &isReallyHTTP);
00187             if (isReallyHTTP) {
00188                 httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
00189                 // set back to true so we'll know that this request is issuing
00190                 // a HEAD request.  this is used down in OnStartRequest to
00191                 // handle cases where we need to repeat the request as a normal
00192                 // GET to deal with server borkage.
00193                 mAllowHead = PR_TRUE;
00194             }
00195         }
00196     }
00197     return NS_OK;
00198 }
00199 
00200 NS_IMETHODIMP
00201 nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver,
00202                          nsISupports *aObserverContext)
00203 {
00204     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00205 
00206     // Hook us up to listen to redirects and the like (this creates a reference
00207     // cycle!)
00208     mChannel->SetNotificationCallbacks(this);
00209     
00210     // and start the request:
00211     nsresult rv = mChannel->AsyncOpen(this, nsnull);
00212     if (NS_FAILED(rv))
00213         mChannel = nsnull;
00214     else {
00215         // ok, wait for OnStartRequest to fire.
00216         mIsPending = PR_TRUE;
00217         mObserver = aObserver;
00218         mObserverContext = aObserverContext;
00219     }
00220     return rv;
00221 }
00222 
00223 NS_IMETHODIMP
00224 nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
00225 {
00226     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00227     NS_ADDREF(*aChannel = mChannel);
00228     return NS_OK;
00229 }
00230 
00231 //-----------------------------------------------------------------------------
00232 // nsIRequest methods:
00233 //-----------------------------------------------------------------------------
00234 
00235 NS_IMETHODIMP
00236 nsURIChecker::GetName(nsACString &aName)
00237 {
00238     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00239     return mChannel->GetName(aName);
00240 }
00241 
00242 NS_IMETHODIMP
00243 nsURIChecker::IsPending(PRBool *aPendingRet)
00244 {
00245     *aPendingRet = mIsPending;
00246     return NS_OK;
00247 }
00248 
00249 NS_IMETHODIMP
00250 nsURIChecker::GetStatus(nsresult* aStatusRet)
00251 {
00252     *aStatusRet = mStatus;
00253     return NS_OK;
00254 }
00255 
00256 NS_IMETHODIMP
00257 nsURIChecker::Cancel(nsresult status)
00258 {
00259     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00260     return mChannel->Cancel(status);
00261 }
00262 
00263 NS_IMETHODIMP
00264 nsURIChecker::Suspend()
00265 {
00266     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00267     return mChannel->Suspend();
00268 }
00269 
00270 NS_IMETHODIMP
00271 nsURIChecker::Resume()
00272 {
00273     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00274     return mChannel->Resume();
00275 }
00276 
00277 NS_IMETHODIMP
00278 nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
00279 {
00280     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00281     return mChannel->GetLoadGroup(aLoadGroup);
00282 }
00283 
00284 NS_IMETHODIMP
00285 nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
00286 {
00287     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00288     return mChannel->SetLoadGroup(aLoadGroup);
00289 }
00290 
00291 NS_IMETHODIMP
00292 nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
00293 {
00294     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00295     return mChannel->GetLoadFlags(aLoadFlags);
00296 }
00297 
00298 NS_IMETHODIMP
00299 nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
00300 {
00301     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
00302     return mChannel->SetLoadFlags(aLoadFlags);
00303 }
00304 
00305 //-----------------------------------------------------------------------------
00306 // nsIStreamListener methods:
00307 //-----------------------------------------------------------------------------
00308 
00309 NS_IMETHODIMP
00310 nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt)
00311 {
00312     NS_ASSERTION(aRequest == mChannel, "unexpected request");
00313 
00314     nsresult rv = CheckStatus();
00315     if (rv != NS_BASE_STREAM_WOULD_BLOCK)
00316         SetStatusAndCallBack(rv);
00317 
00318     // cancel the request (we don't care to look at the data).
00319     return NS_BINDING_ABORTED;
00320 }
00321 
00322 NS_IMETHODIMP
00323 nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
00324                              nsresult statusCode)
00325 {
00326     // NOTE: we may have kicked off a subsequent request, so we should not do
00327     // any cleanup unless this request matches the one we are currently using.
00328     if (mChannel == request) {
00329         // break reference cycle between us and the channel (see comment in
00330         // AsyncCheckURI)
00331         mChannel = nsnull;
00332     }
00333     return NS_OK;
00334 }
00335 
00336 NS_IMETHODIMP
00337 nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
00338                                nsIInputStream *aInput, PRUint32 aOffset,
00339                                PRUint32 aCount)
00340 {
00341     NS_NOTREACHED("nsURIChecker::OnDataAvailable");
00342     return NS_BINDING_ABORTED;
00343 }
00344 
00345 //-----------------------------------------------------------------------------
00346 // nsIInterfaceRequestor methods:
00347 //-----------------------------------------------------------------------------
00348 
00349 NS_IMETHODIMP
00350 nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
00351 {
00352     if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
00353         nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
00354         if (req)
00355             return req->GetInterface(aIID, aResult);
00356     }
00357     return QueryInterface(aIID, aResult);
00358 }
00359 
00360 //-----------------------------------------------------------------------------
00361 // nsIChannelEventSink methods:
00362 //-----------------------------------------------------------------------------
00363 
00364 NS_IMETHODIMP
00365 nsURIChecker::OnChannelRedirect(nsIChannel *aOldChannel,
00366                                 nsIChannel *aNewChannel,
00367                                 PRUint32    aFlags)
00368 {
00369     // We have a new channel
00370     mChannel = aNewChannel;
00371     return NS_OK;
00372 }