chromium/components/cronet/android/java/src/org/chromium/net/impl/CronetExceptionTranslationUtils.java

// Copyright 2023 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.impl;

import static org.chromium.net.impl.HttpEngineNativeProvider.EXT_API_LEVEL;
import static org.chromium.net.impl.HttpEngineNativeProvider.EXT_VERSION;

import android.net.http.HttpException;

import androidx.annotation.RequiresExtension;

import org.chromium.net.CronetException;

@RequiresExtension(extension = EXT_API_LEVEL, version = EXT_VERSION)
class CronetExceptionTranslationUtils {
    @SuppressWarnings("unchecked")
    public static <T, E extends Exception> T executeTranslatingCronetExceptions(
            CronetWork<T, E> work, Class<E> nonCronetException) throws CronetException, E {
        try {
            return work.run();
        } catch (Exception e) {
            if (isUncheckedAndroidCronetException(e)) {
                throw translateUncheckedAndroidCronetException(e);
            } else if (isCheckedAndroidCronetException(e)) {
                throw translateCheckedAndroidCronetException(e);
            } else if (nonCronetException.isInstance(e)) {
                throw (E) e;
            } else {
                throw e;
            }
        }
    }

    public static boolean isUncheckedAndroidCronetException(Exception e) {
        return (e instanceof android.net.http.InlineExecutionProhibitedException);
    }

    public static boolean isCheckedAndroidCronetException(Exception e) {
        return (e instanceof HttpException);
    }

    public static RuntimeException translateUncheckedAndroidCronetException(Exception e) {
        if (!isUncheckedAndroidCronetException(e)) {
            throw new IllegalArgumentException("Not an Android Cronet exception", e);
        }

        if (e instanceof android.net.http.InlineExecutionProhibitedException) {
            // InlineExecutionProhibitedException is final so we can't have our own flavor
            org.chromium.net.InlineExecutionProhibitedException wrappedException =
                    new org.chromium.net.InlineExecutionProhibitedException();
            wrappedException.initCause(e);
            return wrappedException;
        }

        throw new UnsupportedOperationException("Unchecked exception translation discrepancy", e);
    }

    public static CronetException translateCheckedAndroidCronetException(Exception e) {
        if (!isCheckedAndroidCronetException(e)) {
            throw new IllegalArgumentException("Not an Android Cronet exception", e);
        }

        if (e instanceof android.net.http.QuicException) {
            return new AndroidQuicExceptionWrapper((android.net.http.QuicException) e);
        } else if (e instanceof android.net.http.NetworkException) {
            return new AndroidNetworkExceptionWrapper((android.net.http.NetworkException) e);
        } else if (e instanceof android.net.http.CallbackException) {
            return new AndroidCallbackExceptionWrapper((android.net.http.CallbackException) e);
        } else if (e instanceof HttpException) {
            return new AndroidHttpExceptionWrapper((HttpException) e);
        }

        throw new UnsupportedOperationException("Checked exception translation discrepancy", e);
    }

    interface CronetWork<T, E extends Exception> {
        T run() throws E;
    }

    private CronetExceptionTranslationUtils() {}
}