WebExecutorSupport.cpp 14 KB
Newer Older
1
2
3
4
5
6
7
8
9
/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <algorithm>

#include "WebExecutorSupport.h"

10
11
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIHttpChannel.h"
12
13
14
15
#include "nsIHttpChannelInternal.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsINSSErrorsService.h"
#include "nsIUploadChannel2.h"
16
#include "nsIX509Cert.h"
17
18
19
20
21

#include "nsIDNSService.h"
#include "nsIDNSListener.h"
#include "nsIDNSRecord.h"

22
23
24
25
#include "mozilla/java/GeckoWebExecutorWrappers.h"
#include "mozilla/java/WebMessageWrappers.h"
#include "mozilla/java/WebRequestErrorWrappers.h"
#include "mozilla/java/WebResponseWrappers.h"
26
#include "mozilla/net/DNS.h"  // for NetAddr
27
#include "mozilla/net/CookieJarSettings.h"
28
#include "mozilla/Preferences.h"
29
#include "GeckoViewStreamListener.h"
30
#include "nsIPrivateBrowsingChannel.h"
31

32
#include "nsNetUtil.h"  // for NS_NewURI, NS_NewChannel, NS_NewStreamLoader
33

34
#include "InetAddress.h"  // for java::sdk::InetAddress and java::sdk::UnknownHostException
35
#include "ReferrerInfo.h"
36
37
38
39
40
41

namespace mozilla {
using namespace net;

namespace widget {

42
static void CompleteWithError(java::GeckoResult::Param aResult,
43
                              nsresult aStatus, nsIChannel* aChannel) {
44
45
  nsCOMPtr<nsINSSErrorsService> errSvc =
      do_GetService("@mozilla.org/nss_errors_service;1");
46
47
48
49
50
51
52
53
  MOZ_ASSERT(errSvc);

  uint32_t errorClass;
  nsresult rv = errSvc->GetErrorClass(aStatus, &errorClass);
  if (NS_FAILED(rv)) {
    errorClass = 0;
  }

54
55
  jni::ByteArray::LocalRef certBytes;
  if (aChannel) {
56
57
    std::tie(certBytes, std::ignore) =
        GeckoViewStreamListener::CertificateFromChannel(aChannel);
58
59
  }

60
  java::WebRequestError::LocalRef error = java::WebRequestError::FromGeckoError(
61
      int64_t(aStatus), NS_ERROR_GET_MODULE(aStatus), errorClass, certBytes);
62
63
64
65

  aResult->CompleteExceptionally(error.Cast<jni::Throwable>());
}

66
67
68
69
70
static void CompleteWithError(java::GeckoResult::Param aResult,
                              nsresult aStatus) {
  CompleteWithError(aResult, aStatus, nullptr);
}

71
72
class ByteBufferStream final : public nsIInputStream {
 public:
73
74
  NS_DECL_THREADSAFE_ISUPPORTS

75
  explicit ByteBufferStream(jni::ByteBuffer::Param buffer)
76
      : mBuffer(buffer), mPosition(0), mClosed(false) {
77
78
79
80
81
    MOZ_ASSERT(mBuffer);
    MOZ_ASSERT(mBuffer->Address());
  }

  NS_IMETHOD
82
  Close() override {
83
84
85
86
87
    mClosed = true;
    return NS_OK;
  }

  NS_IMETHOD
88
  Available(uint64_t* aResult) override {
89
90
91
92
93
94
95
96
97
    if (mClosed) {
      return NS_BASE_STREAM_CLOSED;
    }

    *aResult = (mBuffer->Capacity() - mPosition);
    return NS_OK;
  }

  NS_IMETHOD
98
  Read(char* aBuf, uint32_t aCount, uint32_t* aCountRead) override {
99
100
101
102
    if (mClosed) {
      return NS_BASE_STREAM_CLOSED;
    }

103
104
    *aCountRead = uint32_t(
        std::min(uint64_t(mBuffer->Capacity() - mPosition), uint64_t(aCount)));
105
106

    if (*aCountRead > 0) {
107
      memcpy(aBuf, (char*)mBuffer->Address() + mPosition, *aCountRead);
108
109
110
111
112
113
114
      mPosition += *aCountRead;
    }

    return NS_OK;
  }

