Skip to content

Commit

Permalink
Merge branch 'main' into order-queryparams
Browse files Browse the repository at this point in the history
  • Loading branch information
easel authored Nov 26, 2023
2 parents 3290ef5 + b43dbc4 commit 4b2e3b4
Show file tree
Hide file tree
Showing 24 changed files with 214 additions and 105 deletions.
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = 3.7.14
version = 3.7.17
maxColumn = 120

align.preset = more
Expand Down
4 changes: 2 additions & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import sbt.Keys.scalaVersion
object Dependencies {
val JwtCoreVersion = "9.1.1"
val NettyVersion = "4.1.101.Final"
val NettyIncubatorVersion = "0.0.20.Final"
val NettyIncubatorVersion = "0.0.24.Final"
val ScalaCompactCollectionVersion = "2.11.0"
val ZioVersion = "2.0.19"
val ZioCliVersion = "0.5.0"
val ZioSchemaVersion = "0.4.15"
val ZioSchemaVersion = "0.4.16"
val SttpVersion = "3.3.18"

val `jwt-core` = "com.github.jwt-scala" %% "jwt-core" % JwtCoreVersion
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.9.6
sbt.version=1.9.7
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.6")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.3")
addSbtPlugin("io.spray" % "sbt-revolver" % "0.10.0")
addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.18.0")
addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.19.0")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12")
addSbtPlugin("dev.zio" % "zio-sbt-website" % "0.3.10")
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.10.0")
Expand Down
10 changes: 5 additions & 5 deletions zio-http/src/main/scala/zio/http/FormField.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ object FormField {

private[http] def getContentType(ast: Chunk[FormAST]): MediaType =
ast.collectFirst {
case header: FormAST.Header if header.name == "Content-Type" =>
case header: FormAST.Header if header.name.equalsIgnoreCase("Content-Type") =>
MediaType
.forContentType(header.value)
.getOrElse(MediaType.application.`octet-stream`) // Unknown content type defaults to binary
Expand All @@ -200,13 +200,13 @@ object FormField {
)(implicit trace: Trace): ZIO[Any, FormDecodingError, FormField] = {
val extract =
ast.foldLeft((Option.empty[FormAST.Header], Option.empty[FormAST.Header], Option.empty[FormAST.Header])) {
case (accum, header: FormAST.Header) if header.name == "Content-Disposition" =>
case (accum, header: FormAST.Header) if header.name.equalsIgnoreCase("Content-Disposition") =>
(Some(header), accum._2, accum._3)
case (accum, header: FormAST.Header) if header.name == "Content-Type" =>
case (accum, header: FormAST.Header) if header.name.equalsIgnoreCase("Content-Type") =>
(accum._1, Some(header), accum._3)
case (accum, header: FormAST.Header) if header.name == "Content-Transfer-Encoding" =>
case (accum, header: FormAST.Header) if header.name.equalsIgnoreCase("Content-Transfer-Encoding") =>
(accum._1, accum._2, Some(header))
case (accum, _) => accum
case (accum, _) => accum
}

for {
Expand Down
13 changes: 13 additions & 0 deletions zio-http/src/main/scala/zio/http/Handler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,19 @@ sealed trait Handler[-R, +Err, -In, +Out] { self =>
): Handler[R1, Err1, In, Out1] =
self.foldHandler(err => Handler.fromZIO(f(err)), Handler.succeed(_))

/**
* Transforms all failures of the handler effectfully except pure
* interruption.
*/
final def mapErrorCauseZIO[R1 <: R, Err1, Out1 >: Out](
f: Cause[Err] => ZIO[R1, Err1, Out1],
)(implicit trace: Trace): Handler[R1, Err1, In, Out1] =
self.foldCauseHandler(
err =>
if (err.isInterruptedOnly) Handler.failCause(err.asInstanceOf[Cause[Nothing]]) else Handler.fromZIO(f(err)),
Handler.succeed(_),
)

/**
* Returns a new handler where the error channel has been merged into the
* success channel to their common combined type.
Expand Down
15 changes: 6 additions & 9 deletions zio-http/src/main/scala/zio/http/Header.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import scala.util.{Either, Failure, Success, Try}
import zio._

import zio.http.codec.RichTextCodec
import zio.http.endpoint.openapi.OpenAPI.SecurityScheme.Http
import zio.http.internal.DateEncoding

sealed trait Header {
Expand Down Expand Up @@ -2480,16 +2481,12 @@ object Header {
private val codec: RichTextCodec[ContentType] = {

// char `.` according to BNF not allowed as `token`, but here tolerated
val token = RichTextCodec.filter(_ => true).validate("not a token") {
case ' ' | '(' | ')' | '<' | '>' | '@' | ',' | ';' | ':' | '\\' | '"' | '/' | '[' | ']' | '?' | '=' => false
case _ => true
}
val tokenQuoted = RichTextCodec.filter(_ => true).validate("not a quoted token") {
case ' ' | '"' => false
case _ => true
}
val token = RichTextCodec.charsNot(' ', '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=')

val tokenQuoted = RichTextCodec.charsNot(' ', '"')

val type1 = RichTextCodec.string.collectOrFail("unsupported main type") {
case value if MediaType.mainTypeMap.get(value).isDefined => value
case value if MediaType.mainTypeMap.contains(value) => value
}
val type1x = (RichTextCodec.literalCI("x-") ~ token.repeat.string).transform[String](in => s"${in._1}${in._2}")(in => ("x-", s"${in.substring(2)}"))
val codecType1 = (type1 | type1x).transform[String](_.merge) {
Expand Down
26 changes: 26 additions & 0 deletions zio-http/src/main/scala/zio/http/Route.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ sealed trait Route[-Env, +Err] { self =>
Handled(rpm.routePattern, handler2, location)
}

final def handleErrorCauseZIO(
f: Cause[Err] => ZIO[Any, Nothing, Response],
)(implicit trace: Trace): Route[Env, Nothing] =
self match {
case Provided(route, env) => Provided(route.handleErrorCauseZIO(f), env)
case Augmented(route, aspect) => Augmented(route.handleErrorCauseZIO(f), aspect)
case Handled(routePattern, handler, location) => Handled(routePattern, handler, location)

case Unhandled(rpm, handler, zippable, location) =>
val handler2: Handler[Env, Response, Request, Response] = {
val paramHandler =
Handler.fromFunctionZIO[(rpm.Context, Request)] { case (ctx, request) =>
rpm.routePattern.decode(request.method, request.path) match {
case Left(error) => ZIO.dieMessage(error)
case Right(value) =>
val params = rpm.zippable.zip(value, ctx)

handler(zippable.zip(params, request))
}
}
rpm.aspect.applyHandlerContext(paramHandler.mapErrorCauseZIO(f))
}

Handled(rpm.routePattern, handler2, location)
}

/**
* Determines if the route is defined for the specified request.
*/
Expand Down
3 changes: 3 additions & 0 deletions zio-http/src/main/scala/zio/http/Routes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ final class Routes[-Env, +Err] private (val routes: Chunk[zio.http.Route[Env, Er
def handleErrorCause(f: Cause[Err] => Response)(implicit trace: Trace): Routes[Env, Nothing] =
new Routes(routes.map(_.handleErrorCause(f)))

def handleErrorCauseZIO(f: Cause[Err] => ZIO[Any, Nothing, Response])(implicit trace: Trace): Routes[Env, Nothing] =
new Routes(routes.map(_.handleErrorCauseZIO(f)))

/**
* Returns new routes that have each been provided the specified environment,
* thus eliminating their requirement for any specific environment.
Expand Down
66 changes: 44 additions & 22 deletions zio-http/src/main/scala/zio/http/Scheme.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,52 +21,63 @@ import zio.stacktracer.TracingImplicits.disableAutoTrace

sealed trait Scheme { self =>
def encode: String = self match {
case Scheme.HTTP => "http"
case Scheme.HTTPS => "https"
case Scheme.WS => "ws"
case Scheme.WSS => "wss"
case Scheme.HTTP => "http"
case Scheme.HTTPS => "https"
case Scheme.WS => "ws"
case Scheme.WSS => "wss"
case Scheme.Custom(scheme) => scheme
}

def isHttp: Boolean = !isWebSocket
def isHttp: Boolean = self match {
case Scheme.HTTP | Scheme.HTTPS => true
case _ => false
}

def isWebSocket: Boolean = self match {
case Scheme.WS => true
case Scheme.WSS => true
case _ => false
}

def isSecure: Boolean = self match {
case Scheme.HTTPS => true
case Scheme.WSS => true
case _ => false
def isSecure: Option[Boolean] = self match {
case Scheme.HTTPS | Scheme.WSS => Some(true)
case Scheme.HTTP | Scheme.WS => Some(false)
case _ => None
}

def defaultPort: Int = self match {
case Scheme.HTTP => 80
case Scheme.HTTPS => 443
case Scheme.WS => 80
case Scheme.WSS => 443
/** default ports is only define for the Schemes: http, https, ws, wss */
def defaultPort: Option[Int] = self match {
case Scheme.HTTP => Some(Scheme.defaultPortForHTTP)
case Scheme.HTTPS => Some(Scheme.defaultPortForHTTPS)
case Scheme.WS => Some(Scheme.defaultPortForWS)
case Scheme.WSS => Some(Scheme.defaultPortForWSS)
case Scheme.Custom(_) => None
}

}
object Scheme {

object Scheme {

/**
* Decodes a string to an Option of Scheme. Returns None in case of
* null/non-valid Scheme
*
* The should be lowercase and follow this syntax:
* - Scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
*/
def decode(scheme: String): Option[Scheme] =
Option(unsafe.decode(scheme)(Unsafe.unsafe))

private[zio] object unsafe {
def decode(scheme: String)(implicit unsafe: Unsafe): Scheme = {
if (scheme == null) null
if (scheme == null || scheme.isEmpty) null
else
scheme.length match {
case 5 => Scheme.HTTPS
case 4 => Scheme.HTTP
case 3 => Scheme.WSS
case 2 => Scheme.WS
case _ => null
scheme match {
case "http" => HTTP
case "https" => HTTPS
case "ws" => WS
case "wss" => WSS
case custom => Custom(custom.toLowerCase)
}
}
}
Expand All @@ -78,4 +89,15 @@ object Scheme {
case object WS extends Scheme

case object WSS extends Scheme

/**
* @param scheme
* value MUST not be "http" "https" "ws" "wss"
*/
final case class Custom(scheme: String) extends Scheme

def defaultPortForHTTP = 80
def defaultPortForHTTPS = 443
def defaultPortForWS = 80
def defaultPortForWSS = 443
}
Loading

0 comments on commit 4b2e3b4

Please sign in to comment.