sunchao commented on code in PR #58243:
URL: https://github.com/apache/spark/pull/58243#discussion_r3899579235


##########
core/src/test/scala/org/apache/spark/SparkContextSuite.scala:
##########
@@ -1371,6 +1371,48 @@ class SparkContextSuite extends SparkFunSuite with 
LocalSparkContext with Eventu
     assert(msg.contains("Cannot use the keyword 'proxy' or 'history' in 
reverse proxy URL"))
   }
 
+  test("SPARK-58893: propagate UI_REVERSE_PROXY_URL to spark.ui.proxyBase in 
SparkContext") {
+    val conf = new SparkConf().setAppName("testReverseProxyBase")
+      .setMaster("local")
+      .set(UI_REVERSE_PROXY, true)
+      .set(UI_REVERSE_PROXY_URL, "http://proxyhost:8080/myprefix";)
+    sc = new SparkContext(conf)
+    assert(System.getProperty("spark.ui.proxyBase") ===
+      s"http://proxyhost:8080/myprefix/proxy/${sc.applicationId}";)
+  }
+
+  test("SPARK-58893: fallback spark.ui.proxyBase when UI_REVERSE_PROXY_URL is 
empty") {
+    val sysProxyBase = "http://proxyhost:8080/sysprefix";
+    System.setProperty("spark.ui.proxyBase", sysProxyBase)
+    try {
+      val conf = new SparkConf().setAppName("testReverseProxyBaseSys")
+        .setMaster("local")
+        .set(UI_REVERSE_PROXY, true)
+      sc = new SparkContext(conf)
+      assert(System.getProperty("spark.ui.proxyBase") ===
+        s"$sysProxyBase/proxy/${sc.applicationId}")
+    } finally {
+      System.clearProperty("spark.ui.proxyBase")

Review Comment:
   P1: This clears `spark.ui.proxyBase` while `sc` is still active. After the 
test body, `LocalSparkContext.afterEach` stops that context, and the new stop 
logic restores the captured `sysProxyBase`. The next test therefore starts with 
`http://proxyhost:8080/sysprefix` still set and its assertion for 
`/proxy/<app1>` fails deterministically. Please stop/reset this context before 
clearing the property (or otherwise restore after the stop).



##########
core/src/main/scala/org/apache/spark/ui/JettyUtils.scala:
##########
@@ -206,6 +208,25 @@ private[spark] object JettyUtils extends Logging {
           .orNull
       }
 
+      override def addProxyHeaders(
+          clientRequest: HttpServletRequest,
+          proxyRequest: org.eclipse.jetty.client.api.Request): Unit = {
+        super.addProxyHeaders(clientRequest, proxyRequest)
+        val path = clientRequest.getPathInfo
+        if (path != null) {
+          val prefixTrailingSlashIndex = path.indexOf('/', 1)
+          val prefix = if (prefixTrailingSlashIndex == -1) {
+            path
+          } else {
+            path.substring(0, prefixTrailingSlashIndex)
+          }
+          val existingContext = 
Option(clientRequest.getHeader("X-Forwarded-Context")).getOrElse("")
+          val contextPath = Option(clientRequest.getContextPath).getOrElse("")
+          val proxyContext = existingContext + reverseProxyUrl + contextPath + 
prefix

Review Comment:
   P2: `reverseProxyUrl` is appended without normalizing its trailing slash. A 
valid configured URL such as `https://public.example/cluster/` makes this 
header end in `/cluster//proxy/<id>`; `/` produces `//proxy/<id>`. Worker UIs 
can use `X-Forwarded-Context` as their link root, so those links can miss the 
external route or become scheme-relative. Please strip the trailing slash 
before composing this context, as the redirect path already does.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to