content: package com.akshetpandey.rncronet;
  
  import android.os.ConditionVariable;
  import android.util.Log;
  
  import androidx.annotation.Nullable;
  
  import com.facebook.react.modules.network.OkHttpClientProvider;
  
  import org.chromium.net.CronetException;
  import org.chromium.net.UrlRequest;
  import org.chromium.net.UrlResponseInfo;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.nio.ByteBuffer;
  import java.nio.channels.Channels;
  import java.nio.channels.WritableByteChannel;
  import java.util.List;
  import java.util.Map;
  
  import okhttp3.Call;
  import okhttp3.Callback;
  import okhttp3.EventListener;
  import okhttp3.Headers;
  import okhttp3.MediaType;
  import okhttp3.OkHttpClient;
  import okhttp3.Protocol;
  import okhttp3.Request;
  import okhttp3.Response;
  import okhttp3.ResponseBody;
  
  class RNCronetUrlRequestCallback extends UrlRequest.Callback {
    private static final String TAG = "Callback";
  
    private static final int MAX_FOLLOW_COUNT = 20;
  
    private Request originalRequest;
    private Call mCall;
    @Nullable
    private EventListener eventListener;
    @Nullable
    private Callback responseCallback;
  
    private int followCount;
    private Response mResponse;
  
    @Nullable
    private IOException mException;
    private ConditionVariable mResponseConditon = new ConditionVariable();
  
    private ByteArrayOutputStream mBytesReceived = new ByteArrayOutputStream();
    private WritableByteChannel mReceiveChannel = Channels.newChannel(mBytesReceived);
  
    RNCronetUrlRequestCallback(Request request, Call call) {
      this(request, call, null, null);
    }
  
    RNCronetUrlRequestCallback(Request request, Call call, @Nullable EventListener eventListener, @Nullable Callback responseCallback) {
      originalRequest = request;
      mCall = call;
      mResponse = new Response.Builder()
              .sentRequestAtMillis(System.currentTimeMillis())
              .request(request)
              .protocol(Protocol.HTTP_1_0)
              .code(0)
              .message("")
              .build();
      this.responseCallback = responseCallback;
      this.eventListener = eventListener;
    }
  
    private static Protocol protocolFromNegotiatedProtocol(UrlResponseInfo responseInfo) {
      String negotiatedProtocol = responseInfo.getNegotiatedProtocol().toLowerCase();
      if (negotiatedProtocol.contains("quic")) {
        return Protocol.QUIC;
      } else if (negotiatedProtocol.contains("spdy")) {
        return Protocol.SPDY_3;
      } else if (negotiatedProtocol.contains("h2")) {
        return Protocol.HTTP_2;
      } else if (negotiatedProtocol.contains("1.1")) {
        return Protocol.HTTP_1_1;
      } else {
        return Protocol.HTTP_1_0;
      }
    }
  
    private static Headers headersFromResponse(UrlResponseInfo responseInfo) {
      List<Map.Entry<String, String>> headers = responseInfo.getAllHeadersAsList();
  
      Headers.Builder headerBuilder = new Headers.Builder();
      for (Map.Entry<String, String> entry : headers) {
        try {
          if (entry.getKey().equalsIgnoreCase("content-encoding")) {
            // Strip all content encoding headers as decoding is done handled by cronet
            continue;
          }
          headerBuilder.add(entry.getKey(), entry.getValue());
        } catch (Exception e) {
          Log.w(TAG, "Invalid HTTP header/value: " + entry.getKey() + entry.getValue());
          // Ignore that header
        }
      }
  
      return headerBuilder.build();
    }
  
    private static Response responseFromResponse(Response response, UrlResponseInfo responseInfo) {
      Protocol protocol = protocolFromNegotiatedProtocol(responseInfo);
      Headers headers = headersFromResponse(responseInfo);
  
      return response.newBuilder()
              .receivedResponseAtMillis(System.currentTimeMillis())
              .protocol(protocol)
              .code(responseInfo.getHttpStatusCode())
              .message(responseInfo.getHttpStatusText())
              .headers(headers)
              .build();
    }
  
    Response waitForDone() throws IOException {
      mResponseConditon.block();
      if (mException != null) {
        throw mException;
      }
      return mResponse;
    }
  
    @Override
    public void onRedirectReceived(UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
      if (followCount > MAX_FOLLOW_COUNT) {
        request.cancel();
      }
      followCount += 1;
      OkHttpClient client = OkHttpClientProvider.createClient();
      if (originalRequest.url().isHttps() && newLocationUrl.startsWith("http://") && client.followSslRedirects()) {
        request.followRedirect();
      } else if (!originalRequest.url().isHttps() && newLocationUrl.startsWith("https://") && client.followSslRedirects()) {
        request.followRedirect();
      } else if (client.followRedirects()) {
        request.followRedirect();
      } else {
        request.cancel();
      }
    }
  
    @Override
    public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
      mResponse = responseFromResponse(mResponse, info);
  
      if (eventListener != null) {
        eventListener.responseHeadersEnd(mCall, mResponse);
        eventListener.responseBodyStart(mCall);
      }
  
      request.read(ByteBuffer.allocateDirect(32 * 1024));
    }
  
    @Override
    public void onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) throws Exception {
      byteBuffer.flip();
  
      try {
        mReceiveChannel.write(byteBuffer);
      } catch (IOException e) {
        Log.i(TAG, "IOException during ByteBuffer read. Details: ", e);
        throw e;
      }
  
      byteBuffer.clear();
  
      request.read(byteBuffer);
    }
  
    @Override
    public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
      if (eventListener != null) {
        eventListener.responseBodyEnd(mCall, info.getReceivedByteCount());
      }
  
      String contentTypeString = mResponse.header("content-type");
      MediaType contentType = MediaType.parse(contentTypeString != null ? contentTypeString : "text/plain; charset=\"utf-8\"");
  
      ResponseBody responseBody = ResponseBody.create(contentType, mBytesReceived.toByteArray());
      Request newRequest = originalRequest.newBuilder().url(info.getUrl()).build();
      mResponse = mResponse.newBuilder().body(responseBody).request(newRequest).build();
  
      mResponseConditon.open();
  
      if (eventListener != null) {
        eventListener.callEnd(mCall);
      }
  
      if (responseCallback != null) {
        try {
          responseCallback.onResponse(mCall, mResponse);
        } catch (IOException e) {
          // Pass?
        }
      }
    }
  
    @Override
    public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
      IOException e = new IOException("Cronet Exception Occurred", error);
      mException = e;
      mResponseConditon.open();
      if (eventListener != null) {
        eventListener.callFailed(mCall, e);
      }
      if (responseCallback != null) {
        responseCallback.onFailure(mCall, e);
      }
    }
  
    @Override
    public void onCanceled(UrlRequest request, UrlResponseInfo info) {
      mResponseConditon.open();
      if (eventListener != null) {
        eventListener.callEnd(mCall);
      }
    }
  }
  