1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/HttpChannelParentListener.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,270 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +#include "HttpChannelParentListener.h" 1.14 +#include "mozilla/net/HttpChannelParent.h" 1.15 +#include "mozilla/unused.h" 1.16 +#include "nsIRedirectChannelRegistrar.h" 1.17 +#include "nsIHttpEventSink.h" 1.18 + 1.19 +using mozilla::unused; 1.20 + 1.21 +namespace mozilla { 1.22 +namespace net { 1.23 + 1.24 +HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel) 1.25 + : mNextListener(aInitialChannel) 1.26 + , mRedirectChannelId(0) 1.27 + , mSuspendedForDiversion(false) 1.28 +{ 1.29 +} 1.30 + 1.31 +HttpChannelParentListener::~HttpChannelParentListener() 1.32 +{ 1.33 +} 1.34 + 1.35 +//----------------------------------------------------------------------------- 1.36 +// HttpChannelParentListener::nsISupports 1.37 +//----------------------------------------------------------------------------- 1.38 + 1.39 +NS_IMPL_ISUPPORTS(HttpChannelParentListener, 1.40 + nsIInterfaceRequestor, 1.41 + nsIStreamListener, 1.42 + nsIRequestObserver, 1.43 + nsIChannelEventSink, 1.44 + nsIRedirectResultListener) 1.45 + 1.46 +//----------------------------------------------------------------------------- 1.47 +// HttpChannelParentListener::nsIRequestObserver 1.48 +//----------------------------------------------------------------------------- 1.49 + 1.50 +NS_IMETHODIMP 1.51 +HttpChannelParentListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) 1.52 +{ 1.53 + MOZ_RELEASE_ASSERT(!mSuspendedForDiversion, 1.54 + "Cannot call OnStartRequest if suspended for diversion!"); 1.55 + 1.56 + if (!mNextListener) 1.57 + return NS_ERROR_UNEXPECTED; 1.58 + 1.59 + LOG(("HttpChannelParentListener::OnStartRequest [this=%p]\n", this)); 1.60 + return mNextListener->OnStartRequest(aRequest, aContext); 1.61 +} 1.62 + 1.63 +NS_IMETHODIMP 1.64 +HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest, 1.65 + nsISupports *aContext, 1.66 + nsresult aStatusCode) 1.67 +{ 1.68 + MOZ_RELEASE_ASSERT(!mSuspendedForDiversion, 1.69 + "Cannot call OnStopRequest if suspended for diversion!"); 1.70 + 1.71 + if (!mNextListener) 1.72 + return NS_ERROR_UNEXPECTED; 1.73 + 1.74 + LOG(("HttpChannelParentListener::OnStopRequest: [this=%p status=%ul]\n", 1.75 + this, aStatusCode)); 1.76 + nsresult rv = mNextListener->OnStopRequest(aRequest, aContext, aStatusCode); 1.77 + 1.78 + mNextListener = nullptr; 1.79 + return rv; 1.80 +} 1.81 + 1.82 +//----------------------------------------------------------------------------- 1.83 +// HttpChannelParentListener::nsIStreamListener 1.84 +//----------------------------------------------------------------------------- 1.85 + 1.86 +NS_IMETHODIMP 1.87 +HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest, 1.88 + nsISupports *aContext, 1.89 + nsIInputStream *aInputStream, 1.90 + uint64_t aOffset, 1.91 + uint32_t aCount) 1.92 +{ 1.93 + MOZ_RELEASE_ASSERT(!mSuspendedForDiversion, 1.94 + "Cannot call OnDataAvailable if suspended for diversion!"); 1.95 + 1.96 + if (!mNextListener) 1.97 + return NS_ERROR_UNEXPECTED; 1.98 + 1.99 + LOG(("HttpChannelParentListener::OnDataAvailable [this=%p]\n", this)); 1.100 + return mNextListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); 1.101 +} 1.102 + 1.103 +//----------------------------------------------------------------------------- 1.104 +// HttpChannelParentListener::nsIInterfaceRequestor 1.105 +//----------------------------------------------------------------------------- 1.106 + 1.107 +NS_IMETHODIMP 1.108 +HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result) 1.109 +{ 1.110 + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)) || 1.111 + aIID.Equals(NS_GET_IID(nsIHttpEventSink)) || 1.112 + aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) 1.113 + { 1.114 + return QueryInterface(aIID, result); 1.115 + } 1.116 + 1.117 + nsCOMPtr<nsIInterfaceRequestor> ir; 1.118 + if (mNextListener && 1.119 + NS_SUCCEEDED(CallQueryInterface(mNextListener.get(), 1.120 + getter_AddRefs(ir)))) 1.121 + { 1.122 + return ir->GetInterface(aIID, result); 1.123 + } 1.124 + 1.125 + return NS_NOINTERFACE; 1.126 +} 1.127 + 1.128 +//----------------------------------------------------------------------------- 1.129 +// HttpChannelParentListener::nsIChannelEventSink 1.130 +//----------------------------------------------------------------------------- 1.131 + 1.132 +NS_IMETHODIMP 1.133 +HttpChannelParentListener::AsyncOnChannelRedirect( 1.134 + nsIChannel *oldChannel, 1.135 + nsIChannel *newChannel, 1.136 + uint32_t redirectFlags, 1.137 + nsIAsyncVerifyRedirectCallback* callback) 1.138 +{ 1.139 + nsresult rv; 1.140 + 1.141 + // Register the new channel and obtain id for it 1.142 + nsCOMPtr<nsIRedirectChannelRegistrar> registrar = 1.143 + do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv); 1.144 + NS_ENSURE_SUCCESS(rv, rv); 1.145 + 1.146 + rv = registrar->RegisterChannel(newChannel, &mRedirectChannelId); 1.147 + NS_ENSURE_SUCCESS(rv, rv); 1.148 + 1.149 + LOG(("Registered %p channel under id=%d", newChannel, mRedirectChannelId)); 1.150 + 1.151 + nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel = 1.152 + do_QueryInterface(mNextListener); 1.153 + if (!activeRedirectingChannel) { 1.154 + NS_RUNTIMEABORT("Channel got a redirect response, but doesn't implement " 1.155 + "nsIParentRedirectingChannel to handle it."); 1.156 + } 1.157 + 1.158 + return activeRedirectingChannel->StartRedirect(mRedirectChannelId, 1.159 + newChannel, 1.160 + redirectFlags, 1.161 + callback); 1.162 +} 1.163 + 1.164 +//----------------------------------------------------------------------------- 1.165 +// HttpChannelParentListener::nsIRedirectResultListener 1.166 +//----------------------------------------------------------------------------- 1.167 + 1.168 +NS_IMETHODIMP 1.169 +HttpChannelParentListener::OnRedirectResult(bool succeeded) 1.170 +{ 1.171 + nsresult rv; 1.172 + 1.173 + nsCOMPtr<nsIParentChannel> redirectChannel; 1.174 + if (mRedirectChannelId) { 1.175 + nsCOMPtr<nsIRedirectChannelRegistrar> registrar = 1.176 + do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv); 1.177 + NS_ENSURE_SUCCESS(rv, rv); 1.178 + 1.179 + rv = registrar->GetParentChannel(mRedirectChannelId, 1.180 + getter_AddRefs(redirectChannel)); 1.181 + if (NS_FAILED(rv) || !redirectChannel) { 1.182 + // Redirect might get canceled before we got AsyncOnChannelRedirect 1.183 + LOG(("Registered parent channel not found under id=%d", mRedirectChannelId)); 1.184 + 1.185 + nsCOMPtr<nsIChannel> newChannel; 1.186 + rv = registrar->GetRegisteredChannel(mRedirectChannelId, 1.187 + getter_AddRefs(newChannel)); 1.188 + MOZ_ASSERT(newChannel, "Already registered channel not found"); 1.189 + 1.190 + if (NS_SUCCEEDED(rv)) 1.191 + newChannel->Cancel(NS_BINDING_ABORTED); 1.192 + } 1.193 + 1.194 + // Release all previously registered channels, they are no longer need to be 1.195 + // kept in the registrar from this moment. 1.196 + registrar->DeregisterChannels(mRedirectChannelId); 1.197 + 1.198 + mRedirectChannelId = 0; 1.199 + } 1.200 + 1.201 + if (!redirectChannel) { 1.202 + succeeded = false; 1.203 + } 1.204 + 1.205 + nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel = 1.206 + do_QueryInterface(mNextListener); 1.207 + MOZ_ASSERT(activeRedirectingChannel, 1.208 + "Channel finished a redirect response, but doesn't implement " 1.209 + "nsIParentRedirectingChannel to complete it."); 1.210 + 1.211 + if (activeRedirectingChannel) { 1.212 + activeRedirectingChannel->CompleteRedirect(succeeded); 1.213 + } else { 1.214 + succeeded = false; 1.215 + } 1.216 + 1.217 + if (succeeded) { 1.218 + // Switch to redirect channel and delete the old one. 1.219 + nsCOMPtr<nsIParentChannel> parent; 1.220 + parent = do_QueryInterface(mNextListener); 1.221 + MOZ_ASSERT(parent); 1.222 + parent->Delete(); 1.223 + mNextListener = do_QueryInterface(redirectChannel); 1.224 + MOZ_ASSERT(mNextListener); 1.225 + redirectChannel->SetParentListener(this); 1.226 + } else if (redirectChannel) { 1.227 + // Delete the redirect target channel: continue using old channel 1.228 + redirectChannel->Delete(); 1.229 + } 1.230 + 1.231 + return NS_OK; 1.232 +} 1.233 + 1.234 +//----------------------------------------------------------------------------- 1.235 + 1.236 +nsresult 1.237 +HttpChannelParentListener::SuspendForDiversion() 1.238 +{ 1.239 + if (NS_WARN_IF(mSuspendedForDiversion)) { 1.240 + MOZ_ASSERT(!mSuspendedForDiversion, "Cannot SuspendForDiversion twice!"); 1.241 + return NS_ERROR_UNEXPECTED; 1.242 + } 1.243 + 1.244 + // While this is set, no OnStart/OnData/OnStop callbacks should be forwarded 1.245 + // to mNextListener. 1.246 + mSuspendedForDiversion = true; 1.247 + 1.248 + return NS_OK; 1.249 +} 1.250 + 1.251 +nsresult 1.252 +HttpChannelParentListener::ResumeForDiversion() 1.253 +{ 1.254 + MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!"); 1.255 + 1.256 + // Allow OnStart/OnData/OnStop callbacks to be forwarded to mNextListener. 1.257 + mSuspendedForDiversion = false; 1.258 + 1.259 + return NS_OK; 1.260 +} 1.261 + 1.262 +nsresult 1.263 +HttpChannelParentListener::DivertTo(nsIStreamListener* aListener) 1.264 +{ 1.265 + MOZ_ASSERT(aListener); 1.266 + MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!"); 1.267 + 1.268 + mNextListener = aListener; 1.269 + 1.270 + return ResumeForDiversion(); 1.271 +} 1.272 + 1.273 +}} // mozilla::net