blob: 029425a32b6c3d674fa8024859a3cfd419f3e866 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.webkit;
import android.content.ContextWrapper;
import android.net.Uri;
import android.webkit.WebResourceResponse;
import static androidx.webkit.WebViewAssetLoader.AssetsPathHandler;
import static androidx.webkit.WebViewAssetLoader.PathHandler;
import static androidx.webkit.WebViewAssetLoader.ResourcesPathHandler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import androidx.webkit.internal.AssetHelper;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
@RunWith(AndroidJUnit4.class)
public class WebViewAssetLoaderTest {
private static final String TAG = "WebViewAssetLoaderTest";
private static final String CONTENTS = "Some content for testing";
private static final String ENCODING = "utf-8";
private static String readAsString(InputStream is, String encoding) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[512];
int len = 0;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
return new String(os.toByteArray(), encoding);
}
private static class MockAssetHelper extends AssetHelper {
MockAssetHelper() {
super(null);
}
@Override
public InputStream openAsset(Uri uri) {
return null;
}
@Override
public InputStream openResource(Uri uri) {
return null;
}
}
private static class MockContext extends ContextWrapper {
MockContext() {
super(ApplicationProvider.getApplicationContext());
}
}
private static class TestPathHandler implements PathHandler {
@Override
public WebResourceResponse handle(@NonNull String path) {
try {
InputStream stream = new ByteArrayInputStream(CONTENTS.getBytes(ENCODING));
return new WebResourceResponse(null, null, stream);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
@Test
@SmallTest
public void testCustomPathHandler() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test/"));
assertResponse(response, CONTENTS);
Assert.assertNull("non-registered URL should return null response",
assetLoader.shouldInterceptRequest(Uri.parse("https://foo.bar/")));
}
@Test
@SmallTest
public void testCustomDomain() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.setDomain("test.myDomain.net")
.addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://test.myDomain.net/test/"));
assertResponse(response, CONTENTS);
Assert.assertNull("non-addPathHandlered URL should return null response",
assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test/")));
}
@Test
@SmallTest
public void testAllowingHttp() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.setHttpAllowed(true)
.addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test/"));
assertResponse(response, CONTENTS);
Assert.assertNotNull("didn't match HTTP URL despite allowing HTTP",
assetLoader.shouldInterceptRequest(
Uri.parse("http://appassets.androidplatform.net/test/")));
}
@Test
@SmallTest
public void testDisallowingHttp() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test/"));
assertResponse(response, CONTENTS);
Assert.assertNull("matched HTTP URL despite disallowing HTTP",
assetLoader.shouldInterceptRequest(
Uri.parse("http://appassets.androidplatform.net/test/")));
}
@Test
@SmallTest
public void testHostAssets() throws Throwable {
final String testHtmlContents = "<body><div>test</div></body>";
PathHandler assetsPathHandler = new AssetsPathHandler(new MockAssetHelper() {
@Override
public InputStream openAsset(Uri url) {
if (url.getPath().equals("www/test.html")) {
try {
return new ByteArrayInputStream(testHtmlContents.getBytes(ENCODING));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
});
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/assets/", assetsPathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/assets/www/test.html"));
assertResponse(response, testHtmlContents);
}
@Test
@SmallTest
public void testHostResources() throws Throwable {
final String testHtmlContents = "<body><div>test</div></body>";
PathHandler resourcesPathHandler = new ResourcesPathHandler(new MockAssetHelper() {
@Override
public InputStream openResource(Uri uri) {
if (uri.getPath().equals("raw/test.html")) {
try {
return new ByteArrayInputStream(testHtmlContents.getBytes(ENCODING));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
});
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/res/", resourcesPathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/res/raw/test.html"));
assertResponse(response, testHtmlContents);
}
@Test
@SmallTest
public void testMultiplePathHandlers() throws Throwable {
WebViewAssetLoader.Builder builder = new WebViewAssetLoader.Builder();
for (int i = 1; i <= 5; ++i) {
final String testContent = CONTENTS + Integer.toString(i);
builder.addPathHandler("/test_path_" + Integer.toString(i) + "/", new PathHandler() {
@Override
public WebResourceResponse handle(@NonNull String path) {
try {
InputStream is = new ByteArrayInputStream(testContent.getBytes(ENCODING));
return new WebResourceResponse(null, null, is);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
});
}
WebViewAssetLoader assetLoader = builder.build();
for (int i = 5; i >= 1; --i) {
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test_path_"
+ Integer.toString(i) + "/"));
assertResponse(response, CONTENTS + Integer.toString(i));
}
}
// Fake PathHandler for files ending with .zip
static class FakeZipPathHandler implements PathHandler {
static final String CONTENTS = "This is zip";
@Override
public WebResourceResponse handle(@NonNull String path) {
try {
if (path.endsWith(".zip")) {
InputStream is = new ByteArrayInputStream(CONTENTS.getBytes(ENCODING));
return new WebResourceResponse(null, null, is);
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return null;
}
}
// Fake PathHandler for files ending with .txt
static class FakeTextPathHandler implements PathHandler {
static final String CONTENTS = "This is txt";
@Override
public WebResourceResponse handle(@NonNull String path) {
try {
if (path.endsWith(".txt")) {
InputStream is = new ByteArrayInputStream(CONTENTS.getBytes(ENCODING));
return new WebResourceResponse(null, null, is);
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return null;
}
}
@Test
@SmallTest
public void testMultiplePathHandlersOnTheSamePath() throws Throwable {
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/test_path/", new FakeZipPathHandler())
.addPathHandler("/test_path/", new FakeTextPathHandler())
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test_path/file.zip"));
assertResponse(response, FakeZipPathHandler.CONTENTS);
response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test_path/file.txt"));
assertResponse(response, FakeTextPathHandler.CONTENTS);
response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test_path/file.jpg"));
Assert.assertNull("handled .jpg file, should return a null response", response);
// Register in reverse order to make sure it works regardless of order.
assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/test_path/", new FakeTextPathHandler())
.addPathHandler("/test_path/", new FakeZipPathHandler())
.build();
response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test_path/file.zip"));
assertResponse(response, FakeZipPathHandler.CONTENTS);
response = assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test_path/file.txt"));
assertResponse(response, FakeTextPathHandler.CONTENTS);
}
private static void assertResponse(@Nullable WebResourceResponse response,
@NonNull String expectedContent) throws IOException {
Assert.assertNotNull("failed to match the URL and returned null response", response);
Assert.assertNotNull("matched the URL but returned a null InputStream",
response.getData());
Assert.assertEquals(expectedContent, readAsString(response.getData(), ENCODING));
}
}