diff --git a/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index 248786750fad..5e58f5e5266b 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -753,6 +753,7 @@ package org.mozilla.geckoview { method public void speculativeConnect(@android.support.annotation.NonNull java.lang.String); field public static final int FETCH_FLAGS_ANONYMOUS = 1; field public static final int FETCH_FLAGS_NONE = 0; + field public static final int FETCH_FLAGS_NO_REDIRECTS = 2; } public static interface GeckoWebExecutor.FetchFlags implements java.lang.annotation.Annotation { diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExecutorTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExecutorTest.kt index 55ee1fb39cb1..65f1c65515d3 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExecutorTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebExecutorTest.kt @@ -147,6 +147,15 @@ class WebExecutorTest { assertThat("Status code should match", response.statusCode, equalTo(200)) } + @Test + fun testDisallowRedirect() { + val response = fetch(WebRequest("$TEST_ENDPOINT/redirect-to?url=/status/200"), GeckoWebExecutor.FETCH_FLAGS_NO_REDIRECTS) + + assertThat("URI should match", response.uri, equalTo("$TEST_ENDPOINT/redirect-to?url=/status/200")) + assertThat("Redirected should match", response.redirected, equalTo(false)) + assertThat("Status code should match", response.statusCode, equalTo(302)) + } + @Test fun testRedirectLoop() { thrown.expect(equalTo(WebRequestError(WebRequestError.ERROR_REDIRECT_LOOP, WebRequestError.ERROR_CATEGORY_NETWORK))) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java index 85204ec68267..8c45e1122d1f 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoWebExecutor.java @@ -54,7 +54,7 @@ public class GeckoWebExecutor { } @Retention(RetentionPolicy.SOURCE) - @IntDef({FETCH_FLAGS_NONE, FETCH_FLAGS_ANONYMOUS}) + @IntDef({FETCH_FLAGS_NONE, FETCH_FLAGS_ANONYMOUS, FETCH_FLAGS_NO_REDIRECTS}) public @interface FetchFlags {}; /** @@ -68,6 +68,12 @@ public class GeckoWebExecutor { @WrapForJNI public static final int FETCH_FLAGS_ANONYMOUS = 1; + /** + * Don't automatically follow redirects. + */ + @WrapForJNI + public static final int FETCH_FLAGS_NO_REDIRECTS = 1 << 1; + /** * Create a new GeckoWebExecutor instance. * diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md index 13e288c4c343..73e9f3807663 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md @@ -37,6 +37,9 @@ exclude: true - Changed `WebResponse.body` from a `ByteBuffer` to an `InputStream`. Apps that want access to the entire response body will now need to read the stream themselves. +- Added `GeckoWebExecutor.FETCH_FLAGS_NO_REDIRECTS`, which will cause `GeckoWebExecutor.fetch()` to not + automatically follow HTTP redirects (e.g., 302). + [67.1]: ../GeckoSession.html#getDefaultUserAgent-- ## v66 @@ -148,4 +151,4 @@ exclude: true [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String- [65.25]: ../GeckoResult.html -[api-version]: 5655c3f6a74c860809e57a2d66499633ac23cfcc +[api-version]: a1740e5cb61e34b3180b80f33b0b33243a34d588 diff --git a/widget/android/WebExecutorSupport.cpp b/widget/android/WebExecutorSupport.cpp index 388d9ae6ad4e..ba2a1aac6d5f 100644 --- a/widget/android/WebExecutorSupport.cpp +++ b/widget/android/WebExecutorSupport.cpp @@ -7,10 +7,12 @@ #include "WebExecutorSupport.h" +#include "nsIChannelEventSink.h" #include "nsIHttpChannel.h" #include "nsIHttpChannelInternal.h" #include "nsIHttpHeaderVisitor.h" #include "nsIInputStream.h" +#include "nsIInterfaceRequestor.h" #include "nsIStreamLoader.h" #include "nsINSSErrorsService.h" #include "nsIUploadChannel2.h" @@ -150,11 +152,15 @@ class StreamSupport final nsCOMPtr mRequest; }; -class LoaderListener final : public nsIStreamListener { +class LoaderListener final : public nsIStreamListener, + public nsIInterfaceRequestor, + public nsIChannelEventSink { public: NS_DECL_THREADSAFE_ISUPPORTS - explicit LoaderListener(java::GeckoResult::Param aResult) : mResult(aResult) { + explicit LoaderListener(java::GeckoResult::Param aResult, + bool aAllowRedirects) + : mResult(aResult), mAllowRedirects(aAllowRedirects) { MOZ_ASSERT(mResult); } @@ -211,6 +217,29 @@ class LoaderListener final : public nsIStreamListener { return aInputStream->ReadSegments(WriteSegment, this, aCount, &countRead); } + NS_IMETHOD + GetInterface(const nsIID& aIID, void** aResultOut) override { + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { + *aResultOut = static_cast(this); + NS_ADDREF_THIS(); + return NS_OK; + } + + return NS_ERROR_NO_INTERFACE; + } + + 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; + } + private: static nsresult WriteSegment(nsIInputStream* aInputStream, void* aClosure, const char* aFromSegment, uint32_t aToOffset, @@ -284,9 +313,12 @@ class LoaderListener final : public nsIStreamListener { const java::GeckoResult::GlobalRef mResult; java::GeckoInputStream::GlobalRef mStream; java::GeckoInputStream::Support::GlobalRef mSupport; + + bool mAllowRedirects; }; -NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamListener) +NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamListener, nsIInterfaceRequestor, + nsIChannelEventSink) class DNSListener final : public nsIDNSListener { public: @@ -471,8 +503,14 @@ nsresult WebExecutorSupport::CreateStreamLoader( rv = internalChannel->SetBlockAuthPrompt(true); NS_ENSURE_SUCCESS(rv, rv); + const bool allowRedirects = + !(aFlags & java::GeckoWebExecutor::FETCH_FLAGS_NO_REDIRECTS); + // All done, set up the listener - RefPtr listener = new LoaderListener(aResult); + RefPtr listener = new LoaderListener(aResult, allowRedirects); + + rv = channel->SetNotificationCallbacks(listener); + NS_ENSURE_SUCCESS(rv, rv); // Finally, open the channel rv = httpChannel->AsyncOpen(listener);