Create an Example Server

Since dispatch is a http client, we need a http server to connect to and return results for these user notes.

We can use akka http to create a server. Humorously, akka http also include a http client that is capable if issuing http commands just like dispatch and replaces both dispatch and http-async-client. More on this topic later.

The example server allows you to play around with the requests and the responses on the server side and understand what dispatch, and hence, async-http-client is up to when it builds its requests. By changing out the "routes" below, you can alter the example server to return anything you want. By default its an echo server and it echoes, in akka http format, the request and response. akka parsess the request and responses into akka specific data structures but is good enough for understanding what a real http server sees when programming your client.

An Example Server

I put together an example server based on akka http. It is listed out below but you can also find it on github. You can clone it and run it yourself. I have included both the source and the build.sbt and plugins.

You can execute the server in a shell by running sbt run or "pack" the project into an executable using sbt pack and the running the script that starts the server.

package example

import scala.language._
import akka.actor._
import akka.http.scaladsl
import scaladsl._
import scaladsl.server
import scaladsl.model._
import server._
import server.Directives._
import akka.stream
import stream._
import stream.scaladsl._
import scaladsl.model.StatusCodes._
import com.typesafe.config.ConfigFactory
import scala.io.StdIn
import com.beust.jcommander._
import com.typesafe.scalalogging.LazyLogging
import scala.xml.XML
import XML._
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._

object Args {
  @Parameter(names = Array("-h", "--help"), help = true)
  var help: Boolean = false
}

object Server extends LazyLogging {

  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()
  implicit val ec = system.dispatcher

  val route =
    logRequestResult("example-server") {
      pathEndOrSingleSlash {
        get {
          complete(<h1>you have reached the example server</h1>)
        }
      } ~ path("echo") {
        get {
          extractRequest { req =>
            complete(req.toString)
          }
        }
      } ~ post {
        complete("POST succeeded")
      }
    }

  def main(args: Array[String]): Unit = {

    val j = new JCommander(Args, args.toArray: _*)
    if (Args.help) {
      val sb = new java.lang.StringBuilder()
      j.setProgramName("crmauth")
      j.usage(sb) // only way to get pre-formatted usage info
      sb.append("An application.conf can be use to specify some parameters.")
      println(sb.toString)
      system.terminate()
      return
    }

    val config = ConfigFactory.load()
    val ip = config.getString("http.host")
    val port = config.getInt("http.port")
    val server = ip + ":" + port

    val binding = Http().bindAndHandle(route, ip, port)
    binding onFailure {
      case ex: Exception =>
        logger.error(s"Error binding $server", ex)
        system.terminate()
        return
    }

    println(s"Started server on $server.\nPress ENTER to stop.")
    StdIn.readLine()

    binding flatMap { _.unbind() } onComplete { _ => system.terminate() }
  }
}

the build.sbt is:

name := "example-server"
organization := "example"
version := "0.1.0"
scalaVersion := "2.11.8"

scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")

resolvers += Resolver.url("file://" + Path.userHome.absolutePath + "/.ivy/local")
resolvers += Resolver.sonatypeRepo("releases")
resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "latest.release"
    ,"com.typesafe.akka" %% "akka-contrib" % "latest.release"
    ,"com.typesafe.akka" %% "akka-http-core" % "latest.release"
    ,"com.typesafe.akka" %% "akka-slf4j" % "latest.release"
    ,"com.typesafe.akka" %% "akka-stream" % "latest.release"
    ,"com.typesafe.akka" %% "akka-http-experimental" % "latest.release"
    ,"com.typesafe.akka" %% "akka-http-jackson-experimental" % "latest.release"
    ,"com.typesafe.akka" %% "akka-http-spray-json-experimental" % "latest.release"
    ,"com.typesafe.akka" %% "akka-http-xml-experimental" % "latest.release"
    ,"com.typesafe" % "config" % "latest.release"
    ,"org.scala-lang.modules" %% "scala-xml" % "latest.release"
    ,"com.beust" % "jcommander" % "latest.release"
    ,"ch.qos.logback" % "logback-classic" % "latest.release"
    ,"ch.qos.logback" % "logback-core" % "latest.release"
    ,"net.databinder.dispatch" %% "dispatch-core" % "latest.release"
    ,"com.typesafe.scala-logging" %% "scala-logging" % "latest.release"
    ,"org.scala-lang.modules" %% "scala-async" % "latest.release"
)

EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource

EclipseKeys.withSource := true

packSettings

packMain := Map("example-server" -> "example.Server")

and the plugins for your project/plugins.sbt

addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.8.0")

You can provide a configuration file:

akka {
     //loglevel = DEBUG
}

http {
     host = "localhost"
     host = ${?HOST}
     port = 9000
     port = ${?HTTP_PORT}
}
`

Uncommenting the loglevel prints out the request and response for each request made to the server.

With this example server, you can alter the routes to create server content that your dispatch client can connect to and test receiving in case you run into difficult with your own http service. For example, you could add:

... } ~ path("myjson") { 
  complete("{}")
  }

to add json content to the example server and retrieve it from the dispatch client for testing. We make various modifications to the basic server listed above so check out the git source for additional routes that may have been added after I typed in this section :-)

Last updated