Skip to content

Instantly share code, notes, and snippets.

@borice
Created March 1, 2019 16:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save borice/f68678a19f5c42e9fddbb20b62b043a1 to your computer and use it in GitHub Desktop.
Save borice/f68678a19f5c42e9fddbb20b62b043a1 to your computer and use it in GitHub Desktop.
Enable gzip-compressed JSON payload in op-rabbit
//
// To use the below, simply import Gzip._ instead of import com.spingo.op_rabbit.PlayJsonSupport._
//
import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
import java.util.zip.{GZIPInputStream, GZIPOutputStream}
import com.spingo.op_rabbit._
import play.api.libs.json._
import scala.io.{Codec, Source}
import scala.util.{Failure, Success, Try}
object Gzip {
private val codec: Codec = Codec.UTF8
implicit def gzipJsonMarshaller[T](implicit writer: Writes[T]): RabbitMarshaller[T] =
new RabbitMarshaller[T] {
override protected def contentType: String = "application/json"
override protected def contentEncoding: Option[String] = Some("gzip")
override def marshall(value: T): Array[Byte] = {
val json = writer.writes(value)
val rawBytes = Json.stringify(json).getBytes(codec.charSet)
val byteStream = new ByteArrayOutputStream()
using(new GZIPOutputStream(byteStream)) { outStream =>
outStream.write(rawBytes)
}
byteStream.toByteArray
}
}
implicit def gzipJsonUnmarshaller[T](implicit reader: Reads[T]): RabbitUnmarshaller[T] =
new RabbitUnmarshaller[T] {
override def unmarshall(value: Array[Byte], contentTypeOpt: Option[String], contentEncodingOpt: Option[String]): T = {
val contentType = contentTypeOpt.getOrElse("application/json")
val contentEncoding = contentEncodingOpt.getOrElse("gzip")
if (contentType != "application/json" && contentType != "text/json")
throw MismatchedContentType(contentType, "application/json")
if (contentEncoding != "gzip")
throw GenericMarshallingException("Expected GZIP encoding")
val zipStream = new GZIPInputStream(new ByteArrayInputStream(value))
val data = Source.fromInputStream(zipStream)(codec).mkString
val json = Try(Json.parse(data)) match {
case Success(jsValue) => jsValue
case Failure(e) => throw InvalidFormat(data, e.toString)
}
Json.fromJson[T](json) match {
case JsSuccess(v, _) => v
case JsError(errors) => throw InvalidFormat(data, JsError.toJson(errors).toString)
}
}
}
import scala.language.reflectiveCalls
def using[A, B <: {def close() : Unit}](closeable: B)(f: B => A): A =
try {
f(closeable)
}
finally {
closeable.close()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment