alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Play Framework/Scala example source code file (GzipFilterSpec.scala)

This example Play Framework source code file (GzipFilterSpec.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Play Framework (and Scala) source code examples by using tags.

All credit for the original source code belongs to Play Framework; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Play Framework tags/keywords

although, api, bytearrayinputstream, concurrent, future, gzipfilter, head, if, library, play, play framework, string, t, test, utf-8, vary

The GzipFilterSpec.scala Play Framework example source code

/*
 * Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
 */
package play.filters.gzip

import play.api.test._
import play.api.mvc.{HttpConnection, Action, Result}
import play.api.mvc.Results._
import java.util.zip.GZIPInputStream
import java.io.ByteArrayInputStream
import org.apache.commons.io.IOUtils
import scala.concurrent.Future
import play.api.libs.iteratee.{Iteratee, Enumerator}
import scala.util.Random
import org.specs2.matcher.DataTables

object GzipFilterSpec extends PlaySpecification with DataTables {

  sequential

  "The GzipFilter" should {

    "gzip responses" in withApplication(Ok("hello")) {
      checkGzippedBody(makeGzipRequest, "hello")
    }

    """gzip a response if (and only if) it is accepted and preferred by the request.
      |Although not explicitly mentioned in RFC 2616 sect. 14.3, the default qvalue
      |is assumed to be 1 for all mentioned codings. If no "*" is present, unmentioned
      |codings are assigned a qvalue of 0, except the identity coding which gets q=0.001,
      |which is the lowest possible acceptable qvalue.
      |This seems to be the most consistent behaviour with respect to the other "accept"
      |header fields described in sect 14.1-5.""".stripMargin in withApplication(Ok("meep")) {

      val (plain, gzipped) = (None, Some("gzip"))

      "Accept-Encoding of request"          || "Response" |
      //------------------------------------++------------+
      "gzip"                                !! gzipped    |
      "compress,gzip"                       !! gzipped    |
      "compress, gzip"                      !! gzipped    |
      "gzip,compress"                       !! gzipped    |
      "deflate, gzip,compress"              !! gzipped    |
      "gzip, compress"                      !! gzipped    |
      "identity, gzip, compress"            !! gzipped    |
      "GZip"                                !! gzipped    |
      "*"                                   !! gzipped    |
      "*;q=0"                               !! plain      |
      "*; q=0"                              !! plain      |
      "*;q=0.000"                           !! plain      |
      "gzip;q=0"                            !! plain      |
      "gzip; q=0.00"                        !! plain      |
      "*;q=0, gZIP"                         !! gzipped    |
      "compress;q=0.1, *;q=0, gzip"         !! gzipped    |
      "compress;q=0.1, *;q=0, gzip;q=0.005" !! gzipped    |
      "compress, gzip;q=0.001"              !! gzipped    |
      "compress, gzip;q=0.002"              !! gzipped    |
      "compress;q=1, *;q=0, gzip;q=0.000"   !! plain      |
      "compress;q=1, *;q=0"                 !! plain      |
      "identity"                            !! plain      |
      "gzip;q=0.5, identity"                !! plain      |
      "gzip;q=0.5, identity;q=1"            !! plain      |
      "gzip;q=0.6, identity;q=0.5"          !! gzipped    |
      "*;q=0.7, gzip;q=0.6, identity;q=0.4" !! gzipped    |
      ""                                    !! plain      |> {

      (codings, expectedEncoding) =>
        header(CONTENT_ENCODING, requestAccepting(codings)) must be equalTo(expectedEncoding)
      }
    }

    "not gzip responses when not requested" in withApplication(Ok("hello")) {
      checkNotGzipped(route(FakeRequest()).get, "hello")
    }

    "not gzip HEAD requests" in withApplication(Ok) {
      checkNotGzipped(route(FakeRequest("HEAD", "/").withHeaders(ACCEPT_ENCODING -> "gzip")).get, "")
    }

    "not gzip no content responses" in withApplication(NoContent) {
      checkNotGzipped(makeGzipRequest, "")
    }

    "not gzip not modified responses" in withApplication(NotModified) {
      checkNotGzipped(makeGzipRequest, "")
    }

    "not gzip chunked responses" in withApplication(Ok.chunked(Enumerator("foo", "bar"))) {
      val result = makeGzipRequest
      header(CONTENT_ENCODING, result) must beNone
      header(CONTENT_LENGTH, result) must beNone
      header(TRANSFER_ENCODING, result) must beSome("chunked")
      new String(await(await(result).body &> dechunk |>>> Iteratee.consume[Array[Byte]]()), "UTF-8") must_== "foobar"
    }

    "not gzip event stream responses" in withApplication(Ok.feed(Enumerator("foo", "bar")).as("text/event-stream")) {
      checkNotGzipped(makeGzipRequest, "foobar")
    }

    "filter content length headers" in withApplication(
      Ok("hello")
        .withHeaders(CONTENT_LENGTH -> Integer.toString(328974))
        // http connection close will trigger the gzip filter not to buffer
        .copy(connection = HttpConnection.Close)
    ) {
      val result = makeGzipRequest
      checkGzipped(result)
      header(CONTENT_LENGTH, result) must beNone
    }

    val body = Random.nextString(1000)

    "not buffer more than the configured threshold" in withApplication(Ok(body).withHeaders(CONTENT_LENGTH -> "1000")) {
      val result = makeGzipRequest
      checkGzipped(result)
      header(CONTENT_LENGTH, result) must beNone
      header(TRANSFER_ENCODING, result) must beSome("chunked")
      gunzip(await(await(result).body &> dechunk |>>> Iteratee.consume[Array[Byte]]())) must_== body
    }

    "buffer the first chunk even if it exceeds the threshold" in withApplication(Ok("foobarblah"), 15) {
      checkGzippedBody(makeGzipRequest, "foobarblah")
    }

    "preserve original headers" in withApplication(Redirect("/foo")) {
      val result = makeGzipRequest
      checkGzipped(result)
      header(LOCATION, result) must beSome("/foo")
    }

    "preserve original Vary header values" in withApplication(Ok("hello").withHeaders(VARY -> "original")) {
      val result = makeGzipRequest
      checkGzipped(result)
      header(VARY, result) must beSome.which(header => header contains "original,")
    }
  }

  def withApplication[T](result: Result, buffer: Int = 1024)(block: => T): T = {
    running(FakeApplication(withRoutes = {
      case _ => new GzipFilter(gzip = Gzip.gzip(512), chunkedThreshold = buffer).apply(Action(result))
    }))(block)
  }

  def gzipRequest = FakeRequest().withHeaders(ACCEPT_ENCODING -> "gzip")

  def makeGzipRequest = route(gzipRequest).get

  def requestAccepting(codings: String) = route(FakeRequest().withHeaders(ACCEPT_ENCODING -> codings)).get

  def gunzip(bytes: Array[Byte]): String = {
    val is = new GZIPInputStream(new ByteArrayInputStream(bytes))
    val result = IOUtils.toString(is, "UTF-8")
    is.close()
    result
  }

  def checkGzipped(result: Future[Result]) = {
    header(CONTENT_ENCODING, result) must beSome("gzip")
  }

  def checkGzippedBody(result: Future[Result], body: String) = {
    checkGzipped(result)
    val resultBody = contentAsBytes(result)
    header(CONTENT_LENGTH, result) must beSome(Integer.toString(resultBody.length))
    gunzip(resultBody) must_== body
  }

  def checkNotGzipped(result: Future[Result], body: String) = {
    header(CONTENT_ENCODING, result) must beNone
    contentAsString(result) must_== body
  }
}

Other Play Framework source code examples

Here is a short list of links related to this Play Framework GzipFilterSpec.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.