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

next.utils.json.scala Maven / Gradle / Ivy

package otoroshi.next.utils

import akka.stream.Materializer
import akka.util.ByteString
import otoroshi.next.plugins.api.{NgPluginHttpRequest, NgPluginHttpResponse}
import otoroshi.utils.http.DN
import otoroshi.utils.http.RequestImplicits.EnhancedRequestHeader
import otoroshi.utils.syntax.implicits._
import play.api.libs.json._
import play.api.libs.ws.{DefaultWSCookie, WSCookie}
import play.api.mvc.{Cookie, RequestHeader}

import java.security.cert.X509Certificate
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}

object JsonHelpers {

  def requestBody(request: NgPluginHttpRequest)(implicit ec: ExecutionContext, mat: Materializer): Future[JsValue] = {
    if (request.hasBody) {
      request.body.runFold(ByteString.empty)(_ ++ _).map { b =>
        val arr = b.toArray[Byte]
        Writes.arrayWrites[Byte].writes(arr)
      }
    } else {
      JsNull.vfuture
    }
  }

  def responseBody(
      response: NgPluginHttpResponse
  )(implicit ec: ExecutionContext, mat: Materializer): Future[JsValue] = {
    response.body.runFold(ByteString.empty)(_ ++ _).map { b =>
      val arr = b.toArray[Byte]
      if (arr.isEmpty) {
        JsNull
      } else {
        Writes.arrayWrites[Byte].writes(arr)
      }
    }
  }

  def reader[A](f: => A): JsResult[A] = Try {
    f
  } match {
    case Failure(err)   => JsError(err.getMessage)
    case Success(value) => JsSuccess(value)
  }

  def clientCertChainToJson(chain: Option[Seq[X509Certificate]]): JsValue = chain match {
    case None      => JsNull
    case Some(seq) => {
      JsArray(
        seq.map(c =>
          Json.obj(
            "subjectDN"    -> DN(c.getSubjectDN.getName).stringify,
            "issuerDN"     -> DN(c.getIssuerDN.getName).stringify,
            "notAfter"     -> c.getNotAfter.getTime,
            "notBefore"    -> c.getNotBefore.getTime,
            "serialNumber" -> c.getSerialNumber.toString(16),
            "subjectCN"    -> Option(DN(c.getSubjectDN.getName).stringify)
              .flatMap(_.split(",").toSeq.map(_.trim).find(_.toLowerCase().startsWith("cn=")))
              .map(_.replace("CN=", "").replace("cn=", ""))
              .getOrElse(DN(c.getSubjectDN.getName).stringify)
              .asInstanceOf[String],
            "issuerCN"     -> Option(DN(c.getIssuerDN.getName).stringify)
              .flatMap(_.split(",").toSeq.map(_.trim).find(_.toLowerCase().startsWith("cn=")))
              .map(_.replace("CN=", "").replace("cn=", ""))
              .getOrElse(DN(c.getIssuerDN.getName).stringify)
              .asInstanceOf[String]
          )
        )
      )
    }
  }
  def requestToJson(request: RequestHeader): JsValue = {
    Json.obj(
      "id"                -> request.id,
      "method"            -> request.method,
      "headers"           -> request.headers.toSimpleMap,
      "cookies"           -> JsArray(request.cookies.toSeq.map(c => cookieToJson(c))),
      "tls"               -> request.theSecuredTrusted,
      "uri"               -> request.uri,
      "query"             -> request.queryString,
      "path"              -> request.path,
      "version"           -> request.version,
      "has_body"          -> request.theHasBody,
      "remote"            -> request.remoteAddress,
      "client_cert_chain" -> JsonHelpers.clientCertChainToJson(request.clientCertificateChain)
    )
  }
  def cookieToJson(cookie: Cookie): JsValue = {
    Json.obj(
      "name"      -> cookie.name,
      "value"     -> cookie.value,
      "path"      -> cookie.path,
      "domain"    -> cookie.domain,
      "http_only" -> cookie.httpOnly,
      "max_age"   -> cookie.maxAge,
      "secure"    -> cookie.secure,
      "same_site" -> cookie.sameSite.map(_.value)
    )
  }
  def wsCookieToJson(cookie: WSCookie): JsValue = {
    cookie match {
      case cc: otoroshi.utils.http.WSCookieWithSameSite => wsCookieWithSameSiteToJson(cc)
      case _                                            =>
        Json.obj(
          "name"      -> cookie.name,
          "value"     -> cookie.value,
          "path"      -> cookie.path,
          "domain"    -> cookie.domain,
          "http_only" -> cookie.httpOnly,
          "max_age"   -> cookie.maxAge,
          "secure"    -> cookie.secure
        )
    }
  }

  def wsCookieWithSameSiteToJson(cookie: otoroshi.utils.http.WSCookieWithSameSite): JsValue = {
    Json.obj(
      "name"      -> cookie.name,
      "value"     -> cookie.value,
      "path"      -> cookie.path,
      "domain"    -> cookie.domain,
      "http_only" -> cookie.httpOnly,
      "max_age"   -> cookie.maxAge,
      "secure"    -> cookie.secure,
      "sameSite"  -> cookie.sameSite.map(ss => JsString.apply(ss.value)).getOrElse(JsNull).as[JsValue]
    )
  }
  def cookieFromJson(cookie: JsValue): WSCookie = {
    DefaultWSCookie(
      name = cookie.select("name").as[String],
      value = cookie.select("value").as[String],
      domain = cookie.select("domain").asOpt[String],
      path = cookie.select("path").asOpt[String],
      maxAge = cookie.select("max_age").asOpt[Long],
      secure = cookie.select("secure").asOpt[Boolean].getOrElse(false),
      httpOnly = cookie.select("http_only").asOpt[Boolean].getOrElse(false)
    )
  }
  def errToJson(error: Throwable): JsValue = {
    Json.obj(
      "message" -> error.getMessage,
      "cause"   -> Option(error.getCause).map(errToJson).getOrElse(JsNull).as[JsValue],
      "stack"   -> JsArray(
        error.getStackTrace.toSeq.map(el =>
          Json.obj(
            "class_loader_name" -> el.getClassLoaderName,
            "module_name"       -> el.getModuleName,
            "module_version"    -> el.getModuleVersion,
            "declaring_class"   -> el.getClassName,
            "method_name"       -> el.getMethodName,
            "file_name"         -> el.getFileName,
            "line_number"       -> el.getLineNumber
          )
        )
      )
    )
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy