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$30948/537243469@68b89065)

val request = Request(Method.GET, uri("/"))
// request: org.http4s.Request = Request(method=GET, uri=/, headers=Headers())

// Do not call 'unsafePerformSync' in your code - see note at bottom.
val response = service.orNotFound(request).unsafePerformSync
// response: org.http4s.Response = Response(status=200, headers=Headers(Content-Type: text/plain; charset=UTF-8, Content-Length: 117))

val body = response.as[String].unsafePerformSync
// 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$31064/209839822@1575acf)

// Do not call 'unsafePerformSync' in your code - see note at bottom.
val response = zipService.orNotFound(request).unsafePerformSync
// response: org.http4s.Response = Response(status=200, headers=Headers(Content-Type: text/plain; charset=UTF-8, Content-Length: 117))

val body = response.as[String].unsafePerformSync
// 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 'unsafePerformSync' in your code - see note at bottom.
val response = zipService.orNotFound(zipRequest).unsafePerformSync
// response: org.http4s.Response = Response(status=200, headers=Headers(Content-Type: text/plain; charset=UTF-8, Content-Encoding: gzip))

val body = response.as[String].unsafePerformSync
// 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 unsafePerformSync to extract values out of a Task so that they will be printed out. You should not call unsafePerformSync 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].