netwerk/base/src/nsURIChecker.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsURIChecker.h"
     7 #include "nsIAuthPrompt.h"
     8 #include "nsIHttpChannel.h"
     9 #include "nsNetUtil.h"
    10 #include "nsString.h"
    11 #include "nsIAsyncVerifyRedirectCallback.h"
    13 //-----------------------------------------------------------------------------
    15 static bool
    16 ServerIsNES3x(nsIHttpChannel *httpChannel)
    17 {
    18     nsAutoCString server;
    19     httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
    20     // case sensitive string comparison is OK here.  the server string
    21     // is a well-known value, so we should not have to worry about it
    22     // being case-smashed or otherwise case-mutated.
    23     return StringBeginsWith(server,
    24                             NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
    25 }
    27 //-----------------------------------------------------------------------------
    29 NS_IMPL_ISUPPORTS(nsURIChecker,
    30                   nsIURIChecker,
    31                   nsIRequest,
    32                   nsIRequestObserver,
    33                   nsIStreamListener,
    34                   nsIChannelEventSink,
    35                   nsIInterfaceRequestor)
    37 nsURIChecker::nsURIChecker()
    38     : mStatus(NS_OK)
    39     , mIsPending(false)
    40     , mAllowHead(true)
    41 {
    42 }
    44 void
    45 nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
    46 {
    47     mStatus = aStatus;
    48     mIsPending = false;
    50     if (mObserver) {
    51         mObserver->OnStartRequest(this, mObserverContext);
    52         mObserver->OnStopRequest(this, mObserverContext, mStatus);
    53         mObserver = nullptr;
    54         mObserverContext = nullptr;
    55     }
    56 }
    58 nsresult
    59 nsURIChecker::CheckStatus()
    60 {
    61     NS_ASSERTION(mChannel, "no channel");
    63     nsresult status;
    64     nsresult rv = mChannel->GetStatus(&status);
    65     // DNS errors and other obvious problems will return failure status
    66     if (NS_FAILED(rv) || NS_FAILED(status))
    67         return NS_BINDING_FAILED;
    69     // If status is zero, it might still be an error if it's http:
    70     // http has data even when there's an error like a 404.
    71     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
    72     if (!httpChannel)
    73         return NS_BINDING_SUCCEEDED;
    75     uint32_t responseStatus;
    76     rv = httpChannel->GetResponseStatus(&responseStatus);
    77     if (NS_FAILED(rv))
    78         return NS_BINDING_FAILED;
    80     // If it's between 200-299, it's valid:
    81     if (responseStatus / 100 == 2)
    82         return NS_BINDING_SUCCEEDED;
    84     // If we got a 404 (not found), we need some extra checking:
    85     // toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
    86     // hosted http://www.mozilla.org, generate a 404 and will have to be
    87     // retried without the head.
    88     if (responseStatus == 404) {
    89         if (mAllowHead && ServerIsNES3x(httpChannel)) {
    90             mAllowHead = false;
    92             // save the current value of mChannel in case we can't issue
    93             // the new request for some reason.
    94             nsCOMPtr<nsIChannel> lastChannel = mChannel;
    96             nsCOMPtr<nsIURI> uri;
    97             uint32_t loadFlags;
    99             rv  = lastChannel->GetOriginalURI(getter_AddRefs(uri));
   100             nsresult tmp = lastChannel->GetLoadFlags(&loadFlags);
   101             if (NS_FAILED(tmp)) {
   102               rv = tmp;
   103             }
   105             // XXX we are carrying over the load flags, but what about other
   106             // parameters that may have been set on lastChannel??
   108             if (NS_SUCCEEDED(rv)) {
   109                 rv = Init(uri);
   110                 if (NS_SUCCEEDED(rv)) {
   111                     rv = mChannel->SetLoadFlags(loadFlags);
   112                     if (NS_SUCCEEDED(rv)) {
   113                         rv = AsyncCheck(mObserver, mObserverContext);
   114                         // if we succeeded in loading the new channel, then we
   115                         // want to return without notifying our observer.
   116                         if (NS_SUCCEEDED(rv))
   117                             return NS_BASE_STREAM_WOULD_BLOCK;
   118                     }
   119                 }
   120             }
   121             // it is important to update this so our observer will be able
   122             // to access our baseChannel attribute if they want.
   123             mChannel = lastChannel;
   124         }
   125     }
   127     // If we get here, assume the resource does not exist.
   128     return NS_BINDING_FAILED;
   129 }
   131 //-----------------------------------------------------------------------------
   132 // nsIURIChecker methods:
   133 //-----------------------------------------------------------------------------
   135 NS_IMETHODIMP
   136 nsURIChecker::Init(nsIURI *aURI)
   137 {
   138     nsresult rv;
   139     nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
   140     if (NS_FAILED(rv)) return rv;
   142     rv = ios->NewChannelFromURI(aURI, getter_AddRefs(mChannel));
   143     if (NS_FAILED(rv)) return rv;
   145     if (mAllowHead) {
   146         mAllowHead = false;
   147         // See if it's an http channel, which needs special treatment:
   148         nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   149         if (httpChannel) {
   150             // We can have an HTTP channel that has a non-HTTP URL if
   151             // we're doing FTP via an HTTP proxy, for example.  See for
   152             // example bug 148813
   153             bool isReallyHTTP = false;
   154             aURI->SchemeIs("http", &isReallyHTTP);
   155             if (!isReallyHTTP)
   156                 aURI->SchemeIs("https", &isReallyHTTP);
   157             if (isReallyHTTP) {
   158                 httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
   159                 // set back to true so we'll know that this request is issuing
   160                 // a HEAD request.  this is used down in OnStartRequest to
   161                 // handle cases where we need to repeat the request as a normal
   162                 // GET to deal with server borkage.
   163                 mAllowHead = true;
   164             }
   165         }
   166     }
   167     return NS_OK;
   168 }
   170 NS_IMETHODIMP
   171 nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver,
   172                          nsISupports *aObserverContext)
   173 {
   174     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   176     // Hook us up to listen to redirects and the like (this creates a reference
   177     // cycle!)
   178     mChannel->SetNotificationCallbacks(this);
   180     // and start the request:
   181     nsresult rv = mChannel->AsyncOpen(this, nullptr);
   182     if (NS_FAILED(rv))
   183         mChannel = nullptr;
   184     else {
   185         // ok, wait for OnStartRequest to fire.
   186         mIsPending = true;
   187         mObserver = aObserver;
   188         mObserverContext = aObserverContext;
   189     }
   190     return rv;
   191 }
   193 NS_IMETHODIMP
   194 nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
   195 {
   196     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   197     NS_ADDREF(*aChannel = mChannel);
   198     return NS_OK;
   199 }
   201 //-----------------------------------------------------------------------------
   202 // nsIRequest methods:
   203 //-----------------------------------------------------------------------------
   205 NS_IMETHODIMP
   206 nsURIChecker::GetName(nsACString &aName)
   207 {
   208     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   209     return mChannel->GetName(aName);
   210 }
   212 NS_IMETHODIMP
   213 nsURIChecker::IsPending(bool *aPendingRet)
   214 {
   215     *aPendingRet = mIsPending;
   216     return NS_OK;
   217 }
   219 NS_IMETHODIMP
   220 nsURIChecker::GetStatus(nsresult* aStatusRet)
   221 {
   222     *aStatusRet = mStatus;
   223     return NS_OK;
   224 }
   226 NS_IMETHODIMP
   227 nsURIChecker::Cancel(nsresult status)
   228 {
   229     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   230     return mChannel->Cancel(status);
   231 }
   233 NS_IMETHODIMP
   234 nsURIChecker::Suspend()
   235 {
   236     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   237     return mChannel->Suspend();
   238 }
   240 NS_IMETHODIMP
   241 nsURIChecker::Resume()
   242 {
   243     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   244     return mChannel->Resume();
   245 }
   247 NS_IMETHODIMP
   248 nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
   249 {
   250     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   251     return mChannel->GetLoadGroup(aLoadGroup);
   252 }
   254 NS_IMETHODIMP
   255 nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
   256 {
   257     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   258     return mChannel->SetLoadGroup(aLoadGroup);
   259 }
   261 NS_IMETHODIMP
   262 nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
   263 {
   264     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   265     return mChannel->GetLoadFlags(aLoadFlags);
   266 }
   268 NS_IMETHODIMP
   269 nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
   270 {
   271     NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   272     return mChannel->SetLoadFlags(aLoadFlags);
   273 }
   275 //-----------------------------------------------------------------------------
   276 // nsIRequestObserver methods:
   277 //-----------------------------------------------------------------------------
   279 NS_IMETHODIMP
   280 nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt)
   281 {
   282     NS_ASSERTION(aRequest == mChannel, "unexpected request");
   284     nsresult rv = CheckStatus();
   285     if (rv != NS_BASE_STREAM_WOULD_BLOCK)
   286         SetStatusAndCallBack(rv);
   288     // cancel the request (we don't care to look at the data).
   289     return NS_BINDING_ABORTED;
   290 }
   292 NS_IMETHODIMP
   293 nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
   294                              nsresult statusCode)
   295 {
   296     // NOTE: we may have kicked off a subsequent request, so we should not do
   297     // any cleanup unless this request matches the one we are currently using.
   298     if (mChannel == request) {
   299         // break reference cycle between us and the channel (see comment in
   300         // AsyncCheckURI)
   301         mChannel = nullptr;
   302     }
   303     return NS_OK;
   304 }
   306 //-----------------------------------------------------------------------------
   307 // nsIStreamListener methods:
   308 //-----------------------------------------------------------------------------
   310 NS_IMETHODIMP
   311 nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
   312                                nsIInputStream *aInput, uint64_t aOffset,
   313                                uint32_t aCount)
   314 {
   315     NS_NOTREACHED("nsURIChecker::OnDataAvailable");
   316     return NS_BINDING_ABORTED;
   317 }
   319 //-----------------------------------------------------------------------------
   320 // nsIInterfaceRequestor methods:
   321 //-----------------------------------------------------------------------------
   323 NS_IMETHODIMP
   324 nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
   325 {
   326     if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
   327         nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
   328         if (req)
   329             return req->GetInterface(aIID, aResult);
   330     }
   331     return QueryInterface(aIID, aResult);
   332 }
   334 //-----------------------------------------------------------------------------
   335 // nsIChannelEventSink methods:
   336 //-----------------------------------------------------------------------------
   338 NS_IMETHODIMP
   339 nsURIChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
   340                                      nsIChannel *aNewChannel,
   341                                      uint32_t aFlags,
   342                                      nsIAsyncVerifyRedirectCallback *callback)
   343 {
   344     // We have a new channel
   345     mChannel = aNewChannel;
   346     callback->OnRedirectVerifyCallback(NS_OK);
   347     return NS_OK;
   348 }

mercurial