Last active
October 17, 2018 14:43
-
-
Save eyalroth/05a24c1ba44a7c74d6d3e8b4e940441a to your computer and use it in GitHub Desktop.
akka-http #2257 - Http server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.net.InetSocketAddress | |
import java.security.{KeyStore, SecureRandom} | |
import akka.actor.ActorSystem | |
import akka.http.scaladsl.Http.{HttpServerTerminated, HttpTerminated, ServerBinding} | |
import akka.http.scaladsl.server.{Route, RouteResult, RoutingLog} | |
import akka.http.scaladsl.settings.{ParserSettings, RoutingSettings} | |
import akka.http.scaladsl.{ConnectionContext, Http} | |
import akka.stream.{ActorMaterializer, ActorMaterializerSettings} | |
import com.typesafe.scalalogging.StrictLogging | |
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory} | |
import java.io.Closeable | |
import scala.concurrent.duration._ | |
import scala.concurrent.{Await, Future} | |
import scala.util.control.NonFatal | |
import scala.util.{Failure, Success} | |
class HttpServer(host: String, port: Int, route: Route, secure: Boolean)(implicit val materializer: ActorMaterializer) | |
extends Closeable with StrictLogging { | |
/* --- Data Members --- */ | |
private var connectedBinding: Option[ServerBinding] = None | |
/* --- Constructors --- */ | |
def this(host: String, port: Int, route: Route, secure: Boolean, system: ActorSystem, dispatcherName: String) = { | |
this(host, port, route, secure)( | |
ActorMaterializer(ActorMaterializerSettings(system).withDispatcher(s"akka.dispatchers.$dispatcherName"))(system)) | |
} | |
def this(configuration: HttpServerConfig, route: Route, system: ActorSystem) = { | |
this(configuration.host, configuration.port, route, configuration.secure, system, configuration.dispatcherName) | |
} | |
/* --- Methods --- */ | |
/* --- Public Methods --- */ | |
def start(): InetSocketAddress = { | |
connectedBinding match { | |
case Some(binding) => | |
throw new IllegalStateException(s"Already binding: ${binding.localAddress}") | |
case None => | |
logger.info(s"Starting HTTP server on $host:$port") | |
implicit val routingSettings = RoutingSettings(materializer.system.settings.config) | |
implicit val parserSettings = ParserSettings(materializer.system.settings.config) | |
implicit val routingLog = RoutingLog.fromActorSystem(materializer.system) | |
val flow = RouteResult.route2HandlerFlow(route) | |
val bindFuture = Http()(materializer.system).bindAndHandle(flow, host, port, context()) | |
try { | |
val binding = Await.result(bindFuture, BIND_TIMEOUT) | |
connectedBinding = Some(binding) | |
logger.info(s"HTTP server started on ${binding.localAddress}") | |
binding.localAddress | |
} catch { | |
case NonFatal(e) => | |
logger.error(s"HTTP server failed to start", e) | |
throw e | |
} | |
} | |
} | |
def stop(): Future[HttpTerminated] = { | |
connectedBinding match { | |
case Some(binding) => | |
connectedBinding = None | |
binding.terminate(10 seconds) | |
case None => Future.successful(HttpServerTerminated) | |
} | |
} | |
/* --- Private Methods --- */ | |
private def context(): ConnectionContext = { | |
if (secure) { | |
val ks: KeyStore = KeyStore.getInstance("PKCS12") | |
val keystore = getClass.getClassLoader.getResourceAsStream("mycert.p12") | |
val pw = "imabarbiegirl".toCharArray | |
require(keystore != null, "Keystore required!") | |
ks.load(keystore, pw) | |
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509") | |
keyManagerFactory.init(ks, pw) | |
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509") | |
tmf.init(ks) | |
val sslContext: SSLContext = SSLContext.getInstance("TLS") | |
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom) | |
ConnectionContext.https(sslContext) | |
} else { | |
ConnectionContext.noEncryption() | |
} | |
} | |
override def close(): Unit = { | |
Await.ready(stop(), 1 minute).value.get match { | |
case Success(_) => | |
case Failure(exception) => logger.error("Server shut down failed", exception) | |
} | |
} | |
} | |
object HttpServer { | |
/* --- Constants --- */ | |
private val BIND_TIMEOUT = 60 seconds | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment