Skip to content
This repository has been archived by the owner on Sep 12, 2021. It is now read-only.

Commit

Permalink
Merge pull request #40 from cemcatik/gzip
Browse files Browse the repository at this point in the history
Don't try to compress gzipped assets.
  • Loading branch information
akkie committed Oct 1, 2015
2 parents b7575f3 + 5997146 commit dedc3c6
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 2 deletions.
4 changes: 3 additions & 1 deletion app/com/mohiva/play/compressor/CompressorFilter.scala
Expand Up @@ -47,7 +47,9 @@ abstract class CompressorFilter[C <: Compressor](f: => C) extends Filter {
* @return True if the result is a compressible result, false otherwise.
*/
protected def isCompressible(result: Result): Boolean = {
!result.header.headers.get(TRANSFER_ENCODING).exists(_ == HttpProtocol.CHUNKED)
val isChunked = result.header.headers.get(TRANSFER_ENCODING).exists(_ == HttpProtocol.CHUNKED)
val isGzipped = result.header.headers.get(CONTENT_ENCODING).exists(_ == "gzip")
!isChunked && !isGzipped
}

/**
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Expand Up @@ -14,7 +14,8 @@ libraryDependencies ++= Seq(
"com.googlecode.htmlcompressor" % "htmlcompressor" % "1.5.2",
"rhino" % "js" % "1.7R2",
"org.easytesting" % "fest-assert" % "1.4" % Test,
specs2 % Test
specs2 % Test,
filters % Test
)

// org.scalaz.stream#scalaz-stream_2.11 is not in not in maven central or any other repositories
Expand Down
45 changes: 45 additions & 0 deletions test/com/mohiva/play/htmlcompressor/HTMLCompressorFilterSpec.scala
Expand Up @@ -10,14 +10,19 @@
*/
package com.mohiva.play.htmlcompressor

import _root_.java.io.ByteArrayInputStream
import _root_.java.util.zip.GZIPInputStream

import com.mohiva.play.htmlcompressor.fixtures.Application
import org.apache.commons.io.IOUtils
import org.specs2.mutable._
import play.api.mvc._
import play.api.test._
import play.api.test.Helpers._
import play.api.test.FakeApplication
import play.api.{ GlobalSettings, Play }
import com.googlecode.htmlcompressor.compressor.HtmlCompressor
import play.filters.gzip.GzipFilter

/**
* Test case for the [[com.mohiva.play.htmlcompressor.HTMLCompressorFilter]] class.
Expand Down Expand Up @@ -103,6 +108,33 @@ class HTMLCompressorFilterSpec extends Specification {
}
}

"The default filter with Gzip Filter" should {
"first compress then gzip result" in new DefaultWithGzipGlobal {
val Some(original) = route(FakeRequest(GET, "/action"))
val Some(gzipped) = route(FakeRequest(GET, "/action").withHeaders(ACCEPT_ENCODING -> "gzip"))

status(gzipped) must beEqualTo(OK)
contentType(gzipped) must beSome("text/html")
header(CONTENT_ENCODING, gzipped) must beSome("gzip")
gunzip(contentAsBytes(gzipped)) must_== contentAsBytes(original)
}

"not compress already gzipped result" in new DefaultWithGzipGlobal {
// given static.html.gz == gzip(static.html)
// when /static.html is requested
// then Assets controller responds with static.html.gz
// we don't want to further pass this through HTML Compressor

val original = IOUtils.toByteArray(Play.resourceAsStream("static.html").get)
val Some(result) = route(FakeRequest(GET, "/gzipped").withHeaders(ACCEPT_ENCODING -> "gzip"))

status(result) must beEqualTo(OK)
contentType(result) must beSome("text/html")
header(CONTENT_ENCODING, result) must beSome("gzip")
gunzip(contentAsBytes(result)) must_== original
}
}

/**
* Defines the routes for the test.
*/
Expand All @@ -122,6 +154,7 @@ class HTMLCompressorFilterSpec extends Specification {
case ("GET", "/nonHTML") => Some(application.nonHTML)
case ("GET", "/static") => Some(application.staticAsset)
case ("GET", "/chunked") => Some(application.chunked)
case ("GET", "/gzipped") => Some(application.gzipped)
case _ => None
}
}
Expand Down Expand Up @@ -158,4 +191,16 @@ class HTMLCompressorFilterSpec extends Specification {
compressor
})
}

