Try Unfiltered

It’s not hard to write a request handler in the Scala console using Unfiltered. The tricky part is getting everything on the classpath.

Play Project

The approach recommended here uses giter8, a tool for setting up projects based on templates stored in github. Assuming you don’t have giter8 installed and you are on a network-connected Linux or Mac, it’s easy to fix that.

curl https://raw.githubusercontent.com/foundweekends/conscript/master/setup.sh | sh

That is conscript. Its setup.sh places a permanent (assuming you don’t delete it) executable script in ~/bin/cs. At some point you may want to add ~/bin to your executable search path, but these instructions will not assume it is.

~/bin/cs foundweekends/giter8

That installs a g8. Now you have a script to run giter8. The next step creates a sbt project under the current working directory.

~/bin/g8 unfiltered/unfiltered --name=justplayin

Okay, finally we can use this project with sbt to get a console for Unfiltered. You do have sbt setup, don’t you?

cd justplayin
sbt console

Consoled

Now that you have a scala> prompt with the unfiltered-filter and unfiltered-jetty modules on the classpath, let’s have some fun.

sourceimport unfiltered.request._
import unfiltered.response._

val echo = unfiltered.filter.Planify {
  case Path(Seg(p :: Nil)) => ResponseString(p)
}

This filter echo would work with any servlet container. We can use it in a Jetty server right now.

sourceunfiltered.jetty.Server.anylocal.plan(echo).run()

The startup message tells you which open port was selected, and by default it is only listening to requests from 127.0.0.1. So on the same machine, you can make requests to your server. e.g.

curl http://127.0.0.1:<the right port>/hello+world

Fancy desktop web browsers will work too. Notice that exactly one path segment is required for the filter to respond to the request. If you ask for the root path or a deeper path, the echo filter will not handle the request and Jetty responds with a 404 page.

If we want to handle any request, we could broaden the pattern matching expression. (Press enter to stop the running server.)

sourceval echoNice = unfiltered.filter.Planify {
  case Path(Seg(p :: Nil)) => ResponseString(p)
  case _ => ResponseString(
    "I can echo exactly one path element."
  )
}
unfiltered.jetty.Server.anylocal.plan(echoNice).run()

Or we could define another filter chain it to the first.

sourceval nice = unfiltered.filter.Planify {
  case _ => ResponseString(
    "I can echo exactly one path element."
  )
}
unfiltered.jetty.Server.anylocal.plan(echo).plan(nice).run()

Happy now?

The source code for this page can be found here.