From 6f0ea1257e8cf6158bf2010a99d20eeaafbadb36 Mon Sep 17 00:00:00 2001 From: Renato Cavalcanti Date: Sun, 13 Jan 2019 18:37:36 +0100 Subject: [PATCH] adds service name parser (copied from akka-management) (#26132) * adds service name parser * Apply suggestions from code review Co-Authored-By: renatocaval * dangling parenthesis on last line * code formatting * extended tests for Lookup.isValidSrv * make it illegal to create Lookups with empty or null service name * improved tests description * improved error messages * formatting * updated copyright header * removed weird import * removes trailing dot * throw NPE when null is passed --- .../akka/discovery/ServiceDiscovery.scala | 45 ++++++++ .../scala/akka/discovery/LookupSpec.scala | 108 ++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 akka-discovery/src/test/scala/akka/discovery/LookupSpec.scala diff --git a/akka-discovery/src/main/scala/akka/discovery/ServiceDiscovery.scala b/akka-discovery/src/main/scala/akka/discovery/ServiceDiscovery.scala index c4b06adff0..4e8a62bcb5 100644 --- a/akka-discovery/src/main/scala/akka/discovery/ServiceDiscovery.scala +++ b/akka-discovery/src/main/scala/akka/discovery/ServiceDiscovery.scala @@ -128,6 +128,7 @@ object ServiceDiscovery { * For example `portName` could be used to distinguish between * Akka remoting ports and HTTP ports. * + * @throws IllegalArgumentException if [[serviceName]] is 'null' or an empty String */ @ApiMayChange final class Lookup( @@ -135,6 +136,9 @@ final class Lookup( val portName: Option[String], val protocol: Option[String]) { + require(serviceName != null, "'serviceName' cannot be null") + require(serviceName.trim.nonEmpty, "'serviceName' cannot be empty") + /** * Which port for a service e.g. Akka remoting or HTTP. * Maps to "service" for an SRV records. @@ -194,6 +198,47 @@ case object Lookup { * and protocol */ def create(serviceName: String): Lookup = new Lookup(serviceName, None, None) + + private val SrvQuery = """^_(.+?)\._(.+?)\.(.+?)$""".r + + private val DomainName = "^((?!-)[A-Za-z0-9-]{1,63}(? + */ + +package akka.discovery + +import org.scalatest.{ Matchers, OptionValues, WordSpec } + +class LookupSpec extends WordSpec with Matchers with OptionValues { + + // SRV strings with invalid domain names + // should fail to build lookups + val srvWithInvalidDomainNames = List( + "_portName._protocol.service_name.local", + "_portName._protocol.servicename,local", + "_portName._protocol.servicename.local-", + "_portName._protocol.-servicename.local") + + // No SRV that should result in simple A/AAAA lookups + val noSrvLookups = List( + "portName.protocol.serviceName.local", + "serviceName.local", + "_portName.serviceName", + "_serviceName.local", + "_serviceName,local", + "-serviceName.local", + "serviceName.local-") + + "Lookup.parseSrv" should { + + "generate a SRV Lookup from a SRV String" in { + val name = "_portName._protocol.serviceName.local" + val lookup = Lookup.parseSrv(name) + lookup.serviceName shouldBe "serviceName.local" + lookup.portName.value shouldBe "portName" + lookup.protocol.value shouldBe "protocol" + } + + "throw an IllegalArgumentException when passing a 'null' SRV String" in { + assertThrows[NullPointerException] { + Lookup.parseSrv(null) + } + } + + "throw an IllegalArgumentException when passing an empty SRV String" in { + assertThrows[IllegalArgumentException] { + Lookup.parseSrv("") + } + } + + "throw an IllegalArgumentException for any non-conforming SRV String" in { + noSrvLookups.foreach { str ⇒ + withClue(s"parsing '$str'") { + assertThrows[IllegalArgumentException] { + Lookup.parseSrv(str) + } + } + } + } + + "throw an IllegalArgumentException for any SRV with invalid domain names" in { + srvWithInvalidDomainNames.foreach { str ⇒ + withClue(s"parsing '$str'") { + assertThrows[IllegalArgumentException] { + Lookup.parseSrv(str) + } + } + } + } + + } + + "Lookup.isValidSrv" should { + + "return true for any conforming SRV String" in { + Lookup.isValidSrv("_portName._protocol.serviceName.local") shouldBe true + } + + "return false for any non-conforming SRV String" in { + noSrvLookups.foreach { str ⇒ + withClue(s"checking '$str'") { + Lookup.isValidSrv(str) shouldBe false + } + } + } + + "return false if domain part in SRV String is an invalid domain name" in { + srvWithInvalidDomainNames.foreach { str ⇒ + withClue(s"checking '$str'") { + Lookup.isValidSrv(str) shouldBe false + } + } + } + + "return false for empty SRV String" in { + Lookup.isValidSrv("") shouldBe false + } + + "return false for 'null' SRV String" in { + Lookup.isValidSrv(null) shouldBe false + } + + "return true for a SRV with valid domain name" in { + Lookup.isValidSrv("_portName._protocol.serviceName.local") shouldBe true + } + + } +}