Coverage Report

Created: 2018-09-25 14:53

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