=htp #17657 fix supposedly compatible getHostString failing at runtime on Java 6

This commit is contained in:
Johannes Rudolph 2015-06-05 12:34:22 +02:00
parent 01fa9924a5
commit b596687f8b
6 changed files with 102 additions and 18 deletions

View file

@ -25,20 +25,6 @@ private[http] class HttpRequestRendererFactory(userAgentHeader: Option[headers.`
def newRenderer: HttpRequestRenderer = new HttpRequestRenderer
/**
* Retrieve the original host string that was given (IP or DNS name) if running Java 7 or later
* using the getHostString() method. This avoids a reverse DNS query from calling getHostName()
* if the original host string is an IP address.
*/
private[http] val hostString: InetSocketAddress String =
try {
val m = classOf[InetSocketAddress].getDeclaredMethod("getHostString")
require(m.getReturnType == classOf[String])
address m.invoke(address).asInstanceOf[String]
} catch {
case NonFatal(_) _.getHostName
}
final class HttpRequestRenderer extends PushStage[RequestRenderingContext, Source[ByteString, Any]] {
override def onPush(ctx: RequestRenderingContext, opCtx: Context[Source[ByteString, Any]]): SyncDirective = {
@ -107,7 +93,7 @@ private[http] class HttpRequestRendererFactory(userAgentHeader: Option[headers.`
}
case Nil
if (!hostHeaderSeen) r ~~ Host(hostString(ctx.serverAddress), ctx.serverAddress.getPort) ~~ CrLf
if (!hostHeaderSeen) r ~~ Host(ctx.serverAddress) ~~ CrLf
if (!userAgentSeen && userAgentHeader.isDefined) r ~~ userAgentHeader.get ~~ CrLf
if (entity.isChunked && !entity.isKnownEmpty && !transferEncodingSeen)
r ~~ `Transfer-Encoding` ~~ ChunkedBytes ~~ CrLf

View file

@ -0,0 +1,60 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.util
import java.lang.reflect.{ InvocationTargetException, Method }
import java.net.InetSocketAddress
import scala.util.control.NonFatal
/**
* Provides getHostString support for Java 6.
*
* TODO: can be removed once support for Java 6 is dropped.
*
* Internal API
*/
private[http] class EnhancedInetSocketAddress(val address: InetSocketAddress) extends AnyVal {
/**
* Retrieve the original host string that was given (IP or DNS name) if the current JDK has
* a `getHostString` method with the right signature that can be made accessible.
*
* This avoids a reverse DNS query from calling getHostName() if the original host string is an IP address.
* If the reflective call doesn't work it falls back to getHostName.
*/
def getHostStringJava6Compatible: String = EnhancedInetSocketAddress.getHostStringFunction(address)
}
/**
* Internal API
*/
private[http] object EnhancedInetSocketAddress {
private[http] val getHostStringFunction: InetSocketAddress String = {
def fallbackToGetHostName = (_: InetSocketAddress).getHostName
def callReflectively(m: Method) =
(address: InetSocketAddress)
try m.invoke(address).asInstanceOf[String]
catch {
case ite: InvocationTargetException throw ite.getTargetException
}
try {
val m = classOf[InetSocketAddress].getDeclaredMethod("getHostString")
val candidate =
if (m.getReturnType == classOf[String] && m.getParameterTypes.isEmpty) {
if (!m.isAccessible) m.setAccessible(true)
callReflectively(m)
} else fallbackToGetHostName
// probe so that we can be sure a reflective problem only turns up once
// here during construction
candidate(new InetSocketAddress("127.0.0.1", 80))
candidate
} catch {
case NonFatal(_) fallbackToGetHostName
}
}
}

View file

@ -4,7 +4,7 @@
package akka.http.impl.util
import java.net.InetAddress
import java.net.{ InetSocketAddress, InetAddress }
import com.typesafe.config.{ ConfigFactory, Config }
import com.typesafe.config.ConfigFactory._
import scala.util.control.NonFatal
@ -49,7 +49,7 @@ private[http] abstract class SettingsCompanion[T](prefix: String) {
private[http] object SettingsCompanion {
lazy val configAdditions: Config = {
val localHostName =
try InetAddress.getLocalHost.getHostName // TODO: upgrade to `getHostString` once we are on JDK7
try new InetSocketAddress(InetAddress.getLocalHost, 80).getHostStringJava6Compatible
catch { case NonFatal(_) "" }
ConfigFactory.parseMap(Map("akka.http.hostname" -> localHostName).asJava)
}

View file

@ -4,6 +4,8 @@
package akka.http.impl
import java.net.InetSocketAddress
import language.implicitConversions
import language.higherKinds
import java.nio.charset.Charset
@ -38,6 +40,8 @@ package object util {
private[http] implicit def enhanceConfig(config: Config): EnhancedConfig = new EnhancedConfig(config)
private[http] implicit def enhanceString_(s: String): EnhancedString = new EnhancedString(s)
private[http] implicit def enhanceRegex(regex: Regex): EnhancedRegex = new EnhancedRegex(regex)
private[http] implicit def enhanceInetSocketAddress(address: InetSocketAddress): EnhancedInetSocketAddress =
new EnhancedInetSocketAddress(address)
private[http] implicit def enhanceByteStrings(byteStrings: TraversableOnce[ByteString]): EnhancedByteStringTraversableOnce =
new EnhancedByteStringTraversableOnce(byteStrings)
private[http] implicit def enhanceByteStrings[Mat](byteStrings: Source[ByteString, Mat]): EnhancedByteStringSource[Mat] =

View file

@ -90,7 +90,7 @@ sealed abstract case class Expect private () extends ModeledHeader {
// http://tools.ietf.org/html/rfc7230#section-5.4
object Host extends ModeledCompanion {
def apply(address: InetSocketAddress): Host = apply(address.getHostName, address.getPort) // TODO: upgrade to `getHostString` once we are on JDK7
def apply(address: InetSocketAddress): Host = apply(address.getHostStringJava6Compatible, address.getPort)
def apply(host: String): Host = apply(host, 0)
def apply(host: String, port: Int): Host = apply(Uri.Host(host), port)
val empty = Host("")

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.util
import java.net.{ InetAddress, InetSocketAddress }
import org.scalatest.{ Matchers, WordSpec }
class EnhancedInetSocketAddressSpec extends WordSpec with Matchers {
"getHostStringJava6Compatible" should {
"return IPv4 address if InetSocketAddress was created with the address" in {
val addr = likelyReverseResolvableAddress
val socketAddress = new InetSocketAddress(addr, 80)
socketAddress.getHostStringJava6Compatible shouldEqual addr.getHostAddress
}
"return host name if InetSocketAddress was created with host name" in {
val address = new InetSocketAddress("github.com", 80)
address.getHostStringJava6Compatible shouldEqual "github.com"
}
}
/**
* Returns an InetAddress that can likely be reverse looked up, so that
* getHostName returns a DNS address and not the IP. Unfortunately, we
* cannot be sure that a host name was already cached somewhere in which
* case getHostString may still return a host name even without doing
* a reverse lookup at this time. If this start to fail non-deterministically,
* it may be decided that this test needs to be disabled.
*/
def likelyReverseResolvableAddress: InetAddress =
InetAddress.getByAddress(InetAddress.getByName("google.com").getAddress)
}