Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ public class WebSocketModule(context: ReactApplicationContext) :
*/
private fun getCookie(uri: String): String? {
try {
val origin = URI(getDefaultOrigin(uri))
val origin = getCookieLookupUri(uri)
val cookieMap = cookieHandler.get(origin, HashMap<String, List<String>>())
val cookieList = cookieMap["Cookie"]
if (cookieList.isNullOrEmpty()) {
Expand Down Expand Up @@ -459,6 +459,16 @@ public class WebSocketModule(context: ReactApplicationContext) :
customClientBuilder?.apply(builder)
}

/** Map a WebSocket URI's scheme to its HTTP(S) equivalent, e.g. "wss" -> "https". */
private fun httpSchemeFor(requestURI: URI): String =
when (requestURI.scheme) {
"wss" -> "https"
"ws" -> "http"
"http",
"https" -> requestURI.scheme
else -> ""
}

/**
* Get the default HTTP(S) origin for a specific WebSocket URI
*
Expand All @@ -468,14 +478,7 @@ public class WebSocketModule(context: ReactApplicationContext) :
private fun getDefaultOrigin(uri: String): String {
try {
val requestURI = URI(uri)
val scheme =
when (requestURI.scheme) {
"wss" -> "https"
"ws" -> "http"
"http",
"https" -> requestURI.scheme
else -> ""
}
val scheme = httpSchemeFor(requestURI)

val defaultOrigin =
if (requestURI.port != -1) {
Expand All @@ -489,5 +492,31 @@ public class WebSocketModule(context: ReactApplicationContext) :
throw IllegalArgumentException("Unable to set $uri as default origin header")
}
}

/**
* Get the URI used to look up cookies for a specific WebSocket URI, keeping its path so that
* path-scoped cookies are matched correctly. Query and fragment are dropped since cookies are
* scoped by path, not by query or fragment (RFC 6265). userInfo is also dropped so that
* credentials embedded in the URL are never forwarded to the cookie store.
*
* @param uri
* @return A URI with the endpoint converted to HTTP protocol (http[s]://host[:port]/path)
*/
private fun getCookieLookupUri(uri: String): URI {
try {
val requestURI = URI(uri)
return URI(
httpSchemeFor(requestURI),
null,
requestURI.host,
requestURI.port,
requestURI.path,
null,
null,
)
} catch (e: URISyntaxException) {
throw IllegalArgumentException("Unable to get cookie lookup URI from $uri")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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.websocket

import java.net.URI
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class WebSocketModuleTest {

private fun getCookieLookupUri(uri: String): URI {
val method =
WebSocketModule.Companion::class.java.getDeclaredMethod(
"getCookieLookupUri", String::class.java)
method.isAccessible = true
return method.invoke(WebSocketModule.Companion, uri) as URI
}

@Test
fun getCookieLookupUri_keepsPathForCookieMatching() {
val uri = getCookieLookupUri("wss://my.domain/signal-r/hubs/messages")

assertThat(uri.toString()).isEqualTo("https://my.domain/signal-r/hubs/messages")
}

@Test
fun getCookieLookupUri_keepsPortAndQuery() {
val uri = getCookieLookupUri("ws://my.domain:8080/path?token=abc")

assertThat(uri.toString()).isEqualTo("http://my.domain:8080/path?token=abc")
}
}
Loading