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 /** Asynchronous extension of the {@link BaseHttpStack} class. */ 32 public abstract class AsyncHttpStack extends BaseHttpStack { 33 private ExecutorService mBlockingExecutor; 34 private ExecutorService mNonBlockingExecutor; 35 36 public interface OnRequestComplete { 37 /** Invoked when the stack successfully completes a request. */ onSuccess(HttpResponse httpResponse)38 void onSuccess(HttpResponse httpResponse); 39 40 /** Invoked when the stack throws an {@link AuthFailureError} during a request. */ onAuthError(AuthFailureError authFailureError)41 void onAuthError(AuthFailureError authFailureError); 42 43 /** Invoked when the stack throws an {@link IOException} during a request. */ onError(IOException ioException)44 void onError(IOException ioException); 45 } 46 47 /** 48 * Makes an HTTP request with the given parameters, and calls the {@link OnRequestComplete} 49 * callback, with either the {@link HttpResponse} or error that was thrown. 50 * 51 * @param request to perform 52 * @param additionalHeaders to be sent together with {@link Request#getHeaders()} 53 * @param callback to be called after retrieving the {@link HttpResponse} or throwing an error. 54 */ executeRequest( Request<?> request, Map<String, String> additionalHeaders, OnRequestComplete callback)55 public abstract void executeRequest( 56 Request<?> request, Map<String, String> additionalHeaders, OnRequestComplete callback); 57 58 /** 59 * This method sets the non blocking executor to be used by the stack for non-blocking tasks. 60 * This method must be called before executing any requests. 61 */ 62 @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP}) setNonBlockingExecutor(ExecutorService executor)63 public void setNonBlockingExecutor(ExecutorService executor) { 64 mNonBlockingExecutor = executor; 65 } 66 67 /** 68 * This method sets the blocking executor to be used by the stack for potentially blocking 69 * tasks. This method must be called before executing any requests. 70 */ 71 @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP}) setBlockingExecutor(ExecutorService executor)72 public void setBlockingExecutor(ExecutorService executor) { 73 mBlockingExecutor = executor; 74 } 75 76 /** Gets blocking executor to perform any potentially blocking tasks. */ getBlockingExecutor()77 protected ExecutorService getBlockingExecutor() { 78 return mBlockingExecutor; 79 } 80 81 /** Gets non-blocking executor to perform any non-blocking tasks. */ getNonBlockingExecutor()82 protected ExecutorService getNonBlockingExecutor() { 83 return mNonBlockingExecutor; 84 } 85 86 /** 87 * Performs an HTTP request with the given parameters. 88 * 89 * @param request the request to perform 90 * @param additionalHeaders additional headers to be sent together with {@link 91 * Request#getHeaders()} 92 * @return the {@link HttpResponse} 93 * @throws IOException if an I/O error occurs during the request 94 * @throws AuthFailureError if an authentication failure occurs during the request 95 */ 96 @Override executeRequest( Request<?> request, Map<String, String> additionalHeaders)97 public final HttpResponse executeRequest( 98 Request<?> request, Map<String, String> additionalHeaders) 99 throws IOException, AuthFailureError { 100 final CountDownLatch latch = new CountDownLatch(1); 101 final AtomicReference<Response> entry = new AtomicReference<>(); 102 executeRequest( 103 request, 104 additionalHeaders, 105 new OnRequestComplete() { 106 @Override 107 public void onSuccess(HttpResponse httpResponse) { 108 Response response = 109 new Response( 110 httpResponse, 111 /* ioException= */ null, 112 /* authFailureError= */ null); 113 entry.set(response); 114 latch.countDown(); 115 } 116 117 @Override 118 public void onAuthError(AuthFailureError authFailureError) { 119 Response response = 120 new Response( 121 /* httpResponse= */ null, 122 /* ioException= */ null, 123 authFailureError); 124 entry.set(response); 125 latch.countDown(); 126 } 127 128 @Override 129 public void onError(IOException ioException) { 130 Response response = 131 new Response( 132 /* httpResponse= */ null, 133 ioException, 134 /* authFailureError= */ null); 135 entry.set(response); 136 latch.countDown(); 137 } 138 }); 139 try { 140 latch.await(); 141 } catch (InterruptedException e) { 142 VolleyLog.e(e, "while waiting for CountDownLatch"); 143 Thread.currentThread().interrupt(); 144 throw new InterruptedIOException(e.toString()); 145 } 146 Response response = entry.get(); 147 if (response.httpResponse != null) { 148 return response.httpResponse; 149 } else if (response.ioException != null) { 150 throw response.ioException; 151 } else { 152 throw response.authFailureError; 153 } 154 } 155 156 private static class Response { 157 HttpResponse httpResponse; 158 IOException ioException; 159 AuthFailureError authFailureError; 160 Response( @ullable HttpResponse httpResponse, @Nullable IOException ioException, @Nullable AuthFailureError authFailureError)161 private Response( 162 @Nullable HttpResponse httpResponse, 163 @Nullable IOException ioException, 164 @Nullable AuthFailureError authFailureError) { 165 this.httpResponse = httpResponse; 166 this.ioException = ioException; 167 this.authFailureError = authFailureError; 168 } 169 } 170 } 171