Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support HttpEngine openConnection(URL) #8973

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
@@ -1,4 +1,5 @@
[versions]
cronet-fallback = "119.6045.31"
robolectric-nativeruntime-dist-compat = "1.0.9"

# https://developer.android.com/studio/releases
Expand Down Expand Up @@ -128,6 +129,7 @@ play-services-basement = "18.0.1"

[libraries]
android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" }
cronet-fallback = { module = "org.chromium.net:cronet-fallback", version.ref = "cronet-fallback" }
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
spotless-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless-gradle" }
detekt-gradle = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt-gradle" }
Expand Down
3 changes: 3 additions & 0 deletions integration_tests/ctesque/src/main/AndroidManifest.xml
Expand Up @@ -3,6 +3,9 @@
Manifest for ctesque tests
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required for HttpEngineTest -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application />
</manifest>
@@ -0,0 +1,42 @@
package android.net.http;

import static com.google.common.truth.Truth.assertThat;

import android.content.Context;
import android.os.Build;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;

import java.net.HttpURLConnection;
import java.net.URL;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.internal.DoNotInstrument;

@DoNotInstrument
@RunWith(AndroidJUnit4.class)
@Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public class HttpEngineTest {
@Test
public void supportHttpClientOpenConnection() throws Exception {
Context context = ApplicationProvider.getApplicationContext();

HttpEngine engine = new HttpEngine.Builder(context).build();

URL url = new URL("https://google.com/robots.txt");
HttpURLConnection con = (HttpURLConnection) engine.openConnection(url);

try {
assertThat(con.getURL()).isEqualTo(url);

// Check how to test network in robolectric builds
// String response = new String(con.getInputStream().readAllBytes());
// assertThat(response).contains("Disallow");
} finally {
con.disconnect();
}
}
}
2 changes: 2 additions & 0 deletions shadows/framework/build.gradle
Expand Up @@ -57,6 +57,8 @@ dependencies {

api "androidx.test:monitor:$axtMonitorVersion@aar"

implementation(libs.cronet.fallback)

implementation libs.error.prone.annotations
compileOnly libs.findbugs.jsr305
api libs.sqlite4java
Expand Down
@@ -0,0 +1,206 @@
package org.robolectric.shadows;

import android.annotation.NonNull;
import android.content.Context;
import android.net.Network;
import android.net.http.ExperimentalBidirectionalStream;
import android.net.http.ExperimentalHttpEngine;
import android.net.http.ExperimentalUrlRequest;
import android.net.http.HttpEngine;
import android.net.http.IHttpEngineBuilder;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandlerFactory;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.Executor;
import org.chromium.net.CronetEngine;
import org.chromium.net.impl.JavaCronetProvider;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.versioning.AndroidVersions.U;

@SuppressWarnings({"NewApi"})
@Implements(value = HttpEngine.Builder.class, minSdk = U.SDK_INT)
public class ShadowHttpEngine {
private static CronetEngine.Builder cronetEngineBuilder;

@Implementation
protected static IHttpEngineBuilder createBuilderDelegate(Context context) {
if (cronetEngineBuilder != null) {
return new CronetEngineBuilderWrapper(cronetEngineBuilder);
} else {
return new CronetEngineBuilderWrapper(new JavaCronetProvider(context).createBuilder());
}
}

public static void setCronetEngineBuilder(CronetEngine.Builder cronetEngineBuilder) {
ShadowHttpEngine.cronetEngineBuilder = cronetEngineBuilder;
}

@Resetter
public static void reset() {
cronetEngineBuilder = null;
}

static class CronetEngineBuilderWrapper extends IHttpEngineBuilder {
private final CronetEngine.Builder backend;

public CronetEngineBuilderWrapper(CronetEngine.Builder backend) {
this.backend = backend;
}

@Override
public String getDefaultUserAgent() {
return backend.getDefaultUserAgent();
}

@Override
public IHttpEngineBuilder setUserAgent(String userAgent) {
backend.setUserAgent(userAgent);
return this;
}

@Override
public IHttpEngineBuilder setStoragePath(String value) {
backend.setStoragePath(value);
return this;
}

@Override
public IHttpEngineBuilder addQuicHint(String host, int port, int alternatePort) {
backend.addQuicHint(host, port, alternatePort);
return this;
}

@Override
public IHttpEngineBuilder addPublicKeyPins(
String hostName,
Set<byte[]> pinsSha256,
boolean includeSubdomains,
Instant expirationInstant) {
backend.addPublicKeyPins(
hostName, pinsSha256, includeSubdomains, Date.from(expirationInstant));
return this;
}

@Override
public IHttpEngineBuilder setQuicOptions(@NonNull android.net.http.QuicOptions options) {
return this;
}

@Override
public IHttpEngineBuilder setDnsOptions(@NonNull android.net.http.DnsOptions options) {
return this;
}

@Override
public IHttpEngineBuilder setConnectionMigrationOptions(
@NonNull android.net.http.ConnectionMigrationOptions options) {
return this;
}

@Override
public IHttpEngineBuilder enableBrotli(boolean value) {
return this;
}

@Override
protected Set<Integer> getSupportedConfigOptions() {
return Collections.emptySet();
}

@Override
public IHttpEngineBuilder enableNetworkQualityEstimator(boolean value) {
return this;
}

@Override
public IHttpEngineBuilder setThreadPriority(int priority) {
return this;
}

@Override
public IHttpEngineBuilder enableHttp2(boolean b) {
return this;
}

@Override
public IHttpEngineBuilder enableHttpCache(int i, long l) {
return this;
}

@Override
public IHttpEngineBuilder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean b) {
return this;
}

@Override
public IHttpEngineBuilder enableQuic(boolean b) {
return this;
}

@Override
public IHttpEngineBuilder enableSdch(boolean b) {
return this;
}

@Override
public IHttpEngineBuilder setExperimentalOptions(String s) {
return this;
}

public ExperimentalHttpEngine build() {
return new CronetEngineWrapper(backend.build());
}
}

static class CronetEngineWrapper extends ExperimentalHttpEngine {

private final CronetEngine backend;

public CronetEngineWrapper(CronetEngine backend) {
this.backend = backend;
}

@Override
public void shutdown() {
backend.shutdown();
}

@Override
public URLConnection openConnection(URL url) throws IOException {
return backend.openConnection(url);
}

@Override
public URLStreamHandlerFactory createUrlStreamHandlerFactory() {
return backend.createURLStreamHandlerFactory();
}

@Override
public void bindToNetwork(Network network) {
long networkHandle = backend.UNBIND_NETWORK_HANDLE;
if (network != null) {
networkHandle = network.getNetworkHandle();
}
backend.bindToNetwork(networkHandle);
}

@Override
public ExperimentalBidirectionalStream.Builder newBidirectionalStreamBuilder(
String url, Executor executor, android.net.http.BidirectionalStream.Callback callback) {
throw new UnsupportedOperationException("not implemented yet");
}

@Override
public ExperimentalUrlRequest.Builder newUrlRequestBuilder(
String url, Executor executor, android.net.http.UrlRequest.Callback callback) {
throw new UnsupportedOperationException("not implemented yet");
}
}
}