Tengo una request de Vertx y necesito calcular una URL externa visible (pública)
Estoy utilizando Vertx 3 con Kotlin, y en ocasiones tengo que devolver un URI específico desde la perspectiva de la URL pública, que no es lo mismo que la request de Vertx-web cree que es mi URL. Es probable que esto se deba a que mi equilibrador de carga o proxy recibe una URL y luego la reenvía a mi aplicación en una URL interna.
Entonces si hago esto:
- Proxies de service Vert.x de Kotlin con vertx-codegen
- Vertx plus Koutlin coroutines cuelga para siempre
- Cómo crear verticle de fábrica en VertX?
- El código de Vertx Sync se ejecuta varias veces
- Retrofit-Vertx con RxJava2 en Kotlin IllegalStateException message == null
val publicUrl = context.request().absoluteURI()
Terminé con una URL como http://10.10.103.22:8080/some/page
lugar de https://app.mydomain.com/some/page
. ¡Todo está mal con esa URL!
Encontré un encabezado que supuestamente me dice más sobre la request original, como X-Forwarded-Host
pero solo incluye app.mydomain.com
o a veces tiene la aplicación de app.mydomain:80
pero eso no es suficiente para descubrir todo partes de la URL, termino con algo como http://app.mydomain.com:8080/some/page
que aún no es la URL pública correcta.
También necesito manejar no solo mi URL actual, sino también mis URL, como en la página "algo / página1" ir a "algo / página2" en el mismo server. Los mismos problemas mencionados cuando trato de resolver a otra URL porque partes importantes de la URL pública no se pueden get.
¿Existe algún método en Vertx-web que me falte para determinar esta URL pública, o alguna forma idiomática de resolver esto?
Estoy codificando en Kotlin, ¡así que cualquier ejemplo para ese idioma es genial!
Nota: esta pregunta fue escrita y respondida intencionalmente por el autor ( Preguntas que responden por sí mismas ), de modo que las soluciones para problemas interesantes se comparten en SO.
- Vert.x Kotlin Type Mismatch requinetworking Controlador <AsyncResult <Unit >> found (Handler <AsyncResult <Unit >>) -> Unidad
- Vert.x. ¿Cómo crear una aplicación de JVM políglota real?
- VertX Web no elimina cookies
- Controlador de respuesta de coincidencia con request en VertX
- En Vertx, necesito networkingirigir todas las requestes HTTP a la misma URL, pero para HTTPS
- ¿Cómo comstackr y usar el código de Kotlin en time de ejecución?
- Respuesta fragmentada de Vert.x con los datos de la database usando BLOB e hibernación
- ¿Cómo puedo get una request web actual usando Vert.x Web?
Este es un problema más complicado, y la lógica es la misma para la mayoría de los serveres de aplicaciones si aún no proporcionan una function de externalización de URL.
Para hacerlo correctamente, necesita manejar todos estos encabezados:
-
X-Forwarded-Proto
(oX-Forwarded-Scheme: https
, y quizás oddballs comoX-Forwarded-Ssl: on
,Front-End-Https: on
) -
X-Forwarded-Host
(como "myhost.com" o "myhost.com:port") -
X-Forwarded-Port
Y si desea resolver y devolver una URL que no es la actual, también debe considerar:
- parcial sin host, por ejemplo "/ something / here" o "under / me" resolviendo a los serveres protocolo público, host, puerto así como aquel abosolute o ruta relativa
- parcial con host / puerto, por ejemplo "//somehost.com:8983/thing" agregaría el mismo esquema (http / https) como este server y mantendría el rest
- completo, las URL que están totalmente calificadas se devuelven intactas, por lo que son seguras para pasar a esta function ("http: // …", "https: // …") y no se modificarán
Aquí hay un par de funciones de extensión para RoutingContext
que manejarán todos estos casos y retrocederán cuando los encabezados del equilibrador de carga / proxy no estén presentes, por lo que funcionarán tanto en los casos de conexiones directas al server como en los que atraviesan el intermediario. Usted pasa la URL absoluta o relativa (a la página actual) y devolverá una versión pública de la misma.
// 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().filter { it.value.isNullOrBlank() } .map { it.key to it.value }.toMap() return externalizeURI(URI(request().absoluteURI()), resolveUrl, cleanHeaders).toString() }
Que llama a una function interna que hace el trabajo real ( y es más comprobable ya que no hay necesidad de burlarse de 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 = 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) }
Y algunas funciones auxiliares relacionadas:
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, String?> { 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) } fun String.mustStartWith(prefix: Char): String { return if (this.startsWith(prefix)) { this } else { prefix + this } }
- ¿Cuánto time puede durar la propiedad de String en Kotlin?
- ¿Es posible ocultar variables del cierre de lambda?