/**
* A custom global object with default HTML compressor filter and Default Gzip Filter.
*/
class DefaultWithGzipGlobal
extends WithApplication(FakeApplication(withGlobal = Some(new WithFilters(new GzipFilter(), HTMLCompressorFilter()) with RouteSettings)))

def gunzip(bs: Array[Byte]): Array[Byte] = {
val bis = new ByteArrayInputStream(bs)
val gzis = new GZIPInputStream(bis)
IOUtils.toByteArray(gzis)
}
}
Expand Up @@ -63,4 +63,6 @@ class Application extends AssetsBuilder(DefaultHttpErrorHandler) {
val parts = List(" <html> ", " <body> ", " <h1> Title </h1>", " </body> ", " </html> ").map(Html.apply)
Ok.chunked(Enumerator.enumerate(parts))
}

def gzipped = staticAsset
}
Expand Up @@ -18,10 +18,17 @@
import play.Play;
import play.api.mvc.EssentialFilter;

import play.api.mvc.RequestHeader;
import play.api.mvc.ResponseHeader;
import play.filters.gzip.Gzip;
import play.filters.gzip.GzipFilter;
import play.mvc.*;
import scala.runtime.AbstractFunction2;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;

import static org.fest.assertions.Assertions.*;
import static play.test.Helpers.*;
Expand Down Expand Up @@ -193,6 +200,57 @@ public void run() {
});
}

