Running as a JDK http server
To expose endpoints using the
http server built into the JDK
(com.sun.net.httpserver), first add the following dependency:
"com.softwaremill.sttp.tapir" %% "tapir-jdkhttp-server" % "1.13.19"
Then, import the package:
import sttp.tapir.server.jdkhttp.*
and use JdkHttpServer().addEndpoints to expose server endpoints.
These methods require a single, or a list of ServerEndpoints, which can be created by adding server logic
to an endpoint. You can use shortcut extension methods exposed by the package to transform your Endpoints into
ServerEndpoints by calling .handle method family on them (an equivalent to .serverLogic family, but with the effect
type fixed, which simplifies type inference). The handle naming convention was introduced to avoid conflicts with the
original serverLogic methods and also because names are shorter.
For example:
import sttp.tapir.*
import sttp.tapir.server.jdkhttp.*
val helloWorld = endpoint
.get
.in("hello").in(query[String]("name"))
.out(stringBody)
.handle(name => Right(s"Hello, $name!"))
val server: HttpServer =
JdkHttpServer().addEndpoint(helloWorld).start()
Important notice:
This server runs on a single worker thread by default. This is ok for testing, toy projects and things that never see any load.
If you want it to scale please read about the executor configuration option below and set it accordingly.
Given the com.sun.net.httpserver package is standardised and a part of public JDK API since JDK 18 (JEP 408) this server can be
considered stable.
Configuration
The interpreter can be configured by providing an JdkHttpServerOptions value, see server options for
details.
Most options can be configured directly using a JdkHttpServer instance, such as the host and port. Possible options are:
send404WhenRequestNotHandled: Should a 404 response be sent, when the request hasn’t been handled by defined endpoints. This is a safe default, but if there are multiple handlers for the same context path, this should be set tofalse. In that case, you can verify if the request has been handled usingJdkHttpServerInterpreter.isRequestHandled.basePath: Path under which endpoints will be mounted when mounted on JdkHttpServer instance. I.e.: basePath of/apiand endpoint/hellowill result with a real path of/api/hello.port: IP port to which JdkHttpServer instance will be bound. Default is0, which means any random port provided by the OS.host: Hostname or IP address (ie.:localhost,0.0.0.0) to which JdkHttpServer instance will be bound. Default is0.0.0.0which binds the server to all network interfaces available on the OS.executor: Allows you to configure theExecutorwhich will be used to handle HTTP requests. By defaultcom.sun.net.httpserver.HttpServeruses a single thread (a calling thread executor to be precise) executor to handle traffic which might be fine for local toy projects.If you intend to use this HTTP server for any deployments that will run under load it’s absolutely necessary to set an executor that will use proper thread pool to handle the load. Recommended approach is to use
JdkHttpServerOptions.httpExecutormethod to create a ThreadPoolExecutor that will scale under load. You can also use an Executor returned from any of the constructors in thejava.util.concurrent.Executorsclass.Alternatively, if running with a JDK 19+ you can leverage Project Loom and use
Executors.newVirtualThreadPerTaskExecutor()to run each request on a virtual thread. This however means it is possible for your server to be overloaded with work as each request will be given a thread with no backpressure on how many should be executed in parallel.httpsConfigurator: Optional HTTPS configuration. Takes an instance ofcom.sun.net.httpserver.HttpsConfigurator, which is a thin wrapper aroundjavax.net.ssl.SSLContextto configure the SSL termination for this server.backlogSize: Sets the size of server’s tcp connection backlog. This is the maximum number of queued incoming connections to allow on the listening socket. Queued TCP connections exceeding this limit may be rejected by the TCP implementation. If set to 0 or less the system default for backlog size will be used. Default is 0.