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()
}
Related
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'm trying to build a REST server using this tutorial:
https://spindance.com/reactive-rest-services-akka-http/
However, having reached the "Responding with JSON" section, I've noticed that my code doesn't compile, and I can't make POST requests. This is the error that I'm getting:
Error:(59, 18) could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[Health]
entity(as[Health]) { statusReport =>
^
Having looked at other tutorials, I've found out that you need to include an object containing an implicit variable for the class that I'm trying to unmarshall. I did that, and I even imported the httpx library, but I'm still getting this error. My code is given below.
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer
import akka.pattern.ask
import akka.util.Timeout
import spray.json._
import DefaultJsonProtocol._
import spray.httpx.SprayJsonSupport.sprayJsonUnmarshaller
import scala.concurrent.duration._
import scala.io.StdIn
object JsonImplicits extends DefaultJsonProtocol {
implicit val healthFormat = jsonFormat2(Health)
}
object MyApplication {
val host = "localhost"
val port = 8080
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem("simple-rest-system")
// Something to do with flows
implicit val materializer = ActorMaterializer()
// A reference to a specific thread pool
// You can configure thread pool options through it
// It is the engine that executes the actors
implicit val executionContext = system.dispatcher
val requestHandler = system.actorOf(RequestHandler.props(), "requestHandler")
//Define the route
val route : Route = {
implicit val timeout = Timeout(20 seconds)
import JsonImplicits._
import spray.httpx.SprayJsonSupport._
path("health") {
get {
onSuccess(requestHandler ? GetHealthRequest) {
case response: HealthResponse =>
complete(StatusCodes.OK, s"Everything is ${response.health.status}!")
case _ =>
complete(StatusCodes.InternalServerError)
}
}
} ~ post {
// Entity extracts the body of the POST request and then converts it into a
// Health object
entity(as[Health]) { statusReport =>
onSuccess(requestHandler ? SetStatusRequest(statusReport)) {
case response: HealthResponse =>
complete(StatusCodes.OK,s"Posted health as ${response.health.status}!")
case _ =>
complete(StatusCodes.InternalServerError)
}
}
}
}
//Start up and listen for requests
val bindingFuture = Http().bindAndHandle(route, host, port)
println(s"Waiting for requests at http://$host:$port/...\nHit RETURN to terminate")
StdIn.readLine()
//Shutdown
bindingFuture.flatMap(_.unbind())
system.terminate()
}
}
Let's say you have 100 models, and for all of them you want the client to be able to do GET/POST/PUT/DELETE.
I read this article and a few others, but even with the promising title it seems that a lot of code would have to be duplicated for each model. Boilerplate, as some would call it.
Ideally I want to be able to say something like
case class AwesomeCaseClass(primeNumber : Long)
case class EvenMoreAwesomeCaseClass(favoritePrimeNumber : Long)
and then specify either all commands that should be accepted for each class (or maybe things like UPSERT).
I cannot find anyone who has done this, can anyone provide a link or a pointer in the right direction?
When Googling I also came across this, but for Akka HTTP in particular there seemed to only be support for API documentation.
Here is my attempt so far:
package route
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer
import scala.reflect._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
/**
* Created by flyrev on 30.05.16.
*/
case class Test(name: String)
class REST[T](implicit um: FromRequestUnmarshaller[T]) {
def generateRoute(klazz: Class[_]) : Route = {
path("test" / klazz.getSimpleName) {
get {
complete(HttpResponse(entity = "OK", status = 200))
} ~ post {
entity(as[T]) {
// Insert into DB here
(zomg) => complete(HttpResponse(entity = "Would have POSTed that stuff", status = 200))
}
}
} ~ get {
complete(HttpResponse(entity="Not found", status=404))
}
}
}
object GenerateRouteFromClass extends App {
def runtimeClass[T: ClassTag] = classTag[T].runtimeClass
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
import argonaut._
import CodecJson.derive
implicit val um = derive[Test]
Http().bindAndHandle(new REST[Test].generateRoute(runtimeClass[Test]), "localhost", 8080)
}
However, this gives:
Error:(45, 24) could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[route.Test]
Http().bindAndHandle(new REST[Test].generateRoute(runtimeClass[Test]), "localhost", 8080)
^
Error:(45, 24) not enough arguments for constructor REST: (implicit um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[route.Test])route.REST[route.Test].
Unspecified value parameter um.
Http().bindAndHandle(new REST[Test].generateRoute(runtimeClass[Test]), "localhost", 8080)
^
I know this question has been asked many times before, however all of them use spray-json. I wanted to use play-json. This might be why the proposed solutions don't solve the problem.
The trait with the route is in a separate file named RestApi.scala. The Http.bindAndHandle which makes use of it is in the file named Main.scala. Both files without irrelevant element removed are presented below. If you want to see the whole file please click at the links above.
RestApi.scala
package restApi
import akka.actor.{ActorSystem, Props}
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.pattern.ask
import akka.stream.ActorMaterializer
import akka.util.Timeout
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
trait RestApi {
import models._
import cassandraDB.{WriterActor, ReaderActor}
implicit val system: ActorSystem
implicit val materializer: ActorMaterializer
implicit val ec: ExecutionContext
implicit val timeout = Timeout(20 seconds)
val cassandraWriterWorker = system.actorOf(Props[WriterActor], "cassandra-writer-actor")
val cassandraReaderWorker = system.actorOf(Props[ReaderActor], "cassandra-reader-actor")
val route =
pathPrefix("api") {
pathSuffix("contact") {
// the line below has the error
(post & entity(as[Contact])) { contact =>
complete {
cassandraWriterWorker ! contact
StatusCodes.OK
}
}
} ~
pathSuffix("gps"/ "log") {
// an analogous error message is shown in the line below
(post & entity(as[GpsLog])) { gpsLog =>
complete {
cassandraWriterWorker ! gpsLog
StatusCodes.OK
}
}
}
}
}
Main.scala
package initialization
import akka.actor.{ActorSystem, Props}
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import cassandraDB.{ConfigCassandraCluster, ReaderActor, WriterActor}
import gcm.GcmServer
import restApi.RestApi
object Main extends App with ConfigCassandraCluster with RestApi {
override implicit val system = ActorSystem()
override implicit val materializer = ActorMaterializer()
override implicit val ec = system.dispatcher
val write = system.actorOf(Props(new WriterActor(cluster)))
val read = system.actorOf(Props(new ReaderActor(cluster)))
val gcmServer = system.actorOf(Props(new GcmServer(11054...,"AIzaSyCOnVK...")), "gcm-server")
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
I point out the line where the error occurs in RestApi.scala
Error Message:
Error:(31, 26) could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[models.Contact]
(post & entity(as[Contact])) { contact =>
^
The Contact is a data model defined in Contact.scala.
I hope this isn't a duplicate, but I really don't think so. Any help is much appreciated.
It turns out there is an easy solution. Simply add resolvers += Resolver.bintrayRepo("hseeberger", "maven") to your build file and then "de.heikoseeberger" %% "akka-http-play-json" % "1.5.3" to your libraryDependencies and import import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._ into your code. You can now use play-json like you would use spray-json.
I use scalaxb to generate models and client part of the SOAP interface. For testing I use Betamax, which can also be used in Scala. However, scalaxb uses Netty as a transport, which ignores proxy settings set up by Betamax. How would you cope with this situation?
scalaxb uses cake pattern, so the service is built from 3 parts like in the following example:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._
val service = (new stockquote.StockQuoteSoap12Bindings with
scalaxb.SoapClientsAsync with
scalaxb.DispatchHttpClientsAsync {}).service
val fresponse = service.getQuote(Some("GOOG"))
val response = Await.result(fresponse, 5 seconds)
println(response)
And tests:
import co.freeside.betamax.{TapeMode, Recorder}
import co.freeside.betamax.proxy.jetty.ProxyServer
import dispatch._
import org.scalatest.{Tag, FunSuite}
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
class StockquoteSpec extends FunSuite with Betamax {
testWithBetamax("stockquote", Some(TapeMode.READ_WRITE))("stockquote") {
val fresponse = service.getQuote(Some("GOOG"))
val response = Await.result(fresponse, 5 seconds)
println(response)
}
}
trait Betamax {
protected def test(testName: String, testTags: Tag*)(testFun: => Unit)
def testWithBetamax(tape: String, mode: Option[TapeMode] = None)(testName: String, testTags: Tag*)(testFun: => Unit) = {
test(testName, testTags: _*) {
val recorder = new Recorder
val proxyServer = new ProxyServer(recorder)
recorder.insertTape(tape)
recorder.getTape.setMode(mode.getOrElse(recorder.getDefaultMode()))
proxyServer.start()
try {
testFun
} finally {
recorder.ejectTape()
proxyServer.stop()
}
}
}
}
Versions:
net.databinder.dispatch 0.11.2
co.freeside.betamax 1.1.2
com.ning.async-http-client 1.8.10
io.netty.netty 3.9.2.Final
It is indeed possible to use proxy with Netty. Although Netty does not read system properties for proxy settings, the settings can be injected using ProxyServerSelector. It is created in build method of AsyncHttpClientConfig:
if (proxyServerSelector == null && useProxySelector) {
proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector();
}
if (proxyServerSelector == null && useProxyProperties) {
proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties());
}
if (proxyServerSelector == null) {
proxyServerSelector = ProxyServerSelector.NO_PROXY_SELECTOR;
}
The only obstacle is that scalaxb uses default config with useProxyProperties=false. You can override it with custom MyDispatchHttpClientsAsync that you can use when creating the service:
val service = (new stockquote.StockQuoteSoap12Bindings with
scalaxb.SoapClientsAsync with
MyDispatchHttpClientsAsync {}).service
And the source code of MyDispatchHttpClientsAsync (the key point is calling setUseProxyProperties(true)):
import com.ning.http.client.providers.netty.NettyAsyncHttpProvider
import com.ning.http.client.{AsyncHttpClientConfig, AsyncHttpClient}
import scalaxb.HttpClientsAsync
/**
* #author miso
*/
trait MyDispatchHttpClientsAsync extends HttpClientsAsync {
lazy val httpClient = new DispatchHttpClient {}
trait DispatchHttpClient extends HttpClient {
import dispatch._, Defaults._
// Keep it lazy. See https://github.com/eed3si9n/scalaxb/pull/279
lazy val http = new Http(new AsyncHttpClient(new NettyAsyncHttpProvider(new AsyncHttpClientConfig.Builder().setUseProxyProperties(true).build())))
// lazy val http = Http.configure(_.setUseProxyProperties(true)) // Maybe later. See https://github.com/eed3si9n/scalaxb/issues/312
def request(in: String, address: java.net.URI, headers: Map[String, String]): concurrent.Future[String] = {
val req = url(address.toString).setBodyEncoding("UTF-8") <:< headers << in
http(req > as.String)
}
}
}