dataclassBook(valauthor:String,valtitle:String)privatevalbooks:MutableMap<Int,Book>=linkedMapOf(100toBook("Miguel de Cervantes","Don Quixote"),101toBook("William Shakespeare","Hamlet"),102toBook("Homer","The Odyssey"))valserver:Server=Server(adapter){post("/books"){// Require fails if parameter does not existsvalauthor=queryParameters.require("author")valtitle=queryParameters.require("title")valid=(books.keys.maxOrNull()?:0)+1books+=idtoBook(author,title)send(201,id)}get("/books/{id}"){valbookId=pathParameters.require("id").toInt()valbook=books[bookId]if(book!=null)// ok() is a shortcut to send(200)ok("Title: ${book.title}, Author: ${book.author}")elsesend(404,"Book not found")}put("/books/{id}"){valbookId=pathParameters.require("id").toInt()valbook=books[bookId]if(book!=null){books+=bookIdtobook.copy(author=queryParameters["author"]?:book.author,title=queryParameters["title"]?:book.title)ok("Book with id '$bookId' updated")}else{send(404,"Book not found")}}delete("/books/{id}"){valbookId=pathParameters.require("id").toInt()valbook=books[bookId]books-=bookIdif(book!=null)ok("Book with id '$bookId' deleted")elsesend(404,"Book not found")}// Matches path's requests with *any* HTTP method as a fallback (return 404 instead 405)any("/books/{id}"){send(405)}get("/books"){ok(books.keys.joinToString(" ",transform=Int::toString))}}
classCustomException:IllegalArgumentException()valserver:Server=Server(adapter){error(UnsupportedOperationException::class){response.headers["error"]=it.message?:it.javaClass.namesend(599,"Unsupported")}error(IllegalArgumentException::class){response.headers["runtimeError"]=it.message?:it.javaClass.namesend(598,"Runtime")}// Catching `Exception` handles any unhandled exception before (it has to be the last)error(Exception::class){send(500,"Root handler")}// It is possible to execute a handler upon a given status code before returningerror(588){send(578,"588 -> 578")}get("/exception"){throwUnsupportedOperationException("error message")}get("/baseException"){throwCustomException()}get("/unhandledException"){error("error message")}get("/halt"){halt("halted")}get("/588"){halt(588)}}
privatevalusers:Map<String,String>=mapOf("Turing"to"London","Dijkstra"to"Rotterdam")privatevalserver:Server=Server(adapter){before{attributes["start"]=nanoTime()}before("/protected/*"){valauthorization=request.headers["Authorization"]?:halt(401,"Unauthorized")valcredentials=authorization.removePrefix("Basic ")valuserPassword=String(Base64.getDecoder().decode(credentials)).split(":")// Parameters set in call attributes are accessible in other filters and routesattributes["username"]=userPassword[0]attributes["password"]=userPassword[1]}// All matching filters are run in order unless call is haltedbefore("/protected/*"){if(users[attributes["username"]]!=attributes["password"])halt(403,"Forbidden")}get("/protected/hi"){ok("Hello ${attributes["username"]}!")}// After filters are ran even if request was halted beforeafter{response.headers["time"]=nanoTime()-attributes["start"]asLong}}
valserver:Server=Server(adapter){corsPath("/default",CorsSettings())corsPath("/example/org",CorsSettings("example.org"))corsPath("/no/credentials",CorsSettings(supportCredentials=false))corsPath("/only/post",CorsSettings(allowedMethods=setOf(POST)))corsPath("/cache",CorsSettings(preFlightMaxAge=10))corsPath("/exposed/headers",CorsSettings(exposedHeaders=setOf("head")))corsPath("/allowed/headers",CorsSettings(allowedHeaders=setOf("head")))}privatefunRouter.corsPath(path:String,settings:CorsSettings){path(path){// CORS settings can change for different routescors(settings)get("/path"){ok(request.method)}post("/path"){ok(request.method)}put("/path"){ok(request.method)}delete("/path"){ok(request.method)}get{ok(request.method)}post{ok(request.method)}put{ok(request.method)}delete{ok(request.method)}}}
// Key store filesvalidentity="hexagonkt.p12"valtrust="trust.p12"// Default passwords are file name reversedvalkeyStorePassword=identity.reversed()valtrustStorePassword=trust.reversed()// Key stores can be set as URIs to classpath resources (the triple slash is needed)valkeyStore=URL("classpath:ssl/$identity")valtrustStore=URL("classpath:ssl/$trust")valsslSettings=SslSettings(keyStore=keyStore,keyStorePassword=keyStorePassword,trustStore=trustStore,trustStorePassword=trustStorePassword,clientAuth=true// Requires a valid certificate from the client (mutual TLS))valserverSettings=ServerSettings(bindPort=0,protocol=HTTPS,// You can also use HTTP2sslSettings=sslSettings)valserver=serve(serverSettings,serverAdapter){get("/hello"){// We can access the certificate used by the client from the requestvalsubjectDn=request.certificate?.subjectDN?.nameresponse.headers["cert"]=subjectDnok("Hello World!")}}// We'll use the same certificate for the client (in a real scenario it would be different)valclientSettings=ClientSettings(sslSettings=sslSettings)// Create a HTTP client and make a HTTPS requestvalclient=Client(AhcAdapter(),"https://localhost:${server.runtimePort}",clientSettings)client.get("/hello").apply{logger.debug{body}// Assure the certificate received (and returned) by the server is correctassert(headers.require("cert").first().startsWith("CN=hexagonkt.com"))assert(body=="Hello World!")}