| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.net.apihelpers; |
| |
| import androidx.annotation.Nullable; |
| |
| import org.json.JSONObject; |
| |
| import org.chromium.net.CronetException; |
| import org.chromium.net.UrlResponseInfo; |
| |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.Future; |
| |
| /** |
| * Utility class for creating simple, convenient {@code UrlRequest.Callback} implementations for |
| * reading common types of responses. |
| * |
| * <p>Note that the convenience callbacks store the entire response body in memory. We do not |
| * recommend using them if it's possible to stream the response body, or if the response body sizes |
| * can cause strain on the on-device resources. |
| * |
| * <p>The helper callbacks come in two flavors - either the caller provides a callback to be |
| * invoked when the request finishes (successfully or not), or the caller is given a {@link Future} |
| * which completes when Cronet finishes processing the request. |
| */ |
| public class UrlRequestCallbacks { |
| public static ByteArrayCronetCallback forByteArrayBody( |
| RedirectHandler redirectHandler, CronetRequestCompletionListener<byte[]> listener) { |
| return newByteArrayCallback(redirectHandler).addCompletionListener(listener); |
| } |
| |
| public static CallbackAndResponseFuturePair<byte[], ByteArrayCronetCallback> forByteArrayBody( |
| RedirectHandler redirectHandler) { |
| ByteArrayCronetCallback callback = newByteArrayCallback(redirectHandler); |
| Future<CronetResponse<byte[]>> future = addResponseFutureListener(callback); |
| return new CallbackAndResponseFuturePair<>(future, callback); |
| } |
| |
| public static StringCronetCallback forStringBody( |
| RedirectHandler redirectHandler, CronetRequestCompletionListener<String> listener) { |
| return newStringCallback(redirectHandler).addCompletionListener(listener); |
| } |
| |
| public static CallbackAndResponseFuturePair<String, StringCronetCallback> forStringBody( |
| RedirectHandler redirectHandler) { |
| StringCronetCallback callback = newStringCallback(redirectHandler); |
| Future<CronetResponse<String>> future = addResponseFutureListener(callback); |
| return new CallbackAndResponseFuturePair<>(future, callback); |
| } |
| |
| public static JsonCronetCallback forJsonBody( |
| RedirectHandler redirectHandler, CronetRequestCompletionListener<JSONObject> listener) { |
| return newJsonCallback(redirectHandler).addCompletionListener(listener); |
| } |
| |
| public static CallbackAndResponseFuturePair<JSONObject, JsonCronetCallback> forJsonBody( |
| RedirectHandler redirectHandler) { |
| JsonCronetCallback callback = newJsonCallback(redirectHandler); |
| Future<CronetResponse<JSONObject>> future = addResponseFutureListener(callback); |
| return new CallbackAndResponseFuturePair<>(future, callback); |
| } |
| |
| private static ByteArrayCronetCallback newByteArrayCallback(RedirectHandler redirectHandler) { |
| return new ByteArrayCronetCallback() { |
| @Override |
| protected boolean shouldFollowRedirect(UrlResponseInfo info, String newLocationUrl) |
| throws Exception { |
| return redirectHandler.shouldFollowRedirect(info, newLocationUrl); |
| } |
| }; |
| } |
| |
| private static StringCronetCallback newStringCallback(RedirectHandler redirectHandler) { |
| return new StringCronetCallback() { |
| @Override |
| protected boolean shouldFollowRedirect(UrlResponseInfo info, String newLocationUrl) |
| throws Exception { |
| return redirectHandler.shouldFollowRedirect(info, newLocationUrl); |
| } |
| }; |
| } |
| |
| private static JsonCronetCallback newJsonCallback(RedirectHandler redirectHandler) { |
| return new JsonCronetCallback() { |
| @Override |
| protected boolean shouldFollowRedirect(UrlResponseInfo info, String newLocationUrl) |
| throws Exception { |
| return redirectHandler.shouldFollowRedirect(info, newLocationUrl); |
| } |
| }; |
| } |
| |
| private static <T> Future<CronetResponse<T>> addResponseFutureListener( |
| InMemoryTransformCronetCallback<T> callback) { |
| CompletableFuture<CronetResponse<T>> completableFuture = new CompletableFuture<>(); |
| callback.addCompletionListener( |
| new CronetRequestCompletionListener<T>() { |
| @Override |
| public void onFailed( |
| @Nullable UrlResponseInfo info, CronetException exception) { |
| completableFuture.completeExceptionally(exception); |
| } |
| |
| @Override |
| public void onCanceled(@Nullable UrlResponseInfo info) { |
| completableFuture.completeExceptionally( |
| new CronetException("The request was canceled!", null) {}); |
| } |
| |
| @Override |
| public void onSucceeded(UrlResponseInfo info, T body) { |
| completableFuture.complete(new CronetResponse<>(info, body)); |
| } |
| }); |
| return completableFuture; |
| } |
| |
| /** |
| * A named pair-like structure encapsulating Cronet callbacks and associated response futures. |
| * |
| * <p>The request should be used to pass to {@code CronetEngine.newUrlRequest()}, the future |
| * will contain the response to the request. |
| * |
| * @param <CallbackT> the subtype of the callback |
| * @param <ResponseBodyT> The type of the deserialized response body |
| */ |
| public static class CallbackAndResponseFuturePair< |
| ResponseBodyT, CallbackT extends InMemoryTransformCronetCallback<ResponseBodyT>> { |
| private final Future<CronetResponse<ResponseBodyT>> mFuture; |
| private final CallbackT mCallback; |
| |
| CallbackAndResponseFuturePair( |
| Future<CronetResponse<ResponseBodyT>> future, CallbackT callback) { |
| this.mFuture = future; |
| this.mCallback = callback; |
| } |
| |
| public Future<CronetResponse<ResponseBodyT>> getFuture() { |
| return mFuture; |
| } |
| |
| public CallbackT getCallback() { |
| return mCallback; |
| } |
| } |
| |
| private UrlRequestCallbacks() {} |
| } |