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