Skip to content

Instantly share code, notes, and snippets.

@William-Yeh
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save William-Yeh/dd6316b8f93175ab80c7 to your computer and use it in GitHub Desktop.
Save William-Yeh/dd6316b8f93175ab80c7 to your computer and use it in GitHub Desktop.
modified spray-can example from https://github.com/spray/spray
import sbt._
import Keys._
import com.typesafe.sbt.osgi.SbtOsgi._
import sbtunidoc.Plugin._
import UnidocKeys._
import sbtassembly.Plugin._
import AssemblyKeys._
object Build extends Build {
import BuildSettings._
import Dependencies._
// configure prompt to show current project
override lazy val settings = super.settings :+ {
shellPrompt := { s => Project.extract(s).currentProject.id + " > " }
}
// -------------------------------------------------------------------------------------------------------------------
// Root Project
// -------------------------------------------------------------------------------------------------------------------
lazy val root = Project("root",file("."))
.aggregate(docs, examples, site, sprayCaching, sprayCan, sprayCanTests, sprayClient, sprayHttp, sprayHttpx,
sprayIO, sprayIOTests, sprayRouting, sprayRoutingTests, sprayServlet, sprayTestKit, sprayUtil)
.settings(basicSettings: _*)
.settings(noPublishing: _*)
.settings(unidocSettings: _*)
.settings(
unidocProjectFilter in (ScalaUnidoc, unidoc) := inAnyProject -- inProjects(docs, examples, site, sprayCanTests,
sprayCanExamples, sprayClientExamples, sprayIOExamples, sprayRoutingExamples, sprayServletExamples,
serverBenchmark, simpleHttpClient, simpleHttpServer, simpleSprayClient, echoServerExample, onJetty, onSprayCan,
simpleRoutingApp, simpleSprayServletServer),
target in (ScalaUnidoc, unidoc) := (resourceManaged in Compile in site).value / "api" / version.value,
scalacOptions in (ScalaUnidoc, unidoc) += "-Ymacro-no-expand")
// -------------------------------------------------------------------------------------------------------------------
// Modules
// -------------------------------------------------------------------------------------------------------------------
lazy val sprayCaching = Project("spray-caching", file("spray-caching"))
.dependsOn(sprayUtil)
.settings(sprayModuleSettings: _*)
.settings(osgiSettings(exports = Seq("spray.caching")): _*)
.settings(libraryDependencies ++=
provided(akkaActor) ++
compile(clHashMap) ++
test(specs2)
)
lazy val sprayCan = Project("spray-can", file("spray-can"))
.dependsOn(sprayIO, sprayHttp, sprayUtil)
.settings(sprayModuleSettings: _*)
.settings(osgiSettings(exports = Seq("spray.can")): _*)
.settings(libraryDependencies ++=
provided(akkaActor) ++
test(akkaTestKit, specs2)
)
lazy val sprayCanTests = Project("spray-can-tests", file("spray-can-tests"))
.dependsOn(sprayCan, sprayHttp, sprayHttpx, sprayIO, sprayTestKit, sprayUtil)
.settings(sprayModuleSettings: _*)
.settings(noPublishing: _*)
.settings(libraryDependencies ++= test(akkaActor, specs2))
lazy val sprayClient = Project("spray-client", file("spray-client"))
.dependsOn(sprayCan, sprayHttp, sprayHttpx, sprayUtil)
.settings(sprayModuleSettings: _*)
.settings(osgiSettings(exports = Seq("spray.client")): _*)
.settings(libraryDependencies ++=
provided(akkaActor) ++
test(akkaTestKit, specs2)
)
lazy val sprayHttp = Project("spray-http", file("spray-http"))
.dependsOn(sprayUtil)
.settings(sprayModuleSettings: _*)
.settings(osgiSettings(exports = Seq("spray.http")): _*)
.settings(libraryDependencies ++=
compile(parboiled) ++
provided(akkaActor) ++
test(specs2)
)
lazy val sprayHttpx = Project("spray-httpx", file("spray-httpx"))
.dependsOn(sprayHttp, sprayUtil,
sprayIO) // for access to akka.io.Tcp, can go away after upgrade to Akka 2.2
.settings(sprayModuleSettings: _*)
.settings(osgiSettings(exports = Seq("spray.httpx"), imports = Seq(
"spray.json.*;resolution := optional",
"net.liftweb.*;resolution := optional",
"org.json4s.*;resolution := optional",
"twirl.*;resolution := optional",
"play.*;resolution := optional"
)): _*)
.settings(libraryDependencies ++=
compile(mimepull) ++
provided(akkaActor, sprayJson, twirlApi, playTwirlApi, liftJson, json4sNative, json4sJackson, playJson) ++
test(specs2)
)
lazy val sprayIO = Project("spray-io", file("spray-io"))
.dependsOn(sprayUtil, sprayHttp)
.settings(sprayModuleSettings: _*)
.settings(osgiSettings(exports = Seq("spray.io", "akka.io")): _*)
.settings(libraryDependencies ++= provided(akkaActor, scalaReflect))
lazy val sprayIOTests = Project("spray-io-tests", file("spray-io-tests"))
.dependsOn(sprayIO, sprayTestKit, sprayUtil)
.settings(sprayModuleSettings: _*)
.settings(noPublishing: _*)
.settings(libraryDependencies ++= test(akkaActor, specs2, scalatest))
lazy val sprayRouting = Project("spray-routing", file("spray-routing"))
.dependsOn(
sprayCaching % "provided", // for the CachingDirectives trait and CachedUserPassAuthenticator object
sprayCan % "provided", // for the SimpleRoutingApp trait
sprayHttp, sprayHttpx, sprayUtil,
sprayIO) // for access to akka.io.Tcp, can go away after upgrade to Akka 2.2
.settings(sprayModuleSettings: _*)
.settings(spray.boilerplate.BoilerplatePlugin.Boilerplate.settings: _*)
.settings(osgiSettings(exports = Seq("spray.routing"), imports = Seq(
"spray.caching.*;resolution:=optional",
"spray.can.*;resolution:=optional",
"spray.io.*;resolution:=optional"
)): _*)
.settings(libraryDependencies ++=
compile(shapeless) ++
provided(akkaActor)
)
lazy val sprayRoutingTests = Project("spray-routing-tests", file("spray-routing-tests"))
.dependsOn(sprayCaching, sprayHttp, sprayHttpx, sprayRouting, sprayTestKit, sprayUtil)
.settings(sprayModuleSettings: _*)
.settings(noPublishing: _*)
.settings(libraryDependencies ++= test(akkaActor, specs2, shapeless, sprayJson))
lazy val sprayServlet = Project("spray-servlet", file("spray-servlet"))
.dependsOn(sprayHttp, sprayUtil,
sprayIO) // for access to akka.io.Tcp, can go away after upgrade to Akka 2.2
.settings(sprayModuleSettings: _*)
.settings(osgiSettings(exports = Seq("spray.servlet"), imports = Seq("javax.servlet.*;version=\"[2.6,4.0)\"")): _*)
.settings(libraryDependencies ++=
provided(akkaActor, servlet30) ++
test(specs2)
)
lazy val sprayTestKit = Project("spray-testkit", file("spray-testkit"))
.dependsOn(
sprayHttp % "provided",
sprayHttpx % "provided",
sprayIO % "provided",
sprayRouting % "provided",
sprayUtil
)
.settings(sprayModuleSettings: _*)
.settings(libraryDependencies ++= akkaTestKit +: provided(akkaActor, scalatest, specs2))
lazy val sprayUtil = Project("spray-util", file("spray-util"))
.settings(sprayModuleSettings: _*)
.settings(sprayVersionConfGeneration: _*)
.settings(osgiSettings(exports = Seq("spray.util", "akka.spray")): _*)
.settings(libraryDependencies ++=
provided(akkaActor, scalaReflect) ++
test(akkaTestKit, specs2)
)
// -------------------------------------------------------------------------------------------------------------------
// Site Project
// -------------------------------------------------------------------------------------------------------------------
lazy val site = Project("site", file("site"))
.dependsOn(sprayCaching, sprayCan, sprayRouting)
.enablePlugins(play.twirl.sbt.SbtTwirl)
.settings(siteSettings: _*)
.settings(SphinxSupport.settings: _*)
.settings(libraryDependencies ++=
compile(akkaActor, sprayJson) ++
runtime(akkaSlf4j, logback) ++
test(specs2)
)
lazy val docs = Project("docs", file("docs"))
.dependsOn(sprayCaching, sprayCan, sprayClient, sprayHttp, sprayHttpx, sprayIO, sprayRouting,
sprayServlet, sprayTestKit, sprayUtil)
.settings(docsSettings: _*)
.settings(libraryDependencies ++= test(akkaActor, sprayJson, specs2, json4sNative))
// -------------------------------------------------------------------------------------------------------------------
// Example Projects
// -------------------------------------------------------------------------------------------------------------------
lazy val examples = Project("examples", file("examples"))
.aggregate(sprayCanExamples, sprayClientExamples, sprayIOExamples, sprayRoutingExamples, sprayServletExamples)
.settings(exampleSettings: _*)
lazy val sprayCanExamples = Project("spray-can-examples", file("examples/spray-can"))
.aggregate(serverBenchmark, simpleHttpClient, simpleHttpServer)
.settings(exampleSettings: _*)
lazy val serverBenchmark = Project("server-benchmark", file("examples/spray-can/server-benchmark"))
.dependsOn(sprayCan, sprayHttp)
.settings(benchmarkSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor, sprayJson) ++
runtime(akkaSlf4j, logback)
)
lazy val simpleHttpClient = Project("simple-http-client", file("examples/spray-can/simple-http-client"))
.dependsOn(sprayCan, sprayHttp)
.settings(exampleSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor) ++
runtime(akkaSlf4j, logback)
)
lazy val simpleHttpServer = Project("simple-http-server", file("examples/spray-can/simple-http-server"))
.dependsOn(sprayCan, sprayHttp)
.settings(standaloneServerExampleSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor, mimepull) ++
runtime(akkaSlf4j, logback)
)
.settings(assemblySettings: _*)
lazy val sprayClientExamples = Project("spray-client-examples", file("examples/spray-client"))
.aggregate(simpleSprayClient)
.settings(exampleSettings: _*)
lazy val simpleSprayClient = Project("simple-spray-client", file("examples/spray-client/simple-spray-client"))
.dependsOn(sprayClient)
.settings(exampleSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor, sprayJson) ++
runtime(akkaSlf4j, logback)
)
lazy val sprayIOExamples = Project("spray-io-examples", file("examples/spray-io"))
.aggregate(echoServerExample)
.settings(exampleSettings: _*)
lazy val echoServerExample = Project("echo-server", file("examples/spray-io/echo-server"))
.dependsOn(sprayIO)
.settings(standaloneServerExampleSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor) ++
runtime(akkaSlf4j, logback)
)
lazy val sprayRoutingExamples = Project("spray-routing-examples", file("examples/spray-routing"))
.aggregate(onJetty, onSprayCan, simpleRoutingApp)
.settings(exampleSettings: _*)
lazy val onJetty = Project("on-jetty", file("examples/spray-routing/on-jetty"))
.dependsOn(sprayCaching, sprayServlet, sprayRouting, sprayTestKit % "test")
.settings(jettyExampleSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor) ++
test(specs2) ++
runtime(akkaSlf4j, logback) ++
container(jettyWebApp, servlet30)
)
lazy val onSprayCan = Project("on-spray-can", file("examples/spray-routing/on-spray-can"))
.dependsOn(sprayCaching, sprayCan, sprayRouting, sprayTestKit % "test")
.settings(standaloneServerExampleSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor) ++
test(specs2) ++
runtime(akkaSlf4j, logback)
)
lazy val simpleRoutingApp = Project("simple-routing-app", file("examples/spray-routing/simple-routing-app"))
.dependsOn(sprayCan, sprayRouting)
.settings(standaloneServerExampleSettings: _*)
.settings(libraryDependencies ++= compile(akkaActor))
lazy val sprayServletExamples = Project("spray-servlet-examples", file("examples/spray-servlet"))
.aggregate(simpleSprayServletServer)
.settings(exampleSettings: _*)
lazy val simpleSprayServletServer = Project("simple-spray-servlet-server",
file("examples/spray-servlet/simple-spray-servlet-server"))
.dependsOn(sprayHttp, sprayServlet,
sprayIO) // for access to akka.io.Tcp, can go away after upgrade to Akka 2.2
.settings(jettyExampleSettings: _*)
.settings(exampleSettings: _*)
.settings(libraryDependencies ++=
compile(akkaActor) ++
runtime(akkaSlf4j, logback) ++
container(jettyWebApp, servlet30)
)
}
package spray.examples
import scala.concurrent.duration._
import akka.pattern.ask
import akka.util.Timeout
import akka.actor._
import spray.can.Http
import spray.can.server.Stats
import spray.util._
import spray.http._
import HttpMethods._
import MediaTypes._
import spray.can.Http.RegisterChunkHandler
class DemoService extends Actor with ActorLogging {
implicit val timeout: Timeout = 1.second // for the actor 'asks'
import context.dispatcher // ExecutionContext for the futures and scheduler
def receive = {
// when a new connection comes in we register ourselves as the connection handler
case _: Http.Connected => sender ! Http.Register(self)
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
sender ! index
// add a new ECHO endpoint: /echo/text_to_be_echoed
case HttpRequest(GET, Uri.Path(path), _, _, _) if path startsWith "/echo/" =>
val text = path.drop(6)
sender ! HttpResponse(entity = text)
log.info(text)
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
sender ! HttpResponse(entity = "PONG!")
case HttpRequest(GET, Uri.Path("/stream"), _, _, _) =>
val peer = sender // since the Props creator is executed asyncly we need to save the sender ref
context actorOf Props(new Streamer(peer, 25))
case HttpRequest(GET, Uri.Path("/server-stats"), _, _, _) =>
val client = sender
context.actorFor("/user/IO-HTTP/listener-0") ? Http.GetStats onSuccess {
case x: Stats => client ! statsPresentation(x)
}
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
sender ! HttpResponse(entity = "About to throw an exception in the request handling actor, " +
"which triggers an actor restart")
sys.error("BOOM!")
case HttpRequest(GET, Uri.Path(path), _, _, _) if path startsWith "/timeout" =>
log.info("Dropping request, triggering a timeout")
case HttpRequest(GET, Uri.Path("/stop"), _, _, _) =>
sender ! HttpResponse(entity = "Shutting down in 1 second ...")
sender ! Http.Close
context.system.scheduler.scheduleOnce(1.second) { context.system.shutdown() }
case r@HttpRequest(POST, Uri.Path("/file-upload"), headers, entity: HttpEntity.NonEmpty, protocol) =>
// emulate chunked behavior for POST requests to this path
val parts = r.asPartStream()
val client = sender
val handler = context.actorOf(Props(new FileUploadHandler(client, parts.head.asInstanceOf[ChunkedRequestStart])))
parts.tail.foreach(handler !)
case s@ChunkedRequestStart(HttpRequest(POST, Uri.Path("/file-upload"), _, _, _)) =>
val client = sender
val handler = context.actorOf(Props(new FileUploadHandler(client, s)))
sender ! RegisterChunkHandler(handler)
case _: HttpRequest => sender ! HttpResponse(status = 404, entity = "Unknown resource!")
case Timedout(HttpRequest(_, Uri.Path("/timeout/timeout"), _, _, _)) =>
log.info("Dropping Timeout message")
case Timedout(HttpRequest(method, uri, _, _, _)) =>
sender ! HttpResponse(
status = 500,
entity = "The " + method + " request to '" + uri + "' has timed out..."
)
}
////////////// helpers //////////////
lazy val index = HttpResponse(
entity = HttpEntity(`text/html`,
<html>
<body>
<h1>Say hello to <i>spray-can</i>!</h1>
<p>Defined resources:</p>
<ul>
<li><a href="/ping">/ping</a></li>
<li><a href="/stream">/stream</a></li>
<li><a href="/server-stats">/server-stats</a></li>
<li><a href="/crash">/crash</a></li>
<li><a href="/timeout">/timeout</a></li>
<li><a href="/timeout/timeout">/timeout/timeout</a></li>
<li><a href="/stop">/stop</a></li>
<li>ADDED: <a href="/echo"><code><b>/echo/</b></code><i>text_to_be_echoed</i></a></li>
</ul>
<p>Test file upload</p>
<form action ="/file-upload" enctype="multipart/form-data" method="post">
<input type="file" name="datafile" multiple=""></input>
<br/>
<input type="submit">Submit</input>
</form>
</body>
</html>.toString()
)
)
def statsPresentation(s: Stats) = HttpResponse(
entity = HttpEntity(`text/html`,
<html>
<body>
<h1>HttpServer Stats</h1>
<table>
<tr><td>uptime:</td><td>{s.uptime.formatHMS}</td></tr>
<tr><td>totalRequests:</td><td>{s.totalRequests}</td></tr>
<tr><td>openRequests:</td><td>{s.openRequests}</td></tr>
<tr><td>maxOpenRequests:</td><td>{s.maxOpenRequests}</td></tr>
<tr><td>totalConnections:</td><td>{s.totalConnections}</td></tr>
<tr><td>openConnections:</td><td>{s.openConnections}</td></tr>
<tr><td>maxOpenConnections:</td><td>{s.maxOpenConnections}</td></tr>
<tr><td>requestTimeouts:</td><td>{s.requestTimeouts}</td></tr>
</table>
</body>
</html>.toString()
)
)
class Streamer(client: ActorRef, count: Int) extends Actor with ActorLogging {
log.debug("Starting streaming response ...")
// we use the successful sending of a chunk as trigger for scheduling the next chunk
client ! ChunkedResponseStart(HttpResponse(entity = " " * 2048)).withAck(Ok(count))
def receive = {
case Ok(0) =>
log.info("Finalizing response stream ...")
client ! MessageChunk("\nStopped...")
client ! ChunkedMessageEnd
context.stop(self)
case Ok(remaining) =>
log.info("Sending response chunk ...")
context.system.scheduler.scheduleOnce(100 millis span) {
client ! MessageChunk(DateTime.now.toIsoDateTimeString + ", ").withAck(Ok(remaining - 1))
}
case x: Http.ConnectionClosed =>
log.info("Canceling response stream due to {} ...", x)
context.stop(self)
}
// simple case class whose instances we use as send confirmation message for streaming chunks
case class Ok(remaining: Int)
}
}
package spray.examples
import akka.actor.{ActorSystem, Props}
import akka.io.IO
import spray.can.Http
object Main extends App with MySslConfiguration {
implicit val system = ActorSystem()
// the handler actor replies to incoming HttpRequests
val handler = system.actorOf(Props[DemoService], name = "handler")
IO(Http) ! Http.Bind(handler, interface = "0.0.0.0", port = 8080)
}
resolvers += "spray repo" at "http://repo.spray.io"
libraryDependencies ++= Seq(
"com.decodified" %% "scala-ssh" % "0.6.4",
"org.bouncycastle" % "bcprov-jdk16" % "1.46",
"com.jcraft" % "jzlib" % "1.1.3"
)
addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2")
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.0.2")
addSbtPlugin("io.spray" % "sbt-boilerplate" % "0.5.9")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.7.4")
addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.7.0")
addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.3.1")
addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.2")
addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "1.0.0-M7")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
@William-Yeh
Copy link
Author

Used in conjunction with Docker-Spray-HttpServer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment