1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: -*- */
2 /* vim:set expandtab ts=2 sw=2 sts=2 cin: */
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 "HttpLog.h"
8
9 #include "InterceptedChannel.h"
10 #include "nsICancelable.h"
11 #include "nsInputStreamPump.h"
12 #include "nsIPipe.h"
13 #include "nsIStreamListener.h"
14 #include "nsITimedChannel.h"
15 #include "nsHttpChannel.h"
16 #include "HttpChannelChild.h"
17 #include "nsHttpResponseHead.h"
18 #include "nsNetUtil.h"
19 #include "mozilla/ConsoleReportCollector.h"
20 #include "mozilla/dom/ChannelInfo.h"
21 #include "nsIChannelEventSink.h"
22 #include "nsThreadUtils.h"
23
24 namespace mozilla {
25 namespace net {
26
27 extern nsresult DoUpdateExpirationTime(nsHttpChannel* aSelf,
28 nsICacheEntry* aCacheEntry,
29 nsHttpResponseHead* aResponseHead,
30 uint32_t& aExpirationTime);
31 extern nsresult DoAddCacheEntryHeaders(nsHttpChannel* self,
32 nsICacheEntry* entry,
33 nsHttpRequestHead* requestHead,
34 nsHttpResponseHead* responseHead,
35 nsISupports* securityInfo);
36
NS_IMPL_ISUPPORTS(InterceptedChannelBase,nsIInterceptedChannel)37 NS_IMPL_ISUPPORTS(InterceptedChannelBase, nsIInterceptedChannel)
38
39 InterceptedChannelBase::InterceptedChannelBase(
40 nsINetworkInterceptController* aController)
41 : mController(aController),
42 mReportCollector(new ConsoleReportCollector()),
43 mClosed(false),
44 mSynthesizedOrReset(Invalid) {}
45
~InterceptedChannelBase()46 InterceptedChannelBase::~InterceptedChannelBase() {}
47
EnsureSynthesizedResponse()48 void InterceptedChannelBase::EnsureSynthesizedResponse() {
49 if (mSynthesizedResponseHead.isNothing()) {
50 mSynthesizedResponseHead.emplace(new nsHttpResponseHead());
51 }
52 }
53
DoNotifyController()54 void InterceptedChannelBase::DoNotifyController() {
55 nsresult rv = NS_OK;
56
57 if (NS_WARN_IF(!mController)) {
58 rv = ResetInterception();
59 if (NS_FAILED(rv)) {
60 NS_WARNING("Failed to resume intercepted network request");
61 CancelInterception(rv);
62 }
63 return;
64 }
65
66 rv = mController->ChannelIntercepted(this);
67 if (NS_WARN_IF(NS_FAILED(rv))) {
68 rv = ResetInterception();
69 if (NS_FAILED(rv)) {
70 NS_WARNING("Failed to resume intercepted network request");
71 CancelInterception(rv);
72 }
73 }
74 mController = nullptr;
75 }
76
DoSynthesizeStatus(uint16_t aStatus,const nsACString & aReason)77 nsresult InterceptedChannelBase::DoSynthesizeStatus(uint16_t aStatus,
78 const nsACString& aReason) {
79 EnsureSynthesizedResponse();
80
81 // Always assume HTTP 1.1 for synthesized responses.
82 nsAutoCString statusLine;
83 statusLine.AppendLiteral("HTTP/1.1 ");
84 statusLine.AppendInt(aStatus);
85 statusLine.AppendLiteral(" ");
86 statusLine.Append(aReason);
87
88 (*mSynthesizedResponseHead)->ParseStatusLine(statusLine);
89 return NS_OK;
90 }
91
DoSynthesizeHeader(const nsACString & aName,const nsACString & aValue)92 nsresult InterceptedChannelBase::DoSynthesizeHeader(const nsACString& aName,
93 const nsACString& aValue) {
94 EnsureSynthesizedResponse();
95
96 nsAutoCString header = aName + NS_LITERAL_CSTRING(": ") + aValue;
97 // Overwrite any existing header.
98 nsresult rv = (*mSynthesizedResponseHead)->ParseHeaderLine(header);
99 NS_ENSURE_SUCCESS(rv, rv);
100 return NS_OK;
101 }
102
103 NS_IMETHODIMP
GetConsoleReportCollector(nsIConsoleReportCollector ** aCollectorOut)104 InterceptedChannelBase::GetConsoleReportCollector(
105 nsIConsoleReportCollector** aCollectorOut) {
106 MOZ_ASSERT(aCollectorOut);
107 nsCOMPtr<nsIConsoleReportCollector> ref = mReportCollector;
108 ref.forget(aCollectorOut);
109 return NS_OK;
110 }
111
112 NS_IMETHODIMP
SetReleaseHandle(nsISupports * aHandle)113 InterceptedChannelBase::SetReleaseHandle(nsISupports* aHandle) {
114 MOZ_ASSERT(NS_IsMainThread());
115 MOZ_ASSERT(!mReleaseHandle);
116 MOZ_ASSERT(aHandle);
117
118 // We need to keep it and mChannel alive until destructor clear it up.
119 mReleaseHandle = aHandle;
120 return NS_OK;
121 }
122
123 NS_IMETHODIMP
SaveTimeStamps()124 InterceptedChannelBase::SaveTimeStamps() {
125 MOZ_ASSERT(NS_IsMainThread());
126
127 nsCOMPtr<nsIChannel> underlyingChannel;
128 nsresult rv = GetChannel(getter_AddRefs(underlyingChannel));
129 MOZ_ASSERT(NS_SUCCEEDED(rv));
130
131 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(underlyingChannel);
132 MOZ_ASSERT(timedChannel);
133
134 rv = timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
135 MOZ_ASSERT(NS_SUCCEEDED(rv));
136
137 rv = timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
138 MOZ_ASSERT(NS_SUCCEEDED(rv));
139
140 rv = timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
141 MOZ_ASSERT(NS_SUCCEEDED(rv));
142
143 rv = timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
144 MOZ_ASSERT(NS_SUCCEEDED(rv));
145
146 rv = timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
147 MOZ_ASSERT(NS_SUCCEEDED(rv));
148
149 rv = timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
150 MOZ_ASSERT(NS_SUCCEEDED(rv));
151
152 nsCOMPtr<nsIChannel> channel;
153 GetChannel(getter_AddRefs(channel));
154 if (NS_WARN_IF(!channel)) {
155 return NS_ERROR_FAILURE;
156 }
157
158 bool isNonSubresourceRequest =
159 nsContentUtils::IsNonSubresourceRequest(channel);
160 nsCString navigationOrSubresource = isNonSubresourceRequest
161 ? NS_LITERAL_CSTRING("navigation")
162 : NS_LITERAL_CSTRING("subresource");
163
164 nsAutoCString subresourceKey(EmptyCString());
165 GetSubresourceTimeStampKey(channel, subresourceKey);
166
167 // We may have null timestamps if the fetch dispatch runnable was cancelled
168 // and we defaulted to resuming the request.
169 if (!mFinishResponseStart.IsNull() && !mFinishResponseEnd.IsNull()) {
170 MOZ_ASSERT(mSynthesizedOrReset != Invalid);
171
172 Telemetry::HistogramID id =
173 (mSynthesizedOrReset == Synthesized)
174 ? Telemetry::
175 SERVICE_WORKER_FETCH_EVENT_FINISH_SYNTHESIZED_RESPONSE_MS
176 : Telemetry::SERVICE_WORKER_FETCH_EVENT_CHANNEL_RESET_MS;
177 Telemetry::Accumulate(
178 id, navigationOrSubresource,
179 static_cast<uint32_t>(
180 (mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
181 if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
182 Telemetry::Accumulate(
183 id, subresourceKey,
184 static_cast<uint32_t>(
185 (mFinishResponseEnd - mFinishResponseStart).ToMilliseconds()));
186 }
187 }
188
189 Telemetry::Accumulate(
190 Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
191 navigationOrSubresource,
192 static_cast<uint32_t>((mHandleFetchEventStart - mDispatchFetchEventStart)
193 .ToMilliseconds()));
194
195 if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
196 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_FETCH_EVENT_DISPATCH_MS,
197 subresourceKey,
198 static_cast<uint32_t>((mHandleFetchEventStart -
199 mDispatchFetchEventStart)
200 .ToMilliseconds()));
201 }
202
203 if (!mFinishResponseEnd.IsNull()) {
204 Telemetry::Accumulate(
205 Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
206 navigationOrSubresource,
207 static_cast<uint32_t>(
208 (mFinishResponseEnd - mDispatchFetchEventStart).ToMilliseconds()));
209 if (!isNonSubresourceRequest && !subresourceKey.IsEmpty()) {
210 Telemetry::Accumulate(
211 Telemetry::SERVICE_WORKER_FETCH_INTERCEPTION_DURATION_MS,
212 subresourceKey,
213 static_cast<uint32_t>((mFinishResponseEnd - mDispatchFetchEventStart)
214 .ToMilliseconds()));
215 }
216 }
217
218 return rv;
219 }
220
221 /* static */
SecureUpgradeChannelURI(nsIChannel * aChannel)222 already_AddRefed<nsIURI> InterceptedChannelBase::SecureUpgradeChannelURI(
223 nsIChannel* aChannel) {
224 nsCOMPtr<nsIURI> uri;
225 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
226 NS_ENSURE_SUCCESS(rv, nullptr);
227
228 nsCOMPtr<nsIURI> upgradedURI;
229 rv = NS_GetSecureUpgradedURI(uri, getter_AddRefs(upgradedURI));
230 NS_ENSURE_SUCCESS(rv, nullptr);
231
232 return upgradedURI.forget();
233 }
234
InterceptedChannelContent(HttpChannelChild * aChannel,nsINetworkInterceptController * aController,InterceptStreamListener * aListener,bool aSecureUpgrade)235 InterceptedChannelContent::InterceptedChannelContent(
236 HttpChannelChild* aChannel, nsINetworkInterceptController* aController,
237 InterceptStreamListener* aListener, bool aSecureUpgrade)
238 : InterceptedChannelBase(aController),
239 mChannel(aChannel),
240 mStreamListener(aListener),
241 mSecureUpgrade(aSecureUpgrade) {}
242
NotifyController()243 void InterceptedChannelContent::NotifyController() { DoNotifyController(); }
244
245 NS_IMETHODIMP
GetChannel(nsIChannel ** aChannel)246 InterceptedChannelContent::GetChannel(nsIChannel** aChannel) {
247 NS_IF_ADDREF(*aChannel = mChannel);
248 return NS_OK;
249 }
250
251 NS_IMETHODIMP
ResetInterception()252 InterceptedChannelContent::ResetInterception() {
253 if (mClosed) {
254 return NS_ERROR_NOT_AVAILABLE;
255 }
256
257 mReportCollector->FlushConsoleReports(mChannel);
258
259 mChannel->ResetInterception();
260
261 mClosed = true;
262
263 return NS_OK;
264 }
265
266 NS_IMETHODIMP
SynthesizeStatus(uint16_t aStatus,const nsACString & aReason)267 InterceptedChannelContent::SynthesizeStatus(uint16_t aStatus,
268 const nsACString& aReason) {
269 if (mClosed) {
270 return NS_ERROR_NOT_AVAILABLE;
271 }
272
273 return DoSynthesizeStatus(aStatus, aReason);
274 }
275
276 NS_IMETHODIMP
SynthesizeHeader(const nsACString & aName,const nsACString & aValue)277 InterceptedChannelContent::SynthesizeHeader(const nsACString& aName,
278 const nsACString& aValue) {
279 if (mClosed) {
280 return NS_ERROR_NOT_AVAILABLE;
281 }
282
283 return DoSynthesizeHeader(aName, aValue);
284 }
285
286 NS_IMETHODIMP
StartSynthesizedResponse(nsIInputStream * aBody,nsIInterceptedBodyCallback * aBodyCallback,nsICacheInfoChannel * aCacheInfoChannel,const nsACString & aFinalURLSpec,bool aResponseRedirected)287 InterceptedChannelContent::StartSynthesizedResponse(
288 nsIInputStream* aBody, nsIInterceptedBodyCallback* aBodyCallback,
289 nsICacheInfoChannel* aCacheInfoChannel, const nsACString& aFinalURLSpec,
290 bool aResponseRedirected) {
291 if (NS_WARN_IF(mClosed)) {
292 return NS_ERROR_NOT_AVAILABLE;
293 }
294
295 EnsureSynthesizedResponse();
296
297 nsCOMPtr<nsIURI> originalURI;
298 mChannel->GetURI(getter_AddRefs(originalURI));
299
300 nsCOMPtr<nsIURI> responseURI;
301 if (!aFinalURLSpec.IsEmpty()) {
302 nsresult rv = NS_NewURI(getter_AddRefs(responseURI), aFinalURLSpec);
303 NS_ENSURE_SUCCESS(rv, rv);
304 } else if (mSecureUpgrade) {
305 nsresult rv =
306 NS_GetSecureUpgradedURI(originalURI, getter_AddRefs(responseURI));
307 NS_ENSURE_SUCCESS(rv, rv);
308 } else {
309 responseURI = originalURI;
310 }
311
312 bool equal = false;
313 originalURI->Equals(responseURI, &equal);
314 if (!equal) {
315 mChannel->ForceIntercepted(aBody, aBodyCallback, aCacheInfoChannel);
316 mChannel->BeginNonIPCRedirect(responseURI, *mSynthesizedResponseHead.ptr(),
317 aResponseRedirected);
318 } else {
319 mChannel->OverrideWithSynthesizedResponse(
320 mSynthesizedResponseHead.ref(), aBody, aBodyCallback, mStreamListener,
321 aCacheInfoChannel);
322 }
323
324 return NS_OK;
325 }
326
327 NS_IMETHODIMP
FinishSynthesizedResponse()328 InterceptedChannelContent::FinishSynthesizedResponse() {
329 if (NS_WARN_IF(mClosed)) {
330 return NS_ERROR_NOT_AVAILABLE;
331 }
332
333 mReportCollector->FlushConsoleReports(mChannel);
334
335 mStreamListener = nullptr;
336 mClosed = true;
337
338 return NS_OK;
339 }
340
341 NS_IMETHODIMP
CancelInterception(nsresult aStatus)342 InterceptedChannelContent::CancelInterception(nsresult aStatus) {
343 MOZ_ASSERT(NS_FAILED(aStatus));
344
345 if (mClosed) {
346 return NS_ERROR_FAILURE;
347 }
348 mClosed = true;
349
350 mReportCollector->FlushConsoleReports(mChannel);
351
352 Unused << mChannel->Cancel(aStatus);
353 mStreamListener = nullptr;
354
355 return NS_OK;
356 }
357
358 NS_IMETHODIMP
SetChannelInfo(dom::ChannelInfo * aChannelInfo)359 InterceptedChannelContent::SetChannelInfo(dom::ChannelInfo* aChannelInfo) {
360 if (mClosed) {
361 return NS_ERROR_FAILURE;
362 }
363
364 return aChannelInfo->ResurrectInfoOnChannel(mChannel);
365 }
366
367 NS_IMETHODIMP
GetInternalContentPolicyType(nsContentPolicyType * aPolicyType)368 InterceptedChannelContent::GetInternalContentPolicyType(
369 nsContentPolicyType* aPolicyType) {
370 NS_ENSURE_ARG(aPolicyType);
371
372 nsCOMPtr<nsILoadInfo> loadInfo;
373 nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
374 NS_ENSURE_SUCCESS(rv, rv);
375
376 if (loadInfo) {
377 *aPolicyType = loadInfo->InternalContentPolicyType();
378 }
379 return NS_OK;
380 }
381
382 NS_IMETHODIMP
GetSecureUpgradedChannelURI(nsIURI ** aURI)383 InterceptedChannelContent::GetSecureUpgradedChannelURI(nsIURI** aURI) {
384 nsCOMPtr<nsIURI> uri;
385 if (mSecureUpgrade) {
386 uri = SecureUpgradeChannelURI(mChannel);
387 } else {
388 nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
389 NS_ENSURE_SUCCESS(rv, rv);
390 }
391 if (uri) {
392 uri.forget(aURI);
393 return NS_OK;
394 }
395 return NS_ERROR_FAILURE;
396 }
397
398 } // namespace net
399 } // namespace mozilla
400