1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.volley.toolbox; 18 19 import androidx.annotation.Nullable; 20 import androidx.annotation.RestrictTo; 21 import com.android.volley.AuthFailureError; 22 import com.android.volley.Request; 23 import com.android.volley.VolleyLog; 24 import java.io.IOException; 25 import java.io.InterruptedIOException; 26 import java.util.Map; 27 import java.util.concurrent.CountDownLatch; 28 import java.util.concurrent.ExecutorService; 29 import java.util.concurrent.atomic.AtomicReference; 30 31 /** 32 * Asynchronous extension of the {@link BaseHttpStack} class. 33 * 34 * <p><b>WARNING</b>: This API is experimental and subject to breaking changes. Please see 35 * https://github.com/google/volley/wiki/Asynchronous-Volley for more details. 36 */ 37 public abstract class AsyncHttpStack extends BaseHttpStack { 38 private ExecutorService mBlockingExecutor; 39 private ExecutorService mNonBlockingExecutor; 40 41 public interface OnRequestComplete { 42 /** Invoked when the stack successfully completes a request. */ onSuccess(HttpResponse httpResponse)43 void onSuccess(HttpResponse httpResponse); 44 45 /** Invoked when the stack throws an {@link AuthFailureError} during a request. */ onAuthError(AuthFailureError authFailureError)46 void onAuthError(AuthFailureError authFailureError); 47 48 /** Invoked when the stack throws an {@link IOException} during a request. */ onError(IOException ioException)49 void onError(IOException ioException); 50 } 51 52 /** 53 * Makes an HTTP request with the given parameters, and calls the {@link OnRequestComplete} 54 * callback, with either the {@link HttpResponse} or error that was thrown. 55 * 56 * @param request to perform 57 * @param additionalHeaders to be sent together with {@link Request#getHeaders()} 58 * @param callback to be called after retrieving the {@link HttpResponse} or throwing an error. 59 */ executeRequest( Request<?> request, Map<String, String> additionalHeaders, OnRequestComplete callback)60 public abstract void executeRequest( 61 Request<?> request, Map<String, String> additionalHeaders, OnRequestComplete callback); 62 63 /** 64 * This method sets the non blocking executor to be used by the stack for non-blocking tasks. 65 * This method must be called before executing any requests. 66 */ 67 @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP}) setNonBlockingExecutor(ExecutorService executor)68 public void setNonBlockingExecutor(ExecutorService executor) { 69 mNonBlockingExecutor = executor; 70 } 71 72 /** 73 * This method sets the blocking executor to be used by the stack for potentially blocking 74 * tasks. This method must be called before executing any requests. 75 */ 76 @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP}) setBlockingExecutor(ExecutorService executor)77 public void setBlockingExecutor(ExecutorService executor) { 78 mBlockingExecutor = executor; 79 } 80 81 /** Gets blocking executor to perform any potentially blocking tasks. */ getBlockingExecutor()82 protected ExecutorService getBlockingExecutor() { 83 return mBlockingExecutor; 84 } 85 86 /** Gets non-blocking executor to perform any non-blocking tasks. */ getNonBlockingExecutor()87 protected ExecutorService getNonBlockingExecutor() { 88 return mNonBlockingExecutor; 89 } 90 91 /** 92 * Performs an HTTP request with the given parameters. 93 * 94 * @param request the request to perform 95 * @param additionalHeaders additional headers to be sent together with {@link 96 * Request#getHeaders()} 97 * @return the {@link HttpResponse} 98 * @throws IOException if an I/O error occurs during the request 99 * @throws AuthFailureError if an authentication failure occurs during the request 100 */ 101 @Override executeRequest( Request<?> request, Map<String, String> additionalHeaders)102 public final HttpResponse executeRequest( 103 Request<?> request, Map<String, String> additionalHeaders) 104 throws IOException, AuthFailureError { 105 final CountDownLatch latch = new CountDownLatch(1); 106 final AtomicReference<Response> entry = new AtomicReference<>(); 107 executeRequest( 108 request, 109 additionalHeaders, 110 new OnRequestComplete() { 111 @Override 112 public void onSuccess(HttpResponse httpResponse) { 113 Response response = 114 new Response( 115 httpResponse, 116 /* ioException= */ null, 117 /* authFailureError= */ null); 118 entry.set(response); 119 latch.countDown(); 120 } 121 122 @Override 123 public void onAuthError(AuthFailureError authFailureError) { 124 Response response = 125 new Response( 126 /* httpResponse= */ null, 127 /* ioException= */ null, 128 authFailureError); 129 entry.set(response); 130 latch.countDown(); 131 } 132 133 @Override 134 public void onError(IOException ioException) { 135 Response response = 136 new Response( 137 /* httpResponse= */ null, 138 ioException, 139 /* authFailureError= */ null); 140 entry.set(response); 141 latch.countDown(); 142 } 143 }); 144 try { 145 latch.await(); 146 } catch (InterruptedException e) { 147 VolleyLog.e(e, "while waiting for CountDownLatch"); 148 Thread.currentThread().interrupt(); 149 throw new InterruptedIOException(e.toString()); 150 } 151 Response response = entry.get(); 152 if (response.httpResponse != null) { 153 return response.httpResponse; 154 } else if (response.ioException != null) { 155 throw response.ioException; 156 } else { 157 throw response.authFailureError; 158 } 159 } 160 161 private static class Response { 162 HttpResponse httpResponse; 163 IOException ioException; 164 AuthFailureError authFailureError; 165 Response( @ullable HttpResponse httpResponse, @Nullable IOException ioException, @Nullable AuthFailureError authFailureError)166 private Response( 167 @Nullable HttpResponse httpResponse, 168 @Nullable IOException ioException, 169 @Nullable AuthFailureError authFailureError) { 170 this.httpResponse = httpResponse; 171 this.ioException = ioException; 172 this.authFailureError = authFailureError; 173 } 174 } 175 } 176