netwerk/base/src/nsURIChecker.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/nsURIChecker.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,348 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsURIChecker.h"
    1.10 +#include "nsIAuthPrompt.h"
    1.11 +#include "nsIHttpChannel.h"
    1.12 +#include "nsNetUtil.h"
    1.13 +#include "nsString.h"
    1.14 +#include "nsIAsyncVerifyRedirectCallback.h"
    1.15 +
    1.16 +//-----------------------------------------------------------------------------
    1.17 +
    1.18 +static bool
    1.19 +ServerIsNES3x(nsIHttpChannel *httpChannel)
    1.20 +{
    1.21 +    nsAutoCString server;
    1.22 +    httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
    1.23 +    // case sensitive string comparison is OK here.  the server string
    1.24 +    // is a well-known value, so we should not have to worry about it
    1.25 +    // being case-smashed or otherwise case-mutated.
    1.26 +    return StringBeginsWith(server,
    1.27 +                            NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
    1.28 +}
    1.29 +
    1.30 +//-----------------------------------------------------------------------------
    1.31 +
    1.32 +NS_IMPL_ISUPPORTS(nsURIChecker,
    1.33 +                  nsIURIChecker,
    1.34 +                  nsIRequest,
    1.35 +                  nsIRequestObserver,
    1.36 +                  nsIStreamListener,
    1.37 +                  nsIChannelEventSink,
    1.38 +                  nsIInterfaceRequestor)
    1.39 +
    1.40 +nsURIChecker::nsURIChecker()
    1.41 +    : mStatus(NS_OK)
    1.42 +    , mIsPending(false)
    1.43 +    , mAllowHead(true)
    1.44 +{
    1.45 +}
    1.46 +
    1.47 +void
    1.48 +nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
    1.49 +{
    1.50 +    mStatus = aStatus;
    1.51 +    mIsPending = false;
    1.52 +
    1.53 +    if (mObserver) {
    1.54 +        mObserver->OnStartRequest(this, mObserverContext);
    1.55 +        mObserver->OnStopRequest(this, mObserverContext, mStatus);
    1.56 +        mObserver = nullptr;
    1.57 +        mObserverContext = nullptr;
    1.58 +    }
    1.59 +}
    1.60 +
    1.61 +nsresult
    1.62 +nsURIChecker::CheckStatus()
    1.63 +{
    1.64 +    NS_ASSERTION(mChannel, "no channel");
    1.65 +
    1.66 +    nsresult status;
    1.67 +    nsresult rv = mChannel->GetStatus(&status);
    1.68 +    // DNS errors and other obvious problems will return failure status
    1.69 +    if (NS_FAILED(rv) || NS_FAILED(status))
    1.70 +        return NS_BINDING_FAILED;
    1.71 +
    1.72 +    // If status is zero, it might still be an error if it's http:
    1.73 +    // http has data even when there's an error like a 404.
    1.74 +    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
    1.75 +    if (!httpChannel)
    1.76 +        return NS_BINDING_SUCCEEDED;
    1.77 +
    1.78 +    uint32_t responseStatus;
    1.79 +    rv = httpChannel->GetResponseStatus(&responseStatus);
    1.80 +    if (NS_FAILED(rv))
    1.81 +        return NS_BINDING_FAILED;
    1.82 +
    1.83 +    // If it's between 200-299, it's valid:
    1.84 +    if (responseStatus / 100 == 2)
    1.85 +        return NS_BINDING_SUCCEEDED;
    1.86 +
    1.87 +    // If we got a 404 (not found), we need some extra checking:
    1.88 +    // toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
    1.89 +    // hosted http://www.mozilla.org, generate a 404 and will have to be
    1.90 +    // retried without the head.
    1.91 +    if (responseStatus == 404) {
    1.92 +        if (mAllowHead && ServerIsNES3x(httpChannel)) {
    1.93 +            mAllowHead = false;
    1.94 +
    1.95 +            // save the current value of mChannel in case we can't issue
    1.96 +            // the new request for some reason.
    1.97 +            nsCOMPtr<nsIChannel> lastChannel = mChannel;
    1.98 +
    1.99 +            nsCOMPtr<nsIURI> uri;
   1.100 +            uint32_t loadFlags;
   1.101 +
   1.102 +            rv  = lastChannel->GetOriginalURI(getter_AddRefs(uri));
   1.103 +            nsresult tmp = lastChannel->GetLoadFlags(&loadFlags);
   1.104 +            if (NS_FAILED(tmp)) {
   1.105 +              rv = tmp;
   1.106 +            }
   1.107 +
   1.108 +            // XXX we are carrying over the load flags, but what about other
   1.109 +            // parameters that may have been set on lastChannel??
   1.110 +
   1.111 +            if (NS_SUCCEEDED(rv)) {
   1.112 +                rv = Init(uri);
   1.113 +                if (NS_SUCCEEDED(rv)) {
   1.114 +                    rv = mChannel->SetLoadFlags(loadFlags);
   1.115 +                    if (NS_SUCCEEDED(rv)) {
   1.116 +                        rv = AsyncCheck(mObserver, mObserverContext);
   1.117 +                        // if we succeeded in loading the new channel, then we
   1.118 +                        // want to return without notifying our observer.
   1.119 +                        if (NS_SUCCEEDED(rv))
   1.120 +                            return NS_BASE_STREAM_WOULD_BLOCK;
   1.121 +                    }
   1.122 +                }
   1.123 +            }
   1.124 +            // it is important to update this so our observer will be able
   1.125 +            // to access our baseChannel attribute if they want.
   1.126 +            mChannel = lastChannel;
   1.127 +        }
   1.128 +    }
   1.129 +
   1.130 +    // If we get here, assume the resource does not exist.
   1.131 +    return NS_BINDING_FAILED;
   1.132 +}
   1.133 +
   1.134 +//-----------------------------------------------------------------------------
   1.135 +// nsIURIChecker methods:
   1.136 +//-----------------------------------------------------------------------------
   1.137 +
   1.138 +NS_IMETHODIMP
   1.139 +nsURIChecker::Init(nsIURI *aURI)
   1.140 +{
   1.141 +    nsresult rv;
   1.142 +    nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
   1.143 +    if (NS_FAILED(rv)) return rv;
   1.144 +
   1.145 +    rv = ios->NewChannelFromURI(aURI, getter_AddRefs(mChannel));
   1.146 +    if (NS_FAILED(rv)) return rv;
   1.147 +
   1.148 +    if (mAllowHead) {
   1.149 +        mAllowHead = false;
   1.150 +        // See if it's an http channel, which needs special treatment:
   1.151 +        nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   1.152 +        if (httpChannel) {
   1.153 +            // We can have an HTTP channel that has a non-HTTP URL if
   1.154 +            // we're doing FTP via an HTTP proxy, for example.  See for
   1.155 +            // example bug 148813
   1.156 +            bool isReallyHTTP = false;
   1.157 +            aURI->SchemeIs("http", &isReallyHTTP);
   1.158 +            if (!isReallyHTTP)
   1.159 +                aURI->SchemeIs("https", &isReallyHTTP);
   1.160 +            if (isReallyHTTP) {
   1.161 +                httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
   1.162 +                // set back to true so we'll know that this request is issuing
   1.163 +                // a HEAD request.  this is used down in OnStartRequest to
   1.164 +                // handle cases where we need to repeat the request as a normal
   1.165 +                // GET to deal with server borkage.
   1.166 +                mAllowHead = true;
   1.167 +            }
   1.168 +        }
   1.169 +    }
   1.170 +    return NS_OK;
   1.171 +}
   1.172 +
   1.173 +NS_IMETHODIMP
   1.174 +nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver,
   1.175 +                         nsISupports *aObserverContext)
   1.176 +{
   1.177 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.178 +
   1.179 +    // Hook us up to listen to redirects and the like (this creates a reference
   1.180 +    // cycle!)
   1.181 +    mChannel->SetNotificationCallbacks(this);
   1.182 +    
   1.183 +    // and start the request:
   1.184 +    nsresult rv = mChannel->AsyncOpen(this, nullptr);
   1.185 +    if (NS_FAILED(rv))
   1.186 +        mChannel = nullptr;
   1.187 +    else {
   1.188 +        // ok, wait for OnStartRequest to fire.
   1.189 +        mIsPending = true;
   1.190 +        mObserver = aObserver;
   1.191 +        mObserverContext = aObserverContext;
   1.192 +    }
   1.193 +    return rv;
   1.194 +}
   1.195 +
   1.196 +NS_IMETHODIMP
   1.197 +nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
   1.198 +{
   1.199 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.200 +    NS_ADDREF(*aChannel = mChannel);
   1.201 +    return NS_OK;
   1.202 +}
   1.203 +
   1.204 +//-----------------------------------------------------------------------------
   1.205 +// nsIRequest methods:
   1.206 +//-----------------------------------------------------------------------------
   1.207 +
   1.208 +NS_IMETHODIMP
   1.209 +nsURIChecker::GetName(nsACString &aName)
   1.210 +{
   1.211 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.212 +    return mChannel->GetName(aName);
   1.213 +}
   1.214 +
   1.215 +NS_IMETHODIMP
   1.216 +nsURIChecker::IsPending(bool *aPendingRet)
   1.217 +{
   1.218 +    *aPendingRet = mIsPending;
   1.219 +    return NS_OK;
   1.220 +}
   1.221 +
   1.222 +NS_IMETHODIMP
   1.223 +nsURIChecker::GetStatus(nsresult* aStatusRet)
   1.224 +{
   1.225 +    *aStatusRet = mStatus;
   1.226 +    return NS_OK;
   1.227 +}
   1.228 +
   1.229 +NS_IMETHODIMP
   1.230 +nsURIChecker::Cancel(nsresult status)
   1.231 +{
   1.232 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.233 +    return mChannel->Cancel(status);
   1.234 +}
   1.235 +
   1.236 +NS_IMETHODIMP
   1.237 +nsURIChecker::Suspend()
   1.238 +{
   1.239 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.240 +    return mChannel->Suspend();
   1.241 +}
   1.242 +
   1.243 +NS_IMETHODIMP
   1.244 +nsURIChecker::Resume()
   1.245 +{
   1.246 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.247 +    return mChannel->Resume();
   1.248 +}
   1.249 +
   1.250 +NS_IMETHODIMP
   1.251 +nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
   1.252 +{
   1.253 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.254 +    return mChannel->GetLoadGroup(aLoadGroup);
   1.255 +}
   1.256 +
   1.257 +NS_IMETHODIMP
   1.258 +nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
   1.259 +{
   1.260 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.261 +    return mChannel->SetLoadGroup(aLoadGroup);
   1.262 +}
   1.263 +
   1.264 +NS_IMETHODIMP
   1.265 +nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
   1.266 +{
   1.267 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.268 +    return mChannel->GetLoadFlags(aLoadFlags);
   1.269 +}
   1.270 +
   1.271 +NS_IMETHODIMP
   1.272 +nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
   1.273 +{
   1.274 +    NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
   1.275 +    return mChannel->SetLoadFlags(aLoadFlags);
   1.276 +}
   1.277 +
   1.278 +//-----------------------------------------------------------------------------
   1.279 +// nsIRequestObserver methods:
   1.280 +//-----------------------------------------------------------------------------
   1.281 +
   1.282 +NS_IMETHODIMP
   1.283 +nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt)
   1.284 +{
   1.285 +    NS_ASSERTION(aRequest == mChannel, "unexpected request");
   1.286 +
   1.287 +    nsresult rv = CheckStatus();
   1.288 +    if (rv != NS_BASE_STREAM_WOULD_BLOCK)
   1.289 +        SetStatusAndCallBack(rv);
   1.290 +
   1.291 +    // cancel the request (we don't care to look at the data).
   1.292 +    return NS_BINDING_ABORTED;
   1.293 +}
   1.294 +
   1.295 +NS_IMETHODIMP
   1.296 +nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
   1.297 +                             nsresult statusCode)
   1.298 +{
   1.299 +    // NOTE: we may have kicked off a subsequent request, so we should not do
   1.300 +    // any cleanup unless this request matches the one we are currently using.
   1.301 +    if (mChannel == request) {
   1.302 +        // break reference cycle between us and the channel (see comment in
   1.303 +        // AsyncCheckURI)
   1.304 +        mChannel = nullptr;
   1.305 +    }
   1.306 +    return NS_OK;
   1.307 +}
   1.308 +
   1.309 +//-----------------------------------------------------------------------------
   1.310 +// nsIStreamListener methods:
   1.311 +//-----------------------------------------------------------------------------
   1.312 +
   1.313 +NS_IMETHODIMP
   1.314 +nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
   1.315 +                               nsIInputStream *aInput, uint64_t aOffset,
   1.316 +                               uint32_t aCount)
   1.317 +{
   1.318 +    NS_NOTREACHED("nsURIChecker::OnDataAvailable");
   1.319 +    return NS_BINDING_ABORTED;
   1.320 +}
   1.321 +
   1.322 +//-----------------------------------------------------------------------------
   1.323 +// nsIInterfaceRequestor methods:
   1.324 +//-----------------------------------------------------------------------------
   1.325 +
   1.326 +NS_IMETHODIMP
   1.327 +nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
   1.328 +{
   1.329 +    if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
   1.330 +        nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
   1.331 +        if (req)
   1.332 +            return req->GetInterface(aIID, aResult);
   1.333 +    }
   1.334 +    return QueryInterface(aIID, aResult);
   1.335 +}
   1.336 +
   1.337 +//-----------------------------------------------------------------------------
   1.338 +// nsIChannelEventSink methods:
   1.339 +//-----------------------------------------------------------------------------
   1.340 +
   1.341 +NS_IMETHODIMP
   1.342 +nsURIChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
   1.343 +                                     nsIChannel *aNewChannel,
   1.344 +                                     uint32_t aFlags,
   1.345 +                                     nsIAsyncVerifyRedirectCallback *callback)
   1.346 +{
   1.347 +    // We have a new channel
   1.348 +    mChannel = aNewChannel;
   1.349 +    callback->OnRedirectVerifyCallback(NS_OK);
   1.350 +    return NS_OK;
   1.351 +}

mercurial