The interceptor should be used as the last application interceptor to ensure that all other - * interceptors are visited before sending the request on wire and after a response is returned. - * - *
The interceptor is a plug-and-play replacement for the OkHttp stack for the most part, - * however, there are some caveats to keep in mind: - * - *
Translating the UrlResponseInfo is relatively straightforward as the entire object is - * available immediately and is relatively small, so it can easily fit in memory. - * - *
Translating the body is a bit more tricky because of the mismatch between OkHttp and Cronet
- * designs. We invoke Cronet's read and wait for the result using synchronization primitives (see
- * BodySource implementation). The implementation is assuming that there's always at most one read()
- * request in flight (which is safe to assume), and relies on reasonable fairness of thread
- * scheduling, especially when handling cancellations.
- */
-class OkHttpBridgeRequestCallback extends UrlRequest.Callback {
-
- /**
- * The byte buffer capacity for reading Cronet response bodies. Each response callback will
- * allocate its own buffer of this size once the response starts being processed.
- */
- private static final int CRONET_BYTE_BUFFER_CAPACITY = 32 * 1024;
-
- /** A bridge between Cronet's asynchronous callbacks and OkHttp's blocking stream-like reads. */
- private final SettableFuture Has a capacity of 2 - at most one slot for a read result and at most 1 slot for cancellation
- * signal, this guarantees that all inserts are non blocking.
- */
- private final BlockingQueue Note that retrieving data from the {@code Source} instance might block further as the
- * response body is streamed.
- */
- ListenableFuture Note that because of Cronet's limitations
- * (https://developer.android.com/guide/topics/connectivity/cronet/lifecycle#overview) it is
- * impossible to retrieve the body of a redirect response. As a result, a dummy empty body will
- * always be provided.
- */
- public static RedirectStrategy withoutRedirects() {
- return WithoutRedirectsHolder.INSTANCE;
- }
-
- /**
- * Returns a strategy which will follow redirects up to {@link #DEFAULT_REDIRECTS} times. If more
- * redirects are attempted an exception is thrown.
- */
- public static RedirectStrategy defaultStrategy() {
- return DefaultRedirectsHolder.INSTANCE;
- }
-
- private static class WithoutRedirectsHolder {
- private static final RedirectStrategy INSTANCE =
- new RedirectStrategy() {
- @Override
- boolean followRedirects() {
- return false;
- }
-
- @Override
- int numberOfRedirectsToFollow() {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- private static class DefaultRedirectsHolder {
- private static final RedirectStrategy INSTANCE =
- new RedirectStrategy() {
- @Override
- boolean followRedirects() {
- return true;
- }
-
- @Override
- int numberOfRedirectsToFollow() {
- return DEFAULT_REDIRECTS;
- }
- };
- }
-
- private RedirectStrategy() {}
-}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/cronet/RequestBodyConverter.java b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/cronet/RequestBodyConverter.java
deleted file mode 100644
index cbac69bd5..000000000
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/cronet/RequestBodyConverter.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tonapps.wallet.api.cronet;
-
-import java.io.IOException;
-import okhttp3.RequestBody;
-import org.chromium.net.UploadDataProvider;
-
-/** An interface for classes converting from OkHttp to Cronet request bodies. */
-interface RequestBodyConverter {
- UploadDataProvider convertRequestBody(RequestBody requestBody, int writeTimeoutMillis)
- throws IOException;
-}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/cronet/RequestBodyConverterImpl.java b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/cronet/RequestBodyConverterImpl.java
deleted file mode 100644
index 89babfe9d..000000000
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/cronet/RequestBodyConverterImpl.java
+++ /dev/null
@@ -1,319 +0,0 @@
-package com.tonapps.wallet.api.cronet;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
-import androidx.annotation.VisibleForTesting;
-import com.google.common.base.Verify;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.Uninterruptibles;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeoutException;
-import okhttp3.RequestBody;
-import okio.Buffer;
-import okio.BufferedSink;
-import okio.Okio;
-import org.chromium.net.UploadDataProvider;
-import org.chromium.net.UploadDataSink;
-
-final class RequestBodyConverterImpl implements RequestBodyConverter {
-
- private static final long IN_MEMORY_BODY_LENGTH_THRESHOLD_BYTES = 1024 * 1024;
-
- private final InMemoryRequestBodyConverter inMemoryRequestBodyConverter;
- private final StreamingRequestBodyConverter streamingRequestBodyConverter;
-
- RequestBodyConverterImpl(
- InMemoryRequestBodyConverter inMemoryConverter,
- StreamingRequestBodyConverter streamingConverter) {
- this.inMemoryRequestBodyConverter = inMemoryConverter;
- this.streamingRequestBodyConverter = streamingConverter;
- }
-
- static RequestBodyConverterImpl create(ExecutorService bodyReaderExecutor) {
- return new RequestBodyConverterImpl(
- new InMemoryRequestBodyConverter(), new StreamingRequestBodyConverter(bodyReaderExecutor));
- }
-
- @Override
- public UploadDataProvider convertRequestBody(RequestBody requestBody, int writeTimeoutMillis)
- throws IOException {
- long contentLength = requestBody.contentLength();
- if (contentLength == -1 || contentLength > IN_MEMORY_BODY_LENGTH_THRESHOLD_BYTES) {
- return streamingRequestBodyConverter.convertRequestBody(requestBody, writeTimeoutMillis);
- } else {
- return inMemoryRequestBodyConverter.convertRequestBody(requestBody, writeTimeoutMillis);
- }
- }
-
- /**
- * Implementation of {@link RequestBodyConverter} that doesn't need to hold the entire request
- * body in memory.
- *
- *
- *
- *
- *
- *
- * This is repeated until the entire body has been read.
- */
- @VisibleForTesting
- static final class StreamingRequestBodyConverter implements RequestBodyConverter {
-
- private final ExecutorService readerExecutor;
-
- StreamingRequestBodyConverter(ExecutorService readerExecutor) {
- this.readerExecutor = readerExecutor;
- }
-
- @Override
- public UploadDataProvider convertRequestBody(RequestBody requestBody, int writeTimeoutMillis) {
- return new StreamingUploadDataProvider(
- requestBody, new UploadBodyDataBroker(), readerExecutor, writeTimeoutMillis);
- }
-
- private static class StreamingUploadDataProvider extends UploadDataProvider {
- private final RequestBody okHttpRequestBody;
- private final UploadBodyDataBroker broker;
- private final ListeningExecutorService readTaskExecutor;
- private final long writeTimeoutMillis;
-
- /** The future for the task that reads the OkHttp request body in the background. */
- private ListenableFuture> readTaskFuture;
-
- /** The number of bytes we read from the OkHttp body thus far. */
- private long totalBytesReadFromOkHttp;
-
- private StreamingUploadDataProvider(
- RequestBody okHttpRequestBody,
- UploadBodyDataBroker broker,
- ExecutorService readTaskExecutor,
- long writeTimeoutMillis) {
- this.okHttpRequestBody = okHttpRequestBody;
- this.broker = broker;
- if (readTaskExecutor instanceof ListeningExecutorService) {
- this.readTaskExecutor = (ListeningExecutorService) readTaskExecutor;
- } else {
- this.readTaskExecutor = MoreExecutors.listeningDecorator(readTaskExecutor);
- }
-
- // So that we don't have to special case infinity. Int.MAX_VALUE is ~infinity for all
- // practical use cases.
- this.writeTimeoutMillis = writeTimeoutMillis == 0 ? Integer.MAX_VALUE : writeTimeoutMillis;
- }
-
- @Override
- public long getLength() throws IOException {
- return okHttpRequestBody.contentLength();
- }
-
- @Override
- public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) throws IOException {
- ensureReadTaskStarted();
-
- if (getLength() == -1) {
- readUnknownBodyLength(uploadDataSink, byteBuffer);
- } else {
- readKnownBodyLength(uploadDataSink, byteBuffer);
- }
- }
-
- private void readKnownBodyLength(UploadDataSink uploadDataSink, ByteBuffer byteBuffer)
- throws IOException {
- try {
- UploadBodyDataBroker.ReadResult readResult = readFromOkHttp(byteBuffer);
-
- if (totalBytesReadFromOkHttp > getLength()) {
- throw prepareBodyTooLongException(getLength(), totalBytesReadFromOkHttp);
- }
-
- if (totalBytesReadFromOkHttp < getLength()) {
- switch (readResult) {
- case SUCCESS:
- uploadDataSink.onReadSucceeded(false);
- break;
- case END_OF_BODY:
- throw new IOException("The source has been exhausted but we expected more data!");
- }
- return;
- }
- // Else we're handling what's supposed to be the last chunk
- handleLastBodyRead(uploadDataSink, byteBuffer);
-
- } catch (TimeoutException | ExecutionException e) {
- readTaskFuture.cancel(true);
- uploadDataSink.onReadError(new IOException(e));
- }
- }
-
- /**
- * The last body read is special for fixed length bodies - if Cronet receives exactly the
- * right amount of data it won't ask for more, even if there is more data in the stream. As a
- * result, when we read the advertised number of bytes, we need to make sure that the stream
- * is indeed finished.
- */
- private void handleLastBodyRead(UploadDataSink uploadDataSink, ByteBuffer filledByteBuffer)
- throws IOException, TimeoutException, ExecutionException {
- // We reuse the same buffer for the END_OF_DATA read (it should be non-destructive and if
- // it overwrites what's in there we don't mind as that's an error anyway). We just need
- // to make sure we restore the original position afterwards. We don't use mark() / reset()
- // as the mark position can be invalidated by limit manipulation.
- int bufferPosition = filledByteBuffer.position();
- filledByteBuffer.position(0);
-
- UploadBodyDataBroker.ReadResult readResult = readFromOkHttp(filledByteBuffer);
-
- if (!readResult.equals(UploadBodyDataBroker.ReadResult.END_OF_BODY)) {
- throw prepareBodyTooLongException(getLength(), totalBytesReadFromOkHttp);
- }
-
- Verify.verify(
- filledByteBuffer.position() == 0,
- "END_OF_BODY reads shouldn't write anything to the buffer");
-
- // revert the position change
- filledByteBuffer.position(bufferPosition);
-
- uploadDataSink.onReadSucceeded(false);
- }
-
- private void readUnknownBodyLength(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
- try {
- UploadBodyDataBroker.ReadResult readResult = readFromOkHttp(byteBuffer);
- uploadDataSink.onReadSucceeded(readResult.equals(UploadBodyDataBroker.ReadResult.END_OF_BODY));
- } catch (TimeoutException | ExecutionException e) {
- readTaskFuture.cancel(true);
- uploadDataSink.onReadError(new IOException(e));
- }
- }
-
- private void ensureReadTaskStarted() {
- // We don't expect concurrent calls so a simple flag is sufficient
- if (readTaskFuture == null) {
- readTaskFuture =
- readTaskExecutor.submit(
- (Callable