GZip Compression
Http4s provides Middleware, named GZip
, for allowing for the compression of the Response
body using GZip.
Examples in this document have the following dependencies.
libraryDependencies ++= Seq(
"org.http4s" %% "http4s-dsl" % http4sVersion,
"org.http4s" %% "http4s-server" % http4sVersion
)
And we need some imports.
import org.http4s._
import org.http4s.dsl._
Let’s start by making a simple service that returns a (relatively) large string
in its body. We’ll use as[String]
to examine the body.
val service = HttpService {
case _ =>
Ok("I repeat myself when I'm under stress. " * 3)
}
// service: org.http4s.HttpService = Kleisli(org.http4s.package$HttpService$$$Lambda$26427/2123114212@7e500f14)
val request = Request(Method.GET, uri("/"))
// request: org.http4s.Request = Request(method=GET, uri=/, headers=Headers())
// Do not call 'run' in your code - see note at bottom.
val response = service(request).run
// <console>:21: warning: method run in class Task is deprecated (since 7.2): use unsafePerformSync
// val response = service(request).run
// ^
// response: org.http4s.Response = Response(status=200, headers=Headers(Content-Type: text/plain; charset=UTF-8, Content-Length: 117))
val body = response.as[String].run
// <console>:19: warning: method run in class Task is deprecated (since 7.2): use unsafePerformSync
// val body = response.as[String].run
// ^
// body: String = "I repeat myself when I'm under stress. I repeat myself when I'm under stress. I repeat myself when I'm under stress. "
body.length
// res1: Int = 117
Now we can wrap the service in the GZip
middleware.
import org.http4s.server.middleware._
// import org.http4s.server.middleware._
val zipService = GZip(service)
// zipService: org.http4s.HttpService = Kleisli(org.http4s.server.middleware.GZip$$$Lambda$26495/1604874569@6aac3aa4)
// Do not call 'run' in your code - see note at bottom.
val response = zipService(request).run
// <console>:24: warning: method run in class Task is deprecated (since 7.2): use unsafePerformSync
// val response = zipService(request).run
// ^
// response: org.http4s.Response = Response(status=200, headers=Headers(Content-Type: text/plain; charset=UTF-8, Content-Length: 117))
val body = response.as[String].run
// <console>:22: warning: method run in class Task is deprecated (since 7.2): use unsafePerformSync
// val body = response.as[String].run
// ^
// body: String = "I repeat myself when I'm under stress. I repeat myself when I'm under stress. I repeat myself when I'm under stress. "
body.length
// res3: Int = 117
So far, there was no change. That’s because the caller needs to inform us that
they will accept GZipped responses via an Accept-Encoding
header. Acceptable
values for the Accept-Encoding
header are “gzip”, “x-gzip”, and “*”.
val acceptHeader = Header("Accept-Encoding", "gzip")
// acceptHeader: org.http4s.Header.Raw = Accept-Encoding: gzip
val zipRequest = request.putHeaders(acceptHeader)
// zipRequest: request.Self = Request(method=GET, uri=/, headers=Headers(Accept-Encoding: gzip))
// Do not call 'run' in your code - see note at bottom.
val response = zipService(zipRequest).run
// <console>:24: warning: method run in class Task is deprecated (since 7.2): use unsafePerformSync
// val response = zipService(zipRequest).run
// ^
// response: org.http4s.Response = Response(status=200, headers=Headers(Content-Type: text/plain; charset=UTF-8, Content-Encoding: gzip))
val body = response.as[String].run
// <console>:22: warning: method run in class Task is deprecated (since 7.2): use unsafePerformSync
// val body = response.as[String].run
// ^
// body: String = " �????????�T(J-HM,Qȭ,N�IS(�H�S�T�U(�KI-R(.)J-.�S�2?UX�<u???"
body.length
// res5: Int = 59
Notice how the response no longer looks very String-like and it’s shorter in
length. Also, there is a Content-Encoding
header in the response with a value
of “gzip”.
As described in Middleware, services and middleware can be composed such that only some of your endpoints are GZip enabled.
NOTE: In this documentation, we are calling run
to extract values out of a
Task
so that they will be printed out. You should not call run
in your
service or middleware code. You can work with values while keeping them inside the
Task using map
, flatMap
and/or for
. Remember, your service returns a
Task[Response]
.