netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/base/src/nsAsyncRedirectVerifyHelper.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,270 @@
     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 +#ifdef MOZ_LOGGING
    1.10 +#define FORCE_PR_LOG
    1.11 +#endif
    1.12 +
    1.13 +#include "prlog.h"
    1.14 +#include "nsAsyncRedirectVerifyHelper.h"
    1.15 +#include "nsThreadUtils.h"
    1.16 +#include "nsNetUtil.h"
    1.17 +
    1.18 +#include "nsIOService.h"
    1.19 +#include "nsIChannel.h"
    1.20 +#include "nsIHttpChannelInternal.h"
    1.21 +#include "nsIAsyncVerifyRedirectCallback.h"
    1.22 +
    1.23 +#undef LOG
    1.24 +#ifdef PR_LOGGING
    1.25 +static PRLogModuleInfo *
    1.26 +GetRedirectLog()
    1.27 +{
    1.28 +    static PRLogModuleInfo *sLog;
    1.29 +    if (!sLog)
    1.30 +        sLog = PR_NewLogModule("nsRedirect");
    1.31 +    return sLog;
    1.32 +}
    1.33 +#define LOG(args) PR_LOG(GetRedirectLog(), PR_LOG_DEBUG, args)
    1.34 +#else
    1.35 +#define LOG(args)
    1.36 +#endif
    1.37 +
    1.38 +NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper,
    1.39 +                  nsIAsyncVerifyRedirectCallback,
    1.40 +                  nsIRunnable)
    1.41 +
    1.42 +class nsAsyncVerifyRedirectCallbackEvent : public nsRunnable {
    1.43 +public:
    1.44 +    nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
    1.45 +                                       nsresult result)
    1.46 +        : mCallback(cb), mResult(result) {
    1.47 +    }
    1.48 +
    1.49 +    NS_IMETHOD Run()
    1.50 +    {
    1.51 +        LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() "
    1.52 +             "callback to %p with result %x",
    1.53 +             mCallback.get(), mResult));
    1.54 +       (void) mCallback->OnRedirectVerifyCallback(mResult);
    1.55 +       return NS_OK;
    1.56 +    }
    1.57 +private:
    1.58 +    nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
    1.59 +    nsresult mResult;
    1.60 +};
    1.61 +
    1.62 +nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper()
    1.63 +    : mCallbackInitiated(false),
    1.64 +      mExpectedCallbacks(0),
    1.65 +      mResult(NS_OK)
    1.66 +{
    1.67 +}
    1.68 +
    1.69 +nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper()
    1.70 +{
    1.71 +    NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
    1.72 +                 "Did not receive all required callbacks!");
    1.73 +}
    1.74 +
    1.75 +nsresult
    1.76 +nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
    1.77 +                                  uint32_t flags, bool synchronize)
    1.78 +{
    1.79 +    LOG(("nsAsyncRedirectVerifyHelper::Init() "
    1.80 +         "oldChan=%p newChan=%p", oldChan, newChan));
    1.81 +    mOldChan           = oldChan;
    1.82 +    mNewChan           = newChan;
    1.83 +    mFlags             = flags;
    1.84 +    mCallbackThread    = do_GetCurrentThread();
    1.85 +
    1.86 +    if (synchronize)
    1.87 +      mWaitingForRedirectCallback = true;
    1.88 +
    1.89 +    nsresult rv;
    1.90 +    rv = NS_DispatchToMainThread(this);
    1.91 +    NS_ENSURE_SUCCESS(rv, rv);
    1.92 +
    1.93 +    if (synchronize) {
    1.94 +      nsIThread *thread = NS_GetCurrentThread();
    1.95 +      while (mWaitingForRedirectCallback) {
    1.96 +        if (!NS_ProcessNextEvent(thread)) {
    1.97 +          return NS_ERROR_UNEXPECTED;
    1.98 +        }
    1.99 +      }
   1.100 +    }
   1.101 +
   1.102 +    return NS_OK;
   1.103 +}
   1.104 +
   1.105 +NS_IMETHODIMP
   1.106 +nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result)
   1.107 +{
   1.108 +    LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
   1.109 +         "result=%x expectedCBs=%u mResult=%x",
   1.110 +         result, mExpectedCallbacks, mResult));
   1.111 +
   1.112 +    --mExpectedCallbacks;
   1.113 +
   1.114 +    // If response indicates failure we may call back immediately
   1.115 +    if (NS_FAILED(result)) {
   1.116 +        // We chose to store the first failure-value (as opposed to the last)
   1.117 +        if (NS_SUCCEEDED(mResult))
   1.118 +            mResult = result;
   1.119 +
   1.120 +        // If InitCallback() has been called, just invoke the callback and
   1.121 +        // return. Otherwise it will be invoked from InitCallback()
   1.122 +        if (mCallbackInitiated) {
   1.123 +            ExplicitCallback(mResult);
   1.124 +            return NS_OK;
   1.125 +        }
   1.126 +    }
   1.127 +
   1.128 +    // If the expected-counter is in balance and InitCallback() was called, all
   1.129 +    // sinks have agreed that the redirect is ok and we can invoke our callback
   1.130 +    if (mCallbackInitiated && mExpectedCallbacks == 0) {
   1.131 +        ExplicitCallback(mResult);
   1.132 +    }
   1.133 +
   1.134 +    return NS_OK;
   1.135 +}
   1.136 +
   1.137 +nsresult
   1.138 +nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink,
   1.139 +                                                       nsIChannel *oldChannel,
   1.140 +                                                       nsIChannel *newChannel,
   1.141 +                                                       uint32_t flags)
   1.142 +{
   1.143 +    LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
   1.144 +         "sink=%p expectedCBs=%u mResult=%x",
   1.145 +         sink, mExpectedCallbacks, mResult));
   1.146 +
   1.147 +    ++mExpectedCallbacks;
   1.148 +
   1.149 +    if (IsOldChannelCanceled()) {
   1.150 +        LOG(("  old channel has been canceled, cancel the redirect by "
   1.151 +             "emulating OnRedirectVerifyCallback..."));
   1.152 +        (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED);
   1.153 +        return NS_BINDING_ABORTED;
   1.154 +    }
   1.155 +
   1.156 +    nsresult rv =
   1.157 +        sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
   1.158 +
   1.159 +    LOG(("  result=%x expectedCBs=%u", rv, mExpectedCallbacks));
   1.160 +
   1.161 +    // If the sink returns failure from this call the redirect is vetoed. We
   1.162 +    // emulate a callback from the sink in this case in order to perform all
   1.163 +    // the necessary logic.
   1.164 +    if (NS_FAILED(rv)) {
   1.165 +        LOG(("  emulating OnRedirectVerifyCallback..."));
   1.166 +        (void) OnRedirectVerifyCallback(rv);
   1.167 +    }
   1.168 +
   1.169 +    return rv;  // Return the actual status since our caller may need it
   1.170 +}
   1.171 +
   1.172 +void
   1.173 +nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result)
   1.174 +{
   1.175 +    LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   1.176 +         "result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x",
   1.177 +         result, mExpectedCallbacks, mCallbackInitiated, mResult));
   1.178 +
   1.179 +    nsCOMPtr<nsIAsyncVerifyRedirectCallback>
   1.180 +        callback(do_QueryInterface(mOldChan));
   1.181 +
   1.182 +    if (!callback || !mCallbackThread) {
   1.183 +        LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   1.184 +             "callback=%p mCallbackThread=%p", callback.get(), mCallbackThread.get()));
   1.185 +        return;
   1.186 +    }
   1.187 +
   1.188 +    mCallbackInitiated = false;  // reset to ensure only one callback
   1.189 +    mWaitingForRedirectCallback = false;
   1.190 +
   1.191 +    // Now, dispatch the callback on the event-target which called Init()
   1.192 +    nsRefPtr<nsIRunnable> event =
   1.193 +        new nsAsyncVerifyRedirectCallbackEvent(callback, result);
   1.194 +    if (!event) {
   1.195 +        NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   1.196 +                   "failed creating callback event!");
   1.197 +        return;
   1.198 +    }
   1.199 +    nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL);
   1.200 +    if (NS_FAILED(rv)) {
   1.201 +        NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   1.202 +                   "failed dispatching callback event!");
   1.203 +    } else {
   1.204 +        LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   1.205 +             "dispatched callback event=%p", event.get()));
   1.206 +    }
   1.207 +   
   1.208 +}
   1.209 +
   1.210 +void
   1.211 +nsAsyncRedirectVerifyHelper::InitCallback()
   1.212 +{
   1.213 +    LOG(("nsAsyncRedirectVerifyHelper::InitCallback() "
   1.214 +         "expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult));
   1.215 +
   1.216 +    mCallbackInitiated = true;
   1.217 +
   1.218 +    // Invoke the callback if we are done
   1.219 +    if (mExpectedCallbacks == 0)
   1.220 +        ExplicitCallback(mResult);
   1.221 +}
   1.222 +
   1.223 +NS_IMETHODIMP
   1.224 +nsAsyncRedirectVerifyHelper::Run()
   1.225 +{
   1.226 +    /* If the channel got canceled after it fired AsyncOnChannelRedirect
   1.227 +     * and before we got here, mostly because docloader load has been canceled,
   1.228 +     * we must completely ignore this notification and prevent any further
   1.229 +     * notification.
   1.230 +     */
   1.231 +    if (IsOldChannelCanceled()) {
   1.232 +        ExplicitCallback(NS_BINDING_ABORTED);
   1.233 +        return NS_OK;
   1.234 +    }
   1.235 +
   1.236 +    // First, the global observer
   1.237 +    NS_ASSERTION(gIOService, "Must have an IO service at this point");
   1.238 +    LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
   1.239 +    nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan,
   1.240 +                                                     mFlags, this);
   1.241 +    if (NS_FAILED(rv)) {
   1.242 +        ExplicitCallback(rv);
   1.243 +        return NS_OK;
   1.244 +    }
   1.245 +
   1.246 +    // Now, the per-channel observers
   1.247 +    nsCOMPtr<nsIChannelEventSink> sink;
   1.248 +    NS_QueryNotificationCallbacks(mOldChan, sink);
   1.249 +    if (sink) {
   1.250 +        LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
   1.251 +        rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
   1.252 +    }
   1.253 +
   1.254 +    // All invocations to AsyncOnChannelRedirect has been done - call
   1.255 +    // InitCallback() to flag this
   1.256 +    InitCallback();
   1.257 +    return NS_OK;
   1.258 +}
   1.259 +
   1.260 +bool
   1.261 +nsAsyncRedirectVerifyHelper::IsOldChannelCanceled()
   1.262 +{
   1.263 +    bool canceled;
   1.264 +    nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
   1.265 +        do_QueryInterface(mOldChan);
   1.266 +    if (oldChannelInternal) {
   1.267 +        oldChannelInternal->GetCanceled(&canceled);
   1.268 +        if (canceled)
   1.269 +            return true;
   1.270 +    }
   1.271 +
   1.272 +    return false;
   1.273 +}

mercurial