1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "GeckoViewStreamListener.h"
9 #include "InetAddress.h" // for java::sdk::InetAddress and java::sdk::UnknownHostException
10 #include "ReferrerInfo.h"
11 #include "WebExecutorSupport.h"
13 #include "nsIAsyncVerifyRedirectCallback.h"
14 #include "nsIHttpChannel.h"
15 #include "nsIHttpChannelInternal.h"
16 #include "nsIHttpHeaderVisitor.h"
17 #include "nsIDNSService.h"
18 #include "nsIDNSListener.h"
19 #include "nsIDNSRecord.h"
20 #include "nsINSSErrorsService.h"
21 #include "nsNetUtil.h" // for NS_NewURI, NS_NewChannel, NS_NewStreamLoader
22 #include "nsIPrivateBrowsingChannel.h"
23 #include "nsIUploadChannel2.h"
24 #include "nsIX509Cert.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/net/CookieJarSettings.h"
28 #include "mozilla/net/DNS.h" // for NetAddr
29 #include "mozilla/java/GeckoWebExecutorWrappers.h"
30 #include "mozilla/java/WebMessageWrappers.h"
31 #include "mozilla/java/WebRequestErrorWrappers.h"
32 #include "mozilla/java/WebResponseWrappers.h"
39 static void CompleteWithError(java::GeckoResult::Param aResult
,
40 nsresult aStatus
, nsIChannel
* aChannel
) {
41 nsCOMPtr
<nsINSSErrorsService
> errSvc
=
42 do_GetService("@mozilla.org/nss_errors_service;1");
46 nsresult rv
= errSvc
->GetErrorClass(aStatus
, &errorClass
);
51 jni::ByteArray::LocalRef certBytes
;
53 std::tie(certBytes
, std::ignore
) =
54 GeckoViewStreamListener::CertificateFromChannel(aChannel
);
57 java::WebRequestError::LocalRef error
= java::WebRequestError::FromGeckoError(
58 int64_t(aStatus
), NS_ERROR_GET_MODULE(aStatus
), errorClass
, certBytes
);
60 aResult
->CompleteExceptionally(error
.Cast
<jni::Throwable
>());
63 static void CompleteWithError(java::GeckoResult::Param aResult
,
65 CompleteWithError(aResult
, aStatus
, nullptr);
68 class ByteBufferStream final
: public nsIInputStream
{
70 NS_DECL_THREADSAFE_ISUPPORTS
72 explicit ByteBufferStream(jni::ByteBuffer::Param buffer
)
73 : mBuffer(buffer
), mPosition(0), mClosed(false) {
75 MOZ_ASSERT(mBuffer
->Address());
85 Available(uint64_t* aResult
) override
{
87 return NS_BASE_STREAM_CLOSED
;
90 *aResult
= (mBuffer
->Capacity() - mPosition
);
95 Read(char* aBuf
, uint32_t aCount
, uint32_t* aCountRead
) override
{
97 return NS_BASE_STREAM_CLOSED
;
100 *aCountRead
= uint32_t(
101 std::min(uint64_t(mBuffer
->Capacity() - mPosition
), uint64_t(aCount
)));
103 if (*aCountRead
> 0) {
104 memcpy(aBuf
, (char*)mBuffer
->Address() + mPosition
, *aCountRead
);
105 mPosition
+= *aCountRead
;
112 ReadSegments(nsWriteSegmentFun aWriter
, void* aClosure
, uint32_t aCount
,
113 uint32_t* aResult
) override
{
114 return NS_ERROR_NOT_IMPLEMENTED
;
118 IsNonBlocking(bool* aResult
) override
{
124 virtual ~ByteBufferStream() {}
126 const jni::ByteBuffer::GlobalRef mBuffer
;
131 NS_IMPL_ISUPPORTS(ByteBufferStream
, nsIInputStream
)
133 class LoaderListener final
: public GeckoViewStreamListener
{
135 explicit LoaderListener(java::GeckoResult::Param aResult
,
136 bool aAllowRedirects
, bool testStreamFailure
)
137 : GeckoViewStreamListener(),
139 mTestStreamFailure(testStreamFailure
),
140 mAllowRedirects(aAllowRedirects
) {
145 OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
146 uint64_t aOffset
, uint32_t aCount
) override
{
149 if (mTestStreamFailure
) {
150 return NS_ERROR_UNEXPECTED
;
153 // We only need this for the ReadSegments call, the value is unused.
156 aInputStream
->ReadSegments(WriteSegment
, this, aCount
, &countRead
);
157 NS_ENSURE_SUCCESS(rv
, rv
);
162 AsyncOnChannelRedirect(nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
,
164 nsIAsyncVerifyRedirectCallback
* callback
) override
{
165 if (!mAllowRedirects
) {
166 return NS_ERROR_ABORT
;
169 callback
->OnRedirectVerifyCallback(NS_OK
);
173 void SendWebResponse(java::WebResponse::Param aResponse
) override
{
174 mResult
->Complete(aResponse
);
177 void CompleteWithError(nsresult aStatus
, nsIChannel
* aChannel
) override
{
178 ::CompleteWithError(mResult
, aStatus
, aChannel
);
181 virtual ~LoaderListener() {}
183 const java::GeckoResult::GlobalRef mResult
;
184 const bool mTestStreamFailure
;
185 bool mAllowRedirects
;
188 class DNSListener final
: public nsIDNSListener
{
190 NS_DECL_THREADSAFE_ISUPPORTS
192 DNSListener(const nsCString
& aHost
, java::GeckoResult::Param aResult
)
193 : mHost(aHost
), mResult(aResult
) {}
196 OnLookupComplete(nsICancelable
* aRequest
, nsIDNSRecord
* aRecord
,
197 nsresult aStatus
) override
{
198 if (NS_FAILED(aStatus
)) {
199 CompleteUnknownHostError();
203 nsresult rv
= CompleteWithRecord(aRecord
);
205 CompleteUnknownHostError();
212 void CompleteUnknownHostError() {
213 java::sdk::UnknownHostException::LocalRef error
=
214 java::sdk::UnknownHostException::New();
215 mResult
->CompleteExceptionally(error
.Cast
<jni::Throwable
>());
219 nsresult
CompleteWithRecord(nsIDNSRecord
* aRecord
) {
220 nsTArray
<NetAddr
> addrs
;
221 nsCOMPtr
<nsIDNSAddrRecord
> rec
= do_QueryInterface(aRecord
);
223 return NS_ERROR_UNEXPECTED
;
225 nsresult rv
= rec
->GetAddresses(addrs
);
226 NS_ENSURE_SUCCESS(rv
, rv
);
228 jni::ByteArray::LocalRef bytes
;
230 jni::ObjectArray::New
<java::sdk::InetAddress
>(addrs
.Length());
231 for (size_t i
= 0; i
< addrs
.Length(); i
++) {
232 const auto& addr
= addrs
[i
];
233 if (addr
.raw
.family
== AF_INET
) {
234 bytes
= jni::ByteArray::New(
235 reinterpret_cast<const int8_t*>(&addr
.inet
.ip
), 4);
236 } else if (addr
.raw
.family
== AF_INET6
) {
237 bytes
= jni::ByteArray::New(
238 reinterpret_cast<const int8_t*>(&addr
.inet6
.ip
), 16);
240 // We don't handle this, skip it.
244 objects
->SetElement(i
,
245 java::sdk::InetAddress::GetByAddress(mHost
, bytes
));
248 mResult
->Complete(objects
);
252 virtual ~DNSListener() {}
254 const nsCString mHost
;
255 const java::GeckoResult::GlobalRef mResult
;
258 NS_IMPL_ISUPPORTS(DNSListener
, nsIDNSListener
)
260 static nsresult
ConvertCacheMode(int32_t mode
, int32_t& result
) {
262 case java::WebRequest::CACHE_MODE_DEFAULT
:
263 result
= nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT
;
265 case java::WebRequest::CACHE_MODE_NO_STORE
:
266 result
= nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE
;
268 case java::WebRequest::CACHE_MODE_RELOAD
:
269 result
= nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD
;
271 case java::WebRequest::CACHE_MODE_NO_CACHE
:
272 result
= nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE
;
274 case java::WebRequest::CACHE_MODE_FORCE_CACHE
:
275 result
= nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE
;
277 case java::WebRequest::CACHE_MODE_ONLY_IF_CACHED
:
278 result
= nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED
;
281 return NS_ERROR_UNEXPECTED
;
287 static nsresult
SetupHttpChannel(nsIHttpChannel
* aHttpChannel
,
288 nsIChannel
* aChannel
,
289 java::WebRequest::Param aRequest
) {
290 const auto req
= java::WebRequest::LocalRef(aRequest
);
291 const auto reqBase
= java::WebMessage::LocalRef(req
.Cast
<java::WebMessage
>());
294 nsresult rv
= aHttpChannel
->SetRequestMethod(aRequest
->Method()->ToCString());
295 NS_ENSURE_SUCCESS(rv
, rv
);
298 const auto keys
= reqBase
->GetHeaderKeys();
299 const auto values
= reqBase
->GetHeaderValues();
300 nsCString contentType
;
301 for (size_t i
= 0; i
< keys
->Length(); i
++) {
302 const auto key
= jni::String::LocalRef(keys
->GetElement(i
))->ToCString();
304 jni::String::LocalRef(values
->GetElement(i
))->ToCString();
306 if (key
.LowerCaseEqualsASCII("content-type")) {
310 // We clobber any duplicate keys here because we've already merged them
311 // in the upstream WebRequest.
312 rv
= aHttpChannel
->SetRequestHeader(key
, value
, false /* merge */);
313 NS_ENSURE_SUCCESS(rv
, rv
);
317 const auto body
= req
->Body();
319 nsCOMPtr
<nsIInputStream
> stream
= new ByteBufferStream(body
);
321 nsCOMPtr
<nsIUploadChannel2
> uploadChannel(do_QueryInterface(aChannel
, &rv
));
322 NS_ENSURE_SUCCESS(rv
, rv
);
324 rv
= uploadChannel
->ExplicitSetUploadStream(
325 stream
, contentType
, -1, aRequest
->Method()->ToCString(), false);
326 NS_ENSURE_SUCCESS(rv
, rv
);
330 RefPtr
<nsIURI
> referrerUri
;
331 const auto referrer
= req
->Referrer();
333 rv
= NS_NewURI(getter_AddRefs(referrerUri
), referrer
->ToString());
334 NS_ENSURE_SUCCESS(rv
, NS_ERROR_MALFORMED_URI
);
337 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= new dom::ReferrerInfo(referrerUri
);
338 rv
= aHttpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
339 NS_ENSURE_SUCCESS(rv
, rv
);
342 nsCOMPtr
<nsIHttpChannelInternal
> internalChannel(
343 do_QueryInterface(aChannel
, &rv
));
344 NS_ENSURE_SUCCESS(rv
, rv
);
347 rv
= ConvertCacheMode(req
->CacheMode(), cacheMode
);
348 NS_ENSURE_SUCCESS(rv
, rv
);
350 rv
= internalChannel
->SetFetchCacheMode(cacheMode
);
351 NS_ENSURE_SUCCESS(rv
, rv
);
353 if (req
->BeConservative()) {
354 rv
= internalChannel
->SetBeConservative(true);
355 NS_ENSURE_SUCCESS(rv
, rv
);
358 // We don't have any UI
359 rv
= internalChannel
->SetBlockAuthPrompt(true);
360 NS_ENSURE_SUCCESS(rv
, rv
);
365 nsresult
WebExecutorSupport::CreateStreamLoader(
366 java::WebRequest::Param aRequest
, int32_t aFlags
,
367 java::GeckoResult::Param aResult
) {
368 const auto req
= java::WebRequest::LocalRef(aRequest
);
369 const auto reqBase
= java::WebMessage::LocalRef(req
.Cast
<java::WebMessage
>());
371 nsCOMPtr
<nsIURI
> uri
;
372 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), reqBase
->Uri()->ToString());
373 NS_ENSURE_SUCCESS(rv
, NS_ERROR_MALFORMED_URI
);
375 nsCOMPtr
<nsIChannel
> channel
;
376 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
377 nsContentUtils::GetSystemPrincipal(),
378 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
379 nsIContentPolicy::TYPE_OTHER
);
380 NS_ENSURE_SUCCESS(rv
, rv
);
382 if (aFlags
& java::GeckoWebExecutor::FETCH_FLAGS_ANONYMOUS
) {
383 channel
->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS
);
386 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
387 if (aFlags
& java::GeckoWebExecutor::FETCH_FLAGS_PRIVATE
) {
388 nsCOMPtr
<nsIPrivateBrowsingChannel
> pbChannel
= do_QueryInterface(channel
);
389 NS_ENSURE_TRUE(pbChannel
, NS_ERROR_FAILURE
);
390 pbChannel
->SetPrivate(true);
391 cookieJarSettings
= CookieJarSettings::Create(CookieJarSettings::ePrivate
);
393 cookieJarSettings
= CookieJarSettings::Create(CookieJarSettings::eRegular
);
395 MOZ_ASSERT(cookieJarSettings
);
397 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
398 loadInfo
->SetCookieJarSettings(cookieJarSettings
);
400 // setup http/https specific things
401 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
, &rv
));
403 rv
= SetupHttpChannel(httpChannel
, channel
, aRequest
);
404 NS_ENSURE_SUCCESS(rv
, rv
);
407 // set up the listener
408 const bool allowRedirects
=
409 !(aFlags
& java::GeckoWebExecutor::FETCH_FLAGS_NO_REDIRECTS
);
410 const bool testStreamFailure
=
411 (aFlags
& java::GeckoWebExecutor::FETCH_FLAGS_STREAM_FAILURE_TEST
);
413 RefPtr
<LoaderListener
> listener
=
414 new LoaderListener(aResult
, allowRedirects
, testStreamFailure
);
416 rv
= channel
->SetNotificationCallbacks(listener
);
417 NS_ENSURE_SUCCESS(rv
, rv
);
419 // Finally, open the channel
420 return channel
->AsyncOpen(listener
);
423 void WebExecutorSupport::Fetch(jni::Object::Param aRequest
, int32_t aFlags
,
424 jni::Object::Param aResult
) {
425 const auto request
= java::WebRequest::LocalRef(aRequest
);
426 auto result
= java::GeckoResult::LocalRef(aResult
);
428 nsresult rv
= CreateStreamLoader(request
, aFlags
, result
);
430 CompleteWithError(result
, rv
);
434 static nsresult
ResolveHost(nsCString
& host
, java::GeckoResult::Param result
) {
436 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
, &rv
);
437 NS_ENSURE_SUCCESS(rv
, rv
);
439 nsCOMPtr
<nsICancelable
> cancelable
;
440 RefPtr
<DNSListener
> listener
= new DNSListener(host
, result
);
441 rv
= dns
->AsyncResolveNative(host
, nsIDNSService::RESOLVE_TYPE_DEFAULT
, 0,
442 nullptr, listener
, nullptr /* aListenerTarget */,
443 OriginAttributes(), getter_AddRefs(cancelable
));
447 void WebExecutorSupport::Resolve(jni::String::Param aUri
,
448 jni::Object::Param aResult
) {
449 auto result
= java::GeckoResult::LocalRef(aResult
);
451 nsCString uri
= aUri
->ToCString();
452 nsresult rv
= ResolveHost(uri
, result
);
454 java::sdk::UnknownHostException::LocalRef error
=
455 java::sdk::UnknownHostException::New();
456 result
->CompleteExceptionally(error
.Cast
<jni::Throwable
>());
460 } // namespace widget
461 } // namespace mozilla