All Downloads are FREE. Search and download functionalities are using the official Maven repository.

next.plugins.gzip.scala Maven / Gradle / Ivy

package otoroshi.next.plugins

import akka.stream.Materializer
import otoroshi.env.Env
import otoroshi.next.plugins.api.{
  NgPluginCategory,
  NgPluginConfig,
  NgPluginHttpResponse,
  NgPluginVisibility,
  NgRequestTransformer,
  NgStep,
  NgTransformerResponseContext
}
import otoroshi.utils.gzip.GzipConfig
import otoroshi.utils.gzip.GzipConfig.logger
import otoroshi.utils.syntax.implicits._
import play.api.libs.json.{Format, JsError, JsObject, JsResult, JsSuccess, JsValue, Json, Reads}
import play.api.mvc.Result

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}

case class NgGzipConfig(
    excludedPatterns: Seq[String] = Seq.empty[String],
    whiteList: Seq[String] = Seq("text/*", "application/javascript", "application/json"),
    blackList: Seq[String] = Seq.empty[String],
    bufferSize: Int = 8192,
    chunkedThreshold: Int = 102400,
    compressionLevel: Int = 5
) extends NgPluginConfig {
  def json: JsValue           = NgGzipConfig.format.writes(this)
  lazy val legacy: GzipConfig = GzipConfig(
    enabled = true,
    excludedPatterns = excludedPatterns,
    whiteList = whiteList,
    blackList = blackList,
    bufferSize = bufferSize,
    chunkedThreshold = chunkedThreshold,
    compressionLevel = compressionLevel
  )
}

object NgGzipConfig {
  def fromLegacy(settings: GzipConfig): NgGzipConfig = NgGzipConfig(
    excludedPatterns = settings.excludedPatterns,
    whiteList = settings.whiteList,
    blackList = settings.blackList,
    bufferSize = settings.bufferSize,
    chunkedThreshold = settings.chunkedThreshold,
    compressionLevel = settings.compressionLevel
  )
  val format: Format[NgGzipConfig]                   = new Format[NgGzipConfig] {
    override def reads(json: JsValue): JsResult[NgGzipConfig] =
      Try {
        NgGzipConfig(
          excludedPatterns = (json \ "excluded_patterns").asOpt[Seq[String]].getOrElse(Seq.empty[String]),
          whiteList = (json \ "allowed_list").asOpt[Seq[String]].getOrElse(Seq.empty[String]),
          blackList = (json \ "blocked_list").asOpt[Seq[String]].getOrElse(Seq.empty[String]),
          bufferSize = (json \ "buffer_size").asOpt[Int].getOrElse(8192),
          chunkedThreshold = (json \ "chunked_threshold").asOpt[Int].getOrElse(102400),
          compressionLevel = (json \ "compression_level").asOpt[Int].getOrElse(5)
        )
      } match {
        case Success(entity) => JsSuccess(entity)
        case Failure(err)    => JsError(err.getMessage)
      }

    override def writes(o: NgGzipConfig): JsValue =
      Json.obj(
        "excluded_patterns" -> o.excludedPatterns,
        "allowed_list"      -> o.whiteList,
        "blocked_list"      -> o.blackList,
        "buffer_size"       -> o.bufferSize,
        "chunked_threshold" -> o.chunkedThreshold,
        "compression_level" -> o.compressionLevel
      )
  }
}

class GzipResponseCompressor extends NgRequestTransformer {

  private val configReads: Reads[NgGzipConfig] = NgGzipConfig.format

  override def steps: Seq[NgStep]                = Seq(NgStep.TransformResponse)
  override def categories: Seq[NgPluginCategory] = Seq(NgPluginCategory.Transformations, NgPluginCategory.Classic)
  override def visibility: NgPluginVisibility    = NgPluginVisibility.NgUserLand

  override def multiInstance: Boolean                      = true
  override def core: Boolean                               = true
  override def usesCallbacks: Boolean                      = false
  override def transformsRequest: Boolean                  = false
  override def transformsResponse: Boolean                 = true
  override def transformsError: Boolean                    = false
  override def isTransformRequestAsync: Boolean            = true
  override def isTransformResponseAsync: Boolean           = false
  override def name: String                                = "Gzip compression"
  override def description: Option[String]                 = "This plugin can compress responses using gzip".some
  override def defaultConfigObject: Option[NgPluginConfig] = NgGzipConfig().some

  override def transformResponseSync(
      ctx: NgTransformerResponseContext
  )(implicit env: Env, ec: ExecutionContext, mat: Materializer): Either[Result, NgPluginHttpResponse] = {
    val config                                    = ctx.cachedConfig(internalName)(configReads).getOrElse(NgGzipConfig())
    def transform(result: Result): Future[Result] = config.legacy.handleResult(ctx.request, result)
    ctx.attrs.put(otoroshi.next.plugins.Keys.ResultTransformerKey -> transform)
    ctx.otoroshiResponse.right
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy