Internet users
Garden walls breached, again.
Unfiltered Application Architecture
“In my opinion Unfiltered’s main strength is in what it doesn’t do.”
—Maxime Lévesque
What exactly does Unfiltered do?
Unfiltered is a toolkit for servicing HTTP requests in Scala.
Java for the Web (hold the EE)
Works with Google App Engine!
package nescala
import unfiltered.request._
import unfiltered.response._
class Echo extends unfiltered.filter.Plan {
def intent = {
case Path(path) =>
ResponseString(path)
}
}
<web-app>
<filter>
<filter-name>Echo</filter-name>
<filter-class>nescala.Echo</filter-class>
</filter>
<filter-mapping>
<filter-name>Echo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
What if we don’t care about containers?
unfiltered.jetty.Http(8080).filter(
new Echo
).run()
package nescala
import unfiltered.request._
import unfiltered.response._
val echo = unfiltered.netty.cycle.Planify {
case Path(path) =>
ResponseString(path)
}
unfiltered.netty.Http(8080).plan(echo).run()
val magic = unfiltered.netty.async.Planify {
case req@Path("/magic") =>
val context = req.underlying.context
val request = req.underlying.request
val event = req.underlying.event
// todo: something magic!
case req@Path("/meh") =>
req.respond(
BadRequest ~> ResponseString("meeeeh")
)
}
Stealthy incursions.
Purpose-built New I/O servers.
A UNIX program should do one thing well, and leave unrelated tasks to other programs.
—Rob Pike & Brian Kernighan
Kits alter the response based on the request, by composing around your own “intent” function.
This is how you might serve GZip-encoded responses to clients that support it.
def intent = {
case Decodes.GZip(req) =>
ContentEncoding.GZip ~>
ResponseFilter.GZip ~>
handleRequest(req)
case req =>
handleRequest(req)
}
But Unfiltered’s GZip kit can do that for you.
def intent = unfiltered.kit.GZip {
case _ => ResponseString(...)
}
Of course, the raw materials are still there if you need to do your own thing.
def intent = {
case Decodes.GZip(req) =>
ContentEncoding.GZip ~>
ResponseFilter.GZip ~>
ResponseString(...)
case _ =>
BadRequest ~>
ResponseString("you must to accept GZip")
}
Kits can provide alternatives.
def intent = {
case Path(Seg("users" :: Nil)) =>
Users.index()
case Path(Seg("users" :: id :: Nil)) =>
Users.display(id)
case Path(Seg("places" :: Nil)) =>
Places.index()
case Path(Seg("places" :: id :: Nil)) =>
Places.display(id)
...
}
def intent = unfiltered.kit.Routes.specify(
"/users" -> Users.index,
"/users/:id" -> Users.get,
"/places" -> Places.index,
"/places/:id" -> Places.get
...
)
object Users {
def get(req: HttpRequest[_],
pmap: Map[String,String]) = ...
}
Choose your own template adventure
object Users {
def get(req: HttpRequest[_],
pmap: Map[String,String]) =
(for {
id <- pmap.get("id")
user <- UserDB.get(id)
} yield Scalate(req,
"userprofile.ssp",
"user" -> user)
).getOrElse(Pass)
}