Skip to content

Instantly share code, notes, and snippets.

@nimatrueway
Last active June 27, 2023 05:09
Show Gist options
  • Select an option

  • Save nimatrueway/f7ddffd36038d08fe43ae777ab300f6b to your computer and use it in GitHub Desktop.

Select an option

Save nimatrueway/f7ddffd36038d08fe43ae777ab300f6b to your computer and use it in GitHub Desktop.

Revisions

  1. nimatrueway revised this gist Jun 27, 2023. 1 changed file with 8 additions and 16 deletions.
    24 changes: 8 additions & 16 deletions Main.scala
    Original file line number Diff line number Diff line change
    @@ -1,17 +1,16 @@
    import sttp.client3._
    import sttp.model.Uri
    import sttp.tapir._
    import sttp.tapir.server.netty.{NettyFutureServer, NettyFutureServerBinding, NettyFutureServerOptions, NettyOptions}
    import sttp.tapir.server.netty.{NettyFutureServer, NettyFutureServerOptions, NettyOptions}

    import java.net.InetSocketAddress
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.duration._
    import scala.concurrent.{Await, Future, blocking}
    import scala.util.{Failure, Success}

    object Main {

    private val helloWorld = endpoint
    private val helloWorldEndpoint = endpoint
    .get
    .in("hello")
    .out(stringBody)
    @@ -26,19 +25,6 @@ object Main {
    }
    )

    private val bindingF: Future[NettyFutureServerBinding[InetSocketAddress]] =
    NettyFutureServer()
    .port(8080)
    .options(
    NettyFutureServerOptions.default
    .nettyOptions(
    NettyOptions.default
    .copy(shutdownEventLoopGroupOnClose = true) // no effect as it is enabled by default
    )
    )
    .addEndpoint(helloWorld)
    .start()

    def main(args: Array[String]): Unit = {
    implicit val backend = HttpClientFutureBackend()

    @@ -48,6 +34,12 @@ object Main {
    }):Runnable))

    // start server
    val serverOptions = NettyFutureServerOptions.default
    .nettyOptions(
    NettyOptions.default
    .copy(shutdownEventLoopGroupOnClose = true) // no effect as it is enabled by default
    )
    val bindingF = NettyFutureServer().port(8080).options(serverOptions).addEndpoint(helloWorldEndpoint).start()
    val binding = Await.result(bindingF, 10.seconds)
    println("[Server] started.")

  2. nimatrueway revised this gist Jun 27, 2023. No changes.
  3. nimatrueway renamed this gist Jun 27, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. nimatrueway revised this gist Jun 27, 2023. 4 changed files with 144 additions and 1 deletion.
    74 changes: 74 additions & 0 deletions Main.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    import sttp.client3._
    import sttp.model.Uri
    import sttp.tapir._
    import sttp.tapir.server.netty.{NettyFutureServer, NettyFutureServerBinding, NettyFutureServerOptions, NettyOptions}

    import java.net.InetSocketAddress
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.duration._
    import scala.concurrent.{Await, Future, blocking}
    import scala.util.{Failure, Success}

    object Main {

    private val helloWorld = endpoint
    .get
    .in("hello")
    .out(stringBody)
    .serverLogicSuccess(_ =>
    Future {
    println("[Server] received the request.")
    blocking {
    Thread.sleep(3000)
    println("[Server] successfully processed the request.")
    }
    s"Hello World!"
    }
    )

    private val bindingF: Future[NettyFutureServerBinding[InetSocketAddress]] =
    NettyFutureServer()
    .port(8080)
    .options(
    NettyFutureServerOptions.default
    .nettyOptions(
    NettyOptions.default
    .copy(shutdownEventLoopGroupOnClose = true) // no effect as it is enabled by default
    )
    )
    .addEndpoint(helloWorld)
    .start()

    def main(args: Array[String]): Unit = {
    implicit val backend = HttpClientFutureBackend()

    // shutdown hook to print if a signal is received
    Runtime.getRuntime.addShutdownHook(new Thread((() => {
    println("[Application] shutdown signal received.")
    }):Runnable))

    // start server
    val binding = Await.result(bindingF, 10.seconds)
    println("[Server] started.")


    // call my endpoint and then kill me
    println(s"[Client] Sending request.")
    emptyRequest
    .get(Uri.parse("http://localhost:8080/hello").getOrElse(???))
    .send(backend)
    .onComplete {
    case Success(r) => println(s"[Client] Response received: $r")
    case Failure(exception) => exception.printStackTrace()
    }
    // wait until the service receives the request
    Thread.sleep(1000L)

    // kill myself
    println(s"[Client] Stopping server.")
    Await.result(binding.stop(), 10.seconds)

    println(s"[Client] Stopped server.")
    }

    }
    49 changes: 48 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,48 @@
    test
    Minimal reproduction of the issue of tapir-netty server not shutting down gracefully while there are in-flight requests.

    Result:
    ```
    [Server] started.
    [Client] Sending request.
    [Server] received the request.
    [Client] Stopping server.
    sttp.client3.SttpClientException$ConnectException: Exception when sending request: GET http://localhost:8080/hello
    at sttp.client3.SttpClientExceptionExtensions.defaultExceptionToSttpClientException(SttpClientExceptionExtensions.scala:13)
    at sttp.client3.SttpClientExceptionExtensions.defaultExceptionToSttpClientException$(SttpClientExceptionExtensions.scala:11)
    at sttp.client3.SttpClientException$.defaultExceptionToSttpClientException(SttpClientException.scala:24)
    at sttp.client3.HttpClientAsyncBackend.$anonfun$adjustExceptions$1(HttpClientAsyncBackend.scala:147)
    at sttp.client3.SttpClientException$$anonfun$adjustExceptions$1.applyOrElse(SttpClientException.scala:35)
    at sttp.client3.SttpClientException$$anonfun$adjustExceptions$1.applyOrElse(SttpClientException.scala:34)
    at scala.concurrent.impl.Promise$Transformation.run$$$capture(Promise.scala:490)
    at scala.concurrent.impl.Promise$Transformation.run(Promise.scala)
    at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
    at java.base/java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
    Caused by: java.net.ConnectException: Connection refused
    at java.base/sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
    at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:777)
    at java.net.http/jdk.internal.net.http.PlainHttpConnection$ConnectEvent.handle(PlainHttpConnection.java:128)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.handleEvent(HttpClientImpl.java:957)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.lambda$run$3(HttpClientImpl.java:912)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:912)
    [Client] Stopped server.
    [Server] successfully processed the request.
    [Application] shutdown signal received.
    ```

    Expectation:
    ```
    [Server] started.
    [Client] Sending request.
    [Server] received the request.
    [Client] Stopping server.
    [Server] successfully processed the request.
    [Client] Response received: Response(Right(Hello World!),200,,List(content-length: 12, content-type: text/plain; charset=UTF-8),List(),RequestMetadata(GET,http://localhost:8080/hello,Vector()))
    [Client] Stopped server.
    [Application] shutdown signal received.
    ```
    11 changes: 11 additions & 0 deletions build.sbt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    ThisBuild / version := "0.1.0-SNAPSHOT"
    ThisBuild / scalaVersion := "2.13.11"

    lazy val root = (project in file("."))
    .settings(
    libraryDependencies ++= Seq(
    "com.softwaremill.sttp.tapir" %% "tapir-netty-server" % "1.5.5",
    "com.softwaremill.sttp.tapir" %% "tapir-sttp-client" % "1.5.5"
    ),
    name := "scala-tapir-netty-shutdown"
    )
    11 changes: 11 additions & 0 deletions logback.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    <configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
    <pattern>%level %logger{15} - %message%n%xException{10}</pattern>
    </encoder>
    </appender>

    <root level="INFO">
    <appender-ref ref="STDOUT"/>
    </root>
    </configuration>
  5. nimatrueway created this gist Jun 27, 2023.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    test