Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et tw=80 : */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : // HttpLog.h should generally be included first
8 : #include "HttpLog.h"
9 :
10 : #include "HttpChannelParentListener.h"
11 : #include "mozilla/net/HttpChannelParent.h"
12 : #include "mozilla/Unused.h"
13 : #include "nsIAuthPrompt.h"
14 : #include "nsIAuthPrompt2.h"
15 : #include "nsIHttpEventSink.h"
16 : #include "nsIHttpHeaderVisitor.h"
17 : #include "nsIRedirectChannelRegistrar.h"
18 : #include "nsIPromptFactory.h"
19 : #include "nsIWindowWatcher.h"
20 : #include "nsQueryObject.h"
21 :
22 : using mozilla::Unused;
23 :
24 : namespace mozilla {
25 : namespace net {
26 :
27 3 : HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel)
28 : : mNextListener(aInitialChannel)
29 : , mRedirectChannelId(0)
30 : , mSuspendedForDiversion(false)
31 : , mShouldIntercept(false)
32 3 : , mShouldSuspendIntercept(false)
33 : {
34 3 : LOG(("HttpChannelParentListener::HttpChannelParentListener [this=%p, next=%p]",
35 : this, aInitialChannel));
36 3 : }
37 :
38 4 : HttpChannelParentListener::~HttpChannelParentListener()
39 : {
40 6 : }
41 :
42 : //-----------------------------------------------------------------------------
43 : // HttpChannelParentListener::nsISupports
44 : //-----------------------------------------------------------------------------
45 :
46 91 : NS_IMPL_ADDREF(HttpChannelParentListener)
47 90 : NS_IMPL_RELEASE(HttpChannelParentListener)
48 47 : NS_INTERFACE_MAP_BEGIN(HttpChannelParentListener)
49 47 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
50 13 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
51 6 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
52 6 : NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
53 6 : NS_INTERFACE_MAP_ENTRY(nsIRedirectResultListener)
54 6 : NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
55 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
56 0 : if (aIID.Equals(NS_GET_IID(HttpChannelParentListener))) {
57 0 : foundInterface = static_cast<nsIInterfaceRequestor*>(this);
58 : } else
59 0 : NS_INTERFACE_MAP_END
60 :
61 : //-----------------------------------------------------------------------------
62 : // HttpChannelParentListener::nsIRequestObserver
63 : //-----------------------------------------------------------------------------
64 :
65 : NS_IMETHODIMP
66 3 : HttpChannelParentListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
67 : {
68 3 : MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
69 : "Cannot call OnStartRequest if suspended for diversion!");
70 :
71 3 : if (!mNextListener)
72 0 : return NS_ERROR_UNEXPECTED;
73 :
74 3 : LOG(("HttpChannelParentListener::OnStartRequest [this=%p]\n", this));
75 3 : return mNextListener->OnStartRequest(aRequest, aContext);
76 : }
77 :
78 : NS_IMETHODIMP
79 3 : HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest,
80 : nsISupports *aContext,
81 : nsresult aStatusCode)
82 : {
83 3 : MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
84 : "Cannot call OnStopRequest if suspended for diversion!");
85 :
86 3 : if (!mNextListener)
87 0 : return NS_ERROR_UNEXPECTED;
88 :
89 3 : LOG(("HttpChannelParentListener::OnStopRequest: [this=%p status=%" PRIu32 "]\n",
90 : this, static_cast<uint32_t>(aStatusCode)));
91 3 : nsresult rv = mNextListener->OnStopRequest(aRequest, aContext, aStatusCode);
92 :
93 3 : mNextListener = nullptr;
94 3 : return rv;
95 : }
96 :
97 : //-----------------------------------------------------------------------------
98 : // HttpChannelParentListener::nsIStreamListener
99 : //-----------------------------------------------------------------------------
100 :
101 : NS_IMETHODIMP
102 3 : HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest,
103 : nsISupports *aContext,
104 : nsIInputStream *aInputStream,
105 : uint64_t aOffset,
106 : uint32_t aCount)
107 : {
108 3 : MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
109 : "Cannot call OnDataAvailable if suspended for diversion!");
110 :
111 3 : if (!mNextListener)
112 0 : return NS_ERROR_UNEXPECTED;
113 :
114 3 : LOG(("HttpChannelParentListener::OnDataAvailable [this=%p]\n", this));
115 3 : return mNextListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
116 : }
117 :
118 : //-----------------------------------------------------------------------------
119 : // HttpChannelParentListener::nsIInterfaceRequestor
120 : //-----------------------------------------------------------------------------
121 :
122 : NS_IMETHODIMP
123 37 : HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result)
124 : {
125 111 : if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)) ||
126 74 : aIID.Equals(NS_GET_IID(nsIHttpEventSink)) ||
127 108 : aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) ||
128 34 : aIID.Equals(NS_GET_IID(nsIRedirectResultListener)))
129 : {
130 3 : return QueryInterface(aIID, result);
131 : }
132 :
133 68 : nsCOMPtr<nsIInterfaceRequestor> ir;
134 170 : if (mNextListener &&
135 170 : NS_SUCCEEDED(CallQueryInterface(mNextListener.get(),
136 : getter_AddRefs(ir))))
137 : {
138 34 : return ir->GetInterface(aIID, result);
139 : }
140 :
141 0 : if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
142 0 : aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
143 : nsresult rv;
144 : nsCOMPtr<nsIPromptFactory> wwatch =
145 0 : do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
146 0 : NS_ENSURE_SUCCESS(rv, rv);
147 :
148 0 : return wwatch->GetPrompt(nullptr, aIID,
149 0 : reinterpret_cast<void**>(result));
150 : }
151 :
152 0 : return NS_NOINTERFACE;
153 : }
154 :
155 : //-----------------------------------------------------------------------------
156 : // HttpChannelParentListener::nsIChannelEventSink
157 : //-----------------------------------------------------------------------------
158 :
159 : NS_IMETHODIMP
160 0 : HttpChannelParentListener::AsyncOnChannelRedirect(
161 : nsIChannel *oldChannel,
162 : nsIChannel *newChannel,
163 : uint32_t redirectFlags,
164 : nsIAsyncVerifyRedirectCallback* callback)
165 : {
166 0 : LOG(("HttpChannelParentListener::AsyncOnChannelRedirect [this=%p, old=%p, new=%p, flags=%u]",
167 : this, oldChannel, newChannel, redirectFlags));
168 :
169 : nsresult rv;
170 :
171 : nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
172 0 : do_QueryInterface(mNextListener);
173 0 : if (!activeRedirectingChannel) {
174 0 : NS_ERROR("Channel got a redirect response, but doesn't implement "
175 : "nsIParentRedirectingChannel to handle it.");
176 0 : return NS_ERROR_NOT_IMPLEMENTED;
177 : }
178 :
179 : // Register the new channel and obtain id for it
180 : nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
181 0 : do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv);
182 0 : NS_ENSURE_SUCCESS(rv, rv);
183 :
184 0 : rv = registrar->RegisterChannel(newChannel, &mRedirectChannelId);
185 0 : NS_ENSURE_SUCCESS(rv, rv);
186 :
187 0 : LOG(("Registered %p channel under id=%d", newChannel, mRedirectChannelId));
188 :
189 0 : return activeRedirectingChannel->StartRedirect(mRedirectChannelId,
190 : newChannel,
191 : redirectFlags,
192 0 : callback);
193 : }
194 :
195 : //-----------------------------------------------------------------------------
196 : // HttpChannelParentListener::nsIRedirectResultListener
197 : //-----------------------------------------------------------------------------
198 :
199 : NS_IMETHODIMP
200 0 : HttpChannelParentListener::OnRedirectResult(bool succeeded)
201 : {
202 0 : LOG(("HttpChannelParentListener::OnRedirectResult [this=%p, suc=%d]",
203 : this, succeeded));
204 :
205 : nsresult rv;
206 :
207 0 : nsCOMPtr<nsIParentChannel> redirectChannel;
208 0 : if (mRedirectChannelId) {
209 : nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
210 0 : do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv);
211 0 : NS_ENSURE_SUCCESS(rv, rv);
212 :
213 0 : rv = registrar->GetParentChannel(mRedirectChannelId,
214 0 : getter_AddRefs(redirectChannel));
215 0 : if (NS_FAILED(rv) || !redirectChannel) {
216 : // Redirect might get canceled before we got AsyncOnChannelRedirect
217 0 : LOG(("Registered parent channel not found under id=%d", mRedirectChannelId));
218 :
219 0 : nsCOMPtr<nsIChannel> newChannel;
220 0 : rv = registrar->GetRegisteredChannel(mRedirectChannelId,
221 0 : getter_AddRefs(newChannel));
222 0 : MOZ_ASSERT(newChannel, "Already registered channel not found");
223 :
224 0 : if (NS_SUCCEEDED(rv))
225 0 : newChannel->Cancel(NS_BINDING_ABORTED);
226 : }
227 :
228 : // Release all previously registered channels, they are no longer need to be
229 : // kept in the registrar from this moment.
230 0 : registrar->DeregisterChannels(mRedirectChannelId);
231 :
232 0 : mRedirectChannelId = 0;
233 : }
234 :
235 0 : if (!redirectChannel) {
236 0 : succeeded = false;
237 : }
238 :
239 : nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
240 0 : do_QueryInterface(mNextListener);
241 0 : MOZ_ASSERT(activeRedirectingChannel,
242 : "Channel finished a redirect response, but doesn't implement "
243 : "nsIParentRedirectingChannel to complete it.");
244 :
245 0 : if (activeRedirectingChannel) {
246 0 : activeRedirectingChannel->CompleteRedirect(succeeded);
247 : } else {
248 0 : succeeded = false;
249 : }
250 :
251 0 : if (succeeded) {
252 : // Switch to redirect channel and delete the old one.
253 0 : nsCOMPtr<nsIParentChannel> parent;
254 0 : parent = do_QueryInterface(mNextListener);
255 0 : MOZ_ASSERT(parent);
256 0 : parent->Delete();
257 0 : mNextListener = do_QueryInterface(redirectChannel);
258 0 : MOZ_ASSERT(mNextListener);
259 0 : redirectChannel->SetParentListener(this);
260 0 : } else if (redirectChannel) {
261 : // Delete the redirect target channel: continue using old channel
262 0 : redirectChannel->Delete();
263 : }
264 :
265 0 : return NS_OK;
266 : }
267 :
268 : //-----------------------------------------------------------------------------
269 : // HttpChannelParentListener::nsINetworkInterceptController
270 : //-----------------------------------------------------------------------------
271 :
272 : NS_IMETHODIMP
273 0 : HttpChannelParentListener::ShouldPrepareForIntercept(nsIURI* aURI,
274 : bool aIsNonSubresourceRequest,
275 : bool* aShouldIntercept)
276 : {
277 0 : *aShouldIntercept = mShouldIntercept;
278 0 : return NS_OK;
279 : }
280 :
281 : class HeaderVisitor final : public nsIHttpHeaderVisitor
282 : {
283 : nsCOMPtr<nsIInterceptedChannel> mChannel;
284 0 : ~HeaderVisitor()
285 0 : {
286 0 : }
287 : public:
288 0 : explicit HeaderVisitor(nsIInterceptedChannel* aChannel) : mChannel(aChannel)
289 : {
290 0 : }
291 :
292 : NS_DECL_ISUPPORTS
293 :
294 0 : NS_IMETHOD VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
295 : {
296 0 : mChannel->SynthesizeHeader(aHeader, aValue);
297 0 : return NS_OK;
298 : }
299 : };
300 :
301 0 : NS_IMPL_ISUPPORTS(HeaderVisitor, nsIHttpHeaderVisitor)
302 :
303 0 : class FinishSynthesizedResponse : public Runnable
304 : {
305 : nsCOMPtr<nsIInterceptedChannel> mChannel;
306 : public:
307 0 : explicit FinishSynthesizedResponse(nsIInterceptedChannel* aChannel)
308 0 : : Runnable("net::FinishSynthesizedResponse")
309 0 : , mChannel(aChannel)
310 : {
311 0 : }
312 :
313 0 : NS_IMETHOD Run() override
314 : {
315 : // The URL passed as an argument here doesn't matter, since the child will
316 : // receive a redirection notification as a result of this synthesized response.
317 0 : mChannel->FinishSynthesizedResponse(EmptyCString());
318 0 : return NS_OK;
319 : }
320 : };
321 :
322 : NS_IMETHODIMP
323 0 : HttpChannelParentListener::ChannelIntercepted(nsIInterceptedChannel* aChannel)
324 : {
325 0 : if (mShouldSuspendIntercept) {
326 0 : mInterceptedChannel = aChannel;
327 0 : return NS_OK;
328 : }
329 :
330 0 : nsAutoCString statusText;
331 0 : mSynthesizedResponseHead->StatusText(statusText);
332 0 : aChannel->SynthesizeStatus(mSynthesizedResponseHead->Status(), statusText);
333 0 : nsCOMPtr<nsIHttpHeaderVisitor> visitor = new HeaderVisitor(aChannel);
334 : DebugOnly<nsresult> rv =
335 0 : mSynthesizedResponseHead->VisitHeaders(visitor,
336 0 : nsHttpHeaderArray::eFilterResponse);
337 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
338 :
339 0 : nsCOMPtr<nsIRunnable> event = new FinishSynthesizedResponse(aChannel);
340 0 : NS_DispatchToCurrentThread(event);
341 :
342 0 : mSynthesizedResponseHead = nullptr;
343 :
344 0 : MOZ_ASSERT(mNextListener);
345 0 : RefPtr<HttpChannelParent> channel = do_QueryObject(mNextListener);
346 0 : MOZ_ASSERT(channel);
347 0 : channel->ResponseSynthesized();
348 :
349 0 : return NS_OK;
350 : }
351 :
352 : //-----------------------------------------------------------------------------
353 :
354 : nsresult
355 0 : HttpChannelParentListener::SuspendForDiversion()
356 : {
357 0 : if (NS_WARN_IF(mSuspendedForDiversion)) {
358 0 : MOZ_ASSERT(!mSuspendedForDiversion, "Cannot SuspendForDiversion twice!");
359 0 : return NS_ERROR_UNEXPECTED;
360 : }
361 :
362 : // While this is set, no OnStart/OnData/OnStop callbacks should be forwarded
363 : // to mNextListener.
364 0 : mSuspendedForDiversion = true;
365 :
366 0 : return NS_OK;
367 : }
368 :
369 : nsresult
370 0 : HttpChannelParentListener::ResumeForDiversion()
371 : {
372 0 : MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!");
373 :
374 : // Allow OnStart/OnData/OnStop callbacks to be forwarded to mNextListener.
375 0 : mSuspendedForDiversion = false;
376 :
377 0 : return NS_OK;
378 : }
379 :
380 : nsresult
381 0 : HttpChannelParentListener::DivertTo(nsIStreamListener* aListener)
382 : {
383 0 : MOZ_ASSERT(aListener);
384 0 : MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!");
385 :
386 0 : mNextListener = aListener;
387 :
388 0 : return ResumeForDiversion();
389 : }
390 :
391 : void
392 0 : HttpChannelParentListener::SetupInterception(const nsHttpResponseHead& aResponseHead)
393 : {
394 0 : mSynthesizedResponseHead = new nsHttpResponseHead(aResponseHead);
395 0 : mShouldIntercept = true;
396 0 : }
397 :
398 : void
399 0 : HttpChannelParentListener::SetupInterceptionAfterRedirect(bool aShouldIntercept)
400 : {
401 0 : mShouldIntercept = aShouldIntercept;
402 0 : if (mShouldIntercept) {
403 : // When an interception occurs, this channel should suspend all further activity.
404 : // It will be torn down and recreated if necessary.
405 0 : mShouldSuspendIntercept = true;
406 : }
407 0 : }
408 :
409 : void
410 2 : HttpChannelParentListener::ClearInterceptedChannel()
411 : {
412 2 : if (mInterceptedChannel) {
413 0 : mInterceptedChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
414 0 : mInterceptedChannel = nullptr;
415 : }
416 2 : }
417 :
418 : } // namespace net
419 : } // namespace mozilla
|