Merge "EnableHttpCache: Make use of new CtsTestServer cachable response"
diff --git a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
index a05aecd..5f1979f 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/QuicOptionsTest.kt
@@ -19,6 +19,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
 import java.time.Duration
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -30,7 +33,10 @@
         assertThat(quicOptions.allowedQuicHosts).isEmpty()
         assertThat(quicOptions.handshakeUserAgent).isNull()
         assertThat(quicOptions.idleConnectionTimeout).isNull()
-        assertThat(quicOptions.inMemoryServerConfigsCacheSize).isNull()
+        assertFalse(quicOptions.hasInMemoryServerConfigsCacheSize())
+        assertFailsWith(IllegalStateException::class) {
+            quicOptions.inMemoryServerConfigsCacheSize
+        }
     }
 
     @Test
@@ -61,6 +67,7 @@
         val quicOptions = QuicOptions.Builder()
                 .setInMemoryServerConfigsCacheSize(42)
                 .build()
+        assertTrue(quicOptions.hasInMemoryServerConfigsCacheSize())
         assertThat(quicOptions.inMemoryServerConfigsCacheSize)
                 .isEqualTo(42)
     }
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
index e24121f..e4949d3 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
@@ -29,6 +29,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.net.http.HeaderBlock;
 import android.net.http.HttpEngine;
 import android.net.http.HttpException;
 import android.net.http.InlineExecutionProhibitedException;
@@ -43,6 +44,7 @@
 import android.net.http.cts.util.TestUrlRequestCallback;
 import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
 import android.net.http.cts.util.UploadDataProviders;
+import android.webkit.cts.CtsTestServer;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -59,11 +61,16 @@
 import java.net.URLEncoder;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @RunWith(AndroidJUnit4.class)
 public class UrlRequestTest {
@@ -330,6 +337,50 @@
         assertThat(mCallback.mResponseAsString).isEqualTo(body);
     }
 
+    @Test
+    public void testUrlRequest_customHeaders() throws Exception {
+        UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoHeadersUrl());
+
+        List<Map.Entry<String, String>> expectedHeaders = Arrays.asList(
+                Map.entry("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
+                Map.entry("Max-Forwards", "10"),
+                Map.entry("X-Client-Data", "random custom header content"));
+
+        for (Map.Entry<String, String> header : expectedHeaders) {
+            builder.addHeader(header.getKey(), header.getValue());
+        }
+
+        builder.build().start();
+        mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+
+        assertOKStatusCode(mCallback.mResponseInfo);
+
+        List<Map.Entry<String, String>> echoedHeaders =
+                extractEchoedHeaders(mCallback.mResponseInfo.getHeaders());
+
+        // The implementation might decide to add more headers like accepted encodings it handles
+        // internally so the server is likely to see more headers than explicitly set
+        // by the developer.
+        assertThat(echoedHeaders)
+                .containsAtLeastElementsIn(expectedHeaders);
+    }
+
+    private static List<Map.Entry<String, String>> extractEchoedHeaders(HeaderBlock headers) {
+        return headers.getAsList()
+                .stream()
+                .flatMap(input -> {
+                    if (input.getKey().startsWith(CtsTestServer.ECHOED_RESPONSE_HEADER_PREFIX)) {
+                        String strippedKey =
+                                input.getKey().substring(
+                                        CtsTestServer.ECHOED_RESPONSE_HEADER_PREFIX.length());
+                        return Stream.of(Map.entry(strippedKey, input.getValue()));
+                    } else {
+                        return Stream.empty();
+                    }
+                })
+                .collect(Collectors.toList());
+    }
+
     private static class StubUrlRequestCallback implements UrlRequest.Callback {
 
         @Override
diff --git a/Tethering/common/TetheringLib/cronet_enabled/api/current.txt b/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
index 66a0295..bc76b72 100644
--- a/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
+++ b/Tethering/common/TetheringLib/cronet_enabled/api/current.txt
@@ -177,7 +177,8 @@
     method @NonNull public java.util.Set<java.lang.String> getAllowedQuicHosts();
     method @Nullable public String getHandshakeUserAgent();
     method @Nullable public java.time.Duration getIdleConnectionTimeout();
-    method @Nullable public Integer getInMemoryServerConfigsCacheSize();
+    method public int getInMemoryServerConfigsCacheSize();
+    method public boolean hasInMemoryServerConfigsCacheSize();
   }
 
   public static final class QuicOptions.Builder {