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$30733/378145671@1543f17f)

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

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

val body = response.as[String].unsafeRun
// 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$30906/2055732163@5e4d9892)

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

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

val body = response.as[String].unsafeRun
// 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 unsafeRun to extract values out of a 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].