Skip to content

Commit

Permalink
Convert com.facebook.react.modules.network.ForwardingCookieHandler to…
Browse files Browse the repository at this point in the history
… Kotlin (#48088)

Summary:
Pull Request resolved: #48088

Planning to make some changes here for perf, but converting to Kotlin first

Changelog: [Internal]

Differential Revision: D66724321
  • Loading branch information
javache authored and facebook-github-bot committed Dec 4, 2024
1 parent 0217d7e commit b2a01f5
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ constructor(
// make sure to forward cookies for any requests via the okHttpClient
// so that image requests to endpoints that use cookies still work
val container = OkHttpCompat.getCookieJarContainer(client)
val handler = ForwardingCookieHandler(context)
val handler = ForwardingCookieHandler()
container.setCookieJar(JavaNetCookieJar(handler))
return newBuilder(context.applicationContext, client)
.setNetworkFetcher(ReactOkHttpNetworkFetcher(client))
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.modules.network

import android.webkit.CookieManager
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.ReactContext
import java.io.IOException
import java.net.CookieHandler
import java.net.URI

/**
* Cookie handler that forwards all cookies to the WebView CookieManager.
*
* This class relies on CookieManager to persist cookies to disk so cookies may be lost if the
* application is terminated before it syncs.
*/
public class ForwardingCookieHandler() : CookieHandler() {

@Deprecated("Use the default constructor", ReplaceWith("ForwardingCookieHandler()"))
public constructor(@Suppress("UNUSED_PARAMETER") reactContext: ReactContext) : this() {}

@Throws(IOException::class)
override fun get(uri: URI, headers: Map<String, List<String>>): Map<String, List<String>> {
val cookies = cookieManager?.getCookie(uri.toString())
if (cookies.isNullOrEmpty()) {
return emptyMap()
}

return mapOf(COOKIE_HEADER to listOf(cookies))
}

@Throws(IOException::class)
override fun put(uri: URI, headers: Map<String, List<String>>) {
val url = uri.toString()
for ((key, value) in headers) {
if (isCookieHeader(key)) {
addCookies(url, value)
}
}
}

public fun clearCookies(callback: Callback): Unit {
cookieManager?.removeAllCookies { value -> callback.invoke(value) }
}

public fun destroy(): Unit = Unit

public fun addCookies(url: String, cookies: List<String>): Unit {
for (cookie in cookies) {
addCookieAsync(url, cookie)
}
cookieManager?.flush()
}

private fun addCookieAsync(url: String, cookie: String) {
cookieManager?.setCookie(url, cookie, null)
}

private var _cookieManager: CookieManager? = null
private val cookieManager: CookieManager?
/**
* Instantiating CookieManager will load the Chromium task taking a 100ish ms so we do it lazily
* to make sure it's done on a background thread as needed.
*/
get() {
if (_cookieManager == null) {
try {
_cookieManager = CookieManager.getInstance()
} catch (ex: IllegalArgumentException) {
// https://bugs.chromium.org/p/chromium/issues/detail?id=559720
return null
} catch (exception: Exception) {
// Ideally we would like to catch a `MissingWebViewPackageException` here.
// That API is private so we can't access it.
// Historically we used string matching on the error message to understand
// if the exception was a Missing Webview One.
// OEMs have been customizing that message making really hard to catch it.
// Therefore we result to returning null as a default instead of rethrowing
// the exception as it will result in a app crash at runtime.
// a) We will return null for all the other unhandled conditions when a webview provider
// is
// not found.
// b) We already have null checks in place for `getCookieManager()` calls.
// c) We have annotated the method as @Nullable to notify future devs about our return
// type.
return null
}
}

return _cookieManager
}

public companion object {
private const val VERSION_ZERO_HEADER = "Set-cookie"
private const val VERSION_ONE_HEADER = "Set-cookie2"
private const val COOKIE_HEADER = "Cookie"

private fun isCookieHeader(name: String): Boolean =
name.equals(VERSION_ZERO_HEADER, ignoreCase = true) ||
name.equals(VERSION_ONE_HEADER, ignoreCase = true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ public interface ResponseHandler {
customClientBuilder = null;

private final OkHttpClient mClient;
private final ForwardingCookieHandler mCookieHandler;
private final ForwardingCookieHandler mCookieHandler = new ForwardingCookieHandler();
private final @Nullable String mDefaultUserAgent;
private final CookieJarContainer mCookieJarContainer;
private final Set<Integer> mRequestIds;
private final Set<Integer> mRequestIds = new HashSet<>();
private final List<RequestBodyHandler> mRequestBodyHandlers = new ArrayList<>();
private final List<UriHandler> mUriHandlers = new ArrayList<>();
private final List<ResponseHandler> mResponseHandlers = new ArrayList<>();
private boolean mShuttingDown;
private boolean mShuttingDown = false;

public NetworkingModule(
ReactApplicationContext reactContext,
Expand All @@ -121,11 +121,8 @@ public NetworkingModule(
client = clientBuilder.build();
}
mClient = client;
mCookieHandler = new ForwardingCookieHandler(reactContext);
mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
mShuttingDown = false;
mCookieJarContainer = (CookieJarContainer) client.cookieJar();
mDefaultUserAgent = defaultUserAgent;
mRequestIds = new HashSet<>();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,12 @@ public interface ContentHandler {

private final Map<Integer, WebSocket> mWebSocketConnections = new ConcurrentHashMap<>();
private final Map<Integer, ContentHandler> mContentHandlers = new ConcurrentHashMap<>();

private ForwardingCookieHandler mCookieHandler;
private final ForwardingCookieHandler mCookieHandler = new ForwardingCookieHandler();

private static @Nullable CustomClientBuilder customClientBuilder = null;

public WebSocketModule(ReactApplicationContext context) {
super(context);
mCookieHandler = new ForwardingCookieHandler(context);
}

public static void setCustomClientBuilder(CustomClientBuilder ccb) {
Expand Down

0 comments on commit b2a01f5

Please sign in to comment.