netwerk/base/src/nsAsyncRedirectVerifyHelper.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 #ifdef MOZ_LOGGING
     7 #define FORCE_PR_LOG
     8 #endif
    10 #include "prlog.h"
    11 #include "nsAsyncRedirectVerifyHelper.h"
    12 #include "nsThreadUtils.h"
    13 #include "nsNetUtil.h"
    15 #include "nsIOService.h"
    16 #include "nsIChannel.h"
    17 #include "nsIHttpChannelInternal.h"
    18 #include "nsIAsyncVerifyRedirectCallback.h"
    20 #undef LOG
    21 #ifdef PR_LOGGING
    22 static PRLogModuleInfo *
    23 GetRedirectLog()
    24 {
    25     static PRLogModuleInfo *sLog;
    26     if (!sLog)
    27         sLog = PR_NewLogModule("nsRedirect");
    28     return sLog;
    29 }
    30 #define LOG(args) PR_LOG(GetRedirectLog(), PR_LOG_DEBUG, args)
    31 #else
    32 #define LOG(args)
    33 #endif
    35 NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper,
    36                   nsIAsyncVerifyRedirectCallback,
    37                   nsIRunnable)
    39 class nsAsyncVerifyRedirectCallbackEvent : public nsRunnable {
    40 public:
    41     nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
    42                                        nsresult result)
    43         : mCallback(cb), mResult(result) {
    44     }
    46     NS_IMETHOD Run()
    47     {
    48         LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() "
    49              "callback to %p with result %x",
    50              mCallback.get(), mResult));
    51        (void) mCallback->OnRedirectVerifyCallback(mResult);
    52        return NS_OK;
    53     }
    54 private:
    55     nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
    56     nsresult mResult;
    57 };
    59 nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper()
    60     : mCallbackInitiated(false),
    61       mExpectedCallbacks(0),
    62       mResult(NS_OK)
    63 {
    64 }
    66 nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper()
    67 {
    68     NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
    69                  "Did not receive all required callbacks!");
    70 }
    72 nsresult
    73 nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
    74                                   uint32_t flags, bool synchronize)
    75 {
    76     LOG(("nsAsyncRedirectVerifyHelper::Init() "
    77          "oldChan=%p newChan=%p", oldChan, newChan));
    78     mOldChan           = oldChan;
    79     mNewChan           = newChan;
    80     mFlags             = flags;
    81     mCallbackThread    = do_GetCurrentThread();
    83     if (synchronize)
    84       mWaitingForRedirectCallback = true;
    86     nsresult rv;
    87     rv = NS_DispatchToMainThread(this);
    88     NS_ENSURE_SUCCESS(rv, rv);
    90     if (synchronize) {
    91       nsIThread *thread = NS_GetCurrentThread();
    92       while (mWaitingForRedirectCallback) {
    93         if (!NS_ProcessNextEvent(thread)) {
    94           return NS_ERROR_UNEXPECTED;
    95         }
    96       }
    97     }
    99     return NS_OK;
   100 }
   102 NS_IMETHODIMP
   103 nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result)
   104 {
   105     LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
   106          "result=%x expectedCBs=%u mResult=%x",
   107          result, mExpectedCallbacks, mResult));
   109     --mExpectedCallbacks;
   111     // If response indicates failure we may call back immediately
   112     if (NS_FAILED(result)) {
   113         // We chose to store the first failure-value (as opposed to the last)
   114         if (NS_SUCCEEDED(mResult))
   115             mResult = result;
   117         // If InitCallback() has been called, just invoke the callback and
   118         // return. Otherwise it will be invoked from InitCallback()
   119         if (mCallbackInitiated) {
   120             ExplicitCallback(mResult);
   121             return NS_OK;
   122         }
   123     }
   125     // If the expected-counter is in balance and InitCallback() was called, all
   126     // sinks have agreed that the redirect is ok and we can invoke our callback
   127     if (mCallbackInitiated && mExpectedCallbacks == 0) {
   128         ExplicitCallback(mResult);
   129     }
   131     return NS_OK;
   132 }
   134 nsresult
   135 nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink,
   136                                                        nsIChannel *oldChannel,
   137                                                        nsIChannel *newChannel,
   138                                                        uint32_t flags)
   139 {
   140     LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
   141          "sink=%p expectedCBs=%u mResult=%x",
   142          sink, mExpectedCallbacks, mResult));
   144     ++mExpectedCallbacks;
   146     if (IsOldChannelCanceled()) {
   147         LOG(("  old channel has been canceled, cancel the redirect by "
   148              "emulating OnRedirectVerifyCallback..."));
   149         (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED);
   150         return NS_BINDING_ABORTED;
   151     }
   153     nsresult rv =
   154         sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
   156     LOG(("  result=%x expectedCBs=%u", rv, mExpectedCallbacks));
   158     // If the sink returns failure from this call the redirect is vetoed. We
   159     // emulate a callback from the sink in this case in order to perform all
   160     // the necessary logic.
   161     if (NS_FAILED(rv)) {
   162         LOG(("  emulating OnRedirectVerifyCallback..."));
   163         (void) OnRedirectVerifyCallback(rv);
   164     }
   166     return rv;  // Return the actual status since our caller may need it
   167 }
   169 void
   170 nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result)
   171 {
   172     LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   173          "result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x",
   174          result, mExpectedCallbacks, mCallbackInitiated, mResult));
   176     nsCOMPtr<nsIAsyncVerifyRedirectCallback>
   177         callback(do_QueryInterface(mOldChan));
   179     if (!callback || !mCallbackThread) {
   180         LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   181              "callback=%p mCallbackThread=%p", callback.get(), mCallbackThread.get()));
   182         return;
   183     }
   185     mCallbackInitiated = false;  // reset to ensure only one callback
   186     mWaitingForRedirectCallback = false;
   188     // Now, dispatch the callback on the event-target which called Init()
   189     nsRefPtr<nsIRunnable> event =
   190         new nsAsyncVerifyRedirectCallbackEvent(callback, result);
   191     if (!event) {
   192         NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   193                    "failed creating callback event!");
   194         return;
   195     }
   196     nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL);
   197     if (NS_FAILED(rv)) {
   198         NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   199                    "failed dispatching callback event!");
   200     } else {
   201         LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
   202              "dispatched callback event=%p", event.get()));
   203     }
   205 }
   207 void
   208 nsAsyncRedirectVerifyHelper::InitCallback()
   209 {
   210     LOG(("nsAsyncRedirectVerifyHelper::InitCallback() "
   211          "expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult));
   213     mCallbackInitiated = true;
   215     // Invoke the callback if we are done
   216     if (mExpectedCallbacks == 0)
   217         ExplicitCallback(mResult);
   218 }
   220 NS_IMETHODIMP
   221 nsAsyncRedirectVerifyHelper::Run()
   222 {
   223     /* If the channel got canceled after it fired AsyncOnChannelRedirect
   224      * and before we got here, mostly because docloader load has been canceled,
   225      * we must completely ignore this notification and prevent any further
   226      * notification.
   227      */
   228     if (IsOldChannelCanceled()) {
   229         ExplicitCallback(NS_BINDING_ABORTED);
   230         return NS_OK;
   231     }
   233     // First, the global observer
   234     NS_ASSERTION(gIOService, "Must have an IO service at this point");
   235     LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
   236     nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan,
   237                                                      mFlags, this);
   238     if (NS_FAILED(rv)) {
   239         ExplicitCallback(rv);
   240         return NS_OK;
   241     }
   243     // Now, the per-channel observers
   244     nsCOMPtr<nsIChannelEventSink> sink;
   245     NS_QueryNotificationCallbacks(mOldChan, sink);
   246     if (sink) {
   247         LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
   248         rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
   249     }
   251     // All invocations to AsyncOnChannelRedirect has been done - call
   252     // InitCallback() to flag this
   253     InitCallback();
   254     return NS_OK;
   255 }
   257 bool
   258 nsAsyncRedirectVerifyHelper::IsOldChannelCanceled()
   259 {
   260     bool canceled;
   261     nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
   262         do_QueryInterface(mOldChan);
   263     if (oldChannelInternal) {
   264         oldChannelInternal->GetCanceled(&canceled);
   265         if (canceled)
   266             return true;
   267     }
   269     return false;
   270 }

mercurial