=act,rem #17911 handle IPv6 Addresses better

* NettyTransport will autowrap IPv6 addresses in brackets if needed
* Address asserts that IPv6 addresses are wrapped in brackets
This commit is contained in:
Johan Andrén 2016-01-05 15:40:10 +01:00
parent d4006dab47
commit 711c407a8f
5 changed files with 65 additions and 5 deletions

View file

@ -0,0 +1,32 @@
/**
* Copyright (C) 2009-2016 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
import org.scalatest.{ WordSpec, ShouldMatchers }
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
class AddressSpec extends WordSpec with ShouldMatchers {
"The Address factory methods" should {
"fail if ipv6 address is not wrapped in brackets" in {
intercept[IllegalArgumentException] {
Address("tcp", "system", "0:0:0:0:0:0:0:1", 2551)
}.getMessage should include("must be wrapped with []")
}
"not fail if ipv6 address is wrapped in brackets" in {
Address("tcp", "system", "[0:0:0:0:0:0:0:1]", 2551)
}
"not fail with an ipv4 address" in {
Address("tcp", "system", "192.168.0.1", 2551)
}
"not fail with a hostname address" in {
Address("tcp", "system", "example.com", 2551)
}
}
}

View file

@ -75,7 +75,11 @@ object Address {
/**
* Constructs a new Address with the specified protocol, system name, host and port
*/
def apply(protocol: String, system: String, host: String, port: Int) = new Address(protocol, system, Some(host), Some(port))
def apply(protocol: String, system: String, host: String, port: Int) = {
require(!host.contains(":") || host.startsWith("["), s"IPv6 address $host must be wrapped with [] to be safe")
new Address(protocol, system, Some(host), Some(port))
}
}
private[akka] trait PathUtils {

View file

@ -360,6 +360,7 @@ akka {
# The hostname or ip clients should connect to.
# InetAddress.getLocalHost.getHostAddress is used if empty
# if using an IPv6 address wrap it in brackets, like so: [fe80::a65e:60ff:fee5:ba9f%en0]
hostname = ""
# Use this setting to bind a network interface to a different port

View file

@ -238,16 +238,32 @@ private[transport] object NettyTransport {
val uniqueIdCounter = new AtomicInteger(0)
def addressFromSocketAddress(addr: SocketAddress, schemeIdentifier: String, systemName: String,
hostName: Option[String], port: Option[Int]): Option[Address] = addr match {
case sa: InetSocketAddress Some(Address(schemeIdentifier, systemName,
hostName.getOrElse(sa.getAddress.getHostAddress), port.getOrElse(sa.getPort))) // perhaps use getHostString in jdk 1.7
case _ None
hostName: Option[String], port: Option[Int]): Option[Address] = {
addr match {
case sa: InetSocketAddress
Some(Address(
schemeIdentifier,
systemName,
ipv6SafeHostname(hostName.getOrElse(sa.getHostString)),
port.getOrElse(sa.getPort)))
case _ None
}
}
// Need to do like this for binary compatibility reasons
def addressFromSocketAddress(addr: SocketAddress, schemeIdentifier: String, systemName: String,
hostName: Option[String]): Option[Address] =
addressFromSocketAddress(addr, schemeIdentifier, systemName, hostName, port = None)
/**
* Wraps an ipv6 address hostname in [] to avoid having it parsed as hostname + port.
*/
private def ipv6SafeHostname(unsafeHostname: String): String =
if (unsafeHostname.contains(":") && !unsafeHostname.startsWith("[")) s"[$unsafeHostname]"
else unsafeHostname
}
// FIXME: Split into separate UDP and TCP classes

View file

@ -116,6 +116,13 @@ class NettyTransportSpec extends WordSpec with Matchers with BindBehaviour {
Await.result(sys.terminate(), Duration.Inf)
}
"wrap ipv6 addresses in brackets if missing" in {
val socketAddress = InetSocketAddress.createUnresolved("0:0:0:0:0:0:0:1", 2552)
val address = NettyTransport.addressFromSocketAddress(socketAddress, "tcp", "example", None, None)
address.flatMap(_.host) should ===(Some("[0:0:0:0:0:0:0:1]"))
}
}
}