  NS_IMETHOD
115
116
  ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
               uint32_t* aResult) override {
117
118
119
120
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  NS_IMETHOD
121
  IsNonBlocking(bool* aResult) override {
122
123
124
125
    *aResult = false;
    return NS_OK;
  }

126
127
 protected:
  virtual ~ByteBufferStream() {}
128
129
130
131
132
133
134
135

  const jni::ByteBuffer::GlobalRef mBuffer;
  uint64_t mPosition;
  bool mClosed;
};

NS_IMPL_ISUPPORTS(ByteBufferStream, nsIInputStream)

136
class LoaderListener final : public GeckoViewStreamListener {
137
 public:
138
  explicit LoaderListener(java::GeckoResult::Param aResult,
139
                          bool aAllowRedirects, bool testStreamFailure)
140
141
      : GeckoViewStreamListener(),
        mResult(aResult),
142
143
        mTestStreamFailure(testStreamFailure),
        mAllowRedirects(aAllowRedirects) {
144
145
146
147
    MOZ_ASSERT(mResult);
  }

  NS_IMETHOD
148
149
  OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
                  uint64_t aOffset, uint32_t aCount) override {
150
151
    MOZ_ASSERT(mStream);

152
    if (mTestStreamFailure) {
153
      return NS_ERROR_UNEXPECTED;
154
155
    }

156
157
    // We only need this for the ReadSegments call, the value is unused.
    uint32_t countRead;
158
159
160
161
    nsresult rv =
        aInputStream->ReadSegments(WriteSegment, this, aCount, &countRead);
    NS_ENSURE_SUCCESS(rv, rv);
    return rv;
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  }

  NS_IMETHOD
  AsyncOnChannelRedirect(nsIChannel* aOldChannel, nsIChannel* aNewChannel,
                         uint32_t flags,
                         nsIAsyncVerifyRedirectCallback* callback) override {
    if (!mAllowRedirects) {
      return NS_ERROR_ABORT;
    }

    callback->OnRedirectVerifyCallback(NS_OK);
    return NS_OK;
  }

176
177
  void SendWebResponse(java::WebResponse::Param aResponse) override {
    mResult->Complete(aResponse);
178
  }
179

180
181
  void CompleteWithError(nsresult aStatus, nsIChannel* aChannel) override {
    ::CompleteWithError(mResult, aStatus, aChannel);
182
183
184
185
186
  }

  virtual ~LoaderListener() {}

  const java::GeckoResult::GlobalRef mResult;
187
  const bool mTestStreamFailure;
188
  bool mAllowRedirects;
189
190
};

191
192
class DNSListener final : public nsIDNSListener {
 public:
193
194
195
  NS_DECL_THREADSAFE_ISUPPORTS

  DNSListener(const nsCString& aHost, java::GeckoResult::Param aResult)
196
      : mHost(aHost), mResult(aResult) {}
197
198
199

  NS_IMETHOD
  OnLookupComplete(nsICancelable* aRequest, nsIDNSRecord* aRecord,
200
                   nsresult aStatus) override {
201
    if (NS_FAILED(aStatus)) {
202
      CompleteUnknownHostError();
203
204
205
206
207
      return NS_OK;
    }

    nsresult rv = CompleteWithRecord(aRecord);
    if (NS_FAILED(rv)) {
208
      CompleteUnknownHostError();
209
210
211
212
213
214
      return NS_OK;
    }

    return NS_OK;
  }

215
216
217
218
219
220
  void CompleteUnknownHostError() {
    java::sdk::UnknownHostException::LocalRef error =
        java::sdk::UnknownHostException::New();
    mResult->CompleteExceptionally(error.Cast<jni::Throwable>());
  }

221
222
 private:
  nsresult CompleteWithRecord(nsIDNSRecord* aRecord) {
223
    nsTArray<NetAddr> addrs;
224
225
226
227
228
    nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
    if (!rec) {
      return NS_ERROR_UNEXPECTED;
    }
    nsresult rv = rec->GetAddresses(addrs);
229
230
231
    NS_ENSURE_SUCCESS(rv, rv);

    jni::ByteArray::LocalRef bytes;
232
233
    auto objects =
        jni::ObjectArray::New<java::sdk::InetAddress>(addrs.Length());
234
235
236
    for (size_t i = 0; i < addrs.Length(); i++) {
      const auto& addr = addrs[i];
      if (addr.raw.family == AF_INET) {
237
238
        bytes = jni::ByteArray::New(
            reinterpret_cast<const int8_t*>(&addr.inet.ip), 4);
239
      } else if (addr.raw.family == AF_INET6) {
240
241
        bytes = jni::ByteArray::New(
            reinterpret_cast<const int8_t*>(&addr.inet6.ip), 16);
242
243
244
245
246
      } else {
        // We don't handle this, skip it.
        continue;
      }

247
248
      objects->SetElement(i,
                          java::sdk::InetAddress::GetByAddress(mHost, bytes));
249
250
251
252
253
254
    }

    mResult->Complete(objects);
    return NS_OK;
  }

255
  virtual ~DNSListener() {}
256
257
258
259
260
261
262

