Skip to content

Instantly share code, notes, and snippets.

@jroper
Last active December 20, 2015 19:38
Show Gist options
  • Save jroper/6184296 to your computer and use it in GitHub Desktop.
Save jroper/6184296 to your computer and use it in GitHub Desktop.
Transparent HEAD request handling in Play 2.1. Note that it may be more efficient to implement HEAD support in an action directly, as this may avoid unnecessary opening of resources or rendering things.
import play.api.libs.iteratee.{Done, Iteratee, Enumerator}
import play.api.mvc._
import play.api._
import play.api.libs.concurrent.Execution.Implicits._
object Global extends GlobalSettings {
override def onRouteRequest(req: RequestHeader) = {
// Lookup handler
super.onRouteRequest(req) match {
// If none was found and this is a HEAD request
case None if req.method == "HEAD" => {
// Try to look an action up again, but this time pretend it was a GET request
super.onRouteRequest(req.copy(method = "GET")) match {
// We found one, wrap it in a new action
case Some(wrapped: EssentialAction) => Some(new EssentialAction() with RequestTaggingHandler {
def apply(req: RequestHeader) = {
def skipBody(result: Result): Result = result match {
case SimpleResult(header, body) => {
// Tell the body enumerator it's done so that it can clean up resources
body(Done(()))
// Create a copy of the result, but with no content
SimpleResult(header, Enumerator(Results.EmptyContent()))
}
case ChunkedResult(header, body) => {
// Same as for SimpleResult
body(Done(()))
ChunkedResult(header, (it: Iteratee[Array[Byte], Unit]) => it.run)
}
// Unwrap
case AsyncResult(future) => AsyncResult(future.map(skipBody))
}
// Invoke the wrapped action and skip the body it returns
wrapped(req).map(skipBody)
}
// Ensure that request tags are added if necessary
def tagRequest(request: RequestHeader) = wrapped match {
case tagging: RequestTaggingHandler => tagging.tagRequest(request)
case _ => request
}
})
case other => other
}
}
case other => other
}
}
}
@delitescere
Copy link

Beware unintended consequences.

Although GET is safe and idempotent at the protocol level, there are other side-effects that may not be desired if the request was for HEAD.

I recommend making this something that can be explicitly turned on through configuration for those who want it as the default for all routes, and also reusable for those who may want this behaviour but only on selected routes.

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