aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java')
-rw-r--r--src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java b/src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java
new file mode 100644
index 0000000..55892a0
--- /dev/null
+++ b/src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java
@@ -0,0 +1,288 @@
+/*
+ * 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);
+ }
+ }
+}