nsAsyncRedirectVerifyHelper.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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/. */
  5. #include "mozilla/Logging.h"
  6. #include "nsAsyncRedirectVerifyHelper.h"
  7. #include "nsThreadUtils.h"
  8. #include "nsNetUtil.h"
  9. #include "nsIOService.h"
  10. #include "nsIChannel.h"
  11. #include "nsIHttpChannelInternal.h"
  12. #include "nsIAsyncVerifyRedirectCallback.h"
  13. #include "nsILoadInfo.h"
  14. namespace mozilla {
  15. namespace net {
  16. static LazyLogModule gRedirectLog("nsRedirect");
  17. #undef LOG
  18. #define LOG(args) MOZ_LOG(gRedirectLog, LogLevel::Debug, args)
  19. NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper,
  20. nsIAsyncVerifyRedirectCallback,
  21. nsIRunnable)
  22. class nsAsyncVerifyRedirectCallbackEvent : public Runnable {
  23. public:
  24. nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback *cb,
  25. nsresult result)
  26. : mCallback(cb), mResult(result) {
  27. }
  28. NS_IMETHOD Run() override
  29. {
  30. LOG(("nsAsyncVerifyRedirectCallbackEvent::Run() "
  31. "callback to %p with result %x",
  32. mCallback.get(), mResult));
  33. (void) mCallback->OnRedirectVerifyCallback(mResult);
  34. return NS_OK;
  35. }
  36. private:
  37. nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
  38. nsresult mResult;
  39. };
  40. nsAsyncRedirectVerifyHelper::nsAsyncRedirectVerifyHelper()
  41. : mFlags(0),
  42. mWaitingForRedirectCallback(false),
  43. mCallbackInitiated(false),
  44. mExpectedCallbacks(0),
  45. mResult(NS_OK)
  46. {
  47. }
  48. nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper()
  49. {
  50. NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
  51. "Did not receive all required callbacks!");
  52. }
  53. nsresult
  54. nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
  55. uint32_t flags, bool synchronize)
  56. {
  57. LOG(("nsAsyncRedirectVerifyHelper::Init() "
  58. "oldChan=%p newChan=%p", oldChan, newChan));
  59. mOldChan = oldChan;
  60. mNewChan = newChan;
  61. mFlags = flags;
  62. mCallbackThread = do_GetCurrentThread();
  63. if (!(flags & (nsIChannelEventSink::REDIRECT_INTERNAL |
  64. nsIChannelEventSink::REDIRECT_STS_UPGRADE))) {
  65. nsCOMPtr<nsILoadInfo> loadInfo = oldChan->GetLoadInfo();
  66. if (loadInfo && loadInfo->GetDontFollowRedirects()) {
  67. ExplicitCallback(NS_BINDING_ABORTED);
  68. return NS_OK;
  69. }
  70. }
  71. if (synchronize)
  72. mWaitingForRedirectCallback = true;
  73. nsresult rv;
  74. rv = NS_DispatchToMainThread(this);
  75. NS_ENSURE_SUCCESS(rv, rv);
  76. if (synchronize) {
  77. nsIThread *thread = NS_GetCurrentThread();
  78. while (mWaitingForRedirectCallback) {
  79. if (!NS_ProcessNextEvent(thread)) {
  80. return NS_ERROR_UNEXPECTED;
  81. }
  82. }
  83. }
  84. return NS_OK;
  85. }
  86. NS_IMETHODIMP
  87. nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result)
  88. {
  89. LOG(("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
  90. "result=%x expectedCBs=%u mResult=%x",
  91. result, mExpectedCallbacks, mResult));
  92. MOZ_DIAGNOSTIC_ASSERT(mExpectedCallbacks > 0,
  93. "OnRedirectVerifyCallback called more times than expected");
  94. if (mExpectedCallbacks <= 0) {
  95. return NS_ERROR_UNEXPECTED;
  96. }
  97. --mExpectedCallbacks;
  98. // If response indicates failure we may call back immediately
  99. if (NS_FAILED(result)) {
  100. // We chose to store the first failure-value (as opposed to the last)
  101. if (NS_SUCCEEDED(mResult))
  102. mResult = result;
  103. // If InitCallback() has been called, just invoke the callback and
  104. // return. Otherwise it will be invoked from InitCallback()
  105. if (mCallbackInitiated) {
  106. ExplicitCallback(mResult);
  107. return NS_OK;
  108. }
  109. }
  110. // If the expected-counter is in balance and InitCallback() was called, all
  111. // sinks have agreed that the redirect is ok and we can invoke our callback
  112. if (mCallbackInitiated && mExpectedCallbacks == 0) {
  113. ExplicitCallback(mResult);
  114. }
  115. return NS_OK;
  116. }
  117. nsresult
  118. nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink,
  119. nsIChannel *oldChannel,
  120. nsIChannel *newChannel,
  121. uint32_t flags)
  122. {
  123. LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
  124. "sink=%p expectedCBs=%u mResult=%x",
  125. sink, mExpectedCallbacks, mResult));
  126. ++mExpectedCallbacks;
  127. if (IsOldChannelCanceled()) {
  128. LOG((" old channel has been canceled, cancel the redirect by "
  129. "emulating OnRedirectVerifyCallback..."));
  130. (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED);
  131. return NS_BINDING_ABORTED;
  132. }
  133. nsresult rv =
  134. sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
  135. LOG((" result=%x expectedCBs=%u", rv, mExpectedCallbacks));
  136. // If the sink returns failure from this call the redirect is vetoed. We
  137. // emulate a callback from the sink in this case in order to perform all
  138. // the necessary logic.
  139. if (NS_FAILED(rv)) {
  140. LOG((" emulating OnRedirectVerifyCallback..."));
  141. (void) OnRedirectVerifyCallback(rv);
  142. }
  143. return rv; // Return the actual status since our caller may need it
  144. }
  145. void
  146. nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result)
  147. {
  148. LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
  149. "result=%x expectedCBs=%u mCallbackInitiated=%u mResult=%x",
  150. result, mExpectedCallbacks, mCallbackInitiated, mResult));
  151. nsCOMPtr<nsIAsyncVerifyRedirectCallback>
  152. callback(do_QueryInterface(mOldChan));
  153. if (!callback || !mCallbackThread) {
  154. LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
  155. "callback=%p mCallbackThread=%p", callback.get(), mCallbackThread.get()));
  156. return;
  157. }
  158. mCallbackInitiated = false; // reset to ensure only one callback
  159. mWaitingForRedirectCallback = false;
  160. // Now, dispatch the callback on the event-target which called Init()
  161. nsCOMPtr<nsIRunnable> event =
  162. new nsAsyncVerifyRedirectCallbackEvent(callback, result);
  163. if (!event) {
  164. NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
  165. "failed creating callback event!");
  166. return;
  167. }
  168. nsresult rv = mCallbackThread->Dispatch(event, NS_DISPATCH_NORMAL);
  169. if (NS_FAILED(rv)) {
  170. NS_WARNING("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
  171. "failed dispatching callback event!");
  172. } else {
  173. LOG(("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
  174. "dispatched callback event=%p", event.get()));
  175. }
  176. }
  177. void
  178. nsAsyncRedirectVerifyHelper::InitCallback()
  179. {
  180. LOG(("nsAsyncRedirectVerifyHelper::InitCallback() "
  181. "expectedCBs=%d mResult=%x", mExpectedCallbacks, mResult));
  182. mCallbackInitiated = true;
  183. // Invoke the callback if we are done
  184. if (mExpectedCallbacks == 0)
  185. ExplicitCallback(mResult);
  186. }
  187. NS_IMETHODIMP
  188. nsAsyncRedirectVerifyHelper::Run()
  189. {
  190. /* If the channel got canceled after it fired AsyncOnChannelRedirect
  191. * and before we got here, mostly because docloader load has been canceled,
  192. * we must completely ignore this notification and prevent any further
  193. * notification.
  194. */
  195. if (IsOldChannelCanceled()) {
  196. ExplicitCallback(NS_BINDING_ABORTED);
  197. return NS_OK;
  198. }
  199. // First, the global observer
  200. NS_ASSERTION(gIOService, "Must have an IO service at this point");
  201. LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
  202. nsresult rv = gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan,
  203. mFlags, this);
  204. if (NS_FAILED(rv)) {
  205. ExplicitCallback(rv);
  206. return NS_OK;
  207. }
  208. // Now, the per-channel observers
  209. nsCOMPtr<nsIChannelEventSink> sink;
  210. NS_QueryNotificationCallbacks(mOldChan, sink);
  211. if (sink) {
  212. LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
  213. rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
  214. }
  215. // All invocations to AsyncOnChannelRedirect has been done - call
  216. // InitCallback() to flag this
  217. InitCallback();
  218. return NS_OK;
  219. }
  220. bool
  221. nsAsyncRedirectVerifyHelper::IsOldChannelCanceled()
  222. {
  223. bool canceled;
  224. nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
  225. do_QueryInterface(mOldChan);
  226. if (oldChannelInternal) {
  227. oldChannelInternal->GetCanceled(&canceled);
  228. if (canceled) {
  229. return true;
  230. }
  231. } else if (mOldChan) {
  232. // For non-HTTP channels check on the status, failure
  233. // indicates the channel has probably been canceled.
  234. nsresult status = NS_ERROR_FAILURE;
  235. mOldChan->GetStatus(&status);
  236. if (NS_FAILED(status)) {
  237. return true;
  238. }
  239. }
  240. return false;
  241. }
  242. } // namespace net
  243. } // namespace mozilla