Skip to content

Commit 9d49a91

Browse files
authored
Merge pull request #104 from input-output-hk/ETCM-328-external-ip-resolver
ETCM-328: External IP resolver
2 parents 1113264 + 2ea4cdf commit 9d49a91

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.iohk.scalanet.peergroup
2+
3+
import java.io.{BufferedReader, InputStreamReader}
4+
import java.net.{InetAddress, URL}
5+
6+
import monix.eval.Task
7+
import scala.util.control.NonFatal
8+
9+
/** Resolve the external address based on a list of URLs that each return the IP of the caller. */
10+
class ExternalAddressResolver(urls: List[String]) {
11+
def resolve: Task[Option[InetAddress]] =
12+
ExternalAddressResolver.checkUrls(urls)
13+
}
14+
15+
object ExternalAddressResolver {
16+
val default = new ExternalAddressResolver(List("http://checkip.amazonaws.com", "http://bot.whatismyipaddress.com"))
17+
18+
/** Retrieve the external address from a URL that returns a single line containing the IP. */
19+
def checkUrl(url: String): Task[InetAddress] = Task.async { cb =>
20+
try {
21+
val ipCheckUrl = new URL(url)
22+
val in: BufferedReader = new BufferedReader(new InputStreamReader(ipCheckUrl.openStream()))
23+
cb.onSuccess(InetAddress.getByName(in.readLine()))
24+
} catch {
25+
case NonFatal(ex) => cb.onError(ex)
26+
}
27+
}
28+
29+
/** Try multiple URLs until an IP address is found. */
30+
def checkUrls(urls: List[String]): Task[Option[InetAddress]] = {
31+
if (urls.isEmpty) {
32+
Task.now(None)
33+
} else {
34+
checkUrl(urls.head).attempt.flatMap {
35+
case Left(_) =>
36+
checkUrls(urls.tail)
37+
case Right(value) =>
38+
Task.now(Some(value))
39+
}
40+
}
41+
}
42+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.iohk.scalanet.peergroup
2+
3+
import org.scalatest._
4+
import scala.concurrent.duration._
5+
import monix.execution.Scheduler.Implicits.global
6+
7+
class ExternalAddressResolverSpec extends FlatSpec with Matchers {
8+
9+
behavior of "ExternalAddressResolver"
10+
11+
it should "resolve the external IP" in {
12+
val maybeAddress = ExternalAddressResolver.default.resolve.runSyncUnsafe(5.seconds)
13+
14+
maybeAddress should not be empty
15+
maybeAddress.get.isLoopbackAddress shouldBe false
16+
}
17+
18+
it should "return None if all resolutions fail" in {
19+
ExternalAddressResolver.checkUrls(List("", "404.html")).runSyncUnsafe(1.seconds) shouldBe empty
20+
}
21+
}

0 commit comments

Comments
 (0)