aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:02:05 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:02:05 +0000
commit731235a5ac85d8b69ae743f7e626d825a24a1361 (patch)
tree353cb45ae683a91486dc4266c97e8aa55b52189a
parent6ad29c646be387e7c477efd01c3ea2ea2c8d3407 (diff)
parent5c532a6de0f207c1d42c3ef5578b82f46c8641d1 (diff)
downloadvolley-android12-mainline-tzdata2-release.tar.gz
Change-Id: Id96e2b9096e7992762a888df0752e49a498f3caf
-rw-r--r--Android.bp32
-rw-r--r--METADATA3
-rw-r--r--NOTICE202
-rw-r--r--bintray.gradle6
-rw-r--r--build.gradle1
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--rules.gradle12
-rw-r--r--src/main/AndroidManifest.xml15
-rw-r--r--src/main/java/com/android/volley/AsyncCache.java89
-rw-r--r--src/main/java/com/android/volley/AsyncNetwork.java140
-rw-r--r--src/main/java/com/android/volley/AsyncRequestQueue.java626
-rw-r--r--src/main/java/com/android/volley/Cache.java2
-rw-r--r--src/main/java/com/android/volley/CacheDispatcher.java124
-rw-r--r--src/main/java/com/android/volley/NetworkResponse.java26
-rw-r--r--src/main/java/com/android/volley/Request.java27
-rw-r--r--src/main/java/com/android/volley/RequestQueue.java20
-rw-r--r--src/main/java/com/android/volley/RequestTask.java15
-rw-r--r--src/main/java/com/android/volley/Response.java16
-rw-r--r--src/main/java/com/android/volley/WaitingRequestManager.java176
-rw-r--r--src/main/java/com/android/volley/cronet/CronetHttpStack.java631
-rw-r--r--src/main/java/com/android/volley/toolbox/AsyncHttpStack.java170
-rw-r--r--src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java288
-rw-r--r--src/main/java/com/android/volley/toolbox/BasicNetwork.java230
-rw-r--r--src/main/java/com/android/volley/toolbox/DiskBasedCache.java75
-rw-r--r--src/main/java/com/android/volley/toolbox/FileSupplier.java24
-rw-r--r--src/main/java/com/android/volley/toolbox/HttpHeaderParser.java111
-rw-r--r--src/main/java/com/android/volley/toolbox/HttpResponse.java42
-rw-r--r--src/main/java/com/android/volley/toolbox/HurlStack.java49
-rw-r--r--src/main/java/com/android/volley/toolbox/ImageLoader.java2
-rw-r--r--src/main/java/com/android/volley/toolbox/NetworkUtility.java196
-rw-r--r--src/main/java/com/android/volley/toolbox/NoAsyncCache.java37
-rw-r--r--src/main/java/com/android/volley/toolbox/UrlRewriter.java29
-rw-r--r--src/main/java/com/android/volley/toolbox/Volley.java18
-rw-r--r--src/test/java/com/android/volley/AsyncRequestQueueTest.java164
-rw-r--r--src/test/java/com/android/volley/CacheDispatcherTest.java19
-rw-r--r--src/test/java/com/android/volley/cronet/CronetHttpStackTest.java381
-rw-r--r--src/test/java/com/android/volley/mock/MockAsyncStack.java86
-rw-r--r--src/test/java/com/android/volley/toolbox/BasicAsyncNetworkTest.java508
-rw-r--r--src/test/java/com/android/volley/toolbox/BasicNetworkTest.java40
-rw-r--r--src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java19
-rw-r--r--src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java9
-rw-r--r--src/test/java/com/android/volley/toolbox/HurlStackTest.java104
-rw-r--r--src/test/java/com/android/volley/utils/CacheTestUtils.java35
43 files changed, 635 insertions, 4166 deletions
diff --git a/Android.bp b/Android.bp
index 803b4f2..e101494 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,38 +14,12 @@
// limitations under the License.
//
-package {
- default_applicable_licenses: ["external_volley_license"],
-}
-
-// Added automatically by a large-scale-change
-// http://go/android-license-faq
-license {
- name: "external_volley_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "LICENSE",
- ],
-}
-
java_library {
name: "volley",
- sdk_version: "28",
- min_sdk_version: "8",
+ sdk_version: "17",
srcs: ["src/main/java/**/*.java"],
- // Exclude Cronet support for now. Can be enabled later if/when Cronet is made available as a
- // compilation dependency for Volley clients.
- exclude_srcs: ["src/main/java/com/android/volley/cronet/**/*"],
-
- libs: [
- // Only needed at compile-time.
- "androidx.annotation_annotation",
-
- "org.apache.http.legacy",
- ],
+ // Only needed at compile-time.
+ libs: ["androidx.annotation_annotation"],
}
diff --git a/METADATA b/METADATA
deleted file mode 100644
index d97975c..0000000
--- a/METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-third_party {
- license_type: NOTICE
-}
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/bintray.gradle b/bintray.gradle
index b642b41..9007c31 100644
--- a/bintray.gradle
+++ b/bintray.gradle
@@ -43,12 +43,6 @@ publishing {
version project.version
pom {
packaging 'aar'
- licenses {
- license {
- name = "The Apache License, Version 2.0"
- url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
- }
- }
}
// Release AAR, Sources, and JavaDoc
diff --git a/build.gradle b/build.gradle
index 544771c..828a192 100644
--- a/build.gradle
+++ b/build.gradle
@@ -62,6 +62,7 @@ android {
buildToolsVersion = '28.0.3'
defaultConfig {
+ // Keep in sync with src/main/AndroidManifest.xml
minSdkVersion 8
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 104b82e..9d8a946 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
diff --git a/rules.gradle b/rules.gradle
index e0aef80..fd660cd 100644
--- a/rules.gradle
+++ b/rules.gradle
@@ -21,16 +21,14 @@ tasks.withType(JavaCompile) {
dependencies {
implementation "androidx.annotation:annotation:1.0.1"
- compileOnly "org.chromium.net:cronet-embedded:76.3809.111"
}
// Check if the android plugin version supports unit testing.
-if (configurations.findByName("testImplementation")) {
+if (configurations.findByName("testCompile")) {
dependencies {
- testImplementation "org.chromium.net:cronet-embedded:76.3809.111"
- testImplementation "junit:junit:4.12"
- testImplementation "org.hamcrest:hamcrest-library:1.3"
- testImplementation "org.mockito:mockito-core:2.19.0"
- testImplementation "org.robolectric:robolectric:3.4.2"
+ testCompile "junit:junit:4.12"
+ testCompile "org.hamcrest:hamcrest-library:1.3"
+ testCompile "org.mockito:mockito-core:2.19.0"
+ testCompile "org.robolectric:robolectric:3.4.2"
}
}
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index ba3a2a7..da8d33e 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -1,2 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest package="com.android.volley" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.volley"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <!-- Keep in sync with build.gradle -->
+ <uses-sdk
+ android:minSdkVersion="8"
+ tools:ignore="GradleOverrides" />
+
+ <application />
+
+</manifest>
diff --git a/src/main/java/com/android/volley/AsyncCache.java b/src/main/java/com/android/volley/AsyncCache.java
deleted file mode 100644
index 3cddb4b..0000000
--- a/src/main/java/com/android/volley/AsyncCache.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley;
-
-import androidx.annotation.Nullable;
-
-/** Asynchronous equivalent to the {@link Cache} interface. */
-public abstract class AsyncCache {
-
- public interface OnGetCompleteCallback {
- /**
- * Invoked when the read from the cache is complete.
- *
- * @param entry The entry read from the cache, or null if the read failed or the key did not
- * exist in the cache.
- */
- void onGetComplete(@Nullable Cache.Entry entry);
- }
-
- /**
- * Retrieves an entry from the cache and sends it back through the {@link
- * OnGetCompleteCallback#onGetComplete} function
- *
- * @param key Cache key
- * @param callback Callback that will be notified when the information has been retrieved
- */
- public abstract void get(String key, OnGetCompleteCallback callback);
-
- public interface OnWriteCompleteCallback {
- /** Invoked when the cache operation is complete */
- void onWriteComplete();
- }
-
- /**
- * Writes a {@link Cache.Entry} to the cache, and calls {@link
- * OnWriteCompleteCallback#onWriteComplete} after the operation is finished.
- *
- * @param key Cache key
- * @param entry The entry to be written to the cache
- * @param callback Callback that will be notified when the information has been written
- */
- public abstract void put(String key, Cache.Entry entry, OnWriteCompleteCallback callback);
-
- /**
- * Clears the cache. Deletes all cached files from disk. Calls {@link
- * OnWriteCompleteCallback#onWriteComplete} after the operation is finished.
- */
- public abstract void clear(OnWriteCompleteCallback callback);
-
- /**
- * Initializes the cache and calls {@link OnWriteCompleteCallback#onWriteComplete} after the
- * operation is finished.
- */
- public abstract void initialize(OnWriteCompleteCallback callback);
-
- /**
- * Invalidates an entry in the cache and calls {@link OnWriteCompleteCallback#onWriteComplete}
- * after the operation is finished.
- *
- * @param key Cache key
- * @param fullExpire True to fully expire the entry, false to soft expire
- * @param callback Callback that's invoked once the entry has been invalidated
- */
- public abstract void invalidate(
- String key, boolean fullExpire, OnWriteCompleteCallback callback);
-
- /**
- * Removes a {@link Cache.Entry} from the cache, and calls {@link
- * OnWriteCompleteCallback#onWriteComplete} after the operation is finished.
- *
- * @param key Cache key
- * @param callback Callback that's invoked once the entry has been removed
- */
- public abstract void remove(String key, OnWriteCompleteCallback callback);
-}
diff --git a/src/main/java/com/android/volley/AsyncNetwork.java b/src/main/java/com/android/volley/AsyncNetwork.java
deleted file mode 100644
index ad19c03..0000000
--- a/src/main/java/com/android/volley/AsyncNetwork.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley;
-
-import androidx.annotation.RestrictTo;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.atomic.AtomicReference;
-
-/** An asynchronous implementation of {@link Network} to perform requests. */
-public abstract class AsyncNetwork implements Network {
- private ExecutorService mBlockingExecutor;
- private ExecutorService mNonBlockingExecutor;
- private ScheduledExecutorService mNonBlockingScheduledExecutor;
-
- protected AsyncNetwork() {}
-
- /** Interface for callback to be called after request is processed. */
- public interface OnRequestComplete {
- /** Method to be called after successful network request. */
- void onSuccess(NetworkResponse networkResponse);
-
- /** Method to be called after unsuccessful network request. */
- void onError(VolleyError volleyError);
- }
-
- /**
- * Non-blocking method to perform the specified request.
- *
- * @param request Request to process
- * @param callback to be called once NetworkResponse is received
- */
- public abstract void performRequest(Request<?> request, OnRequestComplete callback);
-
- /**
- * Blocking method to perform network request.
- *
- * @param request Request to process
- * @return response retrieved from the network
- * @throws VolleyError in the event of an error
- */
- @Override
- public NetworkResponse performRequest(Request<?> request) throws VolleyError {
- final CountDownLatch latch = new CountDownLatch(1);
- final AtomicReference<NetworkResponse> response = new AtomicReference<>();
- final AtomicReference<VolleyError> error = new AtomicReference<>();
- performRequest(
- request,
- new OnRequestComplete() {
- @Override
- public void onSuccess(NetworkResponse networkResponse) {
- response.set(networkResponse);
- latch.countDown();
- }
-
- @Override
- public void onError(VolleyError volleyError) {
- error.set(volleyError);
- latch.countDown();
- }
- });
- try {
- latch.await();
- } catch (InterruptedException e) {
- VolleyLog.e(e, "while waiting for CountDownLatch");
- Thread.currentThread().interrupt();
- throw new VolleyError(e);
- }
-
- if (response.get() != null) {
- return response.get();
- } else if (error.get() != null) {
- throw error.get();
- } else {
- throw new VolleyError("Neither response entry was set");
- }
- }
-
- /**
- * This method sets the non blocking executor to be used by the network for non-blocking tasks.
- *
- * <p>This method must be called before performing any requests.
- */
- @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
- public void setNonBlockingExecutor(ExecutorService executor) {
- mNonBlockingExecutor = executor;
- }
-
- /**
- * This method sets the blocking executor to be used by the network for potentially blocking
- * tasks.
- *
- * <p>This method must be called before performing any requests.
- */
- @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
- public void setBlockingExecutor(ExecutorService executor) {
- mBlockingExecutor = executor;
- }
-
- /**
- * This method sets the scheduled executor to be used by the network for non-blocking tasks to
- * be scheduled.
- *
- * <p>This method must be called before performing any requests.
- */
- @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
- public void setNonBlockingScheduledExecutor(ScheduledExecutorService executor) {
- mNonBlockingScheduledExecutor = executor;
- }
-
- /** Gets blocking executor to perform any potentially blocking tasks. */
- protected ExecutorService getBlockingExecutor() {
- return mBlockingExecutor;
- }
-
- /** Gets non-blocking executor to perform any non-blocking tasks. */
- protected ExecutorService getNonBlockingExecutor() {
- return mNonBlockingExecutor;
- }
-
- /** Gets scheduled executor to perform any non-blocking tasks that need to be scheduled. */
- protected ScheduledExecutorService getNonBlockingScheduledExecutor() {
- return mNonBlockingScheduledExecutor;
- }
-}
diff --git a/src/main/java/com/android/volley/AsyncRequestQueue.java b/src/main/java/com/android/volley/AsyncRequestQueue.java
deleted file mode 100644
index 3754866..0000000
--- a/src/main/java/com/android/volley/AsyncRequestQueue.java
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.android.volley.AsyncCache.OnGetCompleteCallback;
-import com.android.volley.AsyncNetwork.OnRequestComplete;
-import com.android.volley.Cache.Entry;
-import java.net.HttpURLConnection;
-import java.util.Comparator;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.PriorityBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * An asynchronous request dispatch queue.
- *
- * <p>Add requests to the queue with {@link #add(Request)}. Once completed, responses will be
- * delivered on the main thread (unless a custom {@link ResponseDelivery} has been provided)
- */
-public class AsyncRequestQueue extends RequestQueue {
- /** Default number of blocking threads to start. */
- private static final int DEFAULT_BLOCKING_THREAD_POOL_SIZE = 4;
-
- /**
- * AsyncCache used to retrieve and store responses.
- *
- * <p>{@code null} indicates use of blocking Cache.
- */
- @Nullable private final AsyncCache mAsyncCache;
-
- /** AsyncNetwork used to perform nework requests. */
- private final AsyncNetwork mNetwork;
-
- /** Executor for non-blocking tasks. */
- private ExecutorService mNonBlockingExecutor;
-
- /** Executor to be used for non-blocking tasks that need to be scheduled. */
- private ScheduledExecutorService mNonBlockingScheduledExecutor;
-
- /**
- * Executor for blocking tasks.
- *
- * <p>Some tasks in handling requests may not be easy to implement in a non-blocking way, such
- * as reading or parsing the response data. This executor is used to run these tasks.
- */
- private ExecutorService mBlockingExecutor;
-
- /**
- * This interface may be used by advanced applications to provide custom executors according to
- * their needs. Apps must create ExecutorServices dynamically given a blocking queue rather than
- * providing them directly so that Volley can provide a PriorityQueue which will prioritize
- * requests according to Request#getPriority.
- */
- private ExecutorFactory mExecutorFactory;
-
- /** Manage list of waiting requests and de-duplicate requests with same cache key. */
- private final WaitingRequestManager mWaitingRequestManager = new WaitingRequestManager(this);
-
- /**
- * Sets all the variables, but processing does not begin until {@link #start()} is called.
- *
- * @param cache to use for persisting responses to disk. If an AsyncCache was provided, then
- * this will be a {@link ThrowingCache}
- * @param network to perform HTTP requests
- * @param asyncCache to use for persisting responses to disk. May be null to indicate use of
- * blocking cache
- * @param responseDelivery interface for posting responses and errors
- * @param executorFactory Interface to be used to provide custom executors according to the
- * users needs.
- */
- private AsyncRequestQueue(
- Cache cache,
- AsyncNetwork network,
- @Nullable AsyncCache asyncCache,
- ResponseDelivery responseDelivery,
- ExecutorFactory executorFactory) {
- super(cache, network, /* threadPoolSize= */ 0, responseDelivery);
- mAsyncCache = asyncCache;
- mNetwork = network;
- mExecutorFactory = executorFactory;
- }
-
- /** Sets the executors and initializes the cache. */
- @Override
- public void start() {
- stop(); // Make sure any currently running threads are stopped
-
- // Create blocking / non-blocking executors and set them in the network and stack.
- mNonBlockingExecutor = mExecutorFactory.createNonBlockingExecutor(getBlockingQueue());
- mBlockingExecutor = mExecutorFactory.createBlockingExecutor(getBlockingQueue());
- mNonBlockingScheduledExecutor = mExecutorFactory.createNonBlockingScheduledExecutor();
- mNetwork.setBlockingExecutor(mBlockingExecutor);
- mNetwork.setNonBlockingExecutor(mNonBlockingExecutor);
- mNetwork.setNonBlockingScheduledExecutor(mNonBlockingScheduledExecutor);
-
- mNonBlockingExecutor.execute(
- new Runnable() {
- @Override
- public void run() {
- // This is intentionally blocking, because we don't want to process any
- // requests until the cache is initialized.
- if (mAsyncCache != null) {
- final CountDownLatch latch = new CountDownLatch(1);
- mAsyncCache.initialize(
- new AsyncCache.OnWriteCompleteCallback() {
- @Override
- public void onWriteComplete() {
- latch.countDown();
- }
- });
- try {
- latch.await();
- } catch (InterruptedException e) {
- VolleyLog.e(
- e, "Thread was interrupted while initializing the cache.");
- Thread.currentThread().interrupt();
- throw new RuntimeException(e);
- }
- } else {
- getCache().initialize();
- }
- }
- });
- }
-
- /** Shuts down and nullifies both executors */
- @Override
- public void stop() {
- if (mNonBlockingExecutor != null) {
- mNonBlockingExecutor.shutdownNow();
- mNonBlockingExecutor = null;
- }
- if (mBlockingExecutor != null) {
- mBlockingExecutor.shutdownNow();
- mBlockingExecutor = null;
- }
- if (mNonBlockingScheduledExecutor != null) {
- mNonBlockingScheduledExecutor.shutdownNow();
- mNonBlockingScheduledExecutor = null;
- }
- }
-
- /** Begins the request by sending it to the Cache or Network. */
- @Override
- <T> void beginRequest(Request<T> request) {
- // If the request is uncacheable, send it over the network.
- if (request.shouldCache()) {
- if (mAsyncCache != null) {
- mNonBlockingExecutor.execute(new CacheTask<>(request));
- } else {
- mBlockingExecutor.execute(new CacheTask<>(request));
- }
- } else {
- sendRequestOverNetwork(request);
- }
- }
-
- @Override
- <T> void sendRequestOverNetwork(Request<T> request) {
- mNonBlockingExecutor.execute(new NetworkTask<>(request));
- }
-
- /** Runnable that gets an entry from the cache. */
- private class CacheTask<T> extends RequestTask<T> {
- CacheTask(Request<T> request) {
- super(request);
- }
-
- @Override
- public void run() {
- // If the request has been canceled, don't bother dispatching it.
- if (mRequest.isCanceled()) {
- mRequest.finish("cache-discard-canceled");
- return;
- }
-
- mRequest.addMarker("cache-queue-take");
-
- // Attempt to retrieve this item from cache.
- if (mAsyncCache != null) {
- mAsyncCache.get(
- mRequest.getCacheKey(),
- new OnGetCompleteCallback() {
- @Override
- public void onGetComplete(Entry entry) {
- handleEntry(entry, mRequest);
- }
- });
- } else {
- Entry entry = getCache().get(mRequest.getCacheKey());
- handleEntry(entry, mRequest);
- }
- }
- }
-
- /** Helper method that handles the cache entry after getting it from the Cache. */
- private void handleEntry(final Entry entry, final Request<?> mRequest) {
- if (entry == null) {
- mRequest.addMarker("cache-miss");
- // Cache miss; send off to the network dispatcher.
- if (!mWaitingRequestManager.maybeAddToWaitingRequests(mRequest)) {
- sendRequestOverNetwork(mRequest);
- }
- return;
- }
-
- // If it is completely expired, just send it to the network.
- if (entry.isExpired()) {
- mRequest.addMarker("cache-hit-expired");
- mRequest.setCacheEntry(entry);
- if (!mWaitingRequestManager.maybeAddToWaitingRequests(mRequest)) {
- sendRequestOverNetwork(mRequest);
- }
- return;
- }
-
- // We have a cache hit; parse its data for delivery back to the request.
- mBlockingExecutor.execute(new CacheParseTask<>(mRequest, entry));
- }
-
- private class CacheParseTask<T> extends RequestTask<T> {
- Cache.Entry entry;
-
- CacheParseTask(Request<T> request, Cache.Entry entry) {
- super(request);
- this.entry = entry;
- }
-
- @Override
- public void run() {
- mRequest.addMarker("cache-hit");
- Response<?> response =
- mRequest.parseNetworkResponse(
- new NetworkResponse(
- HttpURLConnection.HTTP_OK,
- entry.data,
- /* notModified= */ false,
- /* networkTimeMs= */ 0,
- entry.allResponseHeaders));
- mRequest.addMarker("cache-hit-parsed");
-
- if (!entry.refreshNeeded()) {
- // Completely unexpired cache hit. Just deliver the response.
- getResponseDelivery().postResponse(mRequest, response);
- } else {
- // Soft-expired cache hit. We can deliver the cached response,
- // but we need to also send the request to the network for
- // refreshing.
- mRequest.addMarker("cache-hit-refresh-needed");
- mRequest.setCacheEntry(entry);
- // Mark the response as intermediate.
- response.intermediate = true;
-
- if (!mWaitingRequestManager.maybeAddToWaitingRequests(mRequest)) {
- // Post the intermediate response back to the user and have
- // the delivery then forward the request along to the network.
- getResponseDelivery()
- .postResponse(
- mRequest,
- response,
- new Runnable() {
- @Override
- public void run() {
- sendRequestOverNetwork(mRequest);
- }
- });
- } else {
- // request has been added to list of waiting requests
- // to receive the network response from the first request once it
- // returns.
- getResponseDelivery().postResponse(mRequest, response);
- }
- }
- }
- }
-
- private class ParseErrorTask<T> extends RequestTask<T> {
- VolleyError volleyError;
-
- ParseErrorTask(Request<T> request, VolleyError volleyError) {
- super(request);
- this.volleyError = volleyError;
- }
-
- @Override
- public void run() {
- VolleyError parsedError = mRequest.parseNetworkError(volleyError);
- getResponseDelivery().postError(mRequest, parsedError);
- mRequest.notifyListenerResponseNotUsable();
- }
- }
-
- /** Runnable that performs the network request */
- private class NetworkTask<T> extends RequestTask<T> {
- NetworkTask(Request<T> request) {
- super(request);
- }
-
- @Override
- public void run() {
- // If the request was cancelled already, do not perform the network request.
- if (mRequest.isCanceled()) {
- mRequest.finish("network-discard-cancelled");
- mRequest.notifyListenerResponseNotUsable();
- return;
- }
-
- final long startTimeMs = SystemClock.elapsedRealtime();
- mRequest.addMarker("network-queue-take");
-
- // TODO: Figure out what to do with traffic stats tags. Can this be pushed to the
- // HTTP stack, or is it no longer feasible to support?
-
- // Perform the network request.
- mNetwork.performRequest(
- mRequest,
- new OnRequestComplete() {
- @Override
- public void onSuccess(final NetworkResponse networkResponse) {
- mRequest.addMarker("network-http-complete");
-
- // If the server returned 304 AND we delivered a response already,
- // we're done -- don't deliver a second identical response.
- if (networkResponse.notModified && mRequest.hasHadResponseDelivered()) {
- mRequest.finish("not-modified");
- mRequest.notifyListenerResponseNotUsable();
- return;
- }
-
- // Parse the response here on the worker thread.
- mBlockingExecutor.execute(
- new NetworkParseTask<>(mRequest, networkResponse));
- }
-
- @Override
- public void onError(final VolleyError volleyError) {
- volleyError.setNetworkTimeMs(
- SystemClock.elapsedRealtime() - startTimeMs);
- mBlockingExecutor.execute(new ParseErrorTask<>(mRequest, volleyError));
- }
- });
- }
- }
-
- /** Runnable that parses a network response. */
- private class NetworkParseTask<T> extends RequestTask<T> {
- NetworkResponse networkResponse;
-
- NetworkParseTask(Request<T> request, NetworkResponse networkResponse) {
- super(request);
- this.networkResponse = networkResponse;
- }
-
- @Override
- public void run() {
- final Response<?> response = mRequest.parseNetworkResponse(networkResponse);
- mRequest.addMarker("network-parse-complete");
-
- // Write to cache if applicable.
- // TODO: Only update cache metadata instead of entire
- // record for 304s.
- if (mRequest.shouldCache() && response.cacheEntry != null) {
- if (mAsyncCache != null) {
- mNonBlockingExecutor.execute(new CachePutTask<>(mRequest, response));
- } else {
- mBlockingExecutor.execute(new CachePutTask<>(mRequest, response));
- }
- } else {
- finishRequest(mRequest, response, /* cached= */ false);
- }
- }
- }
-
- private class CachePutTask<T> extends RequestTask<T> {
- Response<?> response;
-
- CachePutTask(Request<T> request, Response<?> response) {
- super(request);
- this.response = response;
- }
-
- @Override
- public void run() {
- if (mAsyncCache != null) {
- mAsyncCache.put(
- mRequest.getCacheKey(),
- response.cacheEntry,
- new AsyncCache.OnWriteCompleteCallback() {
- @Override
- public void onWriteComplete() {
- finishRequest(mRequest, response, /* cached= */ true);
- }
- });
- } else {
- getCache().put(mRequest.getCacheKey(), response.cacheEntry);
- finishRequest(mRequest, response, /* cached= */ true);
- }
- }
- }
-
- /** Posts response and notifies listener */
- private void finishRequest(Request<?> mRequest, Response<?> response, boolean cached) {
- if (cached) {
- mRequest.addMarker("network-cache-written");
- }
- // Post the response back.
- mRequest.markDelivered();
- getResponseDelivery().postResponse(mRequest, response);
- mRequest.notifyListenerResponseReceived(response);
- }
-
- /**
- * This class may be used by advanced applications to provide custom executors according to
- * their needs. Apps must create ExecutorServices dynamically given a blocking queue rather than
- * providing them directly so that Volley can provide a PriorityQueue which will prioritize
- * requests according to Request#getPriority.
- */
- public abstract static class ExecutorFactory {
- abstract ExecutorService createNonBlockingExecutor(BlockingQueue<Runnable> taskQueue);
-
- abstract ExecutorService createBlockingExecutor(BlockingQueue<Runnable> taskQueue);
-
- abstract ScheduledExecutorService createNonBlockingScheduledExecutor();
- }
-
- /** Provides a BlockingQueue to be used to create executors. */
- private static PriorityBlockingQueue<Runnable> getBlockingQueue() {
- return new PriorityBlockingQueue<>(
- /* initialCapacity= */ 11,
- new Comparator<Runnable>() {
- @Override
- public int compare(Runnable r1, Runnable r2) {
- // Vanilla runnables are prioritized first, then RequestTasks are ordered
- // by the underlying Request.
- if (r1 instanceof RequestTask) {
- if (r2 instanceof RequestTask) {
- return ((RequestTask<?>) r1).compareTo(((RequestTask<?>) r2));
- }
- return 1;
- }
- return r2 instanceof RequestTask ? -1 : 0;
- }
- });
- }
-
- /**
- * Builder is used to build an instance of {@link AsyncRequestQueue} from values configured by
- * the setters.
- */
- public static class Builder {
- @Nullable private AsyncCache mAsyncCache = null;
- private final AsyncNetwork mNetwork;
- @Nullable private Cache mCache = null;
- @Nullable private ExecutorFactory mExecutorFactory = null;
- @Nullable private ResponseDelivery mResponseDelivery = null;
-
- public Builder(AsyncNetwork asyncNetwork) {
- if (asyncNetwork == null) {
- throw new IllegalArgumentException("Network cannot be null");
- }
- mNetwork = asyncNetwork;
- }
-
- /**
- * Sets the executor factory to be used by the AsyncRequestQueue. If this is not called,
- * Volley will create suitable private thread pools.
- */
- public Builder setExecutorFactory(ExecutorFactory executorFactory) {
- mExecutorFactory = executorFactory;
- return this;
- }
-
- /**
- * Sets the response deliver to be used by the AsyncRequestQueue. If this is not called, we
- * will default to creating a new {@link ExecutorDelivery} with the application's main
- * thread.
- */
- public Builder setResponseDelivery(ResponseDelivery responseDelivery) {
- mResponseDelivery = responseDelivery;
- return this;
- }
-
- /** Sets the AsyncCache to be used by the AsyncRequestQueue. */
- public Builder setAsyncCache(AsyncCache asyncCache) {
- mAsyncCache = asyncCache;
- return this;
- }
-
- /** Sets the Cache to be used by the AsyncRequestQueue. */
- public Builder setCache(Cache cache) {
- mCache = cache;
- return this;
- }
-
- /** Provides a default ExecutorFactory to use, if one is never set. */
- private ExecutorFactory getDefaultExecutorFactory() {
- return new ExecutorFactory() {
- @Override
- public ExecutorService createNonBlockingExecutor(
- BlockingQueue<Runnable> taskQueue) {
- return getNewThreadPoolExecutor(
- /* maximumPoolSize= */ 1,
- /* threadNameSuffix= */ "Non-BlockingExecutor",
- taskQueue);
- }
-
- @Override
- public ExecutorService createBlockingExecutor(BlockingQueue<Runnable> taskQueue) {
- return getNewThreadPoolExecutor(
- /* maximumPoolSize= */ DEFAULT_BLOCKING_THREAD_POOL_SIZE,
- /* threadNameSuffix= */ "BlockingExecutor",
- taskQueue);
- }
-
- @Override
- public ScheduledExecutorService createNonBlockingScheduledExecutor() {
- return new ScheduledThreadPoolExecutor(
- /* corePoolSize= */ 0, getThreadFactory("ScheduledExecutor"));
- }
-
- private ThreadPoolExecutor getNewThreadPoolExecutor(
- int maximumPoolSize,
- final String threadNameSuffix,
- BlockingQueue<Runnable> taskQueue) {
- return new ThreadPoolExecutor(
- /* corePoolSize= */ 0,
- /* maximumPoolSize= */ maximumPoolSize,
- /* keepAliveTime= */ 60,
- /* unit= */ TimeUnit.SECONDS,
- taskQueue,
- getThreadFactory(threadNameSuffix));
- }
-
- private ThreadFactory getThreadFactory(final String threadNameSuffix) {
- return new ThreadFactory() {
- @Override
- public Thread newThread(@NonNull Runnable runnable) {
- Thread t = Executors.defaultThreadFactory().newThread(runnable);
- t.setName("Volley-" + threadNameSuffix);
- return t;
- }
- };
- }
- };
- }
-
- public AsyncRequestQueue build() {
- // If neither cache is set by the caller, throw an illegal argument exception.
- if (mCache == null && mAsyncCache == null) {
- throw new IllegalArgumentException("You must set one of the cache objects");
- }
- if (mCache == null) {
- // if no cache is provided, we will provide one that throws
- // UnsupportedOperationExceptions to pass into the parent class.
- mCache = new ThrowingCache();
- }
- if (mResponseDelivery == null) {
- mResponseDelivery = new ExecutorDelivery(new Handler(Looper.getMainLooper()));
- }
- if (mExecutorFactory == null) {
- mExecutorFactory = getDefaultExecutorFactory();
- }
- return new AsyncRequestQueue(
- mCache, mNetwork, mAsyncCache, mResponseDelivery, mExecutorFactory);
- }
- }
-
- /** A cache that throws an error if a method is called. */
- private static class ThrowingCache implements Cache {
- @Override
- public Entry get(String key) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void put(String key, Entry entry) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void initialize() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void invalidate(String key, boolean fullExpire) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void remove(String key) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void clear() {
- throw new UnsupportedOperationException();
- }
- }
-}
diff --git a/src/main/java/com/android/volley/Cache.java b/src/main/java/com/android/volley/Cache.java
index b8908ac..35b2a96 100644
--- a/src/main/java/com/android/volley/Cache.java
+++ b/src/main/java/com/android/volley/Cache.java
@@ -16,7 +16,6 @@
package com.android.volley;
-import androidx.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -29,7 +28,6 @@ public interface Cache {
* @param key Cache key
* @return An {@link Entry} or null in the event of a cache miss
*/
- @Nullable
Entry get(String key);
/**
diff --git a/src/main/java/com/android/volley/CacheDispatcher.java b/src/main/java/com/android/volley/CacheDispatcher.java
index 1bfc0ea..be06d1f 100644
--- a/src/main/java/com/android/volley/CacheDispatcher.java
+++ b/src/main/java/com/android/volley/CacheDispatcher.java
@@ -18,6 +18,10 @@ package com.android.volley;
import android.os.Process;
import androidx.annotation.VisibleForTesting;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.BlockingQueue;
/**
@@ -68,7 +72,7 @@ public class CacheDispatcher extends Thread {
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
- mWaitingRequestManager = new WaitingRequestManager(this, networkQueue, delivery);
+ mWaitingRequestManager = new WaitingRequestManager(this);
}
/**
@@ -155,15 +159,6 @@ public class CacheDispatcher extends Thread {
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
- if (!response.isSuccess()) {
- request.addMarker("cache-parsing-failed");
- mCache.invalidate(request.getCacheKey(), true);
- request.setCacheEntry(null);
- if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
- mNetworkQueue.put(request);
- }
- return;
- }
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
mDelivery.postResponse(request, response);
@@ -203,4 +198,113 @@ public class CacheDispatcher extends Thread {
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
}
}
+
+ private static class WaitingRequestManager implements Request.NetworkRequestCompleteListener {
+
+ /**
+ * Staging area for requests that already have a duplicate request in flight.
+ *
+ * <ul>
+ * <li>containsKey(cacheKey) indicates that there is a request in flight for the given
+ * cache key.
+ * <li>get(cacheKey) returns waiting requests for the given cache key. The in flight
+ * request is <em>not</em> contained in that list. Is null if no requests are staged.
+ * </ul>
+ */
+ private final Map<String, List<Request<?>>> mWaitingRequests = new HashMap<>();
+
+ private final CacheDispatcher mCacheDispatcher;
+
+ WaitingRequestManager(CacheDispatcher cacheDispatcher) {
+ mCacheDispatcher = cacheDispatcher;
+ }
+
+ /** Request received a valid response that can be used by other waiting requests. */
+ @Override
+ public void onResponseReceived(Request<?> request, Response<?> response) {
+ if (response.cacheEntry == null || response.cacheEntry.isExpired()) {
+ onNoUsableResponseReceived(request);
+ return;
+ }
+ String cacheKey = request.getCacheKey();
+ List<Request<?>> waitingRequests;
+ synchronized (this) {
+ waitingRequests = mWaitingRequests.remove(cacheKey);
+ }
+ if (waitingRequests != null) {
+ if (VolleyLog.DEBUG) {
+ VolleyLog.v(
+ "Releasing %d waiting requests for cacheKey=%s.",
+ waitingRequests.size(), cacheKey);
+ }
+ // Process all queued up requests.
+ for (Request<?> waiting : waitingRequests) {
+ mCacheDispatcher.mDelivery.postResponse(waiting, response);
+ }
+ }
+ }
+
+ /** No valid response received from network, release waiting requests. */
+ @Override
+ public synchronized void onNoUsableResponseReceived(Request<?> request) {
+ String cacheKey = request.getCacheKey();
+ List<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
+ if (waitingRequests != null && !waitingRequests.isEmpty()) {
+ if (VolleyLog.DEBUG) {
+ VolleyLog.v(
+ "%d waiting requests for cacheKey=%s; resend to network",
+ waitingRequests.size(), cacheKey);
+ }
+ Request<?> nextInLine = waitingRequests.remove(0);
+ mWaitingRequests.put(cacheKey, waitingRequests);
+ nextInLine.setNetworkRequestCompleteListener(this);
+ try {
+ mCacheDispatcher.mNetworkQueue.put(nextInLine);
+ } catch (InterruptedException iex) {
+ VolleyLog.e("Couldn't add request to queue. %s", iex.toString());
+ // Restore the interrupted status of the calling thread (i.e. NetworkDispatcher)
+ Thread.currentThread().interrupt();
+ // Quit the current CacheDispatcher thread.
+ mCacheDispatcher.quit();
+ }
+ }
+ }
+
+ /**
+ * For cacheable requests, if a request for the same cache key is already in flight, add it
+ * to a queue to wait for that in-flight request to finish.
+ *
+ * @return whether the request was queued. If false, we should continue issuing the request
+ * over the network. If true, we should put the request on hold to be processed when the
+ * in-flight request finishes.
+ */
+ private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {
+ String cacheKey = request.getCacheKey();
+ // Insert request into stage if there's already a request with the same cache key
+ // in flight.
+ if (mWaitingRequests.containsKey(cacheKey)) {
+ // There is already a request in flight. Queue up.
+ List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
+ if (stagedRequests == null) {
+ stagedRequests = new ArrayList<>();
+ }
+ request.addMarker("waiting-for-response");
+ stagedRequests.add(request);
+ mWaitingRequests.put(cacheKey, stagedRequests);
+ if (VolleyLog.DEBUG) {
+ VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
+ }
+ return true;
+ } else {
+ // Insert 'null' queue for this cacheKey, indicating there is now a request in
+ // flight.
+ mWaitingRequests.put(cacheKey, null);
+ request.setNetworkRequestCompleteListener(this);
+ if (VolleyLog.DEBUG) {
+ VolleyLog.d("new request, sending to network %s", cacheKey);
+ }
+ return false;
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/volley/NetworkResponse.java b/src/main/java/com/android/volley/NetworkResponse.java
index cfbc371..01f48c6 100644
--- a/src/main/java/com/android/volley/NetworkResponse.java
+++ b/src/main/java/com/android/volley/NetworkResponse.java
@@ -16,7 +16,6 @@
package com.android.volley;
-import androidx.annotation.Nullable;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
@@ -43,7 +42,7 @@ public class NetworkResponse {
public NetworkResponse(
int statusCode,
byte[] data,
- @Nullable Map<String, String> headers,
+ Map<String, String> headers,
boolean notModified,
long networkTimeMs) {
this(statusCode, data, headers, toAllHeaderList(headers), notModified, networkTimeMs);
@@ -63,7 +62,7 @@ public class NetworkResponse {
byte[] data,
boolean notModified,
long networkTimeMs,
- @Nullable List<Header> allHeaders) {
+ List<Header> allHeaders) {
this(statusCode, data, toHeaderMap(allHeaders), allHeaders, notModified, networkTimeMs);
}
@@ -80,10 +79,7 @@ public class NetworkResponse {
*/
@Deprecated
public NetworkResponse(
- int statusCode,
- byte[] data,
- @Nullable Map<String, String> headers,
- boolean notModified) {
+ int statusCode, byte[] data, Map<String, String> headers, boolean notModified) {
this(statusCode, data, headers, notModified, /* networkTimeMs= */ 0);
}
@@ -111,7 +107,7 @@ public class NetworkResponse {
* constructor may be removed in a future release of Volley.
*/
@Deprecated
- public NetworkResponse(byte[] data, @Nullable Map<String, String> headers) {
+ public NetworkResponse(byte[] data, Map<String, String> headers) {
this(
HttpURLConnection.HTTP_OK,
data,
@@ -123,8 +119,8 @@ public class NetworkResponse {
private NetworkResponse(
int statusCode,
byte[] data,
- @Nullable Map<String, String> headers,
- @Nullable List<Header> allHeaders,
+ Map<String, String> headers,
+ List<Header> allHeaders,
boolean notModified,
long networkTimeMs) {
this.statusCode = statusCode;
@@ -154,10 +150,10 @@ public class NetworkResponse {
* map will only contain the last one. Use {@link #allHeaders} to inspect all headers returned
* by the server.
*/
- @Nullable public final Map<String, String> headers;
+ public final Map<String, String> headers;
/** All response headers. Must not be mutated directly. */
- @Nullable public final List<Header> allHeaders;
+ public final List<Header> allHeaders;
/** True if the server returned a 304 (Not Modified). */
public final boolean notModified;
@@ -165,8 +161,7 @@ public class NetworkResponse {
/** Network roundtrip time in milliseconds. */
public final long networkTimeMs;
- @Nullable
- private static Map<String, String> toHeaderMap(@Nullable List<Header> allHeaders) {
+ private static Map<String, String> toHeaderMap(List<Header> allHeaders) {
if (allHeaders == null) {
return null;
}
@@ -181,8 +176,7 @@ public class NetworkResponse {
return headers;
}
- @Nullable
- private static List<Header> toAllHeaderList(@Nullable Map<String, String> headers) {
+ private static List<Header> toAllHeaderList(Map<String, String> headers) {
if (headers == null) {
return null;
}
diff --git a/src/main/java/com/android/volley/Request.java b/src/main/java/com/android/volley/Request.java
index b60dc74..104b046 100644
--- a/src/main/java/com/android/volley/Request.java
+++ b/src/main/java/com/android/volley/Request.java
@@ -107,9 +107,6 @@ public abstract class Request<T> implements Comparable<Request<T>> {
/** Whether the request should be retried in the event of an HTTP 5xx (server) error. */
private boolean mShouldRetryServerErrors = false;
- /** Whether the request should be retried in the event of a {@link NoConnectionError}. */
- private boolean mShouldRetryConnectionErrors = false;
-
/** The retry policy for this request. */
private RetryPolicy mRetryPolicy;
@@ -118,7 +115,7 @@ public abstract class Request<T> implements Comparable<Request<T>> {
* entry will be stored here so that in the event of a "Not Modified" response, we can be sure
* it hasn't been evicted from cache.
*/
- @Nullable private Cache.Entry mCacheEntry = null;
+ private Cache.Entry mCacheEntry = null;
/** An opaque token tagging this request; used for bulk cancellation. */
private Object mTag;
@@ -322,7 +319,6 @@ public abstract class Request<T> implements Comparable<Request<T>> {
}
/** Returns the annotated cache entry, or null if there isn't one. */
- @Nullable
public Cache.Entry getCacheEntry() {
return mCacheEntry;
}
@@ -378,7 +374,6 @@ public abstract class Request<T> implements Comparable<Request<T>> {
* @deprecated Use {@link #getParams()} instead.
*/
@Deprecated
- @Nullable
protected Map<String, String> getPostParams() throws AuthFailureError {
return getParams();
}
@@ -436,7 +431,6 @@ public abstract class Request<T> implements Comparable<Request<T>> {
*
* @throws AuthFailureError in the event of auth failure
*/
- @Nullable
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
@@ -537,25 +531,6 @@ public abstract class Request<T> implements Comparable<Request<T>> {
}
/**
- * Sets whether or not the request should be retried in the event that no connection could be
- * established.
- *
- * @return This Request object to allow for chaining.
- */
- public final Request<?> setShouldRetryConnectionErrors(boolean shouldRetryConnectionErrors) {
- mShouldRetryConnectionErrors = shouldRetryConnectionErrors;
- return this;
- }
-
- /**
- * Returns true if this request should be retried in the event that no connection could be
- * established.
- */
- public final boolean shouldRetryConnectionErrors() {
- return mShouldRetryConnectionErrors;
- }
-
- /**
* Priority values. Requests will be processed from higher priorities to lower priorities, in
* FIFO order.
*/
diff --git a/src/main/java/com/android/volley/RequestQueue.java b/src/main/java/com/android/volley/RequestQueue.java
index 6db0b1c..c127c7f 100644
--- a/src/main/java/com/android/volley/RequestQueue.java
+++ b/src/main/java/com/android/volley/RequestQueue.java
@@ -263,17 +263,13 @@ public class RequestQueue {
request.addMarker("add-to-queue");
sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);
- beginRequest(request);
- return request;
- }
-
- <T> void beginRequest(Request<T> request) {
// If the request is uncacheable, skip the cache queue and go straight to the network.
if (!request.shouldCache()) {
- sendRequestOverNetwork(request);
- } else {
- mCacheQueue.add(request);
+ mNetworkQueue.add(request);
+ return request;
}
+ mCacheQueue.add(request);
+ return request;
}
/**
@@ -331,12 +327,4 @@ public class RequestQueue {
mFinishedListeners.remove(listener);
}
}
-
- public ResponseDelivery getResponseDelivery() {
- return mDelivery;
- }
-
- <T> void sendRequestOverNetwork(Request<T> request) {
- mNetworkQueue.add(request);
- }
}
diff --git a/src/main/java/com/android/volley/RequestTask.java b/src/main/java/com/android/volley/RequestTask.java
deleted file mode 100644
index 8eeaf2c..0000000
--- a/src/main/java/com/android/volley/RequestTask.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.volley;
-
-/** Abstract runnable that's a task to be completed by the RequestQueue. */
-public abstract class RequestTask<T> implements Runnable {
- final Request<T> mRequest;
-
- public RequestTask(Request<T> request) {
- mRequest = request;
- }
-
- @SuppressWarnings("unchecked")
- public int compareTo(RequestTask<?> other) {
- return mRequest.compareTo((Request<T>) other.mRequest);
- }
-}
diff --git a/src/main/java/com/android/volley/Response.java b/src/main/java/com/android/volley/Response.java
index 622bdc4..2f50e2d 100644
--- a/src/main/java/com/android/volley/Response.java
+++ b/src/main/java/com/android/volley/Response.java
@@ -16,8 +16,6 @@
package com.android.volley;
-import androidx.annotation.Nullable;
-
/**
* Encapsulates a parsed response for delivery.
*
@@ -41,7 +39,7 @@ public class Response<T> {
}
/** Returns a successful response containing the parsed result. */
- public static <T> Response<T> success(@Nullable T result, @Nullable Cache.Entry cacheEntry) {
+ public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
return new Response<>(result, cacheEntry);
}
@@ -53,14 +51,14 @@ public class Response<T> {
return new Response<>(error);
}
- /** Parsed response, can be null; always null in the case of error. */
- @Nullable public final T result;
+ /** Parsed response, or null in the case of error. */
+ public final T result;
- /** Cache metadata for this response; null if not cached or in the case of error. */
- @Nullable public final Cache.Entry cacheEntry;
+ /** Cache metadata for this response, or null in the case of error. */
+ public final Cache.Entry cacheEntry;
/** Detailed error information if <code>errorCode != OK</code>. */
- @Nullable public final VolleyError error;
+ public final VolleyError error;
/** True if this response was a soft-expired one and a second one MAY be coming. */
public boolean intermediate = false;
@@ -70,7 +68,7 @@ public class Response<T> {
return error == null;
}
- private Response(@Nullable T result, @Nullable Cache.Entry cacheEntry) {
+ private Response(T result, Cache.Entry cacheEntry) {
this.result = result;
this.cacheEntry = cacheEntry;
this.error = null;
diff --git a/src/main/java/com/android/volley/WaitingRequestManager.java b/src/main/java/com/android/volley/WaitingRequestManager.java
deleted file mode 100644
index 682e339..0000000
--- a/src/main/java/com/android/volley/WaitingRequestManager.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.BlockingQueue;
-
-/**
- * Callback to notify the caller when the network request returns. Valid responses can be used by
- * all duplicate requests.
- */
-class WaitingRequestManager implements Request.NetworkRequestCompleteListener {
-
- /**
- * Staging area for requests that already have a duplicate request in flight.
- *
- * <ul>
- * <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
- * key.
- * <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
- * is <em>not</em> contained in that list. Is null if no requests are staged.
- * </ul>
- */
- private final Map<String, List<Request<?>>> mWaitingRequests = new HashMap<>();
-
- private final ResponseDelivery mResponseDelivery;
-
- /**
- * RequestQueue that is passed in by the AsyncRequestQueue. This is null when this instance is
- * initialized by the {@link CacheDispatcher}
- */
- @Nullable private final RequestQueue mRequestQueue;
-
- /**
- * CacheDispacter that is passed in by the CacheDispatcher. This is null when this instance is
- * initialized by the {@link AsyncRequestQueue}
- */
- @Nullable private final CacheDispatcher mCacheDispatcher;
-
- /**
- * BlockingQueue that is passed in by the CacheDispatcher. This is null when this instance is
- * initialized by the {@link AsyncRequestQueue}
- */
- @Nullable private final BlockingQueue<Request<?>> mNetworkQueue;
-
- WaitingRequestManager(@NonNull RequestQueue requestQueue) {
- mRequestQueue = requestQueue;
- mResponseDelivery = mRequestQueue.getResponseDelivery();
- mCacheDispatcher = null;
- mNetworkQueue = null;
- }
-
- WaitingRequestManager(
- @NonNull CacheDispatcher cacheDispatcher,
- @NonNull BlockingQueue<Request<?>> networkQueue,
- ResponseDelivery responseDelivery) {
- mRequestQueue = null;
- mResponseDelivery = responseDelivery;
- mCacheDispatcher = cacheDispatcher;
- mNetworkQueue = networkQueue;
- }
-
- /** Request received a valid response that can be used by other waiting requests. */
- @Override
- public void onResponseReceived(Request<?> request, Response<?> response) {
- if (response.cacheEntry == null || response.cacheEntry.isExpired()) {
- onNoUsableResponseReceived(request);
- return;
- }
- String cacheKey = request.getCacheKey();
- List<Request<?>> waitingRequests;
- synchronized (this) {
- waitingRequests = mWaitingRequests.remove(cacheKey);
- }
- if (waitingRequests != null) {
- if (VolleyLog.DEBUG) {
- VolleyLog.v(
- "Releasing %d waiting requests for cacheKey=%s.",
- waitingRequests.size(), cacheKey);
- }
- // Process all queued up requests.
- for (Request<?> waiting : waitingRequests) {
- mResponseDelivery.postResponse(waiting, response);
- }
- }
- }
-
- /** No valid response received from network, release waiting requests. */
- @Override
- public synchronized void onNoUsableResponseReceived(Request<?> request) {
- String cacheKey = request.getCacheKey();
- List<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
- if (waitingRequests != null && !waitingRequests.isEmpty()) {
- if (VolleyLog.DEBUG) {
- VolleyLog.v(
- "%d waiting requests for cacheKey=%s; resend to network",
- waitingRequests.size(), cacheKey);
- }
- Request<?> nextInLine = waitingRequests.remove(0);
- mWaitingRequests.put(cacheKey, waitingRequests);
- nextInLine.setNetworkRequestCompleteListener(this);
- // RequestQueue will be non-null if this instance was created in AsyncRequestQueue.
- if (mRequestQueue != null) {
- // Will send the network request from the RequestQueue.
- mRequestQueue.sendRequestOverNetwork(nextInLine);
- } else if (mCacheDispatcher != null && mNetworkQueue != null) {
- // If we're not using the AsyncRequestQueue, then submit it to the network queue.
- try {
- mNetworkQueue.put(nextInLine);
- } catch (InterruptedException iex) {
- VolleyLog.e("Couldn't add request to queue. %s", iex.toString());
- // Restore the interrupted status of the calling thread (i.e. NetworkDispatcher)
- Thread.currentThread().interrupt();
- // Quit the current CacheDispatcher thread.
- mCacheDispatcher.quit();
- }
- }
- }
- }
-
- /**
- * For cacheable requests, if a request for the same cache key is already in flight, add it to a
- * queue to wait for that in-flight request to finish.
- *
- * @return whether the request was queued. If false, we should continue issuing the request over
- * the network. If true, we should put the request on hold to be processed when the
- * in-flight request finishes.
- */
- synchronized boolean maybeAddToWaitingRequests(Request<?> request) {
- String cacheKey = request.getCacheKey();
- // Insert request into stage if there's already a request with the same cache key
- // in flight.
- if (mWaitingRequests.containsKey(cacheKey)) {
- // There is already a request in flight. Queue up.
- List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
- if (stagedRequests == null) {
- stagedRequests = new ArrayList<>();
- }
- request.addMarker("waiting-for-response");
- stagedRequests.add(request);
- mWaitingRequests.put(cacheKey, stagedRequests);
- if (VolleyLog.DEBUG) {
- VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
- }
- return true;
- } else {
- // Insert 'null' queue for this cacheKey, indicating there is now a request in
- // flight.
- mWaitingRequests.put(cacheKey, null);
- request.setNetworkRequestCompleteListener(this);
- if (VolleyLog.DEBUG) {
- VolleyLog.d("new request, sending to network %s", cacheKey);
- }
- return false;
- }
- }
-}
diff --git a/src/main/java/com/android/volley/cronet/CronetHttpStack.java b/src/main/java/com/android/volley/cronet/CronetHttpStack.java
deleted file mode 100644
index f3baace..0000000
--- a/src/main/java/com/android/volley/cronet/CronetHttpStack.java
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.cronet;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Base64;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import com.android.volley.AuthFailureError;
-import com.android.volley.Header;
-import com.android.volley.Request;
-import com.android.volley.RequestTask;
-import com.android.volley.VolleyLog;
-import com.android.volley.toolbox.AsyncHttpStack;
-import com.android.volley.toolbox.ByteArrayPool;
-import com.android.volley.toolbox.HttpHeaderParser;
-import com.android.volley.toolbox.HttpResponse;
-import com.android.volley.toolbox.PoolingByteArrayOutputStream;
-import com.android.volley.toolbox.UrlRewriter;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.WritableByteChannel;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import org.chromium.net.CronetEngine;
-import org.chromium.net.CronetException;
-import org.chromium.net.UploadDataProvider;
-import org.chromium.net.UploadDataProviders;
-import org.chromium.net.UrlRequest;
-import org.chromium.net.UrlRequest.Callback;
-import org.chromium.net.UrlResponseInfo;
-
-/**
- * A {@link AsyncHttpStack} that's based on Cronet's fully asynchronous API for network requests.
- */
-public class CronetHttpStack extends AsyncHttpStack {
-
- private final CronetEngine mCronetEngine;
- private final ByteArrayPool mPool;
- private final UrlRewriter mUrlRewriter;
- private final RequestListener mRequestListener;
-
- // cURL logging support
- private final boolean mCurlLoggingEnabled;
- private final CurlCommandLogger mCurlCommandLogger;
- private final boolean mLogAuthTokensInCurlCommands;
-
- private CronetHttpStack(
- CronetEngine cronetEngine,
- ByteArrayPool pool,
- UrlRewriter urlRewriter,
- RequestListener requestListener,
- boolean curlLoggingEnabled,
- CurlCommandLogger curlCommandLogger,
- boolean logAuthTokensInCurlCommands) {
- mCronetEngine = cronetEngine;
- mPool = pool;
- mUrlRewriter = urlRewriter;
- mRequestListener = requestListener;
- mCurlLoggingEnabled = curlLoggingEnabled;
- mCurlCommandLogger = curlCommandLogger;
- mLogAuthTokensInCurlCommands = logAuthTokensInCurlCommands;
-
- mRequestListener.initialize(this);
- }
-
- @Override
- public void executeRequest(
- final Request<?> request,
- final Map<String, String> additionalHeaders,
- final OnRequestComplete callback) {
- if (getBlockingExecutor() == null || getNonBlockingExecutor() == null) {
- throw new IllegalStateException("Must set blocking and non-blocking executors");
- }
- final Callback urlCallback =
- new Callback() {
- PoolingByteArrayOutputStream bytesReceived = null;
- WritableByteChannel receiveChannel = null;
-
- @Override
- public void onRedirectReceived(
- UrlRequest urlRequest,
- UrlResponseInfo urlResponseInfo,
- String newLocationUrl) {
- urlRequest.followRedirect();
- }
-
- @Override
- public void onResponseStarted(
- UrlRequest urlRequest, UrlResponseInfo urlResponseInfo) {
- bytesReceived =
- new PoolingByteArrayOutputStream(
- mPool, getContentLength(urlResponseInfo));
- receiveChannel = Channels.newChannel(bytesReceived);
- urlRequest.read(ByteBuffer.allocateDirect(1024));
- }
-
- @Override
- public void onReadCompleted(
- UrlRequest urlRequest,
- UrlResponseInfo urlResponseInfo,
- ByteBuffer byteBuffer) {
- byteBuffer.flip();
- try {
- receiveChannel.write(byteBuffer);
- byteBuffer.clear();
- urlRequest.read(byteBuffer);
- } catch (IOException e) {
- urlRequest.cancel();
- callback.onError(e);
- }
- }
-
- @Override
- public void onSucceeded(
- UrlRequest urlRequest, UrlResponseInfo urlResponseInfo) {
- List<Header> headers = getHeaders(urlResponseInfo.getAllHeadersAsList());
- HttpResponse response =
- new HttpResponse(
- urlResponseInfo.getHttpStatusCode(),
- headers,
- bytesReceived.toByteArray());
- callback.onSuccess(response);
- }
-
- @Override
- public void onFailed(
- UrlRequest urlRequest,
- UrlResponseInfo urlResponseInfo,
- CronetException e) {
- callback.onError(e);
- }
- };
-
- String url = request.getUrl();
- String rewritten = mUrlRewriter.rewriteUrl(url);
- if (rewritten == null) {
- callback.onError(new IOException("URL blocked by rewriter: " + url));
- return;
- }
- url = rewritten;
-
- // We can call allowDirectExecutor here and run directly on the network thread, since all
- // the callbacks are non-blocking.
- final UrlRequest.Builder builder =
- mCronetEngine
- .newUrlRequestBuilder(url, urlCallback, getNonBlockingExecutor())
- .allowDirectExecutor()
- .disableCache()
- .setPriority(getPriority(request));
- // request.getHeaders() may be blocking, so submit it to the blocking executor.
- getBlockingExecutor()
- .execute(
- new SetUpRequestTask<>(request, url, builder, additionalHeaders, callback));
- }
-
- private class SetUpRequestTask<T> extends RequestTask<T> {
- UrlRequest.Builder builder;
- String url;
- Map<String, String> additionalHeaders;
- OnRequestComplete callback;
- Request<T> request;
-
- SetUpRequestTask(
- Request<T> request,
- String url,
- UrlRequest.Builder builder,
- Map<String, String> additionalHeaders,
- OnRequestComplete callback) {
- super(request);
- // Note that this URL may be different from Request#getUrl() due to the UrlRewriter.
- this.url = url;
- this.builder = builder;
- this.additionalHeaders = additionalHeaders;
- this.callback = callback;
- this.request = request;
- }
-
- @Override
- public void run() {
- try {
- mRequestListener.onRequestPrepared(request, builder);
- CurlLoggedRequestParameters requestParameters = new CurlLoggedRequestParameters();
- setHttpMethod(requestParameters, request);
- setRequestHeaders(requestParameters, request, additionalHeaders);
- requestParameters.applyToRequest(builder, getNonBlockingExecutor());
- UrlRequest urlRequest = builder.build();
- if (mCurlLoggingEnabled) {
- mCurlCommandLogger.logCurlCommand(generateCurlCommand(url, requestParameters));
- }
- urlRequest.start();
- } catch (AuthFailureError authFailureError) {
- callback.onAuthError(authFailureError);
- }
- }
- }
-
- @VisibleForTesting
- public static List<Header> getHeaders(List<Map.Entry<String, String>> headersList) {
- List<Header> headers = new ArrayList<>();
- for (Map.Entry<String, String> header : headersList) {
- headers.add(new Header(header.getKey(), header.getValue()));
- }
- return headers;
- }
-
- /** Sets the connection parameters for the UrlRequest */
- private void setHttpMethod(CurlLoggedRequestParameters requestParameters, Request<?> request)
- throws AuthFailureError {
- switch (request.getMethod()) {
- case Request.Method.DEPRECATED_GET_OR_POST:
- // This is the deprecated way that needs to be handled for backwards compatibility.
- // If the request's post body is null, then the assumption is that the request is
- // GET. Otherwise, it is assumed that the request is a POST.
- byte[] postBody = request.getPostBody();
- if (postBody != null) {
- requestParameters.setHttpMethod("POST");
- addBodyIfExists(requestParameters, request.getPostBodyContentType(), postBody);
- } else {
- requestParameters.setHttpMethod("GET");
- }
- break;
- case Request.Method.GET:
- // Not necessary to set the request method because connection defaults to GET but
- // being explicit here.
- requestParameters.setHttpMethod("GET");
- break;
- case Request.Method.DELETE:
- requestParameters.setHttpMethod("DELETE");
- break;
- case Request.Method.POST:
- requestParameters.setHttpMethod("POST");
- addBodyIfExists(requestParameters, request.getBodyContentType(), request.getBody());
- break;
- case Request.Method.PUT:
- requestParameters.setHttpMethod("PUT");
- addBodyIfExists(requestParameters, request.getBodyContentType(), request.getBody());
- break;
- case Request.Method.HEAD:
- requestParameters.setHttpMethod("HEAD");
- break;
- case Request.Method.OPTIONS:
- requestParameters.setHttpMethod("OPTIONS");
- break;
- case Request.Method.TRACE:
- requestParameters.setHttpMethod("TRACE");
- break;
- case Request.Method.PATCH:
- requestParameters.setHttpMethod("PATCH");
- addBodyIfExists(requestParameters, request.getBodyContentType(), request.getBody());
- break;
- default:
- throw new IllegalStateException("Unknown method type.");
- }
- }
-
- /**
- * Sets the request headers for the UrlRequest.
- *
- * @param requestParameters parameters that we are adding the request headers to
- * @param request to get the headers from
- * @param additionalHeaders for the UrlRequest
- * @throws AuthFailureError is thrown if Request#getHeaders throws ones
- */
- private void setRequestHeaders(
- CurlLoggedRequestParameters requestParameters,
- Request<?> request,
- Map<String, String> additionalHeaders)
- throws AuthFailureError {
- requestParameters.putAllHeaders(additionalHeaders);
- // Request.getHeaders() takes precedence over the given additional (cache) headers).
- requestParameters.putAllHeaders(request.getHeaders());
- }
-
- /** Sets the UploadDataProvider of the UrlRequest.Builder */
- private void addBodyIfExists(
- CurlLoggedRequestParameters requestParameters,
- String contentType,
- @Nullable byte[] body) {
- requestParameters.setBody(contentType, body);
- }
-
- /** Helper method that maps Volley's request priority to Cronet's */
- private int getPriority(Request<?> request) {
- switch (request.getPriority()) {
- case LOW:
- return UrlRequest.Builder.REQUEST_PRIORITY_LOW;
- case HIGH:
- case IMMEDIATE:
- return UrlRequest.Builder.REQUEST_PRIORITY_HIGHEST;
- case NORMAL:
- default:
- return UrlRequest.Builder.REQUEST_PRIORITY_MEDIUM;
- }
- }
-
- private int getContentLength(UrlResponseInfo urlResponseInfo) {
- List<String> content = urlResponseInfo.getAllHeaders().get("Content-Length");
- if (content == null) {
- return 1024;
- } else {
- return Integer.parseInt(content.get(0));
- }
- }
-
- private String generateCurlCommand(String url, CurlLoggedRequestParameters requestParameters) {
- StringBuilder builder = new StringBuilder("curl ");
-
- // HTTP method
- builder.append("-X ").append(requestParameters.getHttpMethod()).append(" ");
-
- // Request headers
- for (Map.Entry<String, String> header : requestParameters.getHeaders().entrySet()) {
- builder.append("--header \"").append(header.getKey()).append(": ");
- if (!mLogAuthTokensInCurlCommands
- && ("Authorization".equals(header.getKey())
- || "Cookie".equals(header.getKey()))) {
- builder.append("[REDACTED]");
- } else {
- builder.append(header.getValue());
- }
- builder.append("\" ");
- }
-
- // URL
- builder.append("\"").append(url).append("\"");
-
- // Request body (if any)
- if (requestParameters.getBody() != null) {
- if (requestParameters.getBody().length >= 1024) {
- builder.append(" [REQUEST BODY TOO LARGE TO INCLUDE]");
- } else if (isBinaryContentForLogging(requestParameters)) {
- String base64 = Base64.encodeToString(requestParameters.getBody(), Base64.NO_WRAP);
- builder.insert(0, "echo '" + base64 + "' | base64 -d > /tmp/$$.bin; ")
- .append(" --data-binary @/tmp/$$.bin");
- } else {
- // Just assume the request body is UTF-8 since this is for debugging.
- try {
- builder.append(" --data-ascii \"")
- .append(new String(requestParameters.getBody(), "UTF-8"))
- .append("\"");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("Could not encode to UTF-8", e);
- }
- }
- }
-
- return builder.toString();
- }
-
- /** Rough heuristic to determine whether the request body is binary, for logging purposes. */
- private boolean isBinaryContentForLogging(CurlLoggedRequestParameters requestParameters) {
- // Check to see if the content is gzip compressed - this means it should be treated as
- // binary content regardless of the content type.
- String contentEncoding = requestParameters.getHeaders().get("Content-Encoding");
- if (contentEncoding != null) {
- String[] encodings = TextUtils.split(contentEncoding, ",");
- for (String encoding : encodings) {
- if ("gzip".equals(encoding.trim())) {
- return true;
- }
- }
- }
-
- // If the content type is a known text type, treat it as text content.
- String contentType = requestParameters.getHeaders().get("Content-Type");
- if (contentType != null) {
- return !contentType.startsWith("text/")
- && !contentType.startsWith("application/xml")
- && !contentType.startsWith("application/json");
- }
-
- // Otherwise, assume it is binary content.
- return true;
- }
-
- /**
- * Builder is used to build an instance of {@link CronetHttpStack} from values configured by the
- * setters.
- */
- public static class Builder {
- private static final int DEFAULT_POOL_SIZE = 4096;
- private CronetEngine mCronetEngine;
- private final Context context;
- private ByteArrayPool mPool;
- private UrlRewriter mUrlRewriter;
- private RequestListener mRequestListener;
- private boolean mCurlLoggingEnabled;
- private CurlCommandLogger mCurlCommandLogger;
- private boolean mLogAuthTokensInCurlCommands;
-
- public Builder(Context context) {
- this.context = context;
- }
-
- /** Sets the CronetEngine to be used. Defaults to a vanialla CronetEngine. */
- public Builder setCronetEngine(CronetEngine engine) {
- mCronetEngine = engine;
- return this;
- }
-
- /** Sets the ByteArrayPool to be used. Defaults to a new pool with 4096 bytes. */
- public Builder setPool(ByteArrayPool pool) {
- mPool = pool;
- return this;
- }
-
- /** Sets the UrlRewriter to be used. Default is to return the original string. */
- public Builder setUrlRewriter(UrlRewriter urlRewriter) {
- mUrlRewriter = urlRewriter;
- return this;
- }
-
- /** Set the optional RequestListener to be used. */
- public Builder setRequestListener(RequestListener requestListener) {
- mRequestListener = requestListener;
- return this;
- }
-
- /**
- * Sets whether cURL logging should be enabled for debugging purposes.
- *
- * <p>When enabled, for each request dispatched to the network, a roughly-equivalent cURL
- * command will be logged to logcat.
- *
- * <p>The command may be missing some headers that are added by Cronet automatically, and
- * the full request body may not be included if it is too large. To inspect the full
- * requests and responses, see {@code CronetEngine#startNetLogToFile}.
- *
- * <p>WARNING: This is only intended for debugging purposes and should never be enabled on
- * production devices.
- *
- * @see #setCurlCommandLogger(CurlCommandLogger)
- * @see #setLogAuthTokensInCurlCommands(boolean)
- */
- public Builder setCurlLoggingEnabled(boolean curlLoggingEnabled) {
- mCurlLoggingEnabled = curlLoggingEnabled;
- return this;
- }
-
- /**
- * Sets the function used to log cURL commands.
- *
- * <p>Allows customization of the logging performed when cURL logging is enabled.
- *
- * <p>By default, when cURL logging is enabled, cURL commands are logged using {@link
- * VolleyLog#v}, e.g. at the verbose log level with the same log tag used by the rest of
- * Volley. This function may optionally be invoked to provide a custom logger.
- *
- * @see #setCurlLoggingEnabled(boolean)
- */
- public Builder setCurlCommandLogger(CurlCommandLogger curlCommandLogger) {
- mCurlCommandLogger = curlCommandLogger;
- return this;
- }
-
- /**
- * Sets whether to log known auth tokens in cURL commands, or redact them.
- *
- * <p>By default, headers which may contain auth tokens (e.g. Authorization or Cookie) will
- * have their values redacted. Passing true to this method will disable this redaction and
- * log the values of these headers.
- *
- * <p>This heuristic is not perfect; tokens that are logged in unknown headers, or in the
- * request body itself, will not be redacted as they cannot be detected generically.
- *
- * @see #setCurlLoggingEnabled(boolean)
- */
- public Builder setLogAuthTokensInCurlCommands(boolean logAuthTokensInCurlCommands) {
- mLogAuthTokensInCurlCommands = logAuthTokensInCurlCommands;
- return this;
- }
-
- public CronetHttpStack build() {
- if (mCronetEngine == null) {
- mCronetEngine = new CronetEngine.Builder(context).build();
- }
- if (mUrlRewriter == null) {
- mUrlRewriter =
- new UrlRewriter() {
- @Override
- public String rewriteUrl(String originalUrl) {
- return originalUrl;
- }
- };
- }
- if (mRequestListener == null) {
- mRequestListener = new RequestListener() {};
- }
- if (mPool == null) {
- mPool = new ByteArrayPool(DEFAULT_POOL_SIZE);
- }
- if (mCurlCommandLogger == null) {
- mCurlCommandLogger =
- new CurlCommandLogger() {
- @Override
- public void logCurlCommand(String curlCommand) {
- VolleyLog.v(curlCommand);
- }
- };
- }
- return new CronetHttpStack(
- mCronetEngine,
- mPool,
- mUrlRewriter,
- mRequestListener,
- mCurlLoggingEnabled,
- mCurlCommandLogger,
- mLogAuthTokensInCurlCommands);
- }
- }
-
- /** Callback interface allowing clients to intercept different parts of the request flow. */
- public abstract static class RequestListener {
- private CronetHttpStack mStack;
-
- void initialize(CronetHttpStack stack) {
- mStack = stack;
- }
-
- /**
- * Called when a request is prepared and about to be sent over the network.
- *
- * <p>Clients may use this callback to customize UrlRequests before they are dispatched,
- * e.g. to enable socket tagging or request finished listeners.
- */
- public void onRequestPrepared(Request<?> request, UrlRequest.Builder requestBuilder) {}
-
- /** @see AsyncHttpStack#getNonBlockingExecutor() */
- protected Executor getNonBlockingExecutor() {
- return mStack.getNonBlockingExecutor();
- }
-
- /** @see AsyncHttpStack#getBlockingExecutor() */
- protected Executor getBlockingExecutor() {
- return mStack.getBlockingExecutor();
- }
- }
-
- /**
- * Interface for logging cURL commands for requests.
- *
- * @see Builder#setCurlCommandLogger(CurlCommandLogger)
- */
- public interface CurlCommandLogger {
- /** Log the given cURL command. */
- void logCurlCommand(String curlCommand);
- }
-
- /**
- * Internal container class for request parameters that impact logged cURL commands.
- *
- * <p>When cURL logging is enabled, an equivalent cURL command to a given request must be
- * generated and logged. However, the Cronet UrlRequest object is write-only. So, we write any
- * relevant parameters into this read-write container so they can be referenced when generating
- * the cURL command (if needed) and then merged into the UrlRequest.
- */
- private static class CurlLoggedRequestParameters {
- private final TreeMap<String, String> mHeaders =
- new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- private String mHttpMethod;
- @Nullable private byte[] mBody;
-
- /**
- * Return the headers to be used for the request.
- *
- * <p>The returned map is case-insensitive.
- */
- TreeMap<String, String> getHeaders() {
- return mHeaders;
- }
-
- /** Apply all the headers in the given map to the request. */
- void putAllHeaders(Map<String, String> headers) {
- mHeaders.putAll(headers);
- }
-
- String getHttpMethod() {
- return mHttpMethod;
- }
-
- void setHttpMethod(String httpMethod) {
- mHttpMethod = httpMethod;
- }
-
- @Nullable
- byte[] getBody() {
- return mBody;
- }
-
- void setBody(String contentType, @Nullable byte[] body) {
- mBody = body;
- if (body != null && !mHeaders.containsKey(HttpHeaderParser.HEADER_CONTENT_TYPE)) {
- // Set the content-type unless it was already set (by Request#getHeaders).
- mHeaders.put(HttpHeaderParser.HEADER_CONTENT_TYPE, contentType);
- }
- }
-
- void applyToRequest(UrlRequest.Builder builder, ExecutorService nonBlockingExecutor) {
- for (Map.Entry<String, String> header : mHeaders.entrySet()) {
- builder.addHeader(header.getKey(), header.getValue());
- }
- builder.setHttpMethod(mHttpMethod);
- if (mBody != null) {
- UploadDataProvider dataProvider = UploadDataProviders.create(mBody);
- builder.setUploadDataProvider(dataProvider, nonBlockingExecutor);
- }
- }
- }
-}
diff --git a/src/main/java/com/android/volley/toolbox/AsyncHttpStack.java b/src/main/java/com/android/volley/toolbox/AsyncHttpStack.java
deleted file mode 100644
index bafab8c..0000000
--- a/src/main/java/com/android/volley/toolbox/AsyncHttpStack.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.toolbox;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import com.android.volley.AuthFailureError;
-import com.android.volley.Request;
-import com.android.volley.VolleyLog;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.atomic.AtomicReference;
-
-/** Asynchronous extension of the {@link BaseHttpStack} class. */
-public abstract class AsyncHttpStack extends BaseHttpStack {
- private ExecutorService mBlockingExecutor;
- private ExecutorService mNonBlockingExecutor;
-
- public interface OnRequestComplete {
- /** Invoked when the stack successfully completes a request. */
- void onSuccess(HttpResponse httpResponse);
-
- /** Invoked when the stack throws an {@link AuthFailureError} during a request. */
- void onAuthError(AuthFailureError authFailureError);
-
- /** Invoked when the stack throws an {@link IOException} during a request. */
- void onError(IOException ioException);
- }
-
- /**
- * Makes an HTTP request with the given parameters, and calls the {@link OnRequestComplete}
- * callback, with either the {@link HttpResponse} or error that was thrown.
- *
- * @param request to perform
- * @param additionalHeaders to be sent together with {@link Request#getHeaders()}
- * @param callback to be called after retrieving the {@link HttpResponse} or throwing an error.
- */
- public abstract void executeRequest(
- Request<?> request, Map<String, String> additionalHeaders, OnRequestComplete callback);
-
- /**
- * This method sets the non blocking executor to be used by the stack for non-blocking tasks.
- * This method must be called before executing any requests.
- */
- @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
- public void setNonBlockingExecutor(ExecutorService executor) {
- mNonBlockingExecutor = executor;
- }
-
- /**
- * This method sets the blocking executor to be used by the stack for potentially blocking
- * tasks. This method must be called before executing any requests.
- */
- @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
- public void setBlockingExecutor(ExecutorService executor) {
- mBlockingExecutor = executor;
- }
-
- /** Gets blocking executor to perform any potentially blocking tasks. */
- protected ExecutorService getBlockingExecutor() {
- return mBlockingExecutor;
- }
-
- /** Gets non-blocking executor to perform any non-blocking tasks. */
- protected ExecutorService getNonBlockingExecutor() {
- return mNonBlockingExecutor;
- }
-
- /**
- * Performs an HTTP request with the given parameters.
- *
- * @param request the request to perform
- * @param additionalHeaders additional headers to be sent together with {@link
- * Request#getHeaders()}
- * @return the {@link HttpResponse}
- * @throws IOException if an I/O error occurs during the request
- * @throws AuthFailureError if an authentication failure occurs during the request
- */
- @Override
- public final HttpResponse executeRequest(
- Request<?> request, Map<String, String> additionalHeaders)
- throws IOException, AuthFailureError {
- final CountDownLatch latch = new CountDownLatch(1);
- final AtomicReference<Response> entry = new AtomicReference<>();
- executeRequest(
- request,
- additionalHeaders,
- new OnRequestComplete() {
- @Override
- public void onSuccess(HttpResponse httpResponse) {
- Response response =
- new Response(
- httpResponse,
- /* ioException= */ null,
- /* authFailureError= */ null);
- entry.set(response);
- latch.countDown();
- }
-
- @Override
- public void onAuthError(AuthFailureError authFailureError) {
- Response response =
- new Response(
- /* httpResponse= */ null,
- /* ioException= */ null,
- authFailureError);
- entry.set(response);
- latch.countDown();
- }
-
- @Override
- public void onError(IOException ioException) {
- Response response =
- new Response(
- /* httpResponse= */ null,
- ioException,
- /* authFailureError= */ null);
- entry.set(response);
- latch.countDown();
- }
- });
- try {
- latch.await();
- } catch (InterruptedException e) {
- VolleyLog.e(e, "while waiting for CountDownLatch");
- Thread.currentThread().interrupt();
- throw new InterruptedIOException(e.toString());
- }
- Response response = entry.get();
- if (response.httpResponse != null) {
- return response.httpResponse;
- } else if (response.ioException != null) {
- throw response.ioException;
- } else {
- throw response.authFailureError;
- }
- }
-
- private static class Response {
- HttpResponse httpResponse;
- IOException ioException;
- AuthFailureError authFailureError;
-
- private Response(
- @Nullable HttpResponse httpResponse,
- @Nullable IOException ioException,
- @Nullable AuthFailureError authFailureError) {
- this.httpResponse = httpResponse;
- this.ioException = ioException;
- this.authFailureError = authFailureError;
- }
- }
-}
diff --git a/src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java b/src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java
deleted file mode 100644
index 55892a0..0000000
--- a/src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.toolbox;
-
-import static com.android.volley.toolbox.NetworkUtility.logSlowRequests;
-
-import android.os.SystemClock;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import com.android.volley.AsyncNetwork;
-import com.android.volley.AuthFailureError;
-import com.android.volley.Header;
-import com.android.volley.NetworkResponse;
-import com.android.volley.Request;
-import com.android.volley.RequestTask;
-import com.android.volley.VolleyError;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-
-/** A network performing Volley requests over an {@link HttpStack}. */
-public class BasicAsyncNetwork extends AsyncNetwork {
-
- private final AsyncHttpStack mAsyncStack;
- private final ByteArrayPool mPool;
-
- /**
- * @param httpStack HTTP stack to be used
- * @param pool a buffer pool that improves GC performance in copy operations
- */
- private BasicAsyncNetwork(AsyncHttpStack httpStack, ByteArrayPool pool) {
- mAsyncStack = httpStack;
- mPool = pool;
- }
-
- @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
- @Override
- public void setBlockingExecutor(ExecutorService executor) {
- super.setBlockingExecutor(executor);
- mAsyncStack.setBlockingExecutor(executor);
- }
-
- @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
- @Override
- public void setNonBlockingExecutor(ExecutorService executor) {
- super.setNonBlockingExecutor(executor);
- mAsyncStack.setNonBlockingExecutor(executor);
- }
-
- /* Method to be called after a successful network request */
- private void onRequestSucceeded(
- final Request<?> request,
- final long requestStartMs,
- final HttpResponse httpResponse,
- final OnRequestComplete callback) {
- final int statusCode = httpResponse.getStatusCode();
- final List<Header> responseHeaders = httpResponse.getHeaders();
- // Handle cache validation.
- if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
- long requestDuration = SystemClock.elapsedRealtime() - requestStartMs;
- callback.onSuccess(
- NetworkUtility.getNotModifiedNetworkResponse(
- request, requestDuration, responseHeaders));
- return;
- }
-
- byte[] responseContents = httpResponse.getContentBytes();
- if (responseContents == null && httpResponse.getContent() == null) {
- // Add 0 byte response as a way of honestly representing a
- // no-content request.
- responseContents = new byte[0];
- }
-
- if (responseContents != null) {
- onResponseRead(
- requestStartMs,
- statusCode,
- httpResponse,
- request,
- callback,
- responseHeaders,
- responseContents);
- return;
- }
-
- // The underlying AsyncHttpStack does not support asynchronous reading of the response into
- // a byte array, so we need to submit a blocking task to copy the response from the
- // InputStream instead.
- final InputStream inputStream = httpResponse.getContent();
- getBlockingExecutor()
- .execute(
- new ResponseParsingTask<>(
- inputStream,
- httpResponse,
- request,
- callback,
- requestStartMs,
- responseHeaders,
- statusCode));
- }
-
- /* Method to be called after a failed network request */
- private void onRequestFailed(
- Request<?> request,
- OnRequestComplete callback,
- IOException exception,
- long requestStartMs,
- @Nullable HttpResponse httpResponse,
- @Nullable byte[] responseContents) {
- try {
- NetworkUtility.handleException(
- request, exception, requestStartMs, httpResponse, responseContents);
- } catch (VolleyError volleyError) {
- callback.onError(volleyError);
- return;
- }
- performRequest(request, callback);
- }
-
- @Override
- public void performRequest(final Request<?> request, final OnRequestComplete callback) {
- if (getBlockingExecutor() == null) {
- throw new IllegalStateException(
- "mBlockingExecuter must be set before making a request");
- }
- final long requestStartMs = SystemClock.elapsedRealtime();
- // Gather headers.
- final Map<String, String> additionalRequestHeaders =
- HttpHeaderParser.getCacheHeaders(request.getCacheEntry());
- mAsyncStack.executeRequest(
- request,
- additionalRequestHeaders,
- new AsyncHttpStack.OnRequestComplete() {
- @Override
- public void onSuccess(HttpResponse httpResponse) {
- onRequestSucceeded(request, requestStartMs, httpResponse, callback);
- }
-
- @Override
- public void onAuthError(AuthFailureError authFailureError) {
- callback.onError(authFailureError);
- }
-
- @Override
- public void onError(IOException ioException) {
- onRequestFailed(
- request,
- callback,
- ioException,
- requestStartMs,
- /* httpResponse= */ null,
- /* responseContents= */ null);
- }
- });
- }
-
- /* Helper method that determines what to do after byte[] is received */
- private void onResponseRead(
- long requestStartMs,
- int statusCode,
- HttpResponse httpResponse,
- Request<?> request,
- OnRequestComplete callback,
- List<Header> responseHeaders,
- byte[] responseContents) {
- // if the request is slow, log it.
- long requestLifetime = SystemClock.elapsedRealtime() - requestStartMs;
- logSlowRequests(requestLifetime, request, responseContents, statusCode);
-
- if (statusCode < 200 || statusCode > 299) {
- onRequestFailed(
- request,
- callback,
- new IOException(),
- requestStartMs,
- httpResponse,
- responseContents);
- return;
- }
-
- callback.onSuccess(
- new NetworkResponse(
- statusCode,
- responseContents,
- /* notModified= */ false,
- SystemClock.elapsedRealtime() - requestStartMs,
- responseHeaders));
- }
-
- private class ResponseParsingTask<T> extends RequestTask<T> {
- InputStream inputStream;
- HttpResponse httpResponse;
- Request<T> request;
- OnRequestComplete callback;
- long requestStartMs;
- List<Header> responseHeaders;
- int statusCode;
-
- ResponseParsingTask(
- InputStream inputStream,
- HttpResponse httpResponse,
- Request<T> request,
- OnRequestComplete callback,
- long requestStartMs,
- List<Header> responseHeaders,
- int statusCode) {
- super(request);
- this.inputStream = inputStream;
- this.httpResponse = httpResponse;
- this.request = request;
- this.callback = callback;
- this.requestStartMs = requestStartMs;
- this.responseHeaders = responseHeaders;
- this.statusCode = statusCode;
- }
-
- @Override
- public void run() {
- byte[] finalResponseContents;
- try {
- finalResponseContents =
- NetworkUtility.inputStreamToBytes(
- inputStream, httpResponse.getContentLength(), mPool);
- } catch (IOException e) {
- onRequestFailed(request, callback, e, requestStartMs, httpResponse, null);
- return;
- }
- onResponseRead(
- requestStartMs,
- statusCode,
- httpResponse,
- request,
- callback,
- responseHeaders,
- finalResponseContents);
- }
- }
-
- /**
- * Builder is used to build an instance of {@link BasicAsyncNetwork} from values configured by
- * the setters.
- */
- public static class Builder {
- private static final int DEFAULT_POOL_SIZE = 4096;
- @NonNull private AsyncHttpStack mAsyncStack;
- private ByteArrayPool mPool;
-
- public Builder(@NonNull AsyncHttpStack httpStack) {
- mAsyncStack = httpStack;
- mPool = null;
- }
-
- /**
- * Sets the ByteArrayPool to be used. If not set, it will default to a pool with the default
- * pool size.
- */
- public Builder setPool(ByteArrayPool pool) {
- mPool = pool;
- return this;
- }
-
- /** Builds the {@link com.android.volley.toolbox.BasicAsyncNetwork} */
- public BasicAsyncNetwork build() {
- if (mPool == null) {
- mPool = new ByteArrayPool(DEFAULT_POOL_SIZE);
- }
- return new BasicAsyncNetwork(mAsyncStack, mPool);
- }
- }
-}
diff --git a/src/main/java/com/android/volley/toolbox/BasicNetwork.java b/src/main/java/com/android/volley/toolbox/BasicNetwork.java
index 06427fe..b527cb9 100644
--- a/src/main/java/com/android/volley/toolbox/BasicNetwork.java
+++ b/src/main/java/com/android/volley/toolbox/BasicNetwork.java
@@ -17,21 +17,41 @@
package com.android.volley.toolbox;
import android.os.SystemClock;
+import com.android.volley.AuthFailureError;
+import com.android.volley.Cache;
+import com.android.volley.Cache.Entry;
+import com.android.volley.ClientError;
import com.android.volley.Header;
import com.android.volley.Network;
+import com.android.volley.NetworkError;
import com.android.volley.NetworkResponse;
+import com.android.volley.NoConnectionError;
import com.android.volley.Request;
+import com.android.volley.RetryPolicy;
+import com.android.volley.ServerError;
+import com.android.volley.TimeoutError;
import com.android.volley.VolleyError;
+import com.android.volley.VolleyLog;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
+import java.util.TreeSet;
/** A network performing Volley requests over an {@link HttpStack}. */
public class BasicNetwork implements Network {
+ protected static final boolean DEBUG = VolleyLog.DEBUG;
+
+ private static final int SLOW_REQUEST_THRESHOLD_MS = 3000;
+
private static final int DEFAULT_POOL_SIZE = 4096;
/**
@@ -99,24 +119,37 @@ public class BasicNetwork implements Network {
try {
// Gather headers.
Map<String, String> additionalRequestHeaders =
- HttpHeaderParser.getCacheHeaders(request.getCacheEntry());
+ getCacheHeaders(request.getCacheEntry());
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
int statusCode = httpResponse.getStatusCode();
responseHeaders = httpResponse.getHeaders();
// Handle cache validation.
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
- long requestDuration = SystemClock.elapsedRealtime() - requestStart;
- return NetworkUtility.getNotModifiedNetworkResponse(
- request, requestDuration, responseHeaders);
+ Entry entry = request.getCacheEntry();
+ if (entry == null) {
+ return new NetworkResponse(
+ HttpURLConnection.HTTP_NOT_MODIFIED,
+ /* data= */ null,
+ /* notModified= */ true,
+ SystemClock.elapsedRealtime() - requestStart,
+ responseHeaders);
+ }
+ // Combine cached and response headers so the response will be complete.
+ List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
+ return new NetworkResponse(
+ HttpURLConnection.HTTP_NOT_MODIFIED,
+ entry.data,
+ /* notModified= */ true,
+ SystemClock.elapsedRealtime() - requestStart,
+ combinedHeaders);
}
// Some responses such as 204s do not have content. We must check.
InputStream inputStream = httpResponse.getContent();
if (inputStream != null) {
responseContents =
- NetworkUtility.inputStreamToBytes(
- inputStream, httpResponse.getContentLength(), mPool);
+ inputStreamToBytes(inputStream, httpResponse.getContentLength());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
@@ -125,8 +158,7 @@ public class BasicNetwork implements Network {
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
- NetworkUtility.logSlowRequests(
- requestLifetime, request, responseContents, statusCode);
+ logSlowRequests(requestLifetime, request, responseContents, statusCode);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
@@ -137,12 +169,141 @@ public class BasicNetwork implements Network {
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
+ } catch (SocketTimeoutException e) {
+ attemptRetryOnException("socket", request, new TimeoutError());
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Bad URL " + request.getUrl(), e);
+ } catch (IOException e) {
+ int statusCode;
+ if (httpResponse != null) {
+ statusCode = httpResponse.getStatusCode();
+ } else {
+ throw new NoConnectionError(e);
+ }
+ VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
+ NetworkResponse networkResponse;
+ if (responseContents != null) {
+ networkResponse =
+ new NetworkResponse(
+ statusCode,
+ responseContents,
+ /* notModified= */ false,
+ SystemClock.elapsedRealtime() - requestStart,
+ responseHeaders);
+ if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED
+ || statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
+ attemptRetryOnException(
+ "auth", request, new AuthFailureError(networkResponse));
+ } else if (statusCode >= 400 && statusCode <= 499) {
+ // Don't retry other client errors.
+ throw new ClientError(networkResponse);
+ } else if (statusCode >= 500 && statusCode <= 599) {
+ if (request.shouldRetryServerErrors()) {
+ attemptRetryOnException(
+ "server", request, new ServerError(networkResponse));
+ } else {
+ throw new ServerError(networkResponse);
+ }
+ } else {
+ // 3xx? No reason to retry.
+ throw new ServerError(networkResponse);
+ }
+ } else {
+ attemptRetryOnException("network", request, new NetworkError());
+ }
+ }
+ }
+ }
+
+ /** Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete. */
+ private void logSlowRequests(
+ long requestLifetime, Request<?> request, byte[] responseContents, int statusCode) {
+ if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
+ VolleyLog.d(
+ "HTTP response for request=<%s> [lifetime=%d], [size=%s], "
+ + "[rc=%d], [retryCount=%s]",
+ request,
+ requestLifetime,
+ responseContents != null ? responseContents.length : "null",
+ statusCode,
+ request.getRetryPolicy().getCurrentRetryCount());
+ }
+ }
+
+ /**
+ * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
+ * request's retry policy, a timeout exception is thrown.
+ *
+ * @param request The request to use.
+ */
+ private static void attemptRetryOnException(
+ String logPrefix, Request<?> request, VolleyError exception) throws VolleyError {
+ RetryPolicy retryPolicy = request.getRetryPolicy();
+ int oldTimeout = request.getTimeoutMs();
+
+ try {
+ retryPolicy.retry(exception);
+ } catch (VolleyError e) {
+ request.addMarker(
+ String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
+ throw e;
+ }
+ request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
+ }
+
+ private Map<String, String> getCacheHeaders(Cache.Entry entry) {
+ // If there's no cache entry, we're done.
+ if (entry == null) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, String> headers = new HashMap<>();
+
+ if (entry.etag != null) {
+ headers.put("If-None-Match", entry.etag);
+ }
+
+ if (entry.lastModified > 0) {
+ headers.put(
+ "If-Modified-Since", HttpHeaderParser.formatEpochAsRfc1123(entry.lastModified));
+ }
+
+ return headers;
+ }
+
+ protected void logError(String what, String url, long start) {
+ long now = SystemClock.elapsedRealtime();
+ VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
+ }
+
+ /** Reads the contents of an InputStream into a byte[]. */
+ private byte[] inputStreamToBytes(InputStream in, int contentLength)
+ throws IOException, ServerError {
+ PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(mPool, contentLength);
+ byte[] buffer = null;
+ try {
+ if (in == null) {
+ throw new ServerError();
+ }
+ buffer = mPool.getBuf(1024);
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
+ }
+ return bytes.toByteArray();
+ } finally {
+ try {
+ // Close the InputStream and release the resources by "consuming the content".
+ if (in != null) {
+ in.close();
+ }
} catch (IOException e) {
- // This will either throw an exception, breaking us from the loop, or will loop
- // again and retry the request.
- NetworkUtility.handleException(
- request, e, requestStart, httpResponse, responseContents);
+ // This can happen if there was an exception above that left the stream in
+ // an invalid state.
+ VolleyLog.v("Error occurred when closing InputStream");
}
+ mPool.returnBuf(buffer);
+ bytes.close();
}
}
@@ -160,4 +321,49 @@ public class BasicNetwork implements Network {
}
return result;
}
+
+ /**
+ * Combine cache headers with network response headers for an HTTP 304 response.
+ *
+ * <p>An HTTP 304 response does not have all header fields. We have to use the header fields
+ * from the cache entry plus the new ones from the response. See also:
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
+ *
+ * @param responseHeaders Headers from the network response.
+ * @param entry The cached response.
+ * @return The combined list of headers.
+ */
+ private static List<Header> combineHeaders(List<Header> responseHeaders, Entry entry) {
+ // First, create a case-insensitive set of header names from the network
+ // response.
+ Set<String> headerNamesFromNetworkResponse = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+ if (!responseHeaders.isEmpty()) {
+ for (Header header : responseHeaders) {
+ headerNamesFromNetworkResponse.add(header.getName());
+ }
+ }
+
+ // Second, add headers from the cache entry to the network response as long as
+ // they didn't appear in the network response, which should take precedence.
+ List<Header> combinedHeaders = new ArrayList<>(responseHeaders);
+ if (entry.allResponseHeaders != null) {
+ if (!entry.allResponseHeaders.isEmpty()) {
+ for (Header header : entry.allResponseHeaders) {
+ if (!headerNamesFromNetworkResponse.contains(header.getName())) {
+ combinedHeaders.add(header);
+ }
+ }
+ }
+ } else {
+ // Legacy caches only have entry.responseHeaders.
+ if (!entry.responseHeaders.isEmpty()) {
+ for (Map.Entry<String, String> header : entry.responseHeaders.entrySet()) {
+ if (!headerNamesFromNetworkResponse.contains(header.getKey())) {
+ combinedHeaders.add(new Header(header.getKey(), header.getValue()));
+ }
+ }
+ }
+ }
+ return combinedHeaders;
+ }
}
diff --git a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
index d4310e0..a6a0c83 100644
--- a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
+++ b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
@@ -55,8 +55,8 @@ public class DiskBasedCache implements Cache {
/** Total amount of space currently used by the cache in bytes. */
private long mTotalSize = 0;
- /** The supplier for the root directory to use for the cache. */
- private final FileSupplier mRootDirectorySupplier;
+ /** The root directory to use for the cache. */
+ private final File mRootDirectory;
/** The maximum size of the cache in bytes. */
private final int mMaxCacheSizeInBytes;
@@ -78,27 +78,8 @@ public class DiskBasedCache implements Cache {
* briefly exceed this size on disk when writing a new entry that pushes it over the limit
* until the ensuing pruning completes.
*/
- public DiskBasedCache(final File rootDirectory, int maxCacheSizeInBytes) {
- mRootDirectorySupplier =
- new FileSupplier() {
- @Override
- public File get() {
- return rootDirectory;
- }
- };
- mMaxCacheSizeInBytes = maxCacheSizeInBytes;
- }
-
- /**
- * Constructs an instance of the DiskBasedCache at the specified directory.
- *
- * @param rootDirectorySupplier The supplier for the root directory of the cache.
- * @param maxCacheSizeInBytes The maximum size of the cache in bytes. Note that the cache may
- * briefly exceed this size on disk when writing a new entry that pushes it over the limit
- * until the ensuing pruning completes.
- */
- public DiskBasedCache(FileSupplier rootDirectorySupplier, int maxCacheSizeInBytes) {
- mRootDirectorySupplier = rootDirectorySupplier;
+ public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
+ mRootDirectory = rootDirectory;
mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}
@@ -112,20 +93,10 @@ public class DiskBasedCache implements Cache {
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}
- /**
- * Constructs an instance of the DiskBasedCache at the specified directory using the default
- * maximum cache size of 5MB.
- *
- * @param rootDirectorySupplier The supplier for the root directory of the cache.
- */
- public DiskBasedCache(FileSupplier rootDirectorySupplier) {
- this(rootDirectorySupplier, DEFAULT_DISK_USAGE_BYTES);
- }
-
/** Clears the cache. Deletes all cached files from disk. */
@Override
public synchronized void clear() {
- File[] files = mRootDirectorySupplier.get().listFiles();
+ File[] files = mRootDirectory.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
@@ -179,14 +150,13 @@ public class DiskBasedCache implements Cache {
*/
@Override
public synchronized void initialize() {
- File rootDirectory = mRootDirectorySupplier.get();
- if (!rootDirectory.exists()) {
- if (!rootDirectory.mkdirs()) {
- VolleyLog.e("Unable to create cache dir %s", rootDirectory.getAbsolutePath());
+ if (!mRootDirectory.exists()) {
+ if (!mRootDirectory.mkdirs()) {
+ VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
}
return;
}
- File[] files = rootDirectory.listFiles();
+ File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
@@ -256,12 +226,12 @@ public class DiskBasedCache implements Cache {
e.size = file.length();
putEntry(key, e);
pruneIfNeeded();
+ return;
} catch (IOException e) {
- boolean deleted = file.delete();
- if (!deleted) {
- VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
- }
- initializeIfRootDirectoryDeleted();
+ }
+ boolean deleted = file.delete();
+ if (!deleted) {
+ VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
}
}
@@ -292,22 +262,7 @@ public class DiskBasedCache implements Cache {
/** Returns a file object for the given cache key. */
public File getFileForKey(String key) {
- return new File(mRootDirectorySupplier.get(), getFilenameForKey(key));
- }
-
- /** Re-initialize the cache if the directory was deleted. */
- private void initializeIfRootDirectoryDeleted() {
- if (!mRootDirectorySupplier.get().exists()) {
- VolleyLog.d("Re-initializing cache after external clearing.");
- mEntries.clear();
- mTotalSize = 0;
- initialize();
- }
- }
-
- /** Represents a supplier for {@link File}s. */
- public interface FileSupplier {
- File get();
+ return new File(mRootDirectory, getFilenameForKey(key));
}
/** Prunes the cache to fit the maximum size. */
diff --git a/src/main/java/com/android/volley/toolbox/FileSupplier.java b/src/main/java/com/android/volley/toolbox/FileSupplier.java
deleted file mode 100644
index 70898a6..0000000
--- a/src/main/java/com/android/volley/toolbox/FileSupplier.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.toolbox;
-
-import java.io.File;
-
-/** Represents a supplier for {@link File}s. */
-public interface FileSupplier {
- File get();
-}
diff --git a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
index 0b29e80..27d1268 100644
--- a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
+++ b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
@@ -16,9 +16,6 @@
package com.android.volley.toolbox;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
import com.android.volley.Cache;
import com.android.volley.Header;
import com.android.volley.NetworkResponse;
@@ -26,30 +23,21 @@ import com.android.volley.VolleyLog;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
-import java.util.TreeSet;
/** Utility methods for parsing HTTP headers. */
public class HttpHeaderParser {
- @RestrictTo({Scope.LIBRARY_GROUP})
- public static final String HEADER_CONTENT_TYPE = "Content-Type";
+ static final String HEADER_CONTENT_TYPE = "Content-Type";
private static final String DEFAULT_CONTENT_CHARSET = "ISO-8859-1";
- private static final String RFC1123_PARSE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
-
- // Hardcode 'GMT' rather than using 'zzz' since some platforms append an extraneous +00:00.
- // See #287.
- private static final String RFC1123_OUTPUT_FORMAT = "EEE, dd MMM yyyy HH:mm:ss 'GMT'";
+ private static final String RFC1123_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
/**
* Extracts a {@link com.android.volley.Cache.Entry} from a {@link NetworkResponse}.
@@ -57,14 +45,10 @@ public class HttpHeaderParser {
* @param response The network response to parse headers from
* @return a cache entry for the given response, or null if the response is not cacheable.
*/
- @Nullable
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
- if (headers == null) {
- return null;
- }
long serverDate = 0;
long lastModified = 0;
@@ -148,29 +132,21 @@ public class HttpHeaderParser {
public static long parseDateAsEpoch(String dateStr) {
try {
// Parse date in RFC1123 format if this header contains one
- return newUsGmtFormatter(RFC1123_PARSE_FORMAT).parse(dateStr).getTime();
+ return newRfc1123Formatter().parse(dateStr).getTime();
} catch (ParseException e) {
// Date in invalid format, fallback to 0
- // If the value is either "0" or "-1" we only log to verbose,
- // these values are pretty common and cause log spam.
- String message = "Unable to parse dateStr: %s, falling back to 0";
- if ("0".equals(dateStr) || "-1".equals(dateStr)) {
- VolleyLog.v(message, dateStr);
- } else {
- VolleyLog.e(e, message, dateStr);
- }
-
+ VolleyLog.e(e, "Unable to parse dateStr: %s, falling back to 0", dateStr);
return 0;
}
}
/** Format an epoch date in RFC1123 format. */
static String formatEpochAsRfc1123(long epoch) {
- return newUsGmtFormatter(RFC1123_OUTPUT_FORMAT).format(new Date(epoch));
+ return newRfc1123Formatter().format(new Date(epoch));
}
- private static SimpleDateFormat newUsGmtFormatter(String format) {
- SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.US);
+ private static SimpleDateFormat newRfc1123Formatter() {
+ SimpleDateFormat formatter = new SimpleDateFormat(RFC1123_FORMAT, Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
return formatter;
}
@@ -183,11 +159,7 @@ public class HttpHeaderParser {
* @return Returns the charset specified in the Content-Type of this header, or the
* defaultCharset if none can be found.
*/
- public static String parseCharset(
- @Nullable Map<String, String> headers, String defaultCharset) {
- if (headers == null) {
- return defaultCharset;
- }
+ public static String parseCharset(Map<String, String> headers, String defaultCharset) {
String contentType = headers.get(HEADER_CONTENT_TYPE);
if (contentType != null) {
String[] params = contentType.split(";", 0);
@@ -208,7 +180,7 @@ public class HttpHeaderParser {
* Returns the charset specified in the Content-Type of this header, or the HTTP default
* (ISO-8859-1) if none can be found.
*/
- public static String parseCharset(@Nullable Map<String, String> headers) {
+ public static String parseCharset(Map<String, String> headers) {
return parseCharset(headers, DEFAULT_CONTENT_CHARSET);
}
@@ -233,69 +205,4 @@ public class HttpHeaderParser {
}
return allHeaders;
}
-
- /**
- * Combine cache headers with network response headers for an HTTP 304 response.
- *
- * <p>An HTTP 304 response does not have all header fields. We have to use the header fields
- * from the cache entry plus the new ones from the response. See also:
- * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
- *
- * @param responseHeaders Headers from the network response.
- * @param entry The cached response.
- * @return The combined list of headers.
- */
- static List<Header> combineHeaders(List<Header> responseHeaders, Cache.Entry entry) {
- // First, create a case-insensitive set of header names from the network
- // response.
- Set<String> headerNamesFromNetworkResponse = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
- if (!responseHeaders.isEmpty()) {
- for (Header header : responseHeaders) {
- headerNamesFromNetworkResponse.add(header.getName());
- }
- }
-
- // Second, add headers from the cache entry to the network response as long as
- // they didn't appear in the network response, which should take precedence.
- List<Header> combinedHeaders = new ArrayList<>(responseHeaders);
- if (entry.allResponseHeaders != null) {
- if (!entry.allResponseHeaders.isEmpty()) {
- for (Header header : entry.allResponseHeaders) {
- if (!headerNamesFromNetworkResponse.contains(header.getName())) {
- combinedHeaders.add(header);
- }
- }
- }
- } else {
- // Legacy caches only have entry.responseHeaders.
- if (!entry.responseHeaders.isEmpty()) {
- for (Map.Entry<String, String> header : entry.responseHeaders.entrySet()) {
- if (!headerNamesFromNetworkResponse.contains(header.getKey())) {
- combinedHeaders.add(new Header(header.getKey(), header.getValue()));
- }
- }
- }
- }
- return combinedHeaders;
- }
-
- static Map<String, String> getCacheHeaders(Cache.Entry entry) {
- // If there's no cache entry, we're done.
- if (entry == null) {
- return Collections.emptyMap();
- }
-
- Map<String, String> headers = new HashMap<>();
-
- if (entry.etag != null) {
- headers.put("If-None-Match", entry.etag);
- }
-
- if (entry.lastModified > 0) {
- headers.put(
- "If-Modified-Since", HttpHeaderParser.formatEpochAsRfc1123(entry.lastModified));
- }
-
- return headers;
- }
}
diff --git a/src/main/java/com/android/volley/toolbox/HttpResponse.java b/src/main/java/com/android/volley/toolbox/HttpResponse.java
index 595f926..9a9294f 100644
--- a/src/main/java/com/android/volley/toolbox/HttpResponse.java
+++ b/src/main/java/com/android/volley/toolbox/HttpResponse.java
@@ -15,9 +15,7 @@
*/
package com.android.volley.toolbox;
-import androidx.annotation.Nullable;
import com.android.volley.Header;
-import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
@@ -28,8 +26,7 @@ public final class HttpResponse {
private final int mStatusCode;
private final List<Header> mHeaders;
private final int mContentLength;
- @Nullable private final InputStream mContent;
- @Nullable private final byte[] mContentBytes;
+ private final InputStream mContent;
/**
* Construct a new HttpResponse for an empty response body.
@@ -56,23 +53,6 @@ public final class HttpResponse {
mHeaders = headers;
mContentLength = contentLength;
mContent = content;
- mContentBytes = null;
- }
-
- /**
- * Construct a new HttpResponse.
- *
- * @param statusCode the HTTP status code of the response
- * @param headers the response headers
- * @param contentBytes a byte[] of the response content. This is an optimization for HTTP stacks
- * that natively support returning a byte[].
- */
- public HttpResponse(int statusCode, List<Header> headers, byte[] contentBytes) {
- mStatusCode = statusCode;
- mHeaders = headers;
- mContentLength = contentBytes.length;
- mContentBytes = contentBytes;
- mContent = null;
}
/** Returns the HTTP status code of the response. */
@@ -91,28 +71,10 @@ public final class HttpResponse {
}
/**
- * If a byte[] was already provided by an HTTP stack that natively supports returning one, this
- * method will return that byte[] as an optimization over copying the bytes from an input
- * stream. It may return null, even if the response has content, as long as mContent is
- * provided.
- */
- @Nullable
- public final byte[] getContentBytes() {
- return mContentBytes;
- }
-
- /**
* Returns an {@link InputStream} of the response content. May be null to indicate that the
* response has no content.
*/
- @Nullable
public final InputStream getContent() {
- if (mContent != null) {
- return mContent;
- } else if (mContentBytes != null) {
- return new ByteArrayInputStream(mContentBytes);
- } else {
- return null;
- }
+ return mContent;
}
}
diff --git a/src/main/java/com/android/volley/toolbox/HurlStack.java b/src/main/java/com/android/volley/toolbox/HurlStack.java
index 35c6a72..f85d42c 100644
--- a/src/main/java/com/android/volley/toolbox/HurlStack.java
+++ b/src/main/java/com/android/volley/toolbox/HurlStack.java
@@ -25,7 +25,6 @@ import java.io.DataOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
@@ -41,7 +40,13 @@ public class HurlStack extends BaseHttpStack {
private static final int HTTP_CONTINUE = 100;
/** An interface for transforming URLs before use. */
- public interface UrlRewriter extends com.android.volley.toolbox.UrlRewriter {}
+ public interface UrlRewriter {
+ /**
+ * Returns a URL to use instead of the provided one, or null to indicate this URL should not
+ * be used at all.
+ */
+ String rewriteUrl(String originalUrl);
+ }
private final UrlRewriter mUrlRewriter;
private final SSLSocketFactory mSslSocketFactory;
@@ -106,7 +111,7 @@ public class HurlStack extends BaseHttpStack {
responseCode,
convertHeaders(connection.getHeaderFields()),
connection.getContentLength(),
- createInputStream(request, connection));
+ new UrlConnectionInputStream(connection));
} finally {
if (!keepConnectionOpen) {
connection.disconnect();
@@ -164,19 +169,6 @@ public class HurlStack extends BaseHttpStack {
}
/**
- * Create and return an InputStream from which the response will be read.
- *
- * <p>May be overridden by subclasses to manipulate or monitor this input stream.
- *
- * @param request current request.
- * @param connection current connection of request.
- * @return an InputStream from which the response will be read.
- */
- protected InputStream createInputStream(Request<?> request, HttpURLConnection connection) {
- return new UrlConnectionInputStream(connection);
- }
-
- /**
* Initializes an {@link InputStream} from the given {@link HttpURLConnection}.
*
* @param connection
@@ -231,7 +223,7 @@ public class HurlStack extends BaseHttpStack {
// NOTE: Any request headers added here (via setRequestProperty or addRequestProperty) should be
// checked against the existing properties in the connection and not overridden if already set.
@SuppressWarnings("deprecation")
- /* package */ void setConnectionParametersForRequest(
+ /* package */ static void setConnectionParametersForRequest(
HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
@@ -278,7 +270,7 @@ public class HurlStack extends BaseHttpStack {
}
}
- private void addBodyIfExists(HttpURLConnection connection, Request<?> request)
+ private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
@@ -286,7 +278,7 @@ public class HurlStack extends BaseHttpStack {
}
}
- private void addBody(HttpURLConnection connection, Request<?> request, byte[] body)
+ private static void addBody(HttpURLConnection connection, Request<?> request, byte[] body)
throws IOException {
// Prepare output. There is no need to set Content-Length explicitly,
// since this is handled by HttpURLConnection using the size of the prepared
@@ -297,25 +289,8 @@ public class HurlStack extends BaseHttpStack {
connection.setRequestProperty(
HttpHeaderParser.HEADER_CONTENT_TYPE, request.getBodyContentType());
}
- DataOutputStream out =
- new DataOutputStream(createOutputStream(request, connection, body.length));
+ DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
-
- /**
- * Create and return an OutputStream to which the request body will be written.
- *
- * <p>May be overridden by subclasses to manipulate or monitor this output stream.
- *
- * @param request current request.
- * @param connection current connection of request.
- * @param length size of stream to write.
- * @return an OutputStream to which the request body will be written.
- * @throws IOException if an I/O error occurs while creating the stream.
- */
- protected OutputStream createOutputStream(
- Request<?> request, HttpURLConnection connection, int length) throws IOException {
- return connection.getOutputStream();
- }
}
diff --git a/src/main/java/com/android/volley/toolbox/ImageLoader.java b/src/main/java/com/android/volley/toolbox/ImageLoader.java
index eece2cf..b80072b 100644
--- a/src/main/java/com/android/volley/toolbox/ImageLoader.java
+++ b/src/main/java/com/android/volley/toolbox/ImageLoader.java
@@ -20,7 +20,6 @@ import android.os.Looper;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response.ErrorListener;
@@ -71,7 +70,6 @@ public class ImageLoader {
* LruCache is recommended.
*/
public interface ImageCache {
- @Nullable
Bitmap getBitmap(String url);
void putBitmap(String url, Bitmap bitmap);
diff --git a/src/main/java/com/android/volley/toolbox/NetworkUtility.java b/src/main/java/com/android/volley/toolbox/NetworkUtility.java
deleted file mode 100644
index 44d5904..0000000
--- a/src/main/java/com/android/volley/toolbox/NetworkUtility.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.toolbox;
-
-import android.os.SystemClock;
-import androidx.annotation.Nullable;
-import com.android.volley.AuthFailureError;
-import com.android.volley.Cache;
-import com.android.volley.ClientError;
-import com.android.volley.Header;
-import com.android.volley.NetworkError;
-import com.android.volley.NetworkResponse;
-import com.android.volley.NoConnectionError;
-import com.android.volley.Request;
-import com.android.volley.RetryPolicy;
-import com.android.volley.ServerError;
-import com.android.volley.TimeoutError;
-import com.android.volley.VolleyError;
-import com.android.volley.VolleyLog;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.SocketTimeoutException;
-import java.util.List;
-
-/**
- * Utility class for methods that are shared between {@link BasicNetwork} and {@link
- * BasicAsyncNetwork}
- */
-public final class NetworkUtility {
- private static final int SLOW_REQUEST_THRESHOLD_MS = 3000;
-
- private NetworkUtility() {}
-
- /** Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete. */
- static void logSlowRequests(
- long requestLifetime, Request<?> request, byte[] responseContents, int statusCode) {
- if (VolleyLog.DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
- VolleyLog.d(
- "HTTP response for request=<%s> [lifetime=%d], [size=%s], "
- + "[rc=%d], [retryCount=%s]",
- request,
- requestLifetime,
- responseContents != null ? responseContents.length : "null",
- statusCode,
- request.getRetryPolicy().getCurrentRetryCount());
- }
- }
-
- static NetworkResponse getNotModifiedNetworkResponse(
- Request<?> request, long requestDuration, List<Header> responseHeaders) {
- Cache.Entry entry = request.getCacheEntry();
- if (entry == null) {
- return new NetworkResponse(
- HttpURLConnection.HTTP_NOT_MODIFIED,
- /* data= */ null,
- /* notModified= */ true,
- requestDuration,
- responseHeaders);
- }
- // Combine cached and response headers so the response will be complete.
- List<Header> combinedHeaders = HttpHeaderParser.combineHeaders(responseHeaders, entry);
- return new NetworkResponse(
- HttpURLConnection.HTTP_NOT_MODIFIED,
- entry.data,
- /* notModified= */ true,
- requestDuration,
- combinedHeaders);
- }
-
- /** Reads the contents of an InputStream into a byte[]. */
- static byte[] inputStreamToBytes(InputStream in, int contentLength, ByteArrayPool pool)
- throws IOException {
- PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(pool, contentLength);
- byte[] buffer = null;
- try {
- buffer = pool.getBuf(1024);
- int count;
- while ((count = in.read(buffer)) != -1) {
- bytes.write(buffer, 0, count);
- }
- return bytes.toByteArray();
- } finally {
- try {
- // Close the InputStream and release the resources by "consuming the content".
- if (in != null) {
- in.close();
- }
- } catch (IOException e) {
- // This can happen if there was an exception above that left the stream in
- // an invalid state.
- VolleyLog.v("Error occurred when closing InputStream");
- }
- pool.returnBuf(buffer);
- bytes.close();
- }
- }
-
- /**
- * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
- * request's retry policy, a timeout exception is thrown.
- *
- * @param request The request to use.
- */
- private static void attemptRetryOnException(
- final String logPrefix, final Request<?> request, final VolleyError exception)
- throws VolleyError {
- final RetryPolicy retryPolicy = request.getRetryPolicy();
- final int oldTimeout = request.getTimeoutMs();
- try {
- retryPolicy.retry(exception);
- } catch (VolleyError e) {
- request.addMarker(
- String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
- throw e;
- }
- request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
- }
-
- /**
- * Based on the exception thrown, decides whether to attempt to retry, or to throw the error.
- * Also handles logging.
- */
- static void handleException(
- Request<?> request,
- IOException exception,
- long requestStartMs,
- @Nullable HttpResponse httpResponse,
- @Nullable byte[] responseContents)
- throws VolleyError {
- if (exception instanceof SocketTimeoutException) {
- attemptRetryOnException("socket", request, new TimeoutError());
- } else if (exception instanceof MalformedURLException) {
- throw new RuntimeException("Bad URL " + request.getUrl(), exception);
- } else {
- int statusCode;
- if (httpResponse != null) {
- statusCode = httpResponse.getStatusCode();
- } else {
- if (request.shouldRetryConnectionErrors()) {
- attemptRetryOnException("connection", request, new NoConnectionError());
- return;
- } else {
- throw new NoConnectionError(exception);
- }
- }
- VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
- NetworkResponse networkResponse;
- if (responseContents != null) {
- List<Header> responseHeaders;
- responseHeaders = httpResponse.getHeaders();
- networkResponse =
- new NetworkResponse(
- statusCode,
- responseContents,
- /* notModified= */ false,
- SystemClock.elapsedRealtime() - requestStartMs,
- responseHeaders);
- if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED
- || statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
- attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
- } else if (statusCode >= 400 && statusCode <= 499) {
- // Don't retry other client errors.
- throw new ClientError(networkResponse);
- } else if (statusCode >= 500 && statusCode <= 599) {
- if (request.shouldRetryServerErrors()) {
- attemptRetryOnException(
- "server", request, new ServerError(networkResponse));
- } else {
- throw new ServerError(networkResponse);
- }
- } else {
- // 3xx? No reason to retry.
- throw new ServerError(networkResponse);
- }
- } else {
- attemptRetryOnException("network", request, new NetworkError());
- }
- }
- }
-}
diff --git a/src/main/java/com/android/volley/toolbox/NoAsyncCache.java b/src/main/java/com/android/volley/toolbox/NoAsyncCache.java
deleted file mode 100644
index aa4aeea..0000000
--- a/src/main/java/com/android/volley/toolbox/NoAsyncCache.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.android.volley.toolbox;
-
-import com.android.volley.AsyncCache;
-import com.android.volley.Cache;
-
-/** An AsyncCache that doesn't cache anything. */
-public class NoAsyncCache extends AsyncCache {
- @Override
- public void get(String key, OnGetCompleteCallback callback) {
- callback.onGetComplete(null);
- }
-
- @Override
- public void put(String key, Cache.Entry entry, OnWriteCompleteCallback callback) {
- callback.onWriteComplete();
- }
-
- @Override
- public void clear(OnWriteCompleteCallback callback) {
- callback.onWriteComplete();
- }
-
- @Override
- public void initialize(OnWriteCompleteCallback callback) {
- callback.onWriteComplete();
- }
-
- @Override
- public void invalidate(String key, boolean fullExpire, OnWriteCompleteCallback callback) {
- callback.onWriteComplete();
- }
-
- @Override
- public void remove(String key, OnWriteCompleteCallback callback) {
- callback.onWriteComplete();
- }
-}
diff --git a/src/main/java/com/android/volley/toolbox/UrlRewriter.java b/src/main/java/com/android/volley/toolbox/UrlRewriter.java
deleted file mode 100644
index 8bbb770..0000000
--- a/src/main/java/com/android/volley/toolbox/UrlRewriter.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.toolbox;
-
-import androidx.annotation.Nullable;
-
-/** An interface for transforming URLs before use. */
-public interface UrlRewriter {
- /**
- * Returns a URL to use instead of the provided one, or null to indicate this URL should not be
- * used at all.
- */
- @Nullable
- String rewriteUrl(String originalUrl);
-}
diff --git a/src/main/java/com/android/volley/toolbox/Volley.java b/src/main/java/com/android/volley/toolbox/Volley.java
index bc65c9c..1982802 100644
--- a/src/main/java/com/android/volley/toolbox/Volley.java
+++ b/src/main/java/com/android/volley/toolbox/Volley.java
@@ -86,22 +86,8 @@ public class Volley {
}
private static RequestQueue newRequestQueue(Context context, Network network) {
- final Context appContext = context.getApplicationContext();
- // Use a lazy supplier for the cache directory so that newRequestQueue() can be called on
- // main thread without causing strict mode violation.
- DiskBasedCache.FileSupplier cacheSupplier =
- new DiskBasedCache.FileSupplier() {
- private File cacheDir = null;
-
- @Override
- public File get() {
- if (cacheDir == null) {
- cacheDir = new File(appContext.getCacheDir(), DEFAULT_CACHE_DIR);
- }
- return cacheDir;
- }
- };
- RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheSupplier), network);
+ File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
+ RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
diff --git a/src/test/java/com/android/volley/AsyncRequestQueueTest.java b/src/test/java/com/android/volley/AsyncRequestQueueTest.java
deleted file mode 100644
index 54ff0a1..0000000
--- a/src/test/java/com/android/volley/AsyncRequestQueueTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.volley;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.android.volley.mock.ShadowSystemClock;
-import com.android.volley.toolbox.NoAsyncCache;
-import com.android.volley.toolbox.StringRequest;
-import com.android.volley.utils.ImmediateResponseDelivery;
-import com.google.common.util.concurrent.MoreExecutors;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ScheduledExecutorService;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-/** Unit tests for AsyncRequestQueue, with all dependencies mocked out */
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowSystemClock.class})
-public class AsyncRequestQueueTest {
-
- @Mock private AsyncNetwork mMockNetwork;
- @Mock private ScheduledExecutorService mMockScheduledExecutor;
- private AsyncRequestQueue queue;
-
- @Before
- public void setUp() throws Exception {
- ResponseDelivery mDelivery = new ImmediateResponseDelivery();
- initMocks(this);
- queue =
- new AsyncRequestQueue.Builder(mMockNetwork)
- .setAsyncCache(new NoAsyncCache())
- .setResponseDelivery(mDelivery)
- .setExecutorFactory(
- new AsyncRequestQueue.ExecutorFactory() {
- @Override
- public ExecutorService createNonBlockingExecutor(
- BlockingQueue<Runnable> taskQueue) {
- return MoreExecutors.newDirectExecutorService();
- }
-
- @Override
- public ExecutorService createBlockingExecutor(
- BlockingQueue<Runnable> taskQueue) {
- return MoreExecutors.newDirectExecutorService();
- }
-
- @Override
- public ScheduledExecutorService
- createNonBlockingScheduledExecutor() {
- return mMockScheduledExecutor;
- }
- })
- .build();
- }
-
- @Test
- public void cancelAll_onlyCorrectTag() throws Exception {
- queue.start();
- Object tagA = new Object();
- Object tagB = new Object();
- StringRequest req1 = mock(StringRequest.class);
- when(req1.getTag()).thenReturn(tagA);
- StringRequest req2 = mock(StringRequest.class);
- when(req2.getTag()).thenReturn(tagB);
- StringRequest req3 = mock(StringRequest.class);
- when(req3.getTag()).thenReturn(tagA);
- StringRequest req4 = mock(StringRequest.class);
- when(req4.getTag()).thenReturn(tagA);
-
- queue.add(req1); // A
- queue.add(req2); // B
- queue.add(req3); // A
- queue.cancelAll(tagA);
- queue.add(req4); // A
-
- verify(req1).cancel(); // A cancelled
- verify(req3).cancel(); // A cancelled
- verify(req2, never()).cancel(); // B not cancelled
- verify(req4, never()).cancel(); // A added after cancel not cancelled
- queue.stop();
- }
-
- @Test
- public void add_notifiesListener() throws Exception {
- RequestQueue.RequestEventListener listener = mock(RequestQueue.RequestEventListener.class);
- queue.start();
- queue.addRequestEventListener(listener);
- StringRequest req = mock(StringRequest.class);
-
- queue.add(req);
-
- verify(listener).onRequestEvent(req, RequestQueue.RequestEvent.REQUEST_QUEUED);
- verifyNoMoreInteractions(listener);
- queue.stop();
- }
-
- @Test
- public void finish_notifiesListener() throws Exception {
- RequestQueue.RequestEventListener listener = mock(RequestQueue.RequestEventListener.class);
- queue.start();
- queue.addRequestEventListener(listener);
- StringRequest req = mock(StringRequest.class);
-
- queue.finish(req);
-
- verify(listener).onRequestEvent(req, RequestQueue.RequestEvent.REQUEST_FINISHED);
- verifyNoMoreInteractions(listener);
- queue.stop();
- }
-
- @Test
- public void sendRequestEvent_notifiesListener() throws Exception {
- StringRequest req = mock(StringRequest.class);
- RequestQueue.RequestEventListener listener = mock(RequestQueue.RequestEventListener.class);
- queue.start();
- queue.addRequestEventListener(listener);
-
- queue.sendRequestEvent(req, RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
-
- verify(listener)
- .onRequestEvent(req, RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
- verifyNoMoreInteractions(listener);
- queue.stop();
- }
-
- @Test
- public void removeRequestEventListener_removesListener() throws Exception {
- StringRequest req = mock(StringRequest.class);
- RequestQueue.RequestEventListener listener = mock(RequestQueue.RequestEventListener.class);
- queue.start();
- queue.addRequestEventListener(listener);
- queue.removeRequestEventListener(listener);
-
- queue.sendRequestEvent(req, RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
-
- verifyNoMoreInteractions(listener);
- queue.stop();
- }
-}
diff --git a/src/test/java/com/android/volley/CacheDispatcherTest.java b/src/test/java/com/android/volley/CacheDispatcherTest.java
index aef6785..2592a0b 100644
--- a/src/test/java/com/android/volley/CacheDispatcherTest.java
+++ b/src/test/java/com/android/volley/CacheDispatcherTest.java
@@ -140,25 +140,6 @@ public class CacheDispatcherTest {
assertSame(entry, mRequest.getCacheEntry());
}
- // An fresh cache hit with parse error, does not post a response and queues to the network.
- @Test
- public void freshCacheHit_parseError() throws Exception {
- Request request = mock(Request.class);
- when(request.parseNetworkResponse(any(NetworkResponse.class)))
- .thenReturn(Response.error(new ParseError()));
- when(request.getCacheKey()).thenReturn("cache/key");
- Cache.Entry entry = CacheTestUtils.makeRandomCacheEntry(null, false, false);
- when(mCache.get(anyString())).thenReturn(entry);
-
- mDispatcher.processRequest(request);
-
- verifyNoResponse(mDelivery);
- verify(mNetworkQueue).put(request);
- assertNull(request.getCacheEntry());
- verify(mCache).invalidate("cache/key", true);
- verify(request).addMarker("cache-parsing-failed");
- }
-
@Test
public void duplicateCacheMiss() throws Exception {
StringRequest secondRequest =
diff --git a/src/test/java/com/android/volley/cronet/CronetHttpStackTest.java b/src/test/java/com/android/volley/cronet/CronetHttpStackTest.java
deleted file mode 100644
index cedb6ff..0000000
--- a/src/test/java/com/android/volley/cronet/CronetHttpStackTest.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.cronet;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import com.android.volley.Header;
-import com.android.volley.cronet.CronetHttpStack.CurlCommandLogger;
-import com.android.volley.mock.TestRequest;
-import com.android.volley.toolbox.AsyncHttpStack.OnRequestComplete;
-import com.android.volley.toolbox.UrlRewriter;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.MoreExecutors;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
-import org.chromium.net.CronetEngine;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class CronetHttpStackTest {
- @Mock private CurlCommandLogger mMockCurlCommandLogger;
- @Mock private OnRequestComplete mMockOnRequestComplete;
- @Mock private UrlRewriter mMockUrlRewriter;
-
- // A fake would be ideal here, but Cronet doesn't (yet) provide one, and at the moment we aren't
- // exercising the full response flow.
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private CronetEngine mMockCronetEngine;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void curlLogging_disabled() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- // Default parameters should not enable cURL logging.
- }
- });
-
- stack.executeRequest(
- new TestRequest.Get(), ImmutableMap.<String, String>of(), mMockOnRequestComplete);
-
- verify(mMockCurlCommandLogger, never()).logCurlCommand(anyString());
- }
-
- @Test
- public void curlLogging_simpleTextRequest() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true);
- }
- });
-
- stack.executeRequest(
- new TestRequest.Get(), ImmutableMap.<String, String>of(), mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- assertEquals("curl -X GET \"http://foo.com\"", curlCommandCaptor.getValue());
- }
-
- @Test
- public void curlLogging_rewrittenUrl() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true)
- .setUrlRewriter(mMockUrlRewriter);
- }
- });
- when(mMockUrlRewriter.rewriteUrl("http://foo.com")).thenReturn("http://bar.com");
-
- stack.executeRequest(
- new TestRequest.Get(), ImmutableMap.<String, String>of(), mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- assertEquals("curl -X GET \"http://bar.com\"", curlCommandCaptor.getValue());
- }
-
- @Test
- public void curlLogging_headers_withoutTokens() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true);
- }
- });
-
- stack.executeRequest(
- new TestRequest.Delete() {
- @Override
- public Map<String, String> getHeaders() {
- return ImmutableMap.of(
- "SomeHeader", "SomeValue",
- "Authorization", "SecretToken");
- }
- },
- ImmutableMap.of("SomeOtherHeader", "SomeValue"),
- mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- // NOTE: Header order is stable because the implementation uses a TreeMap.
- assertEquals(
- "curl -X DELETE --header \"Authorization: [REDACTED]\" "
- + "--header \"SomeHeader: SomeValue\" "
- + "--header \"SomeOtherHeader: SomeValue\" \"http://foo.com\"",
- curlCommandCaptor.getValue());
- }
-
- @Test
- public void curlLogging_headers_withTokens() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true)
- .setLogAuthTokensInCurlCommands(true);
- }
- });
-
- stack.executeRequest(
- new TestRequest.Delete() {
- @Override
- public Map<String, String> getHeaders() {
- return ImmutableMap.of(
- "SomeHeader", "SomeValue",
- "Authorization", "SecretToken");
- }
- },
- ImmutableMap.of("SomeOtherHeader", "SomeValue"),
- mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- // NOTE: Header order is stable because the implementation uses a TreeMap.
- assertEquals(
- "curl -X DELETE --header \"Authorization: SecretToken\" "
- + "--header \"SomeHeader: SomeValue\" "
- + "--header \"SomeOtherHeader: SomeValue\" \"http://foo.com\"",
- curlCommandCaptor.getValue());
- }
-
- @Test
- public void curlLogging_textRequest() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true);
- }
- });
-
- stack.executeRequest(
- new TestRequest.PostWithBody() {
- @Override
- public byte[] getBody() {
- try {
- return "hello".getBytes("UTF-8");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String getBodyContentType() {
- return "text/plain; charset=UTF-8";
- }
- },
- ImmutableMap.<String, String>of(),
- mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- assertEquals(
- "curl -X POST "
- + "--header \"Content-Type: text/plain; charset=UTF-8\" \"http://foo.com\" "
- + "--data-ascii \"hello\"",
- curlCommandCaptor.getValue());
- }
-
- @Test
- public void curlLogging_gzipTextRequest() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true);
- }
- });
-
- stack.executeRequest(
- new TestRequest.PostWithBody() {
- @Override
- public byte[] getBody() {
- return new byte[] {1, 2, 3, 4, 5};
- }
-
- @Override
- public String getBodyContentType() {
- return "text/plain";
- }
-
- @Override
- public Map<String, String> getHeaders() {
- return ImmutableMap.of("Content-Encoding", "gzip, identity");
- }
- },
- ImmutableMap.<String, String>of(),
- mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- assertEquals(
- "echo 'AQIDBAU=' | base64 -d > /tmp/$$.bin; curl -X POST "
- + "--header \"Content-Encoding: gzip, identity\" "
- + "--header \"Content-Type: text/plain\" \"http://foo.com\" "
- + "--data-binary @/tmp/$$.bin",
- curlCommandCaptor.getValue());
- }
-
- @Test
- public void curlLogging_binaryRequest() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true);
- }
- });
-
- stack.executeRequest(
- new TestRequest.PostWithBody() {
- @Override
- public byte[] getBody() {
- return new byte[] {1, 2, 3, 4, 5};
- }
-
- @Override
- public String getBodyContentType() {
- return "application/octet-stream";
- }
- },
- ImmutableMap.<String, String>of(),
- mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- assertEquals(
- "echo 'AQIDBAU=' | base64 -d > /tmp/$$.bin; curl -X POST "
- + "--header \"Content-Type: application/octet-stream\" \"http://foo.com\" "
- + "--data-binary @/tmp/$$.bin",
- curlCommandCaptor.getValue());
- }
-
- @Test
- public void curlLogging_largeRequest() {
- CronetHttpStack stack =
- createStack(
- new Consumer<CronetHttpStack.Builder>() {
- @Override
- public void accept(CronetHttpStack.Builder builder) {
- builder.setCurlLoggingEnabled(true);
- }
- });
-
- stack.executeRequest(
- new TestRequest.PostWithBody() {
- @Override
- public byte[] getBody() {
- return new byte[2048];
- }
-
- @Override
- public String getBodyContentType() {
- return "application/octet-stream";
- }
- },
- ImmutableMap.<String, String>of(),
- mMockOnRequestComplete);
-
- ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class);
- verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture());
- assertEquals(
- "curl -X POST "
- + "--header \"Content-Type: application/octet-stream\" \"http://foo.com\" "
- + "[REQUEST BODY TOO LARGE TO INCLUDE]",
- curlCommandCaptor.getValue());
- }
-
- @Test
- public void getHeadersEmptyTest() {
- List<Map.Entry<String, String>> list = new ArrayList<>();
- List<Header> actual = CronetHttpStack.getHeaders(list);
- List<Header> expected = new ArrayList<>();
- assertEquals(expected, actual);
- }
-
- @Test
- public void getHeadersNonEmptyTest() {
- Map<String, String> headers = new HashMap<>();
- for (int i = 1; i < 5; i++) {
- headers.put("key" + i, "value" + i);
- }
- List<Map.Entry<String, String>> list = new ArrayList<>(headers.entrySet());
- List<Header> actual = CronetHttpStack.getHeaders(list);
- List<Header> expected = new ArrayList<>();
- for (int i = 1; i < 5; i++) {
- expected.add(new Header("key" + i, "value" + i));
- }
- assertHeaderListsEqual(expected, actual);
- }
-
- private void assertHeaderListsEqual(List<Header> expected, List<Header> actual) {
- assertEquals(expected.size(), actual.size());
- for (int i = 0; i < expected.size(); i++) {
- assertEquals(expected.get(i).getName(), actual.get(i).getName());
- assertEquals(expected.get(i).getValue(), actual.get(i).getValue());
- }
- }
-
- private CronetHttpStack createStack(Consumer<CronetHttpStack.Builder> stackEditor) {
- CronetHttpStack.Builder builder =
- new CronetHttpStack.Builder(RuntimeEnvironment.application)
- .setCronetEngine(mMockCronetEngine)
- .setCurlCommandLogger(mMockCurlCommandLogger);
- stackEditor.accept(builder);
- CronetHttpStack stack = builder.build();
- stack.setBlockingExecutor(MoreExecutors.newDirectExecutorService());
- stack.setNonBlockingExecutor(MoreExecutors.newDirectExecutorService());
- return stack;
- }
-}
diff --git a/src/test/java/com/android/volley/mock/MockAsyncStack.java b/src/test/java/com/android/volley/mock/MockAsyncStack.java
deleted file mode 100644
index 5ea8343..0000000
--- a/src/test/java/com/android/volley/mock/MockAsyncStack.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.mock;
-
-import com.android.volley.AuthFailureError;
-import com.android.volley.Request;
-import com.android.volley.toolbox.AsyncHttpStack;
-import com.android.volley.toolbox.HttpResponse;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-public class MockAsyncStack extends AsyncHttpStack {
-
- private HttpResponse mResponseToReturn;
-
- private IOException mExceptionToThrow;
-
- private String mLastUrl;
-
- private Map<String, String> mLastHeaders;
-
- private byte[] mLastPostBody;
-
- public String getLastUrl() {
- return mLastUrl;
- }
-
- public Map<String, String> getLastHeaders() {
- return mLastHeaders;
- }
-
- public byte[] getLastPostBody() {
- return mLastPostBody;
- }
-
- public void setResponseToReturn(HttpResponse response) {
- mResponseToReturn = response;
- }
-
- public void setExceptionToThrow(IOException exception) {
- mExceptionToThrow = exception;
- }
-
- @Override
- public void executeRequest(
- Request<?> request, Map<String, String> additionalHeaders, OnRequestComplete callback) {
- if (mExceptionToThrow != null) {
- callback.onError(mExceptionToThrow);
- return;
- }
- mLastUrl = request.getUrl();
- mLastHeaders = new HashMap<>();
- try {
- if (request.getHeaders() != null) {
- mLastHeaders.putAll(request.getHeaders());
- }
- } catch (AuthFailureError authFailureError) {
- callback.onAuthError(authFailureError);
- return;
- }
- if (additionalHeaders != null) {
- mLastHeaders.putAll(additionalHeaders);
- }
- try {
- mLastPostBody = request.getBody();
- } catch (AuthFailureError e) {
- mLastPostBody = null;
- }
- callback.onSuccess(mResponseToReturn);
- }
-}
diff --git a/src/test/java/com/android/volley/toolbox/BasicAsyncNetworkTest.java b/src/test/java/com/android/volley/toolbox/BasicAsyncNetworkTest.java
deleted file mode 100644
index 91d4062..0000000
--- a/src/test/java/com/android/volley/toolbox/BasicAsyncNetworkTest.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.volley.toolbox;
-
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.*;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import com.android.volley.AsyncNetwork;
-import com.android.volley.AuthFailureError;
-import com.android.volley.Cache.Entry;
-import com.android.volley.Header;
-import com.android.volley.NetworkResponse;
-import com.android.volley.NoConnectionError;
-import com.android.volley.Request;
-import com.android.volley.Response;
-import com.android.volley.RetryPolicy;
-import com.android.volley.ServerError;
-import com.android.volley.TimeoutError;
-import com.android.volley.VolleyError;
-import com.android.volley.mock.MockAsyncStack;
-import com.google.common.util.concurrent.MoreExecutors;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.SocketTimeoutException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class BasicAsyncNetworkTest {
-
- @Mock private RetryPolicy mMockRetryPolicy;
- @Mock private AsyncNetwork.OnRequestComplete mockCallback;
- private ExecutorService executor = MoreExecutors.newDirectExecutorService();
-
- @Before
- public void setUp() throws Exception {
- initMocks(this);
- }
-
- @Test
- public void headersAndPostParams() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse =
- new HttpResponse(
- 200,
- Collections.<Header>emptyList(),
- "foobar".getBytes(StandardCharsets.UTF_8));
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- Entry entry = new Entry();
- entry.etag = "foobar";
- entry.lastModified = 1503102002000L;
- request.setCacheEntry(entry);
- perform(request, httpNetwork).get();
- assertEquals("foo", mockAsyncStack.getLastHeaders().get("requestheader"));
- assertEquals("foobar", mockAsyncStack.getLastHeaders().get("If-None-Match"));
- assertEquals(
- "Sat, 19 Aug 2017 00:20:02 GMT",
- mockAsyncStack.getLastHeaders().get("If-Modified-Since"));
- assertEquals(
- "requestpost=foo&",
- new String(mockAsyncStack.getLastPostBody(), StandardCharsets.UTF_8));
- }
-
- @Test
- public void headersAndPostParamsStream() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- ByteArrayInputStream stream = new ByteArrayInputStream("foobar".getBytes("UTF-8"));
- HttpResponse fakeResponse =
- new HttpResponse(200, Collections.<Header>emptyList(), 6, stream);
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- Entry entry = new Entry();
- entry.etag = "foobar";
- entry.lastModified = 1503102002000L;
- request.setCacheEntry(entry);
- perform(request, httpNetwork).get();
- assertEquals("foo", mockAsyncStack.getLastHeaders().get("requestheader"));
- assertEquals("foobar", mockAsyncStack.getLastHeaders().get("If-None-Match"));
- assertEquals(
- "Sat, 19 Aug 2017 00:20:02 GMT",
- mockAsyncStack.getLastHeaders().get("If-Modified-Since"));
- assertEquals(
- "requestpost=foo&",
- new String(mockAsyncStack.getLastPostBody(), StandardCharsets.UTF_8));
- }
-
- @Test
- public void notModified() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- List<Header> headers = new ArrayList<>();
- headers.add(new Header("ServerKeyA", "ServerValueA"));
- headers.add(new Header("ServerKeyB", "ServerValueB"));
- headers.add(new Header("SharedKey", "ServerValueShared"));
- headers.add(new Header("sharedcaseinsensitivekey", "ServerValueShared1"));
- headers.add(new Header("SharedCaseInsensitiveKey", "ServerValueShared2"));
- HttpResponse fakeResponse = new HttpResponse(HttpURLConnection.HTTP_NOT_MODIFIED, headers);
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- Entry entry = new Entry();
- entry.allResponseHeaders = new ArrayList<>();
- entry.allResponseHeaders.add(new Header("CachedKeyA", "CachedValueA"));
- entry.allResponseHeaders.add(new Header("CachedKeyB", "CachedValueB"));
- entry.allResponseHeaders.add(new Header("SharedKey", "CachedValueShared"));
- entry.allResponseHeaders.add(new Header("SHAREDCASEINSENSITIVEKEY", "CachedValueShared1"));
- entry.allResponseHeaders.add(new Header("shAREDcaSEinSENSITIVEkeY", "CachedValueShared2"));
- request.setCacheEntry(entry);
- httpNetwork.performRequest(request, mockCallback);
- NetworkResponse response = perform(request, httpNetwork).get();
- List<Header> expectedHeaders = new ArrayList<>();
- // Should have all server headers + cache headers that didn't show up in server response.
- expectedHeaders.add(new Header("ServerKeyA", "ServerValueA"));
- expectedHeaders.add(new Header("ServerKeyB", "ServerValueB"));
- expectedHeaders.add(new Header("SharedKey", "ServerValueShared"));
- expectedHeaders.add(new Header("sharedcaseinsensitivekey", "ServerValueShared1"));
- expectedHeaders.add(new Header("SharedCaseInsensitiveKey", "ServerValueShared2"));
- expectedHeaders.add(new Header("CachedKeyA", "CachedValueA"));
- expectedHeaders.add(new Header("CachedKeyB", "CachedValueB"));
- assertThat(expectedHeaders, containsInAnyOrder(response.allHeaders.toArray(new Header[0])));
- }
-
- @Test
- public void notModified_legacyCache() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- List<Header> headers = new ArrayList<>();
- headers.add(new Header("ServerKeyA", "ServerValueA"));
- headers.add(new Header("ServerKeyB", "ServerValueB"));
- headers.add(new Header("SharedKey", "ServerValueShared"));
- headers.add(new Header("sharedcaseinsensitivekey", "ServerValueShared1"));
- headers.add(new Header("SharedCaseInsensitiveKey", "ServerValueShared2"));
- HttpResponse fakeResponse = new HttpResponse(HttpURLConnection.HTTP_NOT_MODIFIED, headers);
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- Entry entry = new Entry();
- entry.responseHeaders = new HashMap<>();
- entry.responseHeaders.put("CachedKeyA", "CachedValueA");
- entry.responseHeaders.put("CachedKeyB", "CachedValueB");
- entry.responseHeaders.put("SharedKey", "CachedValueShared");
- entry.responseHeaders.put("SHAREDCASEINSENSITIVEKEY", "CachedValueShared1");
- entry.responseHeaders.put("shAREDcaSEinSENSITIVEkeY", "CachedValueShared2");
- request.setCacheEntry(entry);
- NetworkResponse response = perform(request, httpNetwork).get();
- List<Header> expectedHeaders = new ArrayList<>();
- // Should have all server headers + cache headers that didn't show up in server response.
- expectedHeaders.add(new Header("ServerKeyA", "ServerValueA"));
- expectedHeaders.add(new Header("ServerKeyB", "ServerValueB"));
- expectedHeaders.add(new Header("SharedKey", "ServerValueShared"));
- expectedHeaders.add(new Header("sharedcaseinsensitivekey", "ServerValueShared1"));
- expectedHeaders.add(new Header("SharedCaseInsensitiveKey", "ServerValueShared2"));
- expectedHeaders.add(new Header("CachedKeyA", "CachedValueA"));
- expectedHeaders.add(new Header("CachedKeyB", "CachedValueB"));
- assertThat(expectedHeaders, containsInAnyOrder(response.allHeaders.toArray(new Header[0])));
- }
-
- @Test
- public void socketTimeout() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- mockAsyncStack.setExceptionToThrow(new SocketTimeoutException());
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should retry socket timeouts
- verify(mMockRetryPolicy).retry(any(TimeoutError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test
- public void noConnectionDefault() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- mockAsyncStack.setExceptionToThrow(new IOException());
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should not retry when there is no connection
- verify(mMockRetryPolicy, never()).retry(any(VolleyError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test
- public void noConnectionRetry() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- mockAsyncStack.setExceptionToThrow(new IOException());
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- request.setShouldRetryConnectionErrors(true);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should retry when there is no connection
- verify(mMockRetryPolicy).retry(any(NoConnectionError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test
- public void noConnectionNoRetry() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- mockAsyncStack.setExceptionToThrow(new IOException());
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- request.setShouldRetryConnectionErrors(false);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should not retry when there is no connection
- verify(mMockRetryPolicy, never()).retry(any(VolleyError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test
- public void unauthorized() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse = new HttpResponse(401, Collections.<Header>emptyList());
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should retry in case it's an auth failure.
- verify(mMockRetryPolicy).retry(any(AuthFailureError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test(expected = RuntimeException.class)
- public void malformedUrlRequest() throws VolleyError, ExecutionException, InterruptedException {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- mockAsyncStack.setExceptionToThrow(new MalformedURLException());
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- perform(request, httpNetwork).get();
- }
-
- @Test
- public void forbidden() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse = new HttpResponse(403, Collections.<Header>emptyList());
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should retry in case it's an auth failure.
- verify(mMockRetryPolicy).retry(any(AuthFailureError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test
- public void redirect() throws Exception {
- for (int i = 300; i <= 399; i++) {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse = new HttpResponse(i, Collections.<Header>emptyList());
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- if (i != 304) {
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- } else {
- verify(mockCallback, never()).onError(any(VolleyError.class));
- verify(mockCallback, times(1)).onSuccess(any(NetworkResponse.class));
- }
- // should not retry 300 responses.
- verify(mMockRetryPolicy, never()).retry(any(VolleyError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
- }
-
- @Test
- public void otherClientError() throws Exception {
- for (int i = 400; i <= 499; i++) {
- if (i == 401 || i == 403) {
- // covered above.
- continue;
- }
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse = new HttpResponse(i, Collections.<Header>emptyList());
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should not retry other 400 errors.
- verify(mMockRetryPolicy, never()).retry(any(VolleyError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
- }
-
- @Test
- public void serverError_enableRetries() throws Exception {
- for (int i = 500; i <= 599; i++) {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse = new HttpResponse(i, Collections.<Header>emptyList());
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork =
- new BasicAsyncNetwork.Builder(mockAsyncStack)
- .setPool(new ByteArrayPool(4096))
- .build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- request.setShouldRetryServerErrors(true);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should retry all 500 errors
- verify(mMockRetryPolicy).retry(any(ServerError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
- }
-
- @Test
- public void serverError_disableRetries() throws Exception {
- for (int i = 500; i <= 599; i++) {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse = new HttpResponse(i, Collections.<Header>emptyList());
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onError(any(VolleyError.class));
- verify(mockCallback, never()).onSuccess(any(NetworkResponse.class));
- // should not retry any 500 error w/ HTTP 500 retries turned off (the default).
- verify(mMockRetryPolicy, never()).retry(any(VolleyError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
- }
-
- @Test
- public void notModifiedShortCircuit() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- List<Header> headers = new ArrayList<>();
- headers.add(new Header("ServerKeyA", "ServerValueA"));
- headers.add(new Header("ServerKeyB", "ServerValueB"));
- headers.add(new Header("SharedKey", "ServerValueShared"));
- headers.add(new Header("sharedcaseinsensitivekey", "ServerValueShared1"));
- headers.add(new Header("SharedCaseInsensitiveKey", "ServerValueShared2"));
- HttpResponse fakeResponse = new HttpResponse(HttpURLConnection.HTTP_NOT_MODIFIED, headers);
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onSuccess(any(NetworkResponse.class));
- verify(mockCallback, never()).onError(any(VolleyError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test
- public void performRequestSuccess() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse =
- new HttpResponse(
- 200,
- Collections.<Header>emptyList(),
- "foobar".getBytes(StandardCharsets.UTF_8));
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- httpNetwork.setBlockingExecutor(executor);
- Request<String> request = buildRequest();
- Entry entry = new Entry();
- entry.etag = "foobar";
- entry.lastModified = 1503102002000L;
- request.setCacheEntry(entry);
- httpNetwork.performRequest(request, mockCallback);
- verify(mockCallback, times(1)).onSuccess(any(NetworkResponse.class));
- verify(mockCallback, never()).onError(any(VolleyError.class));
- reset(mMockRetryPolicy, mockCallback);
- }
-
- @Test(expected = IllegalStateException.class)
- public void performRequestNeverSetExecutorTest() throws Exception {
- MockAsyncStack mockAsyncStack = new MockAsyncStack();
- HttpResponse fakeResponse = new HttpResponse(200, Collections.<Header>emptyList());
- mockAsyncStack.setResponseToReturn(fakeResponse);
- BasicAsyncNetwork httpNetwork = new BasicAsyncNetwork.Builder(mockAsyncStack).build();
- Request<String> request = buildRequest();
- perform(request, httpNetwork).get();
- }
-
- /** Helper functions */
- private CompletableFuture<NetworkResponse> perform(Request<?> request, AsyncNetwork network)
- throws VolleyError {
- final CompletableFuture<NetworkResponse> future = new CompletableFuture<>();
- network.performRequest(
- request,
- new AsyncNetwork.OnRequestComplete() {
- @Override
- public void onSuccess(NetworkResponse networkResponse) {
- future.complete(networkResponse);
- }
-
- @Override
- public void onError(VolleyError volleyError) {
- future.complete(null);
- }
- });
- return future;
- }
-
- private static Request<String> buildRequest() {
- return new Request<String>(Request.Method.GET, "http://foo", null) {
-
- @Override
- protected Response<String> parseNetworkResponse(NetworkResponse response) {
- return null;
- }
-
- @Override
- protected void deliverResponse(String response) {}
-
- @Override
- public Map<String, String> getHeaders() {
- Map<String, String> result = new HashMap<String, String>();
- result.put("requestheader", "foo");
- return result;
- }
-
- @Override
- public Map<String, String> getParams() {
- Map<String, String> result = new HashMap<String, String>();
- result.put("requestpost", "foo");
- return result;
- }
- };
- }
-}
diff --git a/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java b/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java
index 3630379..fec0694 100644
--- a/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java
+++ b/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java
@@ -30,7 +30,6 @@ import com.android.volley.AuthFailureError;
import com.android.volley.Cache.Entry;
import com.android.volley.Header;
import com.android.volley.NetworkResponse;
-import com.android.volley.NoConnectionError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.RetryPolicy;
@@ -177,7 +176,7 @@ public class BasicNetworkTest {
}
@Test
- public void noConnectionDefault() throws Exception {
+ public void noConnection() throws Exception {
MockHttpStack mockHttpStack = new MockHttpStack();
mockHttpStack.setExceptionToThrow(new IOException());
BasicNetwork httpNetwork = new BasicNetwork(mockHttpStack);
@@ -194,43 +193,6 @@ public class BasicNetworkTest {
}
@Test
- public void noConnectionRetry() throws Exception {
- MockHttpStack mockHttpStack = new MockHttpStack();
- mockHttpStack.setExceptionToThrow(new IOException());
- BasicNetwork httpNetwork = new BasicNetwork(mockHttpStack);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- request.setShouldRetryConnectionErrors(true);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- try {
- httpNetwork.performRequest(request);
- } catch (VolleyError e) {
- // expected
- }
- // should retry when there is no connection
- verify(mMockRetryPolicy).retry(any(NoConnectionError.class));
- reset(mMockRetryPolicy);
- }
-
- @Test
- public void noConnectionNoRetry() throws Exception {
- MockHttpStack mockHttpStack = new MockHttpStack();
- mockHttpStack.setExceptionToThrow(new IOException());
- BasicNetwork httpNetwork = new BasicNetwork(mockHttpStack);
- Request<String> request = buildRequest();
- request.setRetryPolicy(mMockRetryPolicy);
- request.setShouldRetryConnectionErrors(false);
- doThrow(new VolleyError()).when(mMockRetryPolicy).retry(any(VolleyError.class));
- try {
- httpNetwork.performRequest(request);
- } catch (VolleyError e) {
- // expected
- }
- // should not retry when there is no connection
- verify(mMockRetryPolicy, never()).retry(any(VolleyError.class));
- }
-
- @Test
public void unauthorized() throws Exception {
MockHttpStack mockHttpStack = new MockHttpStack();
HttpResponse fakeResponse = new HttpResponse(401, Collections.<Header>emptyList());
diff --git a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
index db6e491..e499a37 100644
--- a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
+++ b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
@@ -59,7 +59,7 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(sdk = 16)
+@Config(manifest = "src/main/AndroidManifest.xml", sdk = 16)
public class DiskBasedCacheTest {
private static final int MAX_SIZE = 1024 * 1024;
@@ -587,28 +587,11 @@ public class DiskBasedCacheTest {
public void publicMethods() throws Exception {
// Catch-all test to find API-breaking changes.
assertNotNull(DiskBasedCache.class.getConstructor(File.class, int.class));
- assertNotNull(
- DiskBasedCache.class.getConstructor(DiskBasedCache.FileSupplier.class, int.class));
assertNotNull(DiskBasedCache.class.getConstructor(File.class));
- assertNotNull(DiskBasedCache.class.getConstructor(DiskBasedCache.FileSupplier.class));
assertNotNull(DiskBasedCache.class.getMethod("getFileForKey", String.class));
}
- @Test
- public void initializeIfRootDirectoryDeleted() {
- temporaryFolder.delete();
-
- Cache.Entry entry = randomData(101);
- cache.put("key1", entry);
-
- assertThat(cache.get("key1"), is(nullValue()));
-
- // confirm that we can now store entries
- cache.put("key2", entry);
- assertThatEntriesAreEqual(cache.get("key2"), entry);
- }
-
/* Test helpers */
private void assertThatEntriesAreEqual(Cache.Entry actual, Cache.Entry expected) {
diff --git a/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
index 7780c3e..9b670f9 100644
--- a/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
+++ b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java
@@ -67,12 +67,6 @@ public class HttpHeaderParserTest {
}
@Test
- public void parseCacheHeaders_nullHeaders() {
- response = new NetworkResponse(0, null, null, false);
- assertNull(HttpHeaderParser.parseCacheHeaders(response));
- }
-
- @Test
public void parseCacheHeaders_headersSet() {
headers.put("MyCustomHeader", "42");
@@ -288,9 +282,6 @@ public class HttpHeaderParserTest {
// None specified, extra semicolon
headers.put("Content-Type", "text/plain;");
assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers));
-
- // No headers, use default charset
- assertEquals("utf-8", HttpHeaderParser.parseCharset(null, "utf-8"));
}
@Test
diff --git a/src/test/java/com/android/volley/toolbox/HurlStackTest.java b/src/test/java/com/android/volley/toolbox/HurlStackTest.java
index 7508244..c1fc92d 100644
--- a/src/test/java/com/android/volley/toolbox/HurlStackTest.java
+++ b/src/test/java/com/android/volley/toolbox/HurlStackTest.java
@@ -17,7 +17,6 @@
package com.android.volley.toolbox;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
@@ -25,16 +24,11 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.android.volley.Header;
-import com.android.volley.Request;
import com.android.volley.Request.Method;
import com.android.volley.mock.TestRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.FilterInputStream;
-import java.io.FilterOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
@@ -68,26 +62,6 @@ public class HurlStackTest {
protected HttpURLConnection createConnection(URL url) {
return mMockConnection;
}
-
- @Override
- protected InputStream createInputStream(
- Request<?> request, HttpURLConnection connection) {
- return new MonitoringInputStream(
- super.createInputStream(request, connection));
- }
-
- @Override
- protected OutputStream createOutputStream(
- Request<?> request, HttpURLConnection connection, int length)
- throws IOException {
- if (request instanceof MonitoredRequest) {
- return new MonitoringOutputStream(
- super.createOutputStream(request, connection, length),
- (MonitoredRequest) request,
- length);
- }
- return super.createOutputStream(request, connection, length);
- }
};
}
@@ -96,7 +70,7 @@ public class HurlStackTest {
TestRequest.DeprecatedGet request = new TestRequest.DeprecatedGet();
assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection, never()).setRequestMethod(anyString());
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -106,7 +80,7 @@ public class HurlStackTest {
TestRequest.DeprecatedPost request = new TestRequest.DeprecatedPost();
assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("POST");
verify(mMockConnection).setDoOutput(true);
}
@@ -116,7 +90,7 @@ public class HurlStackTest {
TestRequest.Get request = new TestRequest.Get();
assertEquals(request.getMethod(), Method.GET);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("GET");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -126,7 +100,7 @@ public class HurlStackTest {
TestRequest.Post request = new TestRequest.Post();
assertEquals(request.getMethod(), Method.POST);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("POST");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -136,7 +110,7 @@ public class HurlStackTest {
TestRequest.PostWithBody request = new TestRequest.PostWithBody();
assertEquals(request.getMethod(), Method.POST);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("POST");
verify(mMockConnection).setDoOutput(true);
}
@@ -146,7 +120,7 @@ public class HurlStackTest {
TestRequest.Put request = new TestRequest.Put();
assertEquals(request.getMethod(), Method.PUT);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("PUT");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -156,7 +130,7 @@ public class HurlStackTest {
TestRequest.PutWithBody request = new TestRequest.PutWithBody();
assertEquals(request.getMethod(), Method.PUT);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("PUT");
verify(mMockConnection).setDoOutput(true);
}
@@ -166,7 +140,7 @@ public class HurlStackTest {
TestRequest.Delete request = new TestRequest.Delete();
assertEquals(request.getMethod(), Method.DELETE);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("DELETE");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -176,7 +150,7 @@ public class HurlStackTest {
TestRequest.Head request = new TestRequest.Head();
assertEquals(request.getMethod(), Method.HEAD);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("HEAD");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -186,7 +160,7 @@ public class HurlStackTest {
TestRequest.Options request = new TestRequest.Options();
assertEquals(request.getMethod(), Method.OPTIONS);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("OPTIONS");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -196,7 +170,7 @@ public class HurlStackTest {
TestRequest.Trace request = new TestRequest.Trace();
assertEquals(request.getMethod(), Method.TRACE);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("TRACE");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -206,7 +180,7 @@ public class HurlStackTest {
TestRequest.Patch request = new TestRequest.Patch();
assertEquals(request.getMethod(), Method.PATCH);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("PATCH");
verify(mMockConnection, never()).setDoOutput(true);
}
@@ -216,7 +190,7 @@ public class HurlStackTest {
TestRequest.PatchWithBody request = new TestRequest.PatchWithBody();
assertEquals(request.getMethod(), Method.PATCH);
- mHurlStack.setConnectionParametersForRequest(mMockConnection, request);
+ HurlStack.setConnectionParametersForRequest(mMockConnection, request);
verify(mMockConnection).setRequestMethod("PATCH");
verify(mMockConnection).setDoOutput(true);
}
@@ -282,56 +256,4 @@ public class HurlStackTest {
expected.add(new Header("HeaderB", "ValueB_2"));
assertEquals(expected, result);
}
-
- @Test
- public void interceptResponseStream() throws Exception {
- when(mMockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
- when(mMockConnection.getInputStream())
- .thenReturn(new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)));
- HttpResponse response =
- mHurlStack.executeRequest(
- new TestRequest.Get(), Collections.<String, String>emptyMap());
- assertTrue(response.getContent() instanceof MonitoringInputStream);
- }
-
- @Test
- public void interceptRequestStream() throws Exception {
- MonitoredRequest request = new MonitoredRequest();
- mHurlStack.executeRequest(request, Collections.<String, String>emptyMap());
- assertTrue(request.totalRequestBytes > 0);
- assertEquals(request.totalRequestBytes, request.requestBytesRead);
- }
-
- private static class MonitoringInputStream extends FilterInputStream {
- private MonitoringInputStream(InputStream in) {
- super(in);
- }
- }
-
- private static class MonitoringOutputStream extends FilterOutputStream {
- private MonitoredRequest request;
-
- private MonitoringOutputStream(OutputStream out, MonitoredRequest request, int length) {
- super(out);
- this.request = request;
- this.request.totalRequestBytes = length;
- }
-
- @Override
- public void write(int b) throws IOException {
- this.request.requestBytesRead++;
- out.write(b);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- this.request.requestBytesRead += len;
- out.write(b, off, len);
- }
- }
-
- private static class MonitoredRequest extends TestRequest.PostWithBody {
- int requestBytesRead = 0;
- int totalRequestBytes = 0;
- }
}
diff --git a/src/test/java/com/android/volley/utils/CacheTestUtils.java b/src/test/java/com/android/volley/utils/CacheTestUtils.java
index 5980712..49ab996 100644
--- a/src/test/java/com/android/volley/utils/CacheTestUtils.java
+++ b/src/test/java/com/android/volley/utils/CacheTestUtils.java
@@ -16,11 +16,6 @@
package com.android.volley.utils;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-
import com.android.volley.Cache;
import java.util.Random;
@@ -56,34 +51,4 @@ public class CacheTestUtils {
public static Cache.Entry makeRandomCacheEntry(byte[] data) {
return makeRandomCacheEntry(data, false, false);
}
-
- public static void assertThatEntriesAreEqual(Cache.Entry actual, Cache.Entry expected) {
- assertNotNull(actual);
- assertThat(actual.data, is(equalTo(expected.data)));
- assertThat(actual.etag, is(equalTo(expected.etag)));
- assertThat(actual.lastModified, is(equalTo(expected.lastModified)));
- assertThat(actual.responseHeaders, is(equalTo(expected.responseHeaders)));
- assertThat(actual.serverDate, is(equalTo(expected.serverDate)));
- assertThat(actual.softTtl, is(equalTo(expected.softTtl)));
- assertThat(actual.ttl, is(equalTo(expected.ttl)));
- }
-
- public static Cache.Entry randomData(int length) {
- Cache.Entry entry = new Cache.Entry();
- byte[] data = new byte[length];
- new Random(42).nextBytes(data); // explicit seed for reproducible results
- entry.data = data;
- return entry;
- }
-
- public static int getEntrySizeOnDisk(String key) {
- // Header size is:
- // 4 bytes for magic int
- // 8 + len(key) bytes for key (long length)
- // 8 bytes for etag (long length + 0 characters)
- // 32 bytes for serverDate, lastModified, ttl, and softTtl longs
- // 4 bytes for length of header list int
- // == 56 + len(key) bytes total.
- return 56 + key.length();
- }
}