Plans and Intents

Unfiltered conditionally handles incoming requests using partial functions. From the application’s perspective, requests are mapped to code paths by pattern matching. The library uses a particular vocabulary to refer to the agents of this process without ambiguity.

  • An intent is a partial function for matching requests.
  • A plan binds an intent to a particular server interface.

For example, the unfiltered.filter.Plan trait extends the jakarta.servlet.Filter interface. It declares an abstract intent method for clients to define the intent partial function.

Making Plans of Intents

Looking back at the example on the previous page, you might wonder where the plan ends and the intent begins.

sourceimport unfiltered.request._
import unfiltered.response._
val echo = unfiltered.filter.Planify {
  case Path(Seg(p :: Nil)) => ResponseString(p)
}

In this case a plan is constructed directly from an anonymous partial function—that function is the intent. We can define the same plan in more explicit parts, as is usually necessary in a larger application.

sourceobject Echo extends unfiltered.filter.Plan {
  def intent = {
    case Path(Seg(p :: Nil)) => ResponseString(p)
  }
}

Since this kind of plan is an implementation of the servlet filter interface, we can pass it directly to a servlet container.

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

Passing on That

Since an intent is a partial function, it may be undefined for a request. In this case the request may be handled by some other intent or it could produce a 404 error from the server.

Unfiltered also supports an explicit mechanism to specify that a matched request should not be handled by an intent: the Pass object.

sourceobject Public extends unfiltered.filter.Plan {
  def intent = {
    case Path(Seg("admin" :: _)) => Pass
    case Path(Seg(path :: Nil)) => myRenderer(path)
  }
}

This intent avoids handling anything under /admin by matching that condition and passing on it. There are other ways to achieve the same ends, but an explicit Pass is often the most straightforward.

Keep in mind that Scala’s partial functions are unaware of Unfiltered’s Pass mechanism, so the intent above is in fact defined for the excluded paths. But when attached to a plan, requests that evaluate to Pass are treated the same as those that are undefined.

Chaining Intents

When you want to combine intents within a single plan, use onPass:

sourceobject MyPlan extends unfiltered.filter.Plan {
  def intent = publicIntent.onPass(privateIntent)

  val publicIntent = unfiltered.filter.Intent { ??? }
  val privateIntent = unfiltered.filter.Intent { ??? }
}

The onPass method is defined implicitly for PartialFunction with the import of the unfiltered.response._ package. It works like orElse but is aware of the Pass object and it’s optimized to avoid unnecessary reevaluation of pattern guards.

The source code for this page can be found here.