/src/mozilla-central/netwerk/protocol/http/InterceptedHttpChannel.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "InterceptedHttpChannel.h" |
8 | | #include "nsContentSecurityManager.h" |
9 | | #include "nsEscape.h" |
10 | | #include "mozilla/dom/PerformanceStorage.h" |
11 | | |
12 | | namespace mozilla { |
13 | | namespace net { |
14 | | |
15 | | NS_IMPL_ISUPPORTS_INHERITED(InterceptedHttpChannel, |
16 | | HttpBaseChannel, |
17 | | nsIInterceptedChannel, |
18 | | nsICacheInfoChannel, |
19 | | nsIAsyncVerifyRedirectCallback, |
20 | | nsIRequestObserver, |
21 | | nsIStreamListener, |
22 | | nsIChannelWithDivertableParentListener, |
23 | | nsIThreadRetargetableRequest, |
24 | | nsIThreadRetargetableStreamListener) |
25 | | |
26 | | InterceptedHttpChannel::InterceptedHttpChannel(PRTime aCreationTime, |
27 | | const TimeStamp& aCreationTimestamp, |
28 | | const TimeStamp& aAsyncOpenTimestamp) |
29 | | : HttpAsyncAborter<InterceptedHttpChannel>(this) |
30 | | , mProgress(0) |
31 | | , mProgressReported(0) |
32 | | , mSynthesizedStreamLength(-1) |
33 | | , mResumeStartPos(0) |
34 | | , mSynthesizedOrReset(Invalid) |
35 | | , mCallingStatusAndProgress(false) |
36 | | , mDiverting(false) |
37 | 0 | { |
38 | 0 | // Pre-set the creation and AsyncOpen times based on the original channel |
39 | 0 | // we are intercepting. We don't want our extra internal redirect to mask |
40 | 0 | // any time spent processing the channel. |
41 | 0 | mChannelCreationTime = aCreationTime; |
42 | 0 | mChannelCreationTimestamp = aCreationTimestamp; |
43 | 0 | mAsyncOpenTime = aAsyncOpenTimestamp; |
44 | 0 | } |
45 | | |
46 | | void |
47 | | InterceptedHttpChannel::ReleaseListeners() |
48 | 0 | { |
49 | 0 | if (mLoadGroup) { |
50 | 0 | mLoadGroup->RemoveRequest(this, nullptr, mStatus); |
51 | 0 | } |
52 | 0 | HttpBaseChannel::ReleaseListeners(); |
53 | 0 | mSynthesizedResponseHead.reset(); |
54 | 0 | mRedirectChannel = nullptr; |
55 | 0 | mBodyReader = nullptr; |
56 | 0 | mReleaseHandle = nullptr; |
57 | 0 | mProgressSink = nullptr; |
58 | 0 | mBodyCallback = nullptr; |
59 | 0 | mPump = nullptr; |
60 | 0 | mParentChannel = nullptr; |
61 | 0 |
|
62 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mIsPending); |
63 | 0 | } |
64 | | |
65 | | nsresult |
66 | | InterceptedHttpChannel::SetupReplacementChannel(nsIURI *aURI, |
67 | | nsIChannel *aChannel, |
68 | | bool aPreserveMethod, |
69 | | uint32_t aRedirectFlags) |
70 | 0 | { |
71 | 0 | nsresult rv = HttpBaseChannel::SetupReplacementChannel(aURI, aChannel, |
72 | 0 | aPreserveMethod, |
73 | 0 | aRedirectFlags); |
74 | 0 | if (NS_FAILED(rv)) { |
75 | 0 | return rv; |
76 | 0 | } |
77 | 0 | |
78 | 0 | rv = CheckRedirectLimit(aRedirectFlags); |
79 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
80 | 0 |
|
81 | 0 | // While we can't resume an synthetic response, we can still propagate |
82 | 0 | // the resume params across redirects for other channels to handle. |
83 | 0 | if (mResumeStartPos > 0) { |
84 | 0 | nsCOMPtr<nsIResumableChannel> resumable = do_QueryInterface(aChannel); |
85 | 0 | if (!resumable) { |
86 | 0 | return NS_ERROR_NOT_RESUMABLE; |
87 | 0 | } |
88 | 0 | |
89 | 0 | resumable->ResumeAt(mResumeStartPos, mResumeEntityId); |
90 | 0 | } |
91 | 0 |
|
92 | 0 | return NS_OK; |
93 | 0 | } |
94 | | |
95 | | void |
96 | | InterceptedHttpChannel::AsyncOpenInternal() |
97 | 0 | { |
98 | 0 | // If an error occurs in this file we must ensure mListener callbacks are |
99 | 0 | // invoked in some way. We either Cancel() or ResetInterception below |
100 | 0 | // depending on which path we take. |
101 | 0 | nsresult rv = NS_OK; |
102 | 0 |
|
103 | 0 | // We should have pre-set the AsyncOpen time based on the original channel if |
104 | 0 | // timings are enabled. |
105 | 0 | if (mTimingEnabled) { |
106 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mAsyncOpenTime.IsNull()); |
107 | 0 | } |
108 | 0 |
|
109 | 0 | mIsPending = true; |
110 | 0 | mResponseCouldBeSynthesized = true; |
111 | 0 |
|
112 | 0 | if (mLoadGroup) { |
113 | 0 | mLoadGroup->AddRequest(this, nullptr); |
114 | 0 | } |
115 | 0 |
|
116 | 0 | // If we already have a synthesized body then we are pre-synthesized. |
117 | 0 | // This can happen for two reasons: |
118 | 0 | // 1. We have a pre-synthesized redirect in e10s mode. In this case |
119 | 0 | // we should follow the redirect. |
120 | 0 | // 2. We are handling a "fake" redirect for an opaque response. Here |
121 | 0 | // we should just process the synthetic body. |
122 | 0 | if (mBodyReader) { |
123 | 0 | // If we fail in this path, then cancel the channel. We don't want |
124 | 0 | // to ResetInterception() after a synthetic result has already been |
125 | 0 | // produced by the ServiceWorker. |
126 | 0 | auto autoCancel = MakeScopeExit([&] { |
127 | 0 | if (NS_FAILED(rv)) { |
128 | 0 | Cancel(rv); |
129 | 0 | } |
130 | 0 | }); |
131 | 0 |
|
132 | 0 | if (ShouldRedirect()) { |
133 | 0 | rv = FollowSyntheticRedirect(); |
134 | 0 | return; |
135 | 0 | } |
136 | 0 | |
137 | 0 | rv = StartPump(); |
138 | 0 | return; |
139 | 0 | } |
140 | 0 | |
141 | 0 | // If we fail the initial interception, then attempt to ResetInterception |
142 | 0 | // to fall back to network. We only cancel if the reset fails. |
143 | 0 | auto autoReset = MakeScopeExit([&] { |
144 | 0 | if (NS_FAILED(rv)) { |
145 | 0 | rv = ResetInterception(); |
146 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
147 | 0 | Cancel(rv); |
148 | 0 | } |
149 | 0 | } |
150 | 0 | }); |
151 | 0 |
|
152 | 0 | // Otherwise we need to trigger a FetchEvent in a ServiceWorker. |
153 | 0 | nsCOMPtr<nsINetworkInterceptController> controller; |
154 | 0 | GetCallback(controller); |
155 | 0 |
|
156 | 0 | if (NS_WARN_IF(!controller)) { |
157 | 0 | rv = NS_ERROR_DOM_INVALID_STATE_ERR; |
158 | 0 | return; |
159 | 0 | } |
160 | 0 | |
161 | 0 | rv = controller->ChannelIntercepted(this); |
162 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
163 | 0 | } |
164 | | |
165 | | bool |
166 | | InterceptedHttpChannel::ShouldRedirect() const |
167 | 0 | { |
168 | 0 | // Determine if the synthetic response requires us to perform a real redirect. |
169 | 0 | return nsHttpChannel::WillRedirect(mResponseHead) && |
170 | 0 | !mLoadInfo->GetDontFollowRedirects(); |
171 | 0 | } |
172 | | |
173 | | nsresult |
174 | | InterceptedHttpChannel::FollowSyntheticRedirect() |
175 | 0 | { |
176 | 0 | // Perform a real redirect based on the synthetic response. |
177 | 0 |
|
178 | 0 | nsCOMPtr<nsIIOService> ioService; |
179 | 0 | nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); |
180 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
181 | 0 |
|
182 | 0 | nsAutoCString location; |
183 | 0 | rv = mResponseHead->GetHeader(nsHttp::Location, location); |
184 | 0 | NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); |
185 | 0 |
|
186 | 0 | // make sure non-ASCII characters in the location header are escaped. |
187 | 0 | nsAutoCString locationBuf; |
188 | 0 | if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces, locationBuf)) { |
189 | 0 | location = locationBuf; |
190 | 0 | } |
191 | 0 |
|
192 | 0 | nsCOMPtr<nsIURI> redirectURI; |
193 | 0 | rv = ioService->NewURI(nsDependentCString(location.get()), |
194 | 0 | nullptr, |
195 | 0 | mURI, |
196 | 0 | getter_AddRefs(redirectURI)); |
197 | 0 | NS_ENSURE_SUCCESS(rv, NS_ERROR_CORRUPTED_CONTENT); |
198 | 0 |
|
199 | 0 | uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY; |
200 | 0 | if (nsHttp::IsPermanentRedirect(mResponseHead->Status())) { |
201 | 0 | redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT; |
202 | 0 | } |
203 | 0 |
|
204 | 0 | PropagateReferenceIfNeeded(mURI, redirectURI); |
205 | 0 |
|
206 | 0 | bool rewriteToGET = ShouldRewriteRedirectToGET(mResponseHead->Status(), |
207 | 0 | mRequestHead.ParsedMethod()); |
208 | 0 |
|
209 | 0 | nsCOMPtr<nsIChannel> newChannel; |
210 | 0 | nsCOMPtr<nsILoadInfo> redirectLoadInfo = |
211 | 0 | CloneLoadInfoForRedirect(redirectURI, redirectFlags); |
212 | 0 | rv = NS_NewChannelInternal(getter_AddRefs(newChannel), |
213 | 0 | redirectURI, |
214 | 0 | redirectLoadInfo, |
215 | 0 | nullptr, // PerformanceStorage |
216 | 0 | nullptr, // aLoadGroup |
217 | 0 | nullptr, // aCallbacks |
218 | 0 | mLoadFlags, |
219 | 0 | ioService); |
220 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
221 | 0 |
|
222 | 0 | rv = SetupReplacementChannel(redirectURI, newChannel, !rewriteToGET, |
223 | 0 | redirectFlags); |
224 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
225 | 0 |
|
226 | 0 | mRedirectChannel = newChannel.forget(); |
227 | 0 |
|
228 | 0 | rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, redirectFlags); |
229 | 0 |
|
230 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
231 | 0 | OnRedirectVerifyCallback(rv); |
232 | 0 | } |
233 | 0 |
|
234 | 0 | return rv; |
235 | 0 | } |
236 | | |
237 | | nsresult |
238 | | InterceptedHttpChannel::RedirectForResponseURL(nsIURI* aResponseURI, |
239 | | bool aResponseRedirected) |
240 | 0 | { |
241 | 0 | // Perform a service worker redirect to another InterceptedHttpChannel using |
242 | 0 | // the given response URL. It allows content to see the final URL where |
243 | 0 | // appropriate and also helps us enforce cross-origin restrictions. The |
244 | 0 | // resulting channel will then process the synthetic response as normal. This |
245 | 0 | // extra redirect is performed so that listeners treat the result as unsafe |
246 | 0 | // cross-origin data. |
247 | 0 |
|
248 | 0 | nsresult rv = NS_OK; |
249 | 0 |
|
250 | 0 | // We want to pass ownership of the body callback to the new synthesized |
251 | 0 | // channel. We need to hold a reference to the callbacks on the stack |
252 | 0 | // as well, though, so we can call them if a failure occurs. |
253 | 0 | nsCOMPtr<nsIInterceptedBodyCallback> bodyCallback = mBodyCallback.forget(); |
254 | 0 |
|
255 | 0 | RefPtr<InterceptedHttpChannel> newChannel = |
256 | 0 | CreateForSynthesis(mResponseHead, mBodyReader, bodyCallback, |
257 | 0 | mChannelCreationTime, mChannelCreationTimestamp, |
258 | 0 | mAsyncOpenTime); |
259 | 0 |
|
260 | 0 | rv = newChannel->Init(aResponseURI, mCaps, |
261 | 0 | static_cast<nsProxyInfo*>(mProxyInfo.get()), |
262 | 0 | mProxyResolveFlags, mProxyURI, mChannelId); |
263 | 0 |
|
264 | 0 | // If the response has been redirected, propagate all the URLs to content. |
265 | 0 | // Thus, the exact value of the redirect flag does not matter as long as it's |
266 | 0 | // not REDIRECT_INTERNAL. |
267 | 0 | uint32_t flags = aResponseRedirected ? nsIChannelEventSink::REDIRECT_TEMPORARY |
268 | 0 | : nsIChannelEventSink::REDIRECT_INTERNAL; |
269 | 0 |
|
270 | 0 | nsCOMPtr<nsILoadInfo> redirectLoadInfo = |
271 | 0 | CloneLoadInfoForRedirect(aResponseURI, flags); |
272 | 0 | newChannel->SetLoadInfo(redirectLoadInfo); |
273 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
274 | 0 |
|
275 | 0 | // Normally we don't propagate the LoadInfo's service worker tainting |
276 | 0 | // synthesis flag on redirect. A real redirect normally will want to allow |
277 | 0 | // normal tainting to proceed from its starting taint. For this particular |
278 | 0 | // redirect, though, we are performing a redirect to communicate the URL of |
279 | 0 | // the service worker synthetic response itself. This redirect still represents |
280 | 0 | // the synthetic response, so we must preserve the flag. |
281 | 0 | if (redirectLoadInfo && mLoadInfo && |
282 | 0 | mLoadInfo->GetServiceWorkerTaintingSynthesized()) { |
283 | 0 | redirectLoadInfo->SynthesizeServiceWorkerTainting(mLoadInfo->GetTainting()); |
284 | 0 | } |
285 | 0 |
|
286 | 0 | rv = SetupReplacementChannel(aResponseURI, newChannel, true, flags); |
287 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
288 | 0 |
|
289 | 0 | mRedirectChannel = newChannel; |
290 | 0 |
|
291 | 0 | rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags); |
292 | 0 |
|
293 | 0 | if (NS_FAILED(rv)) { |
294 | 0 | // Make sure to call the body callback since we took ownership |
295 | 0 | // above. Neither the new channel or our standard |
296 | 0 | // OnRedirectVerifyCallback() code will invoke the callback. Do it here. |
297 | 0 | bodyCallback->BodyComplete(rv); |
298 | 0 |
|
299 | 0 | OnRedirectVerifyCallback(rv); |
300 | 0 | } |
301 | 0 |
|
302 | 0 | return rv; |
303 | 0 | } |
304 | | |
305 | | nsresult |
306 | | InterceptedHttpChannel::StartPump() |
307 | 0 | { |
308 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mPump); |
309 | 0 | MOZ_DIAGNOSTIC_ASSERT(mBodyReader); |
310 | 0 |
|
311 | 0 | // We don't support resuming an intercepted channel. We can't guarantee the |
312 | 0 | // ServiceWorker will always return the same data and we can't rely on the |
313 | 0 | // http cache code to detect changes. For now, just force the channel to |
314 | 0 | // NS_ERROR_NOT_RESUMABLE which should cause the front-end to recreate the |
315 | 0 | // channel without calling ResumeAt(). |
316 | 0 | // |
317 | 0 | // It would also be possible to convert this information to a range request, |
318 | 0 | // but its unclear if we should do that for ServiceWorker FetchEvents. See: |
319 | 0 | // |
320 | 0 | // https://github.com/w3c/ServiceWorker/issues/1201 |
321 | 0 | if (mResumeStartPos > 0) { |
322 | 0 | return NS_ERROR_NOT_RESUMABLE; |
323 | 0 | } |
324 | 0 | |
325 | 0 | // For progress we trust the content-length for the "maximum" size. |
326 | 0 | // We can't determine the full size from the stream itself since |
327 | 0 | // we may only receive the data incrementally. We can't trust |
328 | 0 | // Available() here. |
329 | 0 | // TODO: We could implement an nsIFixedLengthInputStream interface and |
330 | 0 | // QI to it here. This would let us determine the total length |
331 | 0 | // for streams that support it. See bug 1388774. |
332 | 0 | Unused << GetContentLength(&mSynthesizedStreamLength); |
333 | 0 |
|
334 | 0 | nsresult rv = nsInputStreamPump::Create(getter_AddRefs(mPump), |
335 | 0 | mBodyReader, |
336 | 0 | 0, 0, true); |
337 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
338 | 0 |
|
339 | 0 | rv = mPump->AsyncRead(this, mListenerContext); |
340 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
341 | 0 |
|
342 | 0 | uint32_t suspendCount = mSuspendCount; |
343 | 0 | while (suspendCount--) { |
344 | 0 | mPump->Suspend(); |
345 | 0 | } |
346 | 0 |
|
347 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mCanceled); |
348 | 0 |
|
349 | 0 | return rv; |
350 | 0 | } |
351 | | |
352 | | nsresult |
353 | | InterceptedHttpChannel::OpenRedirectChannel() |
354 | 0 | { |
355 | 0 | nsresult rv = NS_OK; |
356 | 0 |
|
357 | 0 | if (NS_FAILED(mStatus)) { |
358 | 0 | return mStatus; |
359 | 0 | } |
360 | 0 | |
361 | 0 | if (!mRedirectChannel) { |
362 | 0 | return NS_ERROR_DOM_ABORT_ERR; |
363 | 0 | } |
364 | 0 | |
365 | 0 | // Make sure to do this after we received redirect veto answer, |
366 | 0 | // i.e. after all sinks had been notified |
367 | 0 | mRedirectChannel->SetOriginalURI(mOriginalURI); |
368 | 0 |
|
369 | 0 | // open new channel |
370 | 0 | if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) { |
371 | 0 | MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!"); |
372 | 0 | rv = mRedirectChannel->AsyncOpen2(mListener); |
373 | 0 | } |
374 | 0 | else { |
375 | 0 | rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); |
376 | 0 | } |
377 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
378 | 0 |
|
379 | 0 | mStatus = NS_BINDING_REDIRECTED; |
380 | 0 |
|
381 | 0 | return rv; |
382 | 0 | } |
383 | | |
384 | | void |
385 | | InterceptedHttpChannel::MaybeCallStatusAndProgress() |
386 | 0 | { |
387 | 0 | // OnStatus() and OnProgress() must only be called on the main thread. If |
388 | 0 | // we are on a separate thread, then we maybe need to schedule a runnable |
389 | 0 | // to call them asynchronousnly. |
390 | 0 | if (!NS_IsMainThread()) { |
391 | 0 | // Check to see if we are already trying to call OnStatus/OnProgress |
392 | 0 | // asynchronously. If we are, then don't queue up another runnable. |
393 | 0 | // We don't want to flood the main thread. |
394 | 0 | if (mCallingStatusAndProgress) { |
395 | 0 | return; |
396 | 0 | } |
397 | 0 | mCallingStatusAndProgress = true; |
398 | 0 |
|
399 | 0 | nsCOMPtr<nsIRunnable> r = |
400 | 0 | NewRunnableMethod("InterceptedHttpChannel::MaybeCallStatusAndProgress", |
401 | 0 | this, |
402 | 0 | &InterceptedHttpChannel::MaybeCallStatusAndProgress); |
403 | 0 | MOZ_ALWAYS_SUCCEEDS( |
404 | 0 | SystemGroup::Dispatch(TaskCategory::Other, r.forget())); |
405 | 0 |
|
406 | 0 | return; |
407 | 0 | } |
408 | 0 |
|
409 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
410 | 0 |
|
411 | 0 | // We are about to capture out progress position. Clear the flag we use |
412 | 0 | // to de-duplicate progress report runnables. We want any further progress |
413 | 0 | // updates to trigger another runnable. We do this before capture the |
414 | 0 | // progress value since we're using atomics and not a mutex lock. |
415 | 0 | mCallingStatusAndProgress = false; |
416 | 0 |
|
417 | 0 | // Capture the current status from our atomic count. |
418 | 0 | int64_t progress = mProgress; |
419 | 0 |
|
420 | 0 | MOZ_DIAGNOSTIC_ASSERT(progress >= mProgressReported); |
421 | 0 |
|
422 | 0 | // Do nothing if we've already made the calls for this amount of progress |
423 | 0 | // or if the channel is not configured for these calls. Note, the check |
424 | 0 | // for mProgressSink here means we will not fire any spurious late calls |
425 | 0 | // after ReleaseListeners() is executed. |
426 | 0 | if (progress <= mProgressReported || |
427 | 0 | mCanceled || |
428 | 0 | !mProgressSink || |
429 | 0 | (mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) { |
430 | 0 | return; |
431 | 0 | } |
432 | 0 | |
433 | 0 | // Capture the host name on the first set of calls to avoid doing this |
434 | 0 | // string processing repeatedly. |
435 | 0 | if (mProgressReported == 0) { |
436 | 0 | nsAutoCString host; |
437 | 0 | MOZ_ALWAYS_SUCCEEDS(mURI->GetHost(host)); |
438 | 0 | CopyUTF8toUTF16(host, mStatusHost); |
439 | 0 | } |
440 | 0 |
|
441 | 0 | mProgressSink->OnStatus(this, mListenerContext, NS_NET_STATUS_READING, |
442 | 0 | mStatusHost.get()); |
443 | 0 |
|
444 | 0 | mProgressSink->OnProgress(this, mListenerContext, progress, |
445 | 0 | mSynthesizedStreamLength); |
446 | 0 |
|
447 | 0 | mProgressReported = progress; |
448 | 0 | } |
449 | | |
450 | | void |
451 | | InterceptedHttpChannel::MaybeCallBodyCallback() |
452 | 0 | { |
453 | 0 | nsCOMPtr<nsIInterceptedBodyCallback> callback = mBodyCallback.forget(); |
454 | 0 | if (callback) { |
455 | 0 | callback->BodyComplete(mStatus); |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | | // static |
460 | | already_AddRefed<InterceptedHttpChannel> |
461 | | InterceptedHttpChannel::CreateForInterception(PRTime aCreationTime, |
462 | | const TimeStamp& aCreationTimestamp, |
463 | | const TimeStamp& aAsyncOpenTimestamp) |
464 | 0 | { |
465 | 0 | // Create an InterceptedHttpChannel that will trigger a FetchEvent |
466 | 0 | // in a ServiceWorker when opened. |
467 | 0 | RefPtr<InterceptedHttpChannel> ref = |
468 | 0 | new InterceptedHttpChannel(aCreationTime, aCreationTimestamp, |
469 | 0 | aAsyncOpenTimestamp); |
470 | 0 |
|
471 | 0 | return ref.forget(); |
472 | 0 | } |
473 | | |
474 | | // static |
475 | | already_AddRefed<InterceptedHttpChannel> |
476 | | InterceptedHttpChannel::CreateForSynthesis(const nsHttpResponseHead* aHead, |
477 | | nsIInputStream* aBody, |
478 | | nsIInterceptedBodyCallback* aBodyCallback, |
479 | | PRTime aCreationTime, |
480 | | const TimeStamp& aCreationTimestamp, |
481 | | const TimeStamp& aAsyncOpenTimestamp) |
482 | 0 | { |
483 | 0 | MOZ_DIAGNOSTIC_ASSERT(aHead); |
484 | 0 | MOZ_DIAGNOSTIC_ASSERT(aBody); |
485 | 0 |
|
486 | 0 | // Create an InterceptedHttpChannel that already has a synthesized response. |
487 | 0 | // The synthetic response will be processed when opened. A FetchEvent |
488 | 0 | // will not be triggered. |
489 | 0 | RefPtr<InterceptedHttpChannel> ref = |
490 | 0 | new InterceptedHttpChannel(aCreationTime, aCreationTimestamp, |
491 | 0 | aAsyncOpenTimestamp); |
492 | 0 |
|
493 | 0 | ref->mResponseHead = new nsHttpResponseHead(*aHead); |
494 | 0 | ref->mBodyReader = aBody; |
495 | 0 | ref->mBodyCallback = aBodyCallback; |
496 | 0 |
|
497 | 0 | return ref.forget(); |
498 | 0 | } |
499 | | |
500 | | NS_IMETHODIMP |
501 | | InterceptedHttpChannel::Cancel(nsresult aStatus) |
502 | 0 | { |
503 | 0 | // Note: This class has been designed to send all error results through |
504 | 0 | // Cancel(). Don't add calls directly to AsyncAbort() or |
505 | 0 | // DoNotifyListener(). Instead call Cancel(). |
506 | 0 |
|
507 | 0 | if (mCanceled) { |
508 | 0 | return NS_OK; |
509 | 0 | } |
510 | 0 | mCanceled = true; |
511 | 0 |
|
512 | 0 | MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(aStatus)); |
513 | 0 | if (NS_SUCCEEDED(mStatus)) { |
514 | 0 | mStatus = aStatus; |
515 | 0 | } |
516 | 0 |
|
517 | 0 | // Everything is suspended during diversion until it completes. Since the |
518 | 0 | // intercepted channel could be a long-running stream, we need to request that |
519 | 0 | // cancellation be triggered in the child, completing the diversion and |
520 | 0 | // allowing cancellation to run to completion. |
521 | 0 | if (mDiverting) { |
522 | 0 | Unused << mParentChannel->CancelDiversion(); |
523 | 0 | // (We want the pump to be canceled as well, so don't directly return.) |
524 | 0 | } |
525 | 0 |
|
526 | 0 | if (mPump) { |
527 | 0 | return mPump->Cancel(mStatus); |
528 | 0 | } |
529 | 0 | |
530 | 0 | return AsyncAbort(mStatus); |
531 | 0 | } |
532 | | |
533 | | NS_IMETHODIMP |
534 | | InterceptedHttpChannel::Suspend(void) |
535 | 0 | { |
536 | 0 | nsresult rv = SuspendInternal(); |
537 | 0 |
|
538 | 0 | nsresult rvParentChannel = NS_OK; |
539 | 0 | if (mParentChannel) { |
540 | 0 | rvParentChannel = mParentChannel->SuspendMessageDiversion(); |
541 | 0 | } |
542 | 0 |
|
543 | 0 | return NS_FAILED(rv) ? rv : rvParentChannel; |
544 | 0 | } |
545 | | |
546 | | NS_IMETHODIMP |
547 | | InterceptedHttpChannel::Resume(void) |
548 | 0 | { |
549 | 0 | nsresult rv = ResumeInternal(); |
550 | 0 |
|
551 | 0 | nsresult rvParentChannel = NS_OK; |
552 | 0 | if (mParentChannel) { |
553 | 0 | rvParentChannel = mParentChannel->ResumeMessageDiversion(); |
554 | 0 | } |
555 | 0 |
|
556 | 0 | return NS_FAILED(rv) ? rv : rvParentChannel; |
557 | 0 | } |
558 | | |
559 | | NS_IMETHODIMP |
560 | | InterceptedHttpChannel::GetSecurityInfo(nsISupports** aSecurityInfo) |
561 | 0 | { |
562 | 0 | nsCOMPtr<nsISupports> ref(mSecurityInfo); |
563 | 0 | ref.forget(aSecurityInfo); |
564 | 0 | return NS_OK; |
565 | 0 | } |
566 | | |
567 | | NS_IMETHODIMP |
568 | | InterceptedHttpChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext) |
569 | 0 | { |
570 | 0 | if (mCanceled) { |
571 | 0 | return mStatus; |
572 | 0 | } |
573 | 0 | |
574 | 0 | // After this point we should try to return NS_OK and notify the listener |
575 | 0 | // of the result. |
576 | 0 | mListener = aListener; |
577 | 0 |
|
578 | 0 | AsyncOpenInternal(); |
579 | 0 |
|
580 | 0 | return NS_OK; |
581 | 0 | } |
582 | | |
583 | | NS_IMETHODIMP |
584 | | InterceptedHttpChannel::AsyncOpen2(nsIStreamListener* aListener) |
585 | 0 | { |
586 | 0 | nsCOMPtr<nsIStreamListener> listener(aListener); |
587 | 0 | nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener); |
588 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
589 | 0 | Cancel(rv); |
590 | 0 | return rv; |
591 | 0 | } |
592 | 0 | return AsyncOpen(listener, nullptr); |
593 | 0 | } |
594 | | |
595 | | NS_IMETHODIMP |
596 | | InterceptedHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage, |
597 | | const nsACString& aCategory) |
598 | 0 | { |
599 | 0 | // Synthetic responses should not trigger CORS blocking. |
600 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
601 | 0 | } |
602 | | |
603 | | NS_IMETHODIMP |
604 | | InterceptedHttpChannel::SetupFallbackChannel(const char* aFallbackKey) |
605 | 0 | { |
606 | 0 | // AppCache should not be used with service worker intercepted channels. |
607 | 0 | // This should never be called. |
608 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
609 | 0 | } |
610 | | |
611 | | NS_IMETHODIMP |
612 | | InterceptedHttpChannel::SetPriority(int32_t aPriority) |
613 | 0 | { |
614 | 0 | mPriority = clamped<int32_t>(aPriority, INT16_MIN, INT16_MAX); |
615 | 0 | return NS_OK; |
616 | 0 | } |
617 | | |
618 | | NS_IMETHODIMP |
619 | | InterceptedHttpChannel::SetClassFlags(uint32_t aClassFlags) |
620 | 0 | { |
621 | 0 | mClassOfService = aClassFlags; |
622 | 0 | return NS_OK; |
623 | 0 | } |
624 | | |
625 | | NS_IMETHODIMP |
626 | | InterceptedHttpChannel::ClearClassFlags(uint32_t aClassFlags) |
627 | 0 | { |
628 | 0 | mClassOfService &= ~aClassFlags; |
629 | 0 | return NS_OK; |
630 | 0 | } |
631 | | |
632 | | NS_IMETHODIMP |
633 | | InterceptedHttpChannel::AddClassFlags(uint32_t aClassFlags) |
634 | 0 | { |
635 | 0 | mClassOfService |= aClassFlags; |
636 | 0 | return NS_OK; |
637 | 0 | } |
638 | | |
639 | | NS_IMETHODIMP |
640 | | InterceptedHttpChannel::ResumeAt(uint64_t aStartPos, |
641 | | const nsACString & aEntityId) |
642 | 0 | { |
643 | 0 | // We don't support resuming synthesized responses, but we do track this |
644 | 0 | // information so it can be passed on to the resulting nsHttpChannel if |
645 | 0 | // ResetInterception is called. |
646 | 0 | mResumeStartPos = aStartPos; |
647 | 0 | mResumeEntityId = aEntityId; |
648 | 0 | return NS_OK; |
649 | 0 | } |
650 | | |
651 | | void |
652 | | InterceptedHttpChannel::DoNotifyListenerCleanup() |
653 | 0 | { |
654 | 0 | // Prefer to cleanup in ReleaseListeners() as it seems to be called |
655 | 0 | // more consistently in necko. |
656 | 0 | } |
657 | | |
658 | | void |
659 | | InterceptedHttpChannel::DoAsyncAbort(nsresult aStatus) |
660 | 0 | { |
661 | 0 | Unused << AsyncAbort(aStatus); |
662 | 0 | } |
663 | | |
664 | | |
665 | | NS_IMETHODIMP |
666 | | InterceptedHttpChannel::ResetInterception(void) |
667 | 0 | { |
668 | 0 | if (mCanceled) { |
669 | 0 | return mStatus; |
670 | 0 | } |
671 | 0 | |
672 | 0 | uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL; |
673 | 0 |
|
674 | 0 | nsCOMPtr<nsIChannel> newChannel; |
675 | 0 | nsCOMPtr<nsILoadInfo> redirectLoadInfo = |
676 | 0 | CloneLoadInfoForRedirect(mURI, flags); |
677 | 0 | nsresult rv = NS_NewChannelInternal(getter_AddRefs(newChannel), |
678 | 0 | mURI, |
679 | 0 | redirectLoadInfo, |
680 | 0 | nullptr, // PerformanceStorage |
681 | 0 | nullptr, // aLoadGroup |
682 | 0 | nullptr, // aCallbacks |
683 | 0 | mLoadFlags); |
684 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
685 | 0 |
|
686 | 0 | rv = SetupReplacementChannel(mURI, newChannel, true, flags); |
687 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
688 | 0 |
|
689 | 0 | nsCOMPtr<nsITimedChannel> newTimedChannel = do_QueryInterface(newChannel); |
690 | 0 | if (newTimedChannel) { |
691 | 0 | if (!mAsyncOpenTime.IsNull()) { |
692 | 0 | newTimedChannel->SetAsyncOpen(mAsyncOpenTime); |
693 | 0 | } |
694 | 0 | if (!mChannelCreationTimestamp.IsNull()) { |
695 | 0 | newTimedChannel->SetChannelCreation(mChannelCreationTimestamp); |
696 | 0 | } |
697 | 0 | } |
698 | 0 |
|
699 | 0 | if (mRedirectMode != nsIHttpChannelInternal::REDIRECT_MODE_MANUAL) { |
700 | 0 | nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL; |
701 | 0 | rv = newChannel->GetLoadFlags(&loadFlags); |
702 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
703 | 0 | loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER; |
704 | 0 | rv = newChannel->SetLoadFlags(loadFlags); |
705 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
706 | 0 | } |
707 | 0 |
|
708 | 0 | mRedirectChannel = newChannel.forget(); |
709 | 0 |
|
710 | 0 | rv = gHttpHandler->AsyncOnChannelRedirect(this, mRedirectChannel, flags); |
711 | 0 |
|
712 | 0 | if (NS_FAILED(rv)) { |
713 | 0 | OnRedirectVerifyCallback(rv); |
714 | 0 | } |
715 | 0 |
|
716 | 0 | return rv; |
717 | 0 | } |
718 | | |
719 | | NS_IMETHODIMP |
720 | | InterceptedHttpChannel::SynthesizeStatus(uint16_t aStatus, |
721 | | const nsACString& aReason) |
722 | 0 | { |
723 | 0 | if (mCanceled) { |
724 | 0 | return mStatus; |
725 | 0 | } |
726 | 0 | |
727 | 0 | if (!mSynthesizedResponseHead) { |
728 | 0 | mSynthesizedResponseHead.reset(new nsHttpResponseHead()); |
729 | 0 | } |
730 | 0 |
|
731 | 0 | nsAutoCString statusLine; |
732 | 0 | statusLine.AppendLiteral("HTTP/1.1 "); |
733 | 0 | statusLine.AppendInt(aStatus); |
734 | 0 | statusLine.AppendLiteral(" "); |
735 | 0 | statusLine.Append(aReason); |
736 | 0 |
|
737 | 0 | mSynthesizedResponseHead->ParseStatusLine(statusLine); |
738 | 0 | return NS_OK; |
739 | 0 | } |
740 | | |
741 | | NS_IMETHODIMP |
742 | | InterceptedHttpChannel::SynthesizeHeader(const nsACString& aName, |
743 | | const nsACString& aValue) |
744 | 0 | { |
745 | 0 | if (mCanceled) { |
746 | 0 | return mStatus; |
747 | 0 | } |
748 | 0 | |
749 | 0 | if (!mSynthesizedResponseHead) { |
750 | 0 | mSynthesizedResponseHead.reset(new nsHttpResponseHead()); |
751 | 0 | } |
752 | 0 |
|
753 | 0 | nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue; |
754 | 0 | // Overwrite any existing header. |
755 | 0 | nsresult rv = mSynthesizedResponseHead->ParseHeaderLine(header); |
756 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
757 | 0 | return NS_OK; |
758 | 0 | } |
759 | | |
760 | | NS_IMETHODIMP |
761 | | InterceptedHttpChannel::StartSynthesizedResponse(nsIInputStream* aBody, |
762 | | nsIInterceptedBodyCallback* aBodyCallback, |
763 | | nsICacheInfoChannel* aSynthesizedCacheInfo, |
764 | | const nsACString& aFinalURLSpec, |
765 | | bool aResponseRedirected) |
766 | 0 | { |
767 | 0 | nsresult rv = NS_OK; |
768 | 0 |
|
769 | 0 | auto autoCleanup = MakeScopeExit([&] { |
770 | 0 | // Auto-cancel on failure. Do this first to get mStatus set, if necessary. |
771 | 0 | if (NS_FAILED(rv)) { |
772 | 0 | Cancel(rv); |
773 | 0 | } |
774 | 0 |
|
775 | 0 | // If we early exit before taking ownership of the body, then automatically |
776 | 0 | // invoke the callback. This could be due to an error or because we're not |
777 | 0 | // going to consume it due to a redirect, etc. |
778 | 0 | if (aBodyCallback) { |
779 | 0 | aBodyCallback->BodyComplete(mStatus); |
780 | 0 | } |
781 | 0 | }); |
782 | 0 |
|
783 | 0 | if (NS_FAILED(mStatus)) { |
784 | 0 | // Return NS_OK. The channel should fire callbacks with an error code |
785 | 0 | // if it was cancelled before this point. |
786 | 0 | return NS_OK; |
787 | 0 | } |
788 | 0 | |
789 | 0 | // Take ownership of the body callbacks If a failure occurs we will |
790 | 0 | // automatically Cancel() the channel. This will then invoke OnStopRequest() |
791 | 0 | // which will invoke the correct callback. In the case of an opaque response |
792 | 0 | // redirect we pass ownership of the callback to the new channel. |
793 | 0 | mBodyCallback = aBodyCallback; |
794 | 0 | aBodyCallback = nullptr; |
795 | 0 |
|
796 | 0 | mSynthesizedCacheInfo = aSynthesizedCacheInfo; |
797 | 0 |
|
798 | 0 | if (!mSynthesizedResponseHead) { |
799 | 0 | mSynthesizedResponseHead.reset(new nsHttpResponseHead()); |
800 | 0 | } |
801 | 0 |
|
802 | 0 | mResponseHead = mSynthesizedResponseHead.release(); |
803 | 0 |
|
804 | 0 | if (ShouldRedirect()) { |
805 | 0 | rv = FollowSyntheticRedirect(); |
806 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
807 | 0 |
|
808 | 0 | return NS_OK; |
809 | 0 | } |
810 | 0 | |
811 | 0 | // Intercepted responses should already be decoded. |
812 | 0 | SetApplyConversion(false); |
813 | 0 |
|
814 | 0 | // Errors and redirects may not have a body. Synthesize an empty string stream |
815 | 0 | // here so later code can be simpler. |
816 | 0 | mBodyReader = aBody; |
817 | 0 | if (!mBodyReader) { |
818 | 0 | rv = NS_NewCStringInputStream(getter_AddRefs(mBodyReader), EmptyCString()); |
819 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
820 | 0 | } |
821 | 0 |
|
822 | 0 | nsCOMPtr<nsIURI> responseURI; |
823 | 0 | if (!aFinalURLSpec.IsEmpty()) { |
824 | 0 | rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec); |
825 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
826 | 0 | } else { |
827 | 0 | responseURI = mURI; |
828 | 0 | } |
829 | 0 |
|
830 | 0 | bool equal = false; |
831 | 0 | Unused << mURI->Equals(responseURI, &equal); |
832 | 0 | if (!equal) { |
833 | 0 | rv = RedirectForResponseURL(responseURI, aResponseRedirected); |
834 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
835 | 0 |
|
836 | 0 | return NS_OK; |
837 | 0 | } |
838 | 0 | |
839 | 0 | rv = StartPump(); |
840 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
841 | 0 |
|
842 | 0 | return NS_OK; |
843 | 0 | } |
844 | | |
845 | | NS_IMETHODIMP |
846 | | InterceptedHttpChannel::FinishSynthesizedResponse() |
847 | 0 | { |
848 | 0 | if (mCanceled) { |
849 | 0 | // Return NS_OK. The channel should fire callbacks with an error code |
850 | 0 | // if it was cancelled before this point. |
851 | 0 | return NS_OK; |
852 | 0 | } |
853 | 0 | |
854 | 0 | // TODO: Remove this API after interception moves to the parent process in |
855 | 0 | // e10s mode. |
856 | 0 | |
857 | 0 | return NS_OK; |
858 | 0 | } |
859 | | |
860 | | NS_IMETHODIMP |
861 | | InterceptedHttpChannel::CancelInterception(nsresult aStatus) |
862 | 0 | { |
863 | 0 | return Cancel(aStatus); |
864 | 0 | } |
865 | | |
866 | | NS_IMETHODIMP |
867 | | InterceptedHttpChannel::GetChannel(nsIChannel** aChannel) |
868 | 0 | { |
869 | 0 | nsCOMPtr<nsIChannel> ref(this); |
870 | 0 | ref.forget(aChannel); |
871 | 0 | return NS_OK; |
872 | 0 | } |
873 | | |
874 | | NS_IMETHODIMP |
875 | | InterceptedHttpChannel::GetSecureUpgradedChannelURI(nsIURI** aSecureUpgradedChannelURI) |
876 | 0 | { |
877 | 0 | nsCOMPtr<nsIURI> ref(mURI); |
878 | 0 | ref.forget(aSecureUpgradedChannelURI); |
879 | 0 | return NS_OK; |
880 | 0 | } |
881 | | |
882 | | NS_IMETHODIMP |
883 | | InterceptedHttpChannel::SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) |
884 | 0 | { |
885 | 0 | return aChannelInfo->ResurrectInfoOnChannel(this); |
886 | 0 | } |
887 | | |
888 | | NS_IMETHODIMP |
889 | | InterceptedHttpChannel::GetInternalContentPolicyType(nsContentPolicyType* aPolicyType) |
890 | 0 | { |
891 | 0 | if (mLoadInfo) { |
892 | 0 | *aPolicyType = mLoadInfo->InternalContentPolicyType(); |
893 | 0 | } |
894 | 0 | return NS_OK; |
895 | 0 | } |
896 | | |
897 | | NS_IMETHODIMP |
898 | | InterceptedHttpChannel::GetConsoleReportCollector(nsIConsoleReportCollector** aConsoleReportCollector) |
899 | 0 | { |
900 | 0 | nsCOMPtr<nsIConsoleReportCollector> ref(this); |
901 | 0 | ref.forget(aConsoleReportCollector); |
902 | 0 | return NS_OK; |
903 | 0 | } |
904 | | |
905 | | NS_IMETHODIMP |
906 | | InterceptedHttpChannel::GetLaunchServiceWorkerStart(mozilla::TimeStamp* aTimeStamp) |
907 | 0 | { |
908 | 0 | return HttpBaseChannel::GetLaunchServiceWorkerStart(aTimeStamp); |
909 | 0 | } |
910 | | |
911 | | NS_IMETHODIMP |
912 | | InterceptedHttpChannel::SetLaunchServiceWorkerStart(mozilla::TimeStamp aTimeStamp) |
913 | 0 | { |
914 | 0 | return HttpBaseChannel::SetLaunchServiceWorkerStart(aTimeStamp); |
915 | 0 | } |
916 | | |
917 | | NS_IMETHODIMP |
918 | | InterceptedHttpChannel::GetLaunchServiceWorkerEnd(mozilla::TimeStamp* aTimeStamp) |
919 | 0 | { |
920 | 0 | return HttpBaseChannel::GetLaunchServiceWorkerEnd(aTimeStamp); |
921 | 0 | } |
922 | | |
923 | | NS_IMETHODIMP |
924 | | InterceptedHttpChannel::SetLaunchServiceWorkerEnd(mozilla::TimeStamp aTimeStamp) |
925 | 0 | { |
926 | 0 | return HttpBaseChannel::SetLaunchServiceWorkerEnd(aTimeStamp); |
927 | 0 | } |
928 | | |
929 | | NS_IMETHODIMP |
930 | | InterceptedHttpChannel::SetDispatchFetchEventStart(mozilla::TimeStamp aTimeStamp) |
931 | 0 | { |
932 | 0 | return HttpBaseChannel::SetDispatchFetchEventStart(aTimeStamp); |
933 | 0 | } |
934 | | |
935 | | NS_IMETHODIMP |
936 | | InterceptedHttpChannel::SetDispatchFetchEventEnd(mozilla::TimeStamp aTimeStamp) |
937 | 0 | { |
938 | 0 | return HttpBaseChannel::SetDispatchFetchEventEnd(aTimeStamp); |
939 | 0 | } |
940 | | |
941 | | NS_IMETHODIMP |
942 | | InterceptedHttpChannel::SetHandleFetchEventStart(mozilla::TimeStamp aTimeStamp) |
943 | 0 | { |
944 | 0 | return HttpBaseChannel::SetHandleFetchEventStart(aTimeStamp); |
945 | 0 | } |
946 | | |
947 | | NS_IMETHODIMP |
948 | | InterceptedHttpChannel::SetHandleFetchEventEnd(mozilla::TimeStamp aTimeStamp) |
949 | 0 | { |
950 | 0 | return HttpBaseChannel::SetHandleFetchEventEnd(aTimeStamp); |
951 | 0 | } |
952 | | |
953 | | NS_IMETHODIMP |
954 | | InterceptedHttpChannel::SetFinishResponseStart(mozilla::TimeStamp aTimeStamp) |
955 | 0 | { |
956 | 0 | mFinishResponseStart = aTimeStamp; |
957 | 0 | return NS_OK; |
958 | 0 | } |
959 | | |
960 | | NS_IMETHODIMP |
961 | | InterceptedHttpChannel::SetFinishSynthesizedResponseEnd(mozilla::TimeStamp aTimeStamp) |
962 | 0 | { |
963 | 0 | MOZ_ASSERT(mSynthesizedOrReset == Invalid); |
964 | 0 | mSynthesizedOrReset = Synthesized; |
965 | 0 | mFinishResponseEnd = aTimeStamp; |
966 | 0 | return NS_OK; |
967 | 0 | } |
968 | | |
969 | | NS_IMETHODIMP |
970 | | InterceptedHttpChannel::SetChannelResetEnd(mozilla::TimeStamp aTimeStamp) |
971 | 0 | { |
972 | 0 | MOZ_ASSERT(mSynthesizedOrReset == Invalid); |
973 | 0 | mSynthesizedOrReset = Reset; |
974 | 0 | mFinishResponseEnd = aTimeStamp; |
975 | 0 | return NS_OK; |
976 | 0 | } |
977 | | |
978 | | NS_IMETHODIMP |
979 | | InterceptedHttpChannel::SaveTimeStamps(void) |
980 | 0 | { |
981 | 0 | // If we were not able to start the fetch event for some reason (like |
982 | 0 | // corrupted scripts), then just do nothing here. |
983 | 0 | if (mHandleFetchEventStart.IsNull()) { |
984 | 0 | return NS_OK; |
985 | 0 | } |
986 | 0 | |
987 | 0 | bool isNonSubresourceRequest = nsContentUtils::IsNonSubresourceRequest(this); |
988 | 0 | nsCString navigationOrSubresource = isNonSubresourceRequest ? |
989 | 0 | NS_LITERAL_CSTRING("navigation") : NS_LITERAL_CSTRING("subresource"); |
990 | 0 |
|
991 | 0 | nsAutoCString subresourceKey(EmptyCString()); |
992 | 0 | GetSubresourceTimeStampKey(this, subresourceKey); |
993 | 0 |
|
994 | 0 | // We may have null timestamps if the fetch dispatch runnable was cancelled |
995 | 0 | // and we defaulted to resuming the request. |
996 | 0 | if (!mFinishResponseStart.IsNull() && !mFinishResponseEnd.IsNull()) { |
997 | 0 | Telemetry::HistogramID id = (mSynthesizedOrReset == Synthesized) ? |
998 | 0 | Telemetry::SERVICE_WORKER_FETCH_EVENT_FINISH_SYNTHESIZED_RESPONSE_MS : |
999 | 0 | Telemetry::SERVICE_WORKER_FETCH_EVENT_CHANNEL_RESET_MS; |
1000 | 0 | Telemetry::Accumulate(id, navigationOrSubresource, |
1001 | 0 | static_cast<uint32_t>((mFinishResponseEnd - mFinishResponseStart).ToMilliseconds())); |
1002 | 0 | if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) { |
1003 | 0 | Telemetry::Accumulate(id, subresourceKey, |
1004 | 0 | static_cast<uint32_t>((mFinishResponseEnd - mFinishResponseStart).ToMilliseconds())); |
1005 | 0 | } |
1006 | 0 | } |
1007 | 0 |
|
1008 | 0 | Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS, |
1009 | 0 | navigationOrSubresource, |
1010 | 0 | static_cast<uint32_t>((mHandleFetchEventStart - mDispatchFetchEventStart).ToMilliseconds())); |
1011 | 0 |
|
1012 | 0 | if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) { |
1013 | 0 | Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS, |
1014 | 0 | subresourceKey, |
1015 | 0 | static_cast<uint32_t>((mHandleFetchEventStart - mDispatchFetchEventStart).ToMilliseconds())); |
1016 | 0 | } |
1017 | 0 |
|
1018 | 0 | if (!mFinishResponseEnd.IsNull()) { |
1019 | 0 | Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS, |
1020 | 0 | navigationOrSubresource, |
1021 | 0 | static_cast<uint32_t>((mFinishResponseEnd - mDispatchFetchEventStart).ToMilliseconds())); |
1022 | 0 | if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) { |
1023 | 0 | Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS, |
1024 | 0 | subresourceKey, |
1025 | 0 | static_cast<uint32_t>((mFinishResponseEnd - mDispatchFetchEventStart).ToMilliseconds())); |
1026 | 0 | } |
1027 | 0 | } |
1028 | 0 |
|
1029 | 0 | return NS_OK; |
1030 | 0 | } |
1031 | | |
1032 | | NS_IMETHODIMP |
1033 | | InterceptedHttpChannel::SetReleaseHandle(nsISupports* aHandle) |
1034 | 0 | { |
1035 | 0 | mReleaseHandle = aHandle; |
1036 | 0 | return NS_OK; |
1037 | 0 | } |
1038 | | |
1039 | | NS_IMETHODIMP |
1040 | | InterceptedHttpChannel::OnRedirectVerifyCallback(nsresult rv) |
1041 | 0 | { |
1042 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1043 | 0 |
|
1044 | 0 | if (NS_SUCCEEDED(rv)) { |
1045 | 0 | rv = OpenRedirectChannel(); |
1046 | 0 | } |
1047 | 0 |
|
1048 | 0 | nsCOMPtr<nsIRedirectResultListener> hook; |
1049 | 0 | GetCallback(hook); |
1050 | 0 | if (hook) { |
1051 | 0 | hook->OnRedirectResult(NS_SUCCEEDED(rv)); |
1052 | 0 | } |
1053 | 0 |
|
1054 | 0 | if (NS_FAILED(rv)) { |
1055 | 0 | Cancel(rv); |
1056 | 0 | } |
1057 | 0 |
|
1058 | 0 | MaybeCallBodyCallback(); |
1059 | 0 |
|
1060 | 0 | mIsPending = false; |
1061 | 0 | ReleaseListeners(); |
1062 | 0 |
|
1063 | 0 | return NS_OK; |
1064 | 0 | } |
1065 | | |
1066 | | NS_IMETHODIMP |
1067 | | InterceptedHttpChannel::OnStartRequest(nsIRequest* aRequest, |
1068 | | nsISupports* aContext) |
1069 | 0 | { |
1070 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1071 | 0 |
|
1072 | 0 | if (!mProgressSink) { |
1073 | 0 | GetCallback(mProgressSink); |
1074 | 0 | } |
1075 | 0 |
|
1076 | 0 | if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) { |
1077 | 0 | mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this)); |
1078 | 0 | } |
1079 | 0 |
|
1080 | 0 | if (mListener) { |
1081 | 0 | mListener->OnStartRequest(this, mListenerContext); |
1082 | 0 | } |
1083 | 0 | return NS_OK; |
1084 | 0 | } |
1085 | | |
1086 | | NS_IMETHODIMP |
1087 | | InterceptedHttpChannel::OnStopRequest(nsIRequest* aRequest, |
1088 | | nsISupports* aContext, |
1089 | | nsresult aStatus) |
1090 | 0 | { |
1091 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1092 | 0 |
|
1093 | 0 | if (NS_SUCCEEDED(mStatus)) { |
1094 | 0 | mStatus = aStatus; |
1095 | 0 | } |
1096 | 0 |
|
1097 | 0 | MaybeCallBodyCallback(); |
1098 | 0 |
|
1099 | 0 | // Its possible that we have any async runnable queued to report some |
1100 | 0 | // progress when OnStopRequest() is triggered. Report any left over |
1101 | 0 | // progress immediately. The extra runnable will then do nothing thanks |
1102 | 0 | // to the ReleaseListeners() call below. |
1103 | 0 | MaybeCallStatusAndProgress(); |
1104 | 0 |
|
1105 | 0 | mIsPending = false; |
1106 | 0 |
|
1107 | 0 | // Register entry to the PerformanceStorage resource timing |
1108 | 0 | MaybeReportTimingData(); |
1109 | 0 |
|
1110 | 0 | if (mListener) { |
1111 | 0 | mListener->OnStopRequest(this, mListenerContext, mStatus); |
1112 | 0 | } |
1113 | 0 |
|
1114 | 0 | gHttpHandler->OnStopRequest(this); |
1115 | 0 |
|
1116 | 0 | ReleaseListeners(); |
1117 | 0 |
|
1118 | 0 | return NS_OK; |
1119 | 0 | } |
1120 | | |
1121 | | NS_IMETHODIMP |
1122 | | InterceptedHttpChannel::OnDataAvailable(nsIRequest* aRequest, |
1123 | | nsISupports* aContext, |
1124 | | nsIInputStream* aInputStream, |
1125 | | uint64_t aOffset, |
1126 | | uint32_t aCount) |
1127 | 0 | { |
1128 | 0 | // Any thread if the channel has been retargeted. |
1129 | 0 |
|
1130 | 0 | if (mCanceled || !mListener) { |
1131 | 0 | // If there is no listener, we still need to drain the stream in order |
1132 | 0 | // maintain necko invariants. |
1133 | 0 | uint32_t unused = 0; |
1134 | 0 | aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &unused); |
1135 | 0 | return mStatus; |
1136 | 0 | } |
1137 | 0 | if (mProgressSink) { |
1138 | 0 | if (!(mLoadFlags & HttpBaseChannel::LOAD_BACKGROUND)) { |
1139 | 0 | mProgress = aOffset + aCount; |
1140 | 0 | MaybeCallStatusAndProgress(); |
1141 | 0 | } |
1142 | 0 | } |
1143 | 0 |
|
1144 | 0 | return mListener->OnDataAvailable(this, mListenerContext, aInputStream, |
1145 | 0 | aOffset, aCount); |
1146 | 0 | } |
1147 | | |
1148 | | NS_IMETHODIMP |
1149 | | InterceptedHttpChannel::MessageDiversionStarted(ADivertableParentChannel* aParentChannel) |
1150 | 0 | { |
1151 | 0 | MOZ_ASSERT(!mParentChannel); |
1152 | 0 | mParentChannel = aParentChannel; |
1153 | 0 | mDiverting = true; |
1154 | 0 | uint32_t suspendCount = mSuspendCount; |
1155 | 0 | while(suspendCount--) { |
1156 | 0 | mParentChannel->SuspendMessageDiversion(); |
1157 | 0 | } |
1158 | 0 | return NS_OK; |
1159 | 0 | } |
1160 | | |
1161 | | NS_IMETHODIMP |
1162 | | InterceptedHttpChannel::MessageDiversionStop() |
1163 | 0 | { |
1164 | 0 | MOZ_ASSERT(mParentChannel); |
1165 | 0 | mParentChannel = nullptr; |
1166 | 0 | mDiverting = false; |
1167 | 0 | return NS_OK; |
1168 | 0 | } |
1169 | | |
1170 | | NS_IMETHODIMP |
1171 | | InterceptedHttpChannel::SuspendInternal() |
1172 | 0 | { |
1173 | 0 | ++mSuspendCount; |
1174 | 0 | if (mPump) { |
1175 | 0 | return mPump->Suspend(); |
1176 | 0 | } |
1177 | 0 | return NS_OK; |
1178 | 0 | } |
1179 | | |
1180 | | NS_IMETHODIMP |
1181 | | InterceptedHttpChannel::ResumeInternal() |
1182 | 0 | { |
1183 | 0 | --mSuspendCount; |
1184 | 0 | if (mPump) { |
1185 | 0 | return mPump->Resume(); |
1186 | 0 | } |
1187 | 0 | return NS_OK; |
1188 | 0 | } |
1189 | | |
1190 | | NS_IMETHODIMP |
1191 | | InterceptedHttpChannel::RetargetDeliveryTo(nsIEventTarget* aNewTarget) |
1192 | 0 | { |
1193 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1194 | 0 | NS_ENSURE_ARG(aNewTarget); |
1195 | 0 |
|
1196 | 0 | // If retargeting to the main thread, do nothing. |
1197 | 0 | if (aNewTarget->IsOnCurrentThread()) { |
1198 | 0 | return NS_OK; |
1199 | 0 | } |
1200 | 0 | |
1201 | 0 | // Retargeting is only valid during OnStartRequest for nsIChannels. So |
1202 | 0 | // we should only be called if we have a pump. |
1203 | 0 | if (!mPump) { |
1204 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1205 | 0 | } |
1206 | 0 | |
1207 | 0 | return mPump->RetargetDeliveryTo(aNewTarget); |
1208 | 0 | } |
1209 | | |
1210 | | NS_IMETHODIMP |
1211 | | InterceptedHttpChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget) |
1212 | 0 | { |
1213 | 0 | if (!mPump) { |
1214 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1215 | 0 | } |
1216 | 0 | return mPump->GetDeliveryTarget(aEventTarget); |
1217 | 0 | } |
1218 | | |
1219 | | NS_IMETHODIMP |
1220 | | InterceptedHttpChannel::CheckListenerChain() |
1221 | 0 | { |
1222 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1223 | 0 | nsresult rv = NS_OK; |
1224 | 0 | nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = |
1225 | 0 | do_QueryInterface(mListener, &rv); |
1226 | 0 | if (retargetableListener) { |
1227 | 0 | rv = retargetableListener->CheckListenerChain(); |
1228 | 0 | } |
1229 | 0 | return rv; |
1230 | 0 | } |
1231 | | |
1232 | | //----------------------------------------------------------------------------- |
1233 | | // InterceptedHttpChannel::nsICacheInfoChannel |
1234 | | //----------------------------------------------------------------------------- |
1235 | | // InterceptedHttpChannel does not really implement the nsICacheInfoChannel |
1236 | | // interface, we tranfers parameters to the saved nsICacheInfoChannel(mSynthesizedCacheInfo) |
1237 | | // from StartSynthesizedResponse. And we return false in IsFromCache and |
1238 | | // NS_ERROR_NOT_AVAILABLE for all other methods while the saved |
1239 | | // mSynthesizedCacheInfo does not exist. |
1240 | | NS_IMETHODIMP |
1241 | | InterceptedHttpChannel::IsFromCache(bool *value) |
1242 | 0 | { |
1243 | 0 | if (mSynthesizedCacheInfo) { |
1244 | 0 | return mSynthesizedCacheInfo->IsFromCache(value); |
1245 | 0 | } |
1246 | 0 | *value = false; |
1247 | 0 | return NS_OK; |
1248 | 0 | } |
1249 | | |
1250 | | NS_IMETHODIMP |
1251 | | InterceptedHttpChannel::GetCacheEntryId(uint64_t *aCacheEntryId) |
1252 | 0 | { |
1253 | 0 | if (mSynthesizedCacheInfo) { |
1254 | 0 | return mSynthesizedCacheInfo->GetCacheEntryId(aCacheEntryId); |
1255 | 0 | } |
1256 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1257 | 0 | } |
1258 | | |
1259 | | NS_IMETHODIMP |
1260 | | InterceptedHttpChannel::GetCacheTokenFetchCount(int32_t *_retval) |
1261 | 0 | { |
1262 | 0 | NS_ENSURE_ARG_POINTER(_retval); |
1263 | 0 |
|
1264 | 0 | if (mSynthesizedCacheInfo) { |
1265 | 0 | return mSynthesizedCacheInfo->GetCacheTokenFetchCount(_retval); |
1266 | 0 | } |
1267 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1268 | 0 | } |
1269 | | |
1270 | | NS_IMETHODIMP |
1271 | | InterceptedHttpChannel::GetCacheTokenExpirationTime(uint32_t *_retval) |
1272 | 0 | { |
1273 | 0 | NS_ENSURE_ARG_POINTER(_retval); |
1274 | 0 |
|
1275 | 0 | if (mSynthesizedCacheInfo) { |
1276 | 0 | return mSynthesizedCacheInfo->GetCacheTokenExpirationTime(_retval); |
1277 | 0 | } |
1278 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1279 | 0 | } |
1280 | | |
1281 | | NS_IMETHODIMP |
1282 | | InterceptedHttpChannel::GetCacheTokenCachedCharset(nsACString &_retval) |
1283 | 0 | { |
1284 | 0 | if (mSynthesizedCacheInfo) { |
1285 | 0 | return mSynthesizedCacheInfo->GetCacheTokenCachedCharset(_retval); |
1286 | 0 | } |
1287 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1288 | 0 | } |
1289 | | |
1290 | | NS_IMETHODIMP |
1291 | | InterceptedHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset) |
1292 | 0 | { |
1293 | 0 | if (mSynthesizedCacheInfo) { |
1294 | 0 | return mSynthesizedCacheInfo->SetCacheTokenCachedCharset(aCharset); |
1295 | 0 | } |
1296 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1297 | 0 | } |
1298 | | |
1299 | | NS_IMETHODIMP |
1300 | | InterceptedHttpChannel::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) |
1301 | 0 | { |
1302 | 0 | if (mSynthesizedCacheInfo) { |
1303 | 0 | return mSynthesizedCacheInfo->SetAllowStaleCacheContent(aAllowStaleCacheContent); |
1304 | 0 | } |
1305 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1306 | 0 | } |
1307 | | |
1308 | | NS_IMETHODIMP |
1309 | | InterceptedHttpChannel::GetAllowStaleCacheContent(bool *aAllowStaleCacheContent) |
1310 | 0 | { |
1311 | 0 | if (mSynthesizedCacheInfo) { |
1312 | 0 | return mSynthesizedCacheInfo->GetAllowStaleCacheContent(aAllowStaleCacheContent); |
1313 | 0 | } |
1314 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1315 | 0 | } |
1316 | | |
1317 | | NS_IMETHODIMP |
1318 | | InterceptedHttpChannel::PreferAlternativeDataType(const nsACString & aType) |
1319 | 0 | { |
1320 | 0 | ENSURE_CALLED_BEFORE_ASYNC_OPEN(); |
1321 | 0 | mPreferredCachedAltDataType = aType; |
1322 | 0 | return NS_OK; |
1323 | 0 | } |
1324 | | |
1325 | | NS_IMETHODIMP |
1326 | | InterceptedHttpChannel::GetPreferredAlternativeDataType(nsACString & aType) |
1327 | 0 | { |
1328 | 0 | aType = mPreferredCachedAltDataType; |
1329 | 0 | return NS_OK; |
1330 | 0 | } |
1331 | | |
1332 | | NS_IMETHODIMP |
1333 | | InterceptedHttpChannel::GetAlternativeDataType(nsACString & aType) |
1334 | 0 | { |
1335 | 0 | if (mSynthesizedCacheInfo) { |
1336 | 0 | return mSynthesizedCacheInfo->GetAlternativeDataType(aType); |
1337 | 0 | } |
1338 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1339 | 0 | } |
1340 | | |
1341 | | NS_IMETHODIMP |
1342 | | InterceptedHttpChannel::OpenAlternativeOutputStream(const nsACString & type, int64_t predictedSize, nsIOutputStream * *_retval) |
1343 | 0 | { |
1344 | 0 | if (mSynthesizedCacheInfo) { |
1345 | 0 | return mSynthesizedCacheInfo->OpenAlternativeOutputStream(type, predictedSize, _retval); |
1346 | 0 | } |
1347 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1348 | 0 | } |
1349 | | |
1350 | | NS_IMETHODIMP |
1351 | | InterceptedHttpChannel::GetCacheKey(uint32_t* key) |
1352 | 0 | { |
1353 | 0 | if (mSynthesizedCacheInfo) { |
1354 | 0 | return mSynthesizedCacheInfo->GetCacheKey(key); |
1355 | 0 | } |
1356 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1357 | 0 | } |
1358 | | |
1359 | | NS_IMETHODIMP |
1360 | | InterceptedHttpChannel::SetCacheKey(uint32_t key) |
1361 | 0 | { |
1362 | 0 | if (mSynthesizedCacheInfo) { |
1363 | 0 | return mSynthesizedCacheInfo->SetCacheKey(key); |
1364 | 0 | } |
1365 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1366 | 0 | } |
1367 | | |
1368 | | } // namespace net |
1369 | | } // namespace mozilla |