/**
* Test that a result is first HTML compressed and then gzipped
*/
@Test
public void defaultWithGzipFilterHtmlCompressesAndThenGzipsResult() {
running(fakeApplication(new DefaultWithGzipGlobal()), new Runnable() {
@Override
public void run() {
try {
Result original = route(fakeRequest(GET, "/action"));
Result gzipped = route(fakeRequest(GET, "/action").header(ACCEPT_ENCODING, "gzip"));

assertThat(gzipped.status()).isEqualTo(OK);
assertThat(gzipped.contentType()).isEqualTo("text/html");
assertThat(gzipped.header(CONTENT_ENCODING)).isEqualTo("gzip");
assertThat(gunzip(contentAsBytes(gzipped))).isEqualTo(contentAsBytes(original));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}

/**
* Test if result is not compressed if it's already gzipped
*
* given static.html.gz == gzip(static.html)
* when /static.html is requested
* then Assets controller responds with static.html.gz
* we don't want to further pass this through HTML Compressor
*/
@Test
public void defaultWithGzipFilterNotCompressGzippedResult() {
running(fakeApplication(new DefaultWithGzipGlobal()), new Runnable() {
@Override
public void run() {
try {
byte[] original = IOUtils.toByteArray(Play.application().resourceAsStream("static.html"));
Result result = route(fakeRequest(GET, "/gzipped").header(ACCEPT_ENCODING, "gzip"));

assertThat(result.status()).isEqualTo(OK);
assertThat(result.contentType()).isEqualTo("text/html");
assertThat(result.header(CONTENT_ENCODING)).isEqualTo("gzip");
assertThat(gunzip(contentAsBytes(result))).isEqualTo(original);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}

/**
* Defines the routes for the test.
*/
Expand All @@ -217,6 +275,8 @@ public play.api.mvc.Handler onRouteRequest(Http.RequestHeader request) {
return new Application().staticAsset();
case "/chunked":
return new Application().chunked();
case "/gzipped":
return new Application().gzipped();
default:
return null;
}
Expand Down Expand Up @@ -284,4 +344,39 @@ public HtmlCompressor build() {
return compressor;
}
}

/**
* A custom global object with default HTML compressor filter and Default Gzip Filter.
*/
public class DefaultWithGzipGlobal extends RouteSettings {
@SuppressWarnings("unchecked")
public <T extends EssentialFilter> Class<T>[] filters() {
return new Class[]{ JavaGzipFilter.class, HTMLCompressorFilter.class };
}
}

/**
* Can't just pass GzipFilter.class since Play 2.4 expects the
* filters to be dependency injected. Instead, this class provides
* the no-arg constructor that GlobalSettings.filters() expects.
*/
public static class JavaGzipFilter extends GzipFilter {
public JavaGzipFilter() {
super(Gzip.gzip(8192),
102400,
new AbstractFunction2<RequestHeader, ResponseHeader, Object>() {
@Override
public Object apply(RequestHeader req, ResponseHeader resp) {
return true;
}
}
);
}
}

public byte[] gunzip(byte[] bs) throws IOException {
InputStream bis = new ByteArrayInputStream(bs);
InputStream gzis = new GZIPInputStream(bis);
return IOUtils.toByteArray(gzis);
}
}
45 changes: 45 additions & 0 deletions test/com/mohiva/play/xmlcompressor/XMLCompressorFilterSpec.scala
Expand Up @@ -10,14 +10,19 @@
*/
package com.mohiva.play.xmlcompressor

import _root_.java.io.ByteArrayInputStream
import _root_.java.util.zip.GZIPInputStream

import com.mohiva.play.xmlcompressor.fixtures.Application
import org.apache.commons.io.IOUtils
import org.specs2.mutable._
import play.api.mvc._
import play.api.test._
import play.api.test.Helpers._
import play.api.test.FakeApplication
import play.api.{ Play, GlobalSettings }
import com.googlecode.htmlcompressor.compressor.XmlCompressor
import play.filters.gzip.GzipFilter

/**
* Test case for the [[com.mohiva.play.xmlcompressor.XMLCompressorFilter]] class.
Expand Down Expand Up @@ -104,6 +109,33 @@ class XMLCompressorFilterSpec extends Specification {
}
}

"The default filter with Gzip Filter" should {
"first compress then gzip result" in new DefaultWithGzipGlobal {
val Some(original) = route(FakeRequest(GET, "/action"))
val Some(gzipped) = route(FakeRequest(GET, "/action").withHeaders(ACCEPT_ENCODING -> "gzip"))

status(gzipped) must beEqualTo(OK)
contentType(gzipped) must beSome("application/xml")
header(CONTENT_ENCODING, gzipped) must beSome("gzip")
gunzip(contentAsBytes(gzipped)) must_== contentAsBytes(original)
}

"not compress already gzipped result" in new DefaultWithGzipGlobal {
// given static.html.gz == gzip(static.html)
// when /static.html is requested
// then Assets controller responds with static.html.gz
// we don't want to further pass this through HTML Compressor

val original = IOUtils.toByteArray(Play.resourceAsStream("static.xml").get)
val Some(result) = route(FakeRequest(GET, "/gzipped").withHeaders(ACCEPT_ENCODING -> "gzip"))

status(result) must beEqualTo(OK)
contentType(result) must beSome("application/xml")
header(CONTENT_ENCODING, result) must beSome("gzip")
gunzip(contentAsBytes(result)) must_== original
}
}

/**
* Defines the routes for the test.
*/
Expand All @@ -123,6 +155,7 @@ class XMLCompressorFilterSpec extends Specification {
case ("GET", "/nonXML") => Some(application.nonXML)
case ("GET", "/static") => Some(application.staticAsset)
case ("GET", "/chunked") => Some(application.chunked)
case ("GET", "/gzipped") => Some(application.gzipped)
case _ => None
}
}
Expand Down Expand Up @@ -156,4 +189,16 @@ class XMLCompressorFilterSpec extends Specification {
compressor
})
}

/**
* A custom global object with default HTML compressor filter and Default Gzip Filter.
*/
class DefaultWithGzipGlobal
extends WithApplication(FakeApplication(withGlobal = Some(new WithFilters(new GzipFilter(), XMLCompressorFilter()) with RouteSettings)))

def gunzip(bs: Array[Byte]): Array[Byte] = {
val bis = new ByteArrayInputStream(bs)
val gzis = new GZIPInputStream(bis)
IOUtils.toByteArray(gzis)
}
}
2 changes: 2 additions & 0 deletions test/com/mohiva/play/xmlcompressor/fixtures/Application.scala
Expand Up @@ -60,4 +60,6 @@ class Application extends AssetsBuilder(DefaultHttpErrorHandler) {
val parts = List(" <node> ", " <subnode> ", " text", " </subnode> ", " </node> ").map(Xml.apply)
Ok.chunked(Enumerator.enumerate(parts))
}

def gzipped = staticAsset
}

0 comments on commit dedc3c6

Please sign in to comment.