I've found several resources that provide details on configuring ssl-config options within the application.conf file and I've identified how to access these configurations using AkkaSSLConfig.get(). I've seen that an https context can be created using a AkkaSSLConfig object as a parameter to ConnectionContext.https().
Is it possible to use this for non-http servers? Is the context returned somehow specific to http? I'm trying to take advantage of ssl-config but it isn't clear to me that it provides any advantages for non-http servers and I don't see any convenient way of building a context from the ssl-config definition, in which case it seems I may as well define the context manually.
Lastly, any examples of building the context for non-http servers are difficult to find. It seems the process may be the same as for http servers, but I'm finding that examples often include the use of classes/methods that have 'http' in the name. If anyone knows of a good example I'd be very appreciative.
import java.io.{File, FileInputStream}
import java.security.{KeyStore, SecureRandom}
import akka.actor.ActorSystem
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Directives.pathSingleSlash
import akka.http.scaladsl.{ConnectionContext, Http}
import akka.stream.{ActorMaterializer, TLSClientAuth}
import com.typesafe.sslconfig.akka.AkkaSSLConfig
import com.typesafe.sslconfig.ssl.{KeyManagerConfig, KeyManagerFactoryWrapper, KeyStoreConfig, SSLConfigFactory, SSLConfigSettings}
import javax.net.ssl.{SSLContext, TrustManagerFactory}
import scala.concurrent.{ExecutionContext, Future}
object Test extends App{
implicit val actorSystem: ActorSystem = ActorSystem("test")
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val executionContext: ExecutionContext = actorSystem.dispatcher
val ksConfig: KeyStoreConfig = KeyStoreConfig.apply(data = None,
filePath = Some("/Users/mshaik/testApp/src/main/resources/keystore/localhost.p12")
).withPassword(Some("test"))
val kmConfig: KeyManagerConfig = KeyManagerConfig().withKeyStoreConfigs(List(ksConfig))
val sslConfigSettings: SSLConfigSettings = SSLConfigFactory.defaultConfig.withKeyManagerConfig(kmConfig)
val akkaSSLConfig: AkkaSSLConfig = AkkaSSLConfig.get(actorSystem).withSettings(sslConfigSettings)
val ks: KeyStore = KeyStore.getInstance("PKCS12")
ks.load(new FileInputStream(new File(ksConfig.filePath.get)), ksConfig.password.get.toCharArray)
val kmf: KeyManagerFactoryWrapper = akkaSSLConfig.buildKeyManagerFactory(sslConfigSettings)
kmf.init(ks, ksConfig.password.get.toCharArray)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
tmf.init(ks)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
val ctx: ConnectionContext = ConnectionContext.https(sslContext,
sslConfig = Some(akkaSSLConfig),
clientAuth = Some(TLSClientAuth.Want)
)
var bindingFuture: Future[ServerBinding] = _
Http().setDefaultServerHttpContext(ctx)
val route: Route = pathSingleSlash {
get {
complete(HttpResponse(StatusCodes.OK, entity = "Welcome to base path!"))
}
}
try{
bindingFuture = Http().bindAndHandle(route, "localhost", 8085, connectionContext = ctx)
println( s"Server online at https://localhost:8085/")
} catch {
case ex: Exception =>
println(this.getClass, ex.getMessage, ex)
materializer.shutdown()
actorSystem.terminate()
}
}
I believe the answer to my question is that there isn't much use in thoroughly configuring TLS options within ssl-config when creating a non-HTTP TLS connection.
Not a single example I found shows how to define keystore and truststore parameters within the config and then use those configurations to create the SSLContext object (all examples configure the keystore/truststore parameters manually, within the code). Ultimately I found it wasn't useful to use ssl-config for storing configurations. The only place I found it useful is to obtain the list of default ciphers and default protocols (and hence I still use it in my code).
For reference, below is what I ended up doing to configure the context and initial session structure and create the TCP server. This is very similar to other examples found within documentation as well as some responses here on SO. Some differences in this response: 1) This requires client certificates, 2) This is for a server (as opposed to a client), 3) This code shows how to use factory methods to create the TLS BidiFlow (note the Tcp().bindTls call) 4) This allows you to pass in the Flow that will handle the incoming communications.
object TcpServerBindTls extends StrictLogging {
def apply(hostInterface: String, tcpPort: Int, handler: Flow[ByteString, ByteString, NotUsed])(implicit system: ActorSystem, materializer: ActorMaterializer) = {
val sslContext = buildSSLContext
val firstSession = prepareFirstSession(sslContext)
val connections: Source[Tcp.IncomingConnection, Future[Tcp.ServerBinding]] = Tcp().bindTls(hostInterface, tcpPort, sslContext, firstSession)
connections runForeach { connection =>
logger.info(s"New connection: ${connection}")
connection.handleWith(handler)
}
}
def prepareFirstSession(sslContext: SSLContext)(implicit system: ActorSystem) = {
val sslConfig = AkkaSSLConfig.get(system);
val config = sslConfig.config;
val defaultParams = sslContext.getDefaultSSLParameters();
val defaultProtocols = defaultParams.getProtocols();
val defaultCiphers = defaultParams.getCipherSuites();
val clientAuth = TLSClientAuth.need
defaultParams.setProtocols(defaultProtocols)
defaultParams.setCipherSuites(defaultCiphers)
val firstSession = new TLSProtocol.NegotiateNewSession(None, None, None, None)
.withCipherSuites(defaultCiphers: _*)
.withProtocols(defaultProtocols: _*)
.withParameters(defaultParams)
firstSession
}
def buildSSLContext: SSLContext = {
val bufferedSource = io.Source.fromFile("/path/to/password/file")
val keyStorePassword = bufferedSource.getLines.mkString
bufferedSource.close
val keyStore = KeyStore.getInstance("PKCS12");
val keyStoreLocation = "/path/to/keystore/file/server.p12"
val keyStoreFIS = new FileInputStream(keyStoreLocation)
keyStore.load(keyStoreFIS, keyStorePassword.toCharArray())
val trustStore = KeyStore.getInstance("PKCS12");
val trustStoreLocation = settings.tls.keyStoreLocation;
val trustStoreFIS = new FileInputStream(keyStoreLocation)
trustStore.load(trustStoreFIS, keyStorePassword.toCharArray())
val kmf = KeyManagerFactory.getInstance("SunX509")
kmf.init(keyStore, keyStorePassword.toCharArray())
val tmf = TrustManagerFactory.getInstance("SunX509")
tmf.init(trustStore)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom())
sslContext
}
}
Related
I'm new to Alpakka/Akka Streams and I'm trying to set up a stream where I stream data between two SFTP servers with my system in the middle, here's the code.
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.alpakka.ftp.scaladsl.Sftp
import akka.stream.alpakka.ftp.{FtpCredentials, SftpSettings}
import akka.stream.scaladsl.Keep
import net.schmizz.sshj.{DefaultConfig, SSHClient}
import java.net.InetAddress
class StreamingSftpTransport {
implicit val system: ActorSystem = ActorSystem("dr-service")
implicit val materializer: ActorMaterializer = ActorMaterializer()
private val PORT = 22
private val USER = "testsftp"
private val CREDENTIALS = FtpCredentials.create(USER, "t3st123")
private val BASEPATH = s"/home/$USER"
private val FILE_NAME = "testfile"
// Set up the source system connection
private val SOURCE_HOSTNAME = "host1"
private val sourceSettings = SftpSettings.apply(host = InetAddress.getByName(SOURCE_HOSTNAME))
.withCredentials(CREDENTIALS)
.withPort(22)
private val sourceClient = new SSHClient(new DefaultConfig)
private val configuredSourceClient = Sftp(sourceClient)
// Set up the destination system connection
private val DEST_HOSTNAME = "host2"
private val destSettings = SftpSettings.apply(host = InetAddress.getByName(DEST_HOSTNAME))
.withCredentials(CREDENTIALS)
.withPort(22)
private val destClient = new SSHClient(new DefaultConfig)
private val configuredDestClient = Sftp(destClient)
/**
* Execute the stream from host1 to host2
*/
def doTransfer(): Unit = {
val source = configuredSourceClient.fromPath(s"$BASEPATH/$FILE_NAME", sourceSettings)
val sink = configuredDestClient.toPath(s"$BASEPATH/$FILE_NAME", destSettings)
val runnable = source.toMat(sink)(Keep.right).run()
}
}
I've called this from a unit test with new StreamingSftpTransport.doTransfer() but it never attempts to connect. What am I doing wrong?
As suggested by artur in the comment on my question, I wasn't blocking on the future so the JVM was exiting before the connection could be established.
Adding the following line allowed the connections to be established
Await.result(runnable, 180 seconds)
PS: Don't do this in production :)
in Scala, I have an akka http client class with some local binding:
class AkkaConPoolingHttpClient(
override val timeout: Option[FiniteDuration] = None,
val localBinding: Option[InetSocketAddress] = None,
val userAgentHeader: Option[String] = None)(
implicit val config: HttpClient.Config,
val system: ActorSystem,
val materializer: Materializer)
extends AkkaHttpClient {
protected val http = Http()
override def dispatch(request: HttpRequest): Future[HttpResponse] = {
val effectivePort = request.uri.effectivePort
val connection =
http.outgoingConnection(
request.uri.authority.host.address(),
port = effectivePort,
localAddress = localBinding)
val preparedRequest = userAgentHeader match {
case Some(userAgent) => fixUri(request.withHeaders(request.headers ++ Seq(headers.`User-Agent`(userAgent))))
case None => fixUri(request)
}
Source.single(preparedRequest) via connection runWith Sink.head
}
object AkkaConPoolingHttpClient {
private def fixUri(request: HttpRequest): HttpRequest =
request.withUri(request.uri.toRelative)
}
and I'm trying to see if it reuses the connections and it seems it doesn't:
val connectionCount = new AtomicInteger()
val testServerFuture = Http().bind("127.0.0.1", 0).to {
Sink.foreach { incomingConnection =>
connectionCount.incrementAndGet()
incomingConnection.flow.join(Flow[HttpRequest].map(_ => HttpResponse())).run()
}
}.run()
val testServerPort = Await.result(testServerFuture, defaultExpectTimeout)
.localAddress.getPort
val address = "127.0.0.1"
val addr = Some(new InetSocketAddress(address, 0))
val client = new AkkaConPoolingHttpClient(localBinding = addr)
// Send some requests concurrently
val requests = List(
Get(s"http://127.0.0.1:$testServerPort/1"),
Get(s"http://127.0.0.1:$testServerPort/2"),
Get(s"http://127.0.0.1:$testServerPort/3"))
val responses = Await.result(
Future.sequence(requests.map(client.sendRequest)),
defaultExpectTimeout)
// Send some more requests -- the connections from before should be reused
Thread.sleep(500)
val responses2 = Await.result(
Future.sequence(requests.map(client.sendRequest)),
defaultExpectTimeout)
// Usually this is "3", occasionally "4".
connectionCount.get() must beLessThanOrEqualTo(4)
Unfortunately, the test fails, connectionCount.get() has 6 connections. Why isn't it reuse the connections? what's wrong with this code?
I also tried with:
val effectivePort = request.uri.effectivePort
val clientSettings = ClientConnectionSettings(system).withSocketOptions(SO.ReuseAddress(true) :: Nil)
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
Http().outgoingConnection(
request.uri.authority.host.address(),
port = effectivePort,
localAddress = localBinding,
settings = clientSettings
)
..................
Source.single(preparedRequest)
.via(connectionFlow)
.runWith(Sink.head)
But I still have 6 connections in my test...
Problem
The problem is rooted in the fact that you are creation a new connection for each request. The client code is actually quite clear:
Source.single(preparedRequest) via connection runWith Sink.head
Each request is being sent through a newly instantiated connection. This is due to a general design flaw where you are getting the address from the request:
val connection =
http.outgoingConnection(
request.uri.authority.host.address(), //address comes from request
port = effectivePort,
localAddress = localBinding)
It would be more efficient to establish the address once (ensuring a single Connection), and then each Request would just need the path.
Solution
To use a single connection you'll have to create a single Flow and send all of your requests through that, as described here.
I am rewriting some application layer code in scala from using scalaj to akka-http
in order to reduce the number of third party dependencies in the project (we already use akka for other things in the same project.) The code simply wraps common types of request to an underlying general request provided by the library
Mostly it has been fine, but I am stuck on the problem of optionally adding a proxy to a request.
Requests should either be direct to the destination or via a proxy, determined by a parameter at runtime.
In my scalaj implementation, I have the following helper class and methods
object HttpUtils {
private def request(
host: Host,
method: HttpMethod,
params: Map[String, String],
postData: Option[String],
timeout: Duration,
headers: Seq[(String, String)],
proxy: Option[ProxyConfig]
): HttpResponse[String] = {
// most general request builder. Other methods in the object fill in parameters and wrap this in a Future
val baseRequest = Http(host.url)
val proxiedRequest = addProxy(proxy, baseRequest)
val fullRequest = addPostData(postData)(proxiedRequest)
.method(method.toString)
.params(params)
.headers(headers)
.option(HttpOptions.connTimeout(timeout.toMillis.toInt))
.option(HttpOptions.readTimeout(timeout.toMillis.toInt))
fullRequest.asString // scalaj for send off request and block until response
}
// Other methods ...
private def addProxy(proxy: Option[ProxyConfig], request: HttpRequest): HttpRequest =
proxy.fold(request)((p: ProxyConfig) => request.proxy(p.host, p.port))
}
case class ProxyConfig(host: String, port: Int)
Is there a way to build a similar construct with akka-http?
Akka HTTP does have proxy support that, as of version 10.0.9, is still unstable. Keeping in mind that the API could change, you could do something like the following to handle optional proxy settings:
import java.net.InetSocketAddress
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.{ClientTransport, Http}
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
case class ProxyConfig(host: String, port: Int)
val proxyConfig = Option(ProxyConfig("localhost", 8888))
val clientTransport =
proxyConfig.map(p => ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(p.host, p.port)))
.getOrElse(ClientTransport.TCP)
val settings = ConnectionPoolSettings(system).withTransport(clientTransport)
Http().singleRequest(HttpRequest(uri = "https://google.com"), settings = settings)
In Akka Http 10.2.0, use bindflow for a Flow[HttpRequest, HttpResponse, NotUsed] defined by a RunnableGraph with Flowshape. Insided the RunnableGraph, an Http() outgoingConnection is used to connect to the remote proxy. Some example code:
import akka.actor.typed.ActorSystem
import akka.actor.typed.scaladsl.Behaviors
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import akka.stream._
import akka.stream.scaladsl.{Broadcast, Flow, GraphDSL, Merge}
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration.DurationInt
import scala.io.StdIn
import scala.util.{Failure, Success}
object Main {
def main(args: Array[String]) {
implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "testproxy")
implicit val executionContext: ExecutionContextExecutor = system.executionContext
system.log.info("TestAkkaHttpProxy Main started...")
val remoteHost = "xxx.xxx.xxx.x"
val remotePort = 8000
val proxyHost = "0.0.0.0"
val proxyPort = 8080
val gateway = Flow.fromGraph(GraphDSL.create() { implicit b =>
import GraphDSL.Implicits._
// Broadcast for flow input
val broadcaster = b.add(Broadcast[HttpRequest](1))
// Merge for flow output
val responseMerge = b.add(Merge[HttpResponse](1))
// outgoing client for remote proxy
val remote = Http().outgoingConnection(remoteHost, remotePort)
// filter out header that creates Akka Http warning
val requestConvert = Flow[HttpRequest]
.map(req => { req.mapHeaders(headers => headers.filter(h => h.isNot("timeout-access")))
})
// connect graph
broadcaster.out(0) ~> requestConvert ~> remote ~> responseMerge
// expose ports
FlowShape(broadcaster.in, responseMerge.out)
})
// Akka Http server that binds to Flow (for remote proxy)
Http().newServerAt(proxyHost, proxyPort).bindFlow(gateway)
.onComplete({
case Success(binding) ⇒
println(s"Server is listening on 0.0.0.0:8080")
binding.addToCoordinatedShutdown(hardTerminationDeadline = 10.seconds)
case Failure(e) ⇒
println(s"Binding failed with ${e.getMessage}")
system.terminate()
})
system.log.info("Press RETURN to stop...")
StdIn.readLine()
system.terminate()
}
}
I am trying to make spray-client connect using https to restricted rest api. The problem is that the certificate of the remote server is not registered as trusted, the simple Get() connection is then refused with SSLHandshakeException and I struggle to find any information about how to make this work. This somehow does work from my local machine without a need to change something.
I have found tutorials about how to put the certificate into jvm truststore, however since I am using dokku/docker, AFAIK the jvm instance is container specific (or?). Even though, I may in the future redeploy application on different machines, I'd like to have it defined in the application rather than setting jvm up everytime.
This is the first time I am facing SSL programmatically, so I may make wrong assumptions about how it works. Can you help?
I am not an expert in scala and I have never used spray-client but I will try to help you based on my Java experience.
You have two options, initialize a SSLContext with a TrustManagerFactory from a keystore with the server certificate (SECURE)
File keyStoreFile = new File("./myKeyStore");
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(keyStoreFile), "keyStorePassword".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(ks);
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tmf.getTrustManagers(), new java.security.SecureRandom());
or create a Dummy TrustManagerFactory which accepts any certificate (INSECURE)
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
public class DummyTrustManager implements X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
/* (non-Javadoc)
* #see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[], java.lang.String)
*/
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
/* (non-Javadoc)
* #see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], java.lang.String)
*/
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
}
initialize SSLContext by this way (it is very similar in spray-client)
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[] { new DummyTrustManager() }, new java.security.SecureRandom());
I don't know the Scala syntax but should not be difficult to translate it to you.
Hope this helps.
EDIT (suggested by Matej Briškár): The above is the correct approach, however for spray-client it is not that easy. To make sendReceive work with SSL, you need to first establish connection and then pass this connection to sendReceive.
First create implicit trust manager as described above. For example:
implicit def sslContext: SSLContext = {
val context = SSLContext.getInstance("TLS")
context.init(null, Array[TrustManager](new DummyTrustManager), new SecureRandom())
context
}
Note that this connection will time out after a while so you may want to change this default behaviour.
Then you need to establish the connection that will use this implicit like:
val connection = {
Await.result((IO(Http) ? HostConnectorSetup(host, port = 443, sslEncryption = true)).map { case HostConnectorInfo(hostConnector, _) => hostConnector }, timeout.duration)
}
Note: host means the URL you are trying to reach. Also timeout is coming from outside of this code snippet.
And finally you can use sendReceive(connection) to access the SSL encrypted host.
Note: The original edit had a reference:
According to discussion online the issue is going to be fixed though.
However, the discussion is from 2013 and now it's 2016. The problem of needing a connection be made to get SSL working seems still be there. Not sure if the discussion is relevant, any more.
Here is my 2 cents if you just want to do it in INSECURE way, I just create my sendReceive method to send (HttpRequest, HostConnectorSetup) instead of HttpRequest
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.{SSLContext, TrustManager, X509TrustManager}
import akka.actor.ActorRefFactory
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import spray.can.Http
import spray.can.Http.HostConnectorSetup
import spray.client.pipelining._
import spray.http.{HttpResponse, HttpResponsePart}
import spray.io.ClientSSLEngineProvider
import spray.util._
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
object Test {
// prepare your sslContext and engine Provider
implicit lazy val engineProvider = ClientSSLEngineProvider(engine => engine)
implicit lazy val sslContext: SSLContext = {
val context = SSLContext.getInstance("TLS")
context.init(null, Array[TrustManager](new DummyTrustManager), new SecureRandom)
context
}
private class DummyTrustManager extends X509TrustManager {
def isClientTrusted(cert: Array[X509Certificate]): Boolean = true
def isServerTrusted(cert: Array[X509Certificate]): Boolean = true
override def getAcceptedIssuers: Array[X509Certificate] = Array.empty
override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
}
// rewrite sendReceiveMethod fron spray.client.pipelining
def mySendReceive(implicit refFactory: ActorRefFactory, executionContext: ExecutionContext,
futureTimeout: Timeout = 60.seconds): SendReceive = {
val transport = IO(Http)(actorSystem)
// HttpManager actually also accepts Msg (HttpRequest, HostConnectorSetup)
request =>
val uri = request.uri
val setup = HostConnectorSetup(uri.authority.host.toString, uri.effectivePort, uri.scheme == "https")
transport ? (request, setup) map {
case x: HttpResponse => x
case x: HttpResponsePart => sys.error("sendReceive doesn't support chunked responses, try sendTo instead")
case x: Http.ConnectionClosed => sys.error("Connection closed before reception of response: " + x)
case x => sys.error("Unexpected response from HTTP transport: " + x)
}
}
// use mySendReceive instead spray.client.pipelining.sendReceive
}
While playing with, akka-http experimental 1.0-M2 I am trying to create a simple Hello world example.
import akka.actor.ActorSystem
import akka.http.Http
import akka.http.model.HttpResponse
import akka.http.server.Route
import akka.stream.FlowMaterializer
import akka.http.server.Directives._
object Server extends App {
val host = "127.0.0.1"
val port = "8080"
implicit val system = ActorSystem("my-testing-system")
implicit val fm = FlowMaterializer()
val serverBinding = Http(system).bind(interface = host, port = port)
serverBinding.connections.foreach { connection ⇒
println("Accepted new connection from: " + connection.remoteAddress)
connection handleWith Route.handlerFlow {
path("") {
get {
complete(HttpResponse(entity = "Hello world?"))
}
}
}
}
}
Compilation fails with could not find implicit value for parameter setup: akka.http.server.RoutingSetup
Also, if I change
complete(HttpResponse(entity = "Hello world?"))
with
complete("Hello world?")
I get another error: type mismatch; found : String("Hello world?") required: akka.http.marshalling.ToResponseMarshallable
With research I was able to understand the issue to be lack of Execution Context. To solve both the issue I needed to include this:
implicit val executionContext = system.dispatcher
Looking into akka/http/marshalling/ToResponseMarshallable.scala I see ToResponseMarshallable.apply requires it which returns a Future[HttpResponse].
Also, in akka/http/server/RoutingSetup.scala, RoutingSetup.apply needs it.
May be akka team just needs to add some more #implicitNotFounds. I was able to find not exact but related answer at: direct use of Futures in Akka and spray Marshaller for futures not in implicit scope after upgrading to spray 1.2
Well found - this problem still exists with Akka HTTP 1.0-RC2, so the code for that now must look like this (given their API changes):
import akka.actor.ActorSystem
import akka.http.scaladsl.server._
import akka.http.scaladsl._
import akka.stream.ActorFlowMaterializer
import akka.stream.scaladsl.{Sink, Source}
import akka.http.scaladsl.model.HttpResponse
import Directives._
import scala.concurrent.Future
object BootWithRouting extends App {
val host = "127.0.0.1"
val port = 8080
implicit val system = ActorSystem("my-testing-system")
implicit val fm = ActorFlowMaterializer()
implicit val executionContext = system.dispatcher
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
Http(system).bind(interface = host, port = port)
serverSource.to(Sink.foreach {
connection =>
println("Accepted new connection from: " + connection.remoteAddress)
connection handleWith Route.handlerFlow {
path("") {
get {
complete(HttpResponse(entity = "Hello world?"))
}
}
}
}).run()
}