2012-06-11 18:33:05 +02:00
/* *
* Copyright ( C ) 2009 - 2012 Typesafe Inc . < http : //www.typesafe.com>
*/
2012-06-18 13:57:42 +02:00
package akka.remote
2012-06-11 18:33:05 +02:00
import akka.testkit._
import akka.actor._
import com.typesafe.config._
import akka.dispatch. { Await , Future }
import akka.pattern.ask
import java.io.File
2012-06-15 16:08:07 +02:00
import akka.event. { NoLogging , LoggingAdapter }
2012-06-18 14:55:49 +02:00
import java.security. { NoSuchAlgorithmException , SecureRandom , PrivilegedAction , AccessController }
import netty. { NettySettings , NettySSLSupport }
2012-06-18 17:57:09 +02:00
import javax.net.ssl.SSLException
import akka.util. { Timeout , Duration }
import akka.util.duration._
2012-06-11 18:33:05 +02:00
object Configuration {
// set this in your JAVA_OPTS to see all ssl debug info: "-Djavax.net.debug=ssl,keymanager"
// The certificate will expire in 2109
2012-06-18 18:47:35 +02:00
private val trustStore = getClass . getClassLoader . getResource ( "truststore" ) . getPath
private val keyStore = getClass . getClassLoader . getResource ( "keystore" ) . getPath
2012-06-11 18:33:05 +02:00
private val conf = "" "
akka {
actor . provider = "akka.remote.RemoteActorRefProvider"
2012-06-18 19:31:36 +02:00
test {
2012-06-18 20:07:58 +02:00
single - expect - default = 10 s
filter - leeway = 10 s
default - timeout = 10 s
2012-06-18 19:31:36 +02:00
}
2012-06-11 18:33:05 +02:00
remote . netty {
hostname = localhost
ssl {
enable = on
trust - store = "%s"
key - store = "%s"
random - number - generator = "%s"
2012-06-19 10:36:09 +02:00
enabled - algorithms = [ % s ]
2012-06-18 13:57:42 +02:00
sha1prng - random - source = "/dev/./urandom"
2012-06-11 18:33:05 +02:00
}
}
actor . deployment {
/ blub . remote = "akka://remote-sys@localhost:12346"
/ looker / child . remote = "akka://remote-sys@localhost:12346"
/ looker / child / grandchild . remote = "akka://Ticket1978CommunicationSpec@localhost:12345"
}
}
"" "
2012-06-18 14:55:49 +02:00
def getCipherConfig ( cipher : String , enabled : String * ) : ( String , Boolean , Config ) = try {
2012-06-18 17:18:17 +02:00
val config = ConfigFactory . parseString ( "akka.remote.netty.port=12345" ) . withFallback ( ConfigFactory . parseString ( conf . format ( trustStore , keyStore , cipher , enabled . mkString ( ", " ) ) ) )
2012-06-18 17:57:09 +02:00
val fullConfig = config . withFallback ( AkkaSpec . testConf ) . withFallback ( ConfigFactory . load ) . getConfig ( "akka.remote.netty" )
val settings = new NettySettings ( fullConfig , "placeholder" )
2012-06-18 17:18:17 +02:00
val rng = NettySSLSupport . initializeCustomSecureRandom ( settings . SSLRandomNumberGenerator , settings . SSLRandomSource , NoLogging )
2012-06-18 17:57:09 +02:00
rng . nextInt ( ) // Has to work
settings . SSLRandomNumberGenerator foreach { sRng ⇒ rng . getAlgorithm == sRng || ( throw new NoSuchAlgorithmException ( sRng ) ) }
2012-06-19 10:36:09 +02:00
val engine = NettySSLSupport . initializeClientSSL ( settings , NoLogging ) . getEngine
2012-06-18 17:57:09 +02:00
val gotAllSupported = enabled . toSet -- engine . getSupportedCipherSuites . toSet
val gotAllEnabled = enabled . toSet -- engine . getEnabledCipherSuites . toSet
gotAllSupported . isEmpty || ( throw new IllegalArgumentException ( "Cipher Suite not supported: " + gotAllSupported ) )
gotAllEnabled . isEmpty || ( throw new IllegalArgumentException ( "Cipher Suite not enabled: " + gotAllEnabled ) )
2012-06-19 10:36:09 +02:00
engine . getSupportedProtocols . contains ( settings . SSLProtocol . get ) || ( throw new IllegalArgumentException ( "Protocol not supported: " + settings . SSLProtocol . get ) )
2012-06-18 17:57:09 +02:00
2012-06-18 14:55:49 +02:00
( cipher , true , config )
2012-06-15 16:08:07 +02:00
} catch {
2012-06-19 10:36:09 +02:00
case ( _ : IllegalArgumentException ) | ( _ : NoSuchAlgorithmException ) ⇒ ( cipher , false , AkkaSpec . testConf ) // Cannot match against the message since the message might be localized :S
2012-06-18 14:55:49 +02:00
}
2012-06-11 18:33:05 +02:00
}
2012-06-15 16:08:07 +02:00
import Configuration.getCipherConfig
2012-06-11 18:33:05 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-15 18:05:02 +02:00
class Ticket1978SHA1PRNGSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "SHA1PRNG" , "TLS_RSA_WITH_AES_128_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-15 18:05:02 +02:00
class Ticket1978AES128CounterRNGFastSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "AES128CounterRNGFast" , "TLS_RSA_WITH_AES_128_CBC_SHA" , "TLS_RSA_WITH_AES_256_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
/* *
* Both of the < quote > Secure </ quote > variants require access to the Internet to access random . org .
*/
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-19 10:36:09 +02:00
class Ticket1978AES128CounterRNGSecureSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "AES128CounterRNGSecure" , "TLS_RSA_WITH_AES_128_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
/* *
* Both of the < quote > Secure </ quote > variants require access to the Internet to access random . org .
*/
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-15 18:05:02 +02:00
class Ticket1978AES256CounterRNGSecureSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "AES256CounterRNGSecure" , "TLS_RSA_WITH_AES_256_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
2012-06-15 18:31:28 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
class Ticket1978DefaultRNGSecureSpec extends Ticket1978CommunicationSpec ( getCipherConfig ( "" , "TLS_RSA_WITH_AES_128_CBC_SHA" ) )
2012-06-11 18:33:05 +02:00
@org . junit . runner . RunWith ( classOf [ org . scalatest . junit . JUnitRunner ] )
2012-06-15 16:08:07 +02:00
class Ticket1978NonExistingRNGSecureSpec extends Ticket1978CommunicationSpec ( ( "NonExistingRNG" , false , AkkaSpec . testConf ) )
2012-06-11 18:33:05 +02:00
2012-06-18 17:57:09 +02:00
abstract class Ticket1978CommunicationSpec ( val cipherEnabledconfig : ( String , Boolean , Config ) ) extends AkkaSpec ( cipherEnabledconfig . _3 ) with ImplicitSender {
2012-06-18 20:07:58 +02:00
implicit val timeout : Timeout = Timeout ( 5 seconds )
2012-06-11 18:33:05 +02:00
2012-06-15 16:08:07 +02:00
import RemoteCommunicationSpec._
2012-06-11 18:33:05 +02:00
2012-06-18 14:02:08 +02:00
val other = ActorSystem ( "remote-sys" , ConfigFactory . parseString ( "akka.remote.netty.port=12346" ) . withFallback ( system . settings . config ) )
2012-06-11 18:33:05 +02:00
override def atTermination ( ) {
other . shutdown ( )
2012-06-19 10:36:09 +02:00
other . awaitTermination ( )
2012-06-11 18:33:05 +02:00
}
"SSL Remoting" must {
2012-06-15 16:08:07 +02:00
if ( cipherEnabledconfig . _2 ) {
2012-06-18 14:02:08 +02:00
val remote = other . actorOf ( Props ( new Actor { def receive = { case "ping" ⇒ sender ! ( ( "pong" , sender ) ) } } ) , "echo" )
val here = system . actorFor ( "akka://remote-sys@localhost:12346/user/echo" )
2012-06-15 15:04:54 +02:00
"support remote look-ups" in {
here ! "ping"
2012-06-18 18:47:35 +02:00
expectMsgPF ( timeout . duration ) {
2012-06-15 15:04:54 +02:00
case ( "pong" , s : AnyRef ) if s eq testActor ⇒ true
}
}
2012-06-11 18:33:05 +02:00
2012-06-15 15:04:54 +02:00
"send error message for wrong address" in {
2012-06-18 18:47:35 +02:00
within ( timeout . duration ) {
EventFilter . error ( start = "dropping" , occurrences = 1 ) . intercept {
system . actorFor ( "akka://remotesys@localhost:12346/user/echo" ) ! "ping"
} ( other )
}
2012-06-11 18:33:05 +02:00
}
2012-06-15 15:04:54 +02:00
"support ask" in {
Await . result ( here ? "ping" , timeout . duration ) match {
case ( "pong" , s : akka . pattern . PromiseActorRef ) ⇒ // good
case m ⇒ fail ( m + " was not (pong, AskActorRef)" )
}
}
2012-06-11 18:33:05 +02:00
2012-06-15 15:04:54 +02:00
"send dead letters on remote if actor does not exist" in {
2012-06-18 18:47:35 +02:00
within ( timeout . duration ) {
EventFilter . warning ( pattern = "dead.*buh" , occurrences = 1 ) . intercept {
system . actorFor ( "akka://remote-sys@localhost:12346/does/not/exist" ) ! "buh"
} ( other )
}
2012-06-11 18:33:05 +02:00
}
2012-06-15 15:04:54 +02:00
"create and supervise children on remote node" in {
2012-06-18 18:47:35 +02:00
within ( timeout . duration ) {
val r = system . actorOf ( Props [ Echo ] , "blub" )
r . path . toString must be === "akka://remote-sys@localhost:12346/remote/Ticket1978CommunicationSpec@localhost:12345/user/blub"
r ! 42
expectMsg ( 42 )
EventFilter [ Exception ] ( "crash" , occurrences = 1 ) . intercept {
r ! new Exception ( "crash" )
} ( other )
expectMsg ( "preRestart" )
r ! 42
expectMsg ( 42 )
}
2012-06-15 15:04:54 +02:00
}
2012-06-11 18:33:05 +02:00
2012-06-15 15:04:54 +02:00
"look-up actors across node boundaries" in {
2012-06-18 18:47:35 +02:00
within ( timeout . duration ) {
val l = system . actorOf ( Props ( new Actor {
def receive = {
case ( p : Props , n : String ) ⇒ sender ! context . actorOf ( p , n )
case s : String ⇒ sender ! context . actorFor ( s )
}
} ) , "looker" )
l ! ( Props [ Echo ] , "child" )
val r = expectMsgType [ ActorRef ]
r ! ( Props [ Echo ] , "grandchild" )
val remref = expectMsgType [ ActorRef ]
remref . isInstanceOf [ LocalActorRef ] must be ( true )
val myref = system . actorFor ( system / "looker" / "child" / "grandchild" )
myref . isInstanceOf [ RemoteActorRef ] must be ( true )
myref ! 43
expectMsg ( 43 )
lastSender must be theSameInstanceAs remref
r . asInstanceOf [ RemoteActorRef ] . getParent must be ( l )
system . actorFor ( "/user/looker/child" ) must be theSameInstanceAs r
Await . result ( l ? "child/.." , timeout . duration ) . asInstanceOf [ AnyRef ] must be theSameInstanceAs l
Await . result ( system . actorFor ( system / "looker" / "child" ) ? ".." , timeout . duration ) . asInstanceOf [ AnyRef ] must be theSameInstanceAs l
}
2012-06-15 15:04:54 +02:00
}
2012-06-11 18:33:05 +02:00
2012-06-15 15:04:54 +02:00
"not fail ask across node boundaries" in {
val f = for ( _ ← 1 to 1000 ) yield here ? "ping" mapTo manifest [ ( String , ActorRef ) ]
2012-06-18 18:47:35 +02:00
Await . result ( Future . sequence ( f ) , timeout . duration ) . map ( _ . _1 ) . toSet must be ( Set ( "pong" ) )
2012-06-15 15:04:54 +02:00
}
} else {
"not be run when the cipher is not supported by the platform this test is currently being executed on" ignore {
2012-06-11 18:33:05 +02:00
2012-06-15 15:04:54 +02:00
}
2012-06-11 18:33:05 +02:00
}
}
2012-06-18 13:57:42 +02:00
}