From ff78f6f82e6567ea19e4aa2af07f38a9358658a5 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sat, 18 Feb 2023 11:00:58 +0000 Subject: [PATCH 1/3] Add test demoing `EADDRINUSE` for closed server --- build.sbt | 1 + tests/shared/src/test/scala/epollcat/TcpSuite.scala | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/build.sbt b/build.sbt index 8bc5ac3..24ed02d 100644 --- a/build.sbt +++ b/build.sbt @@ -55,6 +55,7 @@ lazy val tests = crossProject(JVMPlatform, NativePlatform) "org.typelevel" %%% "munit-cats-effect" % munitCEVersion % Test ) ) + .jvmSettings(fork := true) lazy val example = project .in(file("example")) diff --git a/tests/shared/src/test/scala/epollcat/TcpSuite.scala b/tests/shared/src/test/scala/epollcat/TcpSuite.scala index cca3d82..cf28899 100644 --- a/tests/shared/src/test/scala/epollcat/TcpSuite.scala +++ b/tests/shared/src/test/scala/epollcat/TcpSuite.scala @@ -311,4 +311,14 @@ class TcpSuite extends EpollcatSuite { IOServerSocketChannel.open.use_ } + test("closing/re-opening a server does not throw BindException: Address already in use") { + val address = new InetSocketAddress("127.0.0.0", 8080) + IOServerSocketChannel.open.evalTap(_.bind(address)).use { server => + val connect = + IOSocketChannel.open.evalTap(_.connect(address)).surround(IO.sleep(1.second)) + val accept = server.accept.surround(IO.sleep(1.second)) + connect.both(accept).void + } *> IOServerSocketChannel.open.evalTap(_.bind(address)).use_ + } + } From 33ac3475de426c9eeb1bbc9b8281f3ce8f736738 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sat, 18 Feb 2023 11:24:06 +0000 Subject: [PATCH 2/3] Default `SO_REUSEADDR=true` for server socket --- .../epollcat/internal/ch/EpollAsyncServerSocketChannel.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/scala/epollcat/internal/ch/EpollAsyncServerSocketChannel.scala b/core/src/main/scala/epollcat/internal/ch/EpollAsyncServerSocketChannel.scala index c6db729..ad3685f 100644 --- a/core/src/main/scala/epollcat/internal/ch/EpollAsyncServerSocketChannel.scala +++ b/core/src/main/scala/epollcat/internal/ch/EpollAsyncServerSocketChannel.scala @@ -211,6 +211,7 @@ object EpollAsyncServerSocketChannel { case epoll: EventPollingExecutorScheduler => val fd = SocketHelpers.mkNonBlocking() val ch = new EpollAsyncServerSocketChannel(fd) + ch.setOption(StandardSocketOptions.SO_REUSEADDR, java.lang.Boolean.TRUE) ch.unmonitor = epoll.monitor(fd, reads = true, writes = false)(ch) ch case _ => From 233aeeef1ed29711ac6281bf31c02dd7c3e71220 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sat, 18 Feb 2023 11:30:20 +0000 Subject: [PATCH 3/3] Do not hard-code port for test --- .../src/test/scala/epollcat/TcpSuite.scala | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/shared/src/test/scala/epollcat/TcpSuite.scala b/tests/shared/src/test/scala/epollcat/TcpSuite.scala index cf28899..f14b8c4 100644 --- a/tests/shared/src/test/scala/epollcat/TcpSuite.scala +++ b/tests/shared/src/test/scala/epollcat/TcpSuite.scala @@ -312,13 +312,18 @@ class TcpSuite extends EpollcatSuite { } test("closing/re-opening a server does not throw BindException: Address already in use") { - val address = new InetSocketAddress("127.0.0.0", 8080) - IOServerSocketChannel.open.evalTap(_.bind(address)).use { server => - val connect = - IOSocketChannel.open.evalTap(_.connect(address)).surround(IO.sleep(1.second)) - val accept = server.accept.surround(IO.sleep(1.second)) - connect.both(accept).void - } *> IOServerSocketChannel.open.evalTap(_.bind(address)).use_ + IOServerSocketChannel + .open + .evalTap(_.bind(new InetSocketAddress("localhost", 0))) + .use { server => + server.localAddress.flatTap { address => + val connect = + IOSocketChannel.open.evalTap(_.connect(address)).surround(IO.sleep(1.second)) + val accept = server.accept.surround(IO.sleep(1.second)) + connect.both(accept).void + } + } + .flatMap(address => IOServerSocketChannel.open.evalTap(_.bind(address)).use_) } }