chromium/components/cronet/android/java/src/org/chromium/net/impl/AndroidBidirectionalStreamCallbackWrapper.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.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresExtension;

import org.chromium.net.CronetException;
import org.chromium.net.RequestFinishedInfo;

import java.nio.ByteBuffer;
import java.util.Objects;

@RequiresExtension(extension = EXT_API_LEVEL, version = EXT_VERSION)
@SuppressWarnings("Override")
class AndroidBidirectionalStreamCallbackWrapper
        implements android.net.http.BidirectionalStream.Callback {
    private final org.chromium.net.BidirectionalStream.Callback mBackend;
    private AndroidBidirectionalStreamWrapper mWrappedStream;

    public AndroidBidirectionalStreamCallbackWrapper(
            org.chromium.net.BidirectionalStream.Callback backend) {
        this.mBackend = Objects.requireNonNull(backend, "Callback is required.");
    }

    @Override
    public void onStreamReady(android.net.http.BidirectionalStream bidirectionalStream) {
        mBackend.onStreamReady(mWrappedStream);
    }

    @Override
    public void onResponseHeadersReceived(
            android.net.http.BidirectionalStream bidirectionalStream,
            android.net.http.UrlResponseInfo urlResponseInfo) {
        AndroidUrlResponseInfoWrapper specializedResponseInfo =
                AndroidUrlResponseInfoWrapper.createForBidirectionalStream(urlResponseInfo);
        mBackend.onResponseHeadersReceived(mWrappedStream, specializedResponseInfo);
    }

    @Override
    public void onReadCompleted(
            android.net.http.BidirectionalStream bidirectionalStream,
            android.net.http.UrlResponseInfo urlResponseInfo,
            ByteBuffer byteBuffer,
            boolean endOfStream) {
        AndroidUrlResponseInfoWrapper specializedResponseInfo =
                AndroidUrlResponseInfoWrapper.createForBidirectionalStream(urlResponseInfo);
        mBackend.onReadCompleted(mWrappedStream, specializedResponseInfo, byteBuffer, endOfStream);
    }

    @Override
    public void onWriteCompleted(
            android.net.http.BidirectionalStream bidirectionalStream,
            android.net.http.UrlResponseInfo urlResponseInfo,
            ByteBuffer byteBuffer,
            boolean endOfStream) {
        AndroidUrlResponseInfoWrapper specializedResponseInfo =
                AndroidUrlResponseInfoWrapper.createForBidirectionalStream(urlResponseInfo);
        mBackend.onWriteCompleted(mWrappedStream, specializedResponseInfo, byteBuffer, endOfStream);
    }

    @Override
    public void onResponseTrailersReceived(
            @NonNull android.net.http.BidirectionalStream bidirectionalStream,
            @NonNull android.net.http.UrlResponseInfo urlResponseInfo,
            @NonNull android.net.http.HeaderBlock headerBlock) {
        AndroidUrlResponseInfoWrapper specializedResponseInfo =
                AndroidUrlResponseInfoWrapper.createForBidirectionalStream(urlResponseInfo);
        AndroidHeaderBlockWrapper specializedHeaderBlock =
                new AndroidHeaderBlockWrapper(headerBlock);
        mBackend.onResponseTrailersReceived(
                mWrappedStream, specializedResponseInfo, specializedHeaderBlock);
    }

    @Override
    public void onSucceeded(
            android.net.http.BidirectionalStream bidirectionalStream,
            android.net.http.UrlResponseInfo urlResponseInfo) {
        AndroidUrlResponseInfoWrapper specializedResponseInfo =
                AndroidUrlResponseInfoWrapper.createForBidirectionalStream(urlResponseInfo);
        try {
            mBackend.onSucceeded(mWrappedStream, specializedResponseInfo);
        } finally {
            // In a scenario where this throws, the side effect is that it will be propagated to
            // CronetUrlRequest as an error in the callback and mess with the FinalUserCallbackThrew
            // metrics. Because we catch most the exceptions, this side effect is negligible enough
            // to
            // not try to figure out a workaround.
            mWrappedStream.maybeReportMetrics(
                    RequestFinishedInfo.SUCCEEDED, specializedResponseInfo, null);
        }
    }

    @Override
    public void onFailed(
            android.net.http.BidirectionalStream bidirectionalStream,
            android.net.http.UrlResponseInfo urlResponseInfo,
            HttpException e) {
        AndroidUrlResponseInfoWrapper specializedResponseInfo =
                AndroidUrlResponseInfoWrapper.createForBidirectionalStream(urlResponseInfo);
        CronetException exception =
                CronetExceptionTranslationUtils.translateCheckedAndroidCronetException(e);
        try {
            mBackend.onFailed(mWrappedStream, specializedResponseInfo, exception);
        } finally {
            // See comment in onSucceeded.
            mWrappedStream.maybeReportMetrics(
                    RequestFinishedInfo.FAILED, specializedResponseInfo, exception);
        }
    }

    @Override
    public void onCanceled(
            @NonNull android.net.http.BidirectionalStream bidirectionalStream,
            @Nullable android.net.http.UrlResponseInfo urlResponseInfo) {
        AndroidUrlResponseInfoWrapper specializedResponseInfo =
                AndroidUrlResponseInfoWrapper.createForBidirectionalStream(urlResponseInfo);
        try {
            mBackend.onCanceled(mWrappedStream, specializedResponseInfo);
        } finally {
            // See comment in onSucceeded.
            mWrappedStream.maybeReportMetrics(
                    RequestFinishedInfo.CANCELED, specializedResponseInfo, null);
        }
    }

    void setStream(AndroidBidirectionalStreamWrapper stream) {
        mWrappedStream = stream;
    }
}