gpt4 book ai didi

kotlin - 我有一个 Vertx 请求,我需要计算一个外部可见(公共(public))URL

转载 作者:IT老高 更新时间:2023-10-28 13:47:35 24 4
gpt4 key购买 nike

我在 Kotlin 中使用 Vertx 3,有时我需要从公共(public) URL 的角度返回一个特定的 URI,这与 Vertx-web 请求认为我的 URL 不同。这可能是因为我的负载均衡器或代理接收到一个 URL,然后通过内部 URL 转发到我的应用程序。

如果我这样做:

val publicUrl = context.request().absoluteURI() 

我最终得到一个类似 http://10.10.103.22:8080/some/page 的 URL,而不是 https://app.mydomain.com/some/page。该 URL 的一切都错了!

我发现了一个 header ,据说可以告诉我更多关于原始请求的信息,例如 X-Forwarded-Host 但它只包含 app.mydomain.com 或者有时它包含端口 app.mydomain:80 但这还不足以找出 URL 的所有部分,我最终得到类似 http://app.mydomain.com:8080/some/page 仍然不是正确的公共(public) URL。

我不仅需要处理我当前的 URL,还需要处理对等 URL,例如在页面“something/page1”上转到同一服务器上的“something/page2”。当我尝试解析到另一个 URL 时提到了同样的问题,因为公共(public) URL 的重要部分无法获得。

我缺少 Vertx-web 中的方法来确定这个公共(public) URL,还是一些惯用的方法来解决这个问题?

我正在使用 Kotlin 进行编码,因此该语言的任何示例都很棒!

注意: 这个问题是作者特意写的和回答的(Self-Answered Questions),所以有趣的问题的解决方案在SO中共享。

最佳答案

这是一个比较复杂的问题,如果大多数App服务器还没有提供URL外化功能,逻辑是一样的。

要正确执行此操作,您需要处理所有这些 header :

  • X-Forwarded-Proto(或 X-Forwarded-Scheme: https,也许还有像 X-Forwarded-Ssl: on 这样的怪人, Front-End-Https: on)
  • X-Forwarded-Host(如“myhost.com”或“myhost.com:port”)
  • X-Forwarded-Port

如果你想解析并返回一个不是当前的 URL,你还需要考虑:

  • 部分没有主机,例如“/something/here”或“under/me”解析到服务器的公共(public)协议(protocol)、主机、端口以及绝对或相对路径
  • 带有主机/端口的部分,例如“//somehost.com:8983/thing”将添加与此服务器相同的方案(http/https)并保留其余部分
  • 完整的,完全限定的 URL 会原封不动地返回,因此它们可以安全地传递给此函数(“http://...”、“https://...”)并且不会被修改

这是 RoutingContext 的一对扩展函数,它们将处理所有这些情况,并在负载平衡器/代理 header 不存在时回退,因此在直接连接到服务器的两种情况下都可以使用和那些通过中介的人。您传入绝对或相对 URL(到当前页面),它将返回相同的公共(public)版本。

// return current URL as public URL
fun RoutingContext.externalizeUrl(): String {
return externalizeUrl(URI(request().absoluteURI()).pathPlusParmsOfUrl())
}

// resolve a related URL as a public URL
fun RoutingContext.externalizeUrl(resolveUrl: String): String {
val cleanHeaders = request().headers().filterNot { it.value.isNullOrBlank() }
.map { it.key to it.value }.toMap()
return externalizeURI(URI(request().absoluteURI()), resolveUrl, cleanHeaders).toString()
}

调用一个内部函数来做真正的工作(并且更易于测试,因为不需要模拟 RoutingContext):

internal fun externalizeURI(requestUri: URI, resolveUrl: String, headers: Map<String, String>): URI {
// special case of not touching fully qualified resolve URL's
if (resolveUrl.startsWith("http://") || resolveUrl.startsWith("https://")) return URI(resolveUrl)

val forwardedScheme = headers.get("X-Forwarded-Proto")
?: headers.get("X-Forwarded-Scheme")
?: requestUri.getScheme()

// special case of //host/something URL's
if (resolveUrl.startsWith("//")) return URI("$forwardedScheme:$resolveUrl")

val (forwardedHost, forwardedHostOptionalPort) =
dividePort(headers.get("X-Forwarded-Host") ?: requestUri.getHost())

val fallbackPort = requestUri.getPort().let { explicitPort ->
if (explicitPort <= 0) {
if ("https" == forwardedScheme) 443 else 80
} else {
explicitPort
}
}
val requestPort: Int = headers.get("X-Forwarded-Port")?.toInt()
?: forwardedHostOptionalPort
?: fallbackPort
val finalPort = when {
forwardedScheme == "https" && requestPort == 443 -> ""
forwardedScheme == "http" && requestPort == 80 -> ""
else -> ":$requestPort"
}

val restOfUrl = requestUri.pathPlusParmsOfUrl()
return URI("$forwardedScheme://$forwardedHost$finalPort$restOfUrl").resolve(resolveUrl)
}

以及一些相关的辅助函数:

internal fun URI.pathPlusParmsOfUrl(): String {
val path = this.getRawPath().let { if (it.isNullOrBlank()) "" else it.mustStartWith('/') }
val query = this.getRawQuery().let { if (it.isNullOrBlank()) "" else it.mustStartWith('?') }
val fragment = this.getRawFragment().let { if (it.isNullOrBlank()) "" else it.mustStartWith('#') }
return "$path$query$fragment"
}

internal fun dividePort(hostWithOptionalPort: String): Pair<String, Int?> {
val parts = if (hostWithOptionalPort.startsWith('[')) { // ipv6
Pair(hostWithOptionalPort.substringBefore(']') + ']', hostWithOptionalPort.substringAfter("]:", ""))
} else { // ipv4
Pair(hostWithOptionalPort.substringBefore(':'), hostWithOptionalPort.substringAfter(':', ""))
}
return Pair(parts.first, if (parts.second.isNullOrBlank()) null else parts.second.toInt())
}

fun String.mustStartWith(prefix: Char): String {
return if (this.startsWith(prefix)) { this } else { prefix + this }
}

关于kotlin - 我有一个 Vertx 请求,我需要计算一个外部可见(公共(public))URL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39564199/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com