  const nsCString mHost;
  const java::GeckoResult::GlobalRef mResult;
};

NS_IMPL_ISUPPORTS(DNSListener, nsIDNSListener)

263
static nsresult ConvertCacheMode(int32_t mode, int32_t& result) {
264
  switch (mode) {
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
    case java::WebRequest::CACHE_MODE_DEFAULT:
      result = nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT;
      break;
    case java::WebRequest::CACHE_MODE_NO_STORE:
      result = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE;
      break;
    case java::WebRequest::CACHE_MODE_RELOAD:
      result = nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD;
      break;
    case java::WebRequest::CACHE_MODE_NO_CACHE:
      result = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE;
      break;
    case java::WebRequest::CACHE_MODE_FORCE_CACHE:
      result = nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE;
      break;
    case java::WebRequest::CACHE_MODE_ONLY_IF_CACHED:
      result = nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED;
      break;
    default:
      return NS_ERROR_UNEXPECTED;
285
286
287
288
289
  }

  return NS_OK;
}

290
291
292
static nsresult SetupHttpChannel(nsIHttpChannel* aHttpChannel,
                                 nsIChannel* aChannel,
                                 java::WebRequest::Param aRequest) {
293
  const auto req = java::WebRequest::LocalRef(aRequest);
294
  const auto reqBase = java::WebMessage::LocalRef(req.Cast<java::WebMessage>());
295
296

  // Method
297
  nsresult rv = aHttpChannel->SetRequestMethod(aRequest->Method()->ToCString());
298
299
300
301
302
  NS_ENSURE_SUCCESS(rv, rv);

  // Headers
  const auto keys = reqBase->GetHeaderKeys();
  const auto values = reqBase->GetHeaderValues();
303
  nsCString contentType;
304
  for (size_t i = 0; i < keys->Length(); i++) {
305
306
307
308
309
310
311
    const auto key = jni::String::LocalRef(keys->GetElement(i))->ToCString();
    const auto value =
        jni::String::LocalRef(values->GetElement(i))->ToCString();

    if (key.LowerCaseEqualsASCII("content-type")) {
      contentType = value;
    }
312
313
314

    // We clobber any duplicate keys here because we've already merged them
    // in the upstream WebRequest.
315
    rv = aHttpChannel->SetRequestHeader(key, value, false /* merge */);
316
317
318
319
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Body
320
  const auto body = req->Body();
321
322
323
  if (body) {
    nsCOMPtr<nsIInputStream> stream = new ByteBufferStream(body);

324
    nsCOMPtr<nsIUploadChannel2> uploadChannel(do_QueryInterface(aChannel, &rv));
325
326
    NS_ENSURE_SUCCESS(rv, rv);

327
    rv = uploadChannel->ExplicitSetUploadStream(
328
        stream, contentType, -1, aRequest->Method()->ToCString(), false);
329
330
331
332
333
334
335
336
337
338
339
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Referrer
  RefPtr<nsIURI> referrerUri;
  const auto referrer = req->Referrer();
  if (referrer) {
    rv = NS_NewURI(getter_AddRefs(referrerUri), referrer->ToString());
    NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
  }

340
  nsCOMPtr<nsIReferrerInfo> referrerInfo = new dom::ReferrerInfo(referrerUri);
341
  rv = aHttpChannel->SetReferrerInfoWithoutClone(referrerInfo);
342
343
344
  NS_ENSURE_SUCCESS(rv, rv);

  // Cache mode
345
  nsCOMPtr<nsIHttpChannelInternal> internalChannel(
346
      do_QueryInterface(aChannel, &rv));
347
348
349
350
351
352
353
354
355
356
357
358
359
  NS_ENSURE_SUCCESS(rv, rv);

  int32_t cacheMode;
  rv = ConvertCacheMode(req->CacheMode(), cacheMode);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = internalChannel->SetFetchCacheMode(cacheMode);
  NS_ENSURE_SUCCESS(rv, rv);

  // We don't have any UI
  rv = internalChannel->SetBlockAuthPrompt(true);
  NS_ENSURE_SUCCESS(rv, rv);

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  return NS_OK;
}

nsresult WebExecutorSupport::CreateStreamLoader(
    java::WebRequest::Param aRequest, int32_t aFlags,
    java::GeckoResult::Param aResult) {
  const auto req = java::WebRequest::LocalRef(aRequest);
  const auto reqBase = java::WebMessage::LocalRef(req.Cast<java::WebMessage>());

  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_NewURI(getter_AddRefs(uri), reqBase->Uri()->ToString());
  NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);

  nsCOMPtr<nsIChannel> channel;
  rv = NS_NewChannel(getter_AddRefs(channel), uri,
                     nsContentUtils::GetSystemPrincipal(),
376
                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
377
378
379
380
381
382
383
                     nsIContentPolicy::TYPE_OTHER);
  NS_ENSURE_SUCCESS(rv, rv);

  if (aFlags & java::GeckoWebExecutor::FETCH_FLAGS_ANONYMOUS) {
    channel->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS);
  }

384
385
386
387
388
389
  if (aFlags & java::GeckoWebExecutor::FETCH_FLAGS_PRIVATE) {
    nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
    NS_ENSURE_TRUE(pbChannel, NS_ERROR_FAILURE);
    pbChannel->SetPrivate(true);
  }

390
391
392
393
394
  nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
      CookieJarSettings::Create();
  MOZ_ASSERT(cookieJarSettings);

  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
395
396
397
398
399
400
401
402
403
404

  RefPtr<nsIURI> originUri;
  const auto origin = req->Origin();
  if (origin) {
    rv = NS_NewURI(getter_AddRefs(originUri), origin->ToString());
    NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
    OriginAttributes attrs = loadInfo->GetOriginAttributes();
    attrs.SetFirstPartyDomain(true, originUri);
    loadInfo->SetOriginAttributes(attrs);
  }
405
406
407
408
409
410
411
412
413
414
  loadInfo->SetCookieJarSettings(cookieJarSettings);

  // setup http/https specific things
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel, &rv));
  if (httpChannel) {
    rv = SetupHttpChannel(httpChannel, channel, aRequest);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // set up the listener
415
416
  const bool allowRedirects =
      !(aFlags & java::GeckoWebExecutor::FETCH_FLAGS_NO_REDIRECTS);
417
418
419
420
421
  const bool testStreamFailure =
      (aFlags & java::GeckoWebExecutor::FETCH_FLAGS_STREAM_FAILURE_TEST);

  RefPtr<LoaderListener> listener =
      new LoaderListener(aResult, allowRedirects, testStreamFailure);
422
423
424

  rv = channel->SetNotificationCallbacks(listener);
  NS_ENSURE_SUCCESS(rv, rv);
425

426
  // Finally, open the channel
427
  return channel->AsyncOpen(listener);
428
429
430
}

void WebExecutorSupport::Fetch(jni::Object::Param aRequest, int32_t aFlags,
431
                               jni::Object::Param aResult) {
432
433
434
435
436
437
438
439
440
  const auto request = java::WebRequest::LocalRef(aRequest);
  auto result = java::GeckoResult::LocalRef(aResult);

  nsresult rv = CreateStreamLoader(request, aFlags, result);
  if (NS_FAILED(rv)) {
    CompleteWithError(result, rv);
  }
}

441
static nsresult ResolveHost(nsCString& host, java::GeckoResult::Param result) {
442
443
444
445
446
447
  nsresult rv;
  nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsICancelable> cancelable;
  RefPtr<DNSListener> listener = new DNSListener(host, result);
448
449
  rv = dns->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_DEFAULT, 0,
                               nullptr, listener, nullptr /* aListenerTarget */,
450
                               OriginAttributes(), getter_AddRefs(cancelable));
451
452
453
  return rv;
}

454
455
void WebExecutorSupport::Resolve(jni::String::Param aUri,
                                 jni::Object::Param aResult) {
456
457
458
459
460
  auto result = java::GeckoResult::LocalRef(aResult);

  nsCString uri = aUri->ToCString();
  nsresult rv = ResolveHost(uri, result);
  if (NS_FAILED(rv)) {
461
462
463
    java::sdk::UnknownHostException::LocalRef error =
        java::sdk::UnknownHostException::New();
    result->CompleteExceptionally(error.Cast<jni::Throwable>());
464
465
466
  }
}

467
468
}  // namespace widget
}  // namespace mozilla