From f065eaaf4d476c9a66d9e15f30ccc0b520707954 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Wed, 22 Jan 2025 22:07:36 +0100 Subject: [PATCH 01/12] Respond with 401 in case of missing Authorization header --- .../zio/http/endpoint/UnauthorizedSpec.scala | 24 +++++++++++++++++++ .../scala/zio/http/codec/HttpCodecError.scala | 3 +++ .../http/codec/internal/EncoderDecoder.scala | 5 +++- .../scala/zio/http/endpoint/Endpoint.scala | 9 +++---- 4 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 zio-http/jvm/src/test/scala/zio/http/endpoint/UnauthorizedSpec.scala diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/UnauthorizedSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/UnauthorizedSpec.scala new file mode 100644 index 0000000000..74512b3df5 --- /dev/null +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/UnauthorizedSpec.scala @@ -0,0 +1,24 @@ +package zio.http.endpoint + +import zio.ZIO +import zio.test._ + +import zio.http._ +import zio.http.codec._ + +object UnauthorizedSpec extends ZIOSpecDefault { + override def spec = + suite("UnauthorizedSpec")( + test("should respond with 401 Unauthorized when required authorization header is missing") { + val endpoint = Endpoint(Method.GET / "test") + .header(HeaderCodec.authorization) + .out[Unit] + val route = endpoint.implement(_ => ZIO.unit) + val request = + Request(method = Method.GET, url = url"/test") + for { + response <- route.toRoutes.runZIO(request) + } yield assertTrue(Status.Unauthorized == response.status) + }, + ) +} diff --git a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala index bcd97223d8..bcbfd34972 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala @@ -33,6 +33,9 @@ object HttpCodecError { final case class MissingHeader(headerName: String) extends HttpCodecError { def message = s"Missing header $headerName" } + final case object MissingAuthorizationHeader extends HttpCodecError { + def message = "Missing header Authorization" + } final case class MalformedMethod(expected: zio.http.Method, actual: zio.http.Method) extends HttpCodecError { def message = s"Expected $expected but found $actual" } diff --git a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala index 44d99b72dd..988aa6740e 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala @@ -384,7 +384,10 @@ private[codec] object EncoderDecoder { .getOrElse(throw HttpCodecError.MalformedHeader(codec.name, codec.textCodec)) case None => - throw HttpCodecError.MissingHeader(codec.name) + if (codec.name == Header.Authorization.name) + throw HttpCodecError.MissingAuthorizationHeader + else + throw HttpCodecError.MissingHeader(codec.name) }, ) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 3eab47c46c..0ebda4688f 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -27,7 +27,7 @@ import zio.schema.Schema import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ -import zio.http.codec._ +import zio.http.codec.{StatusCodec, _} import zio.http.endpoint.Endpoint.{OutErrors, defaultMediaTypes} /** @@ -340,9 +340,10 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( case Some(HttpCodecError.CustomError("SchemaTransformationFailure", message)) if maybeUnauthedResponse.isDefined && message.endsWith(" auth required") => maybeUnauthedResponse.get - case Some(_) => + case Some(HttpCodecError.MissingAuthorizationHeader) => + Handler.succeed(Response.unauthorized) + case Some(error) => Handler.fromFunctionZIO { (request: zio.http.Request) => - val error = cause.defects.head.asInstanceOf[HttpCodecError] val response = { val outputMediaTypes = ( @@ -355,7 +356,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( } ZIO.succeed(response) } - case None => + case None => Handler.failCause(cause) } } From 63cef82527001e99d5bf25a7735df553ddc5e6a7 Mon Sep 17 00:00:00 2001 From: Denis Mikhaylov Date: Thu, 23 Jan 2025 06:15:21 +0100 Subject: [PATCH 02/12] Make linter happy --- .../shared/src/main/scala/zio/http/codec/HttpCodecError.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala index bcbfd34972..24a6f514c3 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala @@ -33,7 +33,7 @@ object HttpCodecError { final case class MissingHeader(headerName: String) extends HttpCodecError { def message = s"Missing header $headerName" } - final case object MissingAuthorizationHeader extends HttpCodecError { + case object MissingAuthorizationHeader extends HttpCodecError { def message = "Missing header Authorization" } final case class MalformedMethod(expected: zio.http.Method, actual: zio.http.Method) extends HttpCodecError { From 2d509b1589cf743719f5e50876e56c267d114249 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Thu, 23 Jan 2025 10:25:35 +0100 Subject: [PATCH 03/12] Trigger build From 7a2e2276c444bf71a8e77e6448701d9b331985dd Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Thu, 23 Jan 2025 15:47:48 +0100 Subject: [PATCH 04/12] Trigger build From 00f9cacc9304986f962e40355f936094f7a6929d Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Mon, 10 Feb 2025 11:50:06 +0100 Subject: [PATCH 05/12] Fix implementation --- .../src/main/scala/zio/http/Header.scala | 33 +++++++++++-------- .../scala/zio/http/codec/HttpCodecError.scala | 1 - .../http/codec/internal/EncoderDecoder.scala | 5 +-- .../scala/zio/http/endpoint/Endpoint.scala | 2 +- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/Header.scala b/zio-http/shared/src/main/scala/zio/http/Header.scala index 880fa042fa..12fedc10c2 100644 --- a/zio-http/shared/src/main/scala/zio/http/Header.scala +++ b/zio-http/shared/src/main/scala/zio/http/Header.scala @@ -16,26 +16,29 @@ package zio.http -import zio.Config.Secret -import zio._ -import zio.http.codec.{HttpCodecError, RichTextCodec} -import zio.http.internal.{DateEncoding, ErrorConstructor, StringSchemaCodec} -import zio.schema.Schema -import zio.schema.codec.DecodeError -import zio.schema.codec.DecodeError.ReadError -import zio.schema.validation.ValidationError - import java.net.URI import java.nio.charset.{Charset, UnsupportedCharsetException} import java.time.ZonedDateTime import java.util.Base64 import java.util.concurrent.ConcurrentHashMap + import scala.annotation.tailrec import scala.collection.mutable import scala.util.control.NonFatal import scala.util.matching.Regex import scala.util.{Either, Failure, Success, Try} +import zio.Config.Secret +import zio._ + +import zio.schema.Schema +import zio.schema.codec.DecodeError +import zio.schema.codec.DecodeError.ReadError +import zio.schema.validation.ValidationError + +import zio.http.codec.{HttpCodecError, RichTextCodec} +import zio.http.internal.{DateEncoding, ErrorConstructor, StringSchemaCodec} + sealed trait Header { type Self <: Header def self: Self @@ -84,10 +87,7 @@ object Header { private val errorConstructor = new ErrorConstructor { override def missing(fieldName: String): HttpCodecError = - if (fieldName == Header.Authorization.name) - HttpCodecError.MissingAuthorizationHeader - else - HttpCodecError.MissingHeader(fieldName) + HttpCodecError.MissingHeader(fieldName) override def missingAll(fieldNames: Chunk[String]): HttpCodecError = HttpCodecError.MissingHeaders(fieldNames) @@ -177,7 +177,12 @@ object Header { def fromHeadersUnsafe(headers: Headers): HeaderValue = fromHeaders(headers).fold( - e => throw HttpCodecError.DecodingErrorHeader(name, ReadError(Cause.empty, e)), + e => { + if (name == Header.Authorization.name) + throw HttpCodecError.MissingAuthorizationHeader + else + throw HttpCodecError.DecodingErrorHeader(name, ReadError(Cause.empty, e)) + }, identity, ) diff --git a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala index 3c34c3ebcb..cde38c9755 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala @@ -23,7 +23,6 @@ import zio.{Cause, Chunk} import zio.schema.codec.DecodeError import zio.schema.validation.ValidationError -import zio.http.Header.HeaderType import zio.http.{Path, Status} sealed trait HttpCodecError extends Exception with NoStackTrace with Product with Serializable { diff --git a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala index e3684766ce..16148d4b11 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala @@ -16,13 +16,14 @@ package zio.http.codec.internal +import scala.util.Try + import zio._ + import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ import zio.http.codec._ -import scala.util.Try - private[codec] trait EncoderDecoder[-AtomTypes, Value] { self => def decode(config: CodecConfig, url: URL, status: Status, method: Method, headers: Headers, body: Body)(implicit trace: Trace, diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 0ebda4688f..0dcf5af5a9 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -27,7 +27,7 @@ import zio.schema.Schema import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ -import zio.http.codec.{StatusCodec, _} +import zio.http.codec._ import zio.http.endpoint.Endpoint.{OutErrors, defaultMediaTypes} /** From 93f33ccaecf8e60f42c6e7880f1b754d35eeddb4 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 10 Jun 2025 14:24:32 +0200 Subject: [PATCH 06/12] Remove MissingAuthorizationHeader --- .../src/main/scala/zio/http/Header.scala | 28 +++++++------------ .../scala/zio/http/codec/HttpCodecError.scala | 3 -- .../scala/zio/http/endpoint/Endpoint.scala | 17 ++++++----- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/Header.scala b/zio-http/shared/src/main/scala/zio/http/Header.scala index 371cece7a7..09c4e7dc58 100644 --- a/zio-http/shared/src/main/scala/zio/http/Header.scala +++ b/zio-http/shared/src/main/scala/zio/http/Header.scala @@ -16,12 +16,20 @@ package zio.http +import zio.Config.Secret +import zio._ +import zio.http.codec.{HttpCodecError, RichTextCodec} +import zio.http.internal.{DateEncoding, ErrorConstructor, StringSchemaCodec} +import zio.schema.Schema +import zio.schema.codec.DecodeError +import zio.schema.codec.DecodeError.ReadError +import zio.schema.validation.ValidationError + import java.net.URI import java.nio.charset.{Charset, UnsupportedCharsetException} import java.time.ZonedDateTime import java.util.Base64 import java.util.concurrent.ConcurrentHashMap - import scala.annotation.tailrec import scala.collection.mutable import scala.runtime.AbstractFunction11 @@ -29,17 +37,6 @@ import scala.util.control.NonFatal import scala.util.matching.Regex import scala.util.{Either, Failure, Success, Try} -import zio.Config.Secret -import zio._ - -import zio.schema.Schema -import zio.schema.codec.DecodeError -import zio.schema.codec.DecodeError.ReadError -import zio.schema.validation.ValidationError - -import zio.http.codec.{HttpCodecError, RichTextCodec} -import zio.http.internal.{DateEncoding, ErrorConstructor, StringSchemaCodec} - sealed trait Header { type Self <: Header def self: Self @@ -178,12 +175,7 @@ object Header { def fromHeadersUnsafe(headers: Headers): HeaderValue = fromHeaders(headers).fold( - e => { - if (name == Header.Authorization.name) - throw HttpCodecError.MissingAuthorizationHeader - else - throw HttpCodecError.DecodingErrorHeader(name, ReadError(Cause.empty, e)) - }, + e => throw HttpCodecError.DecodingErrorHeader(name, ReadError(Cause.empty, e)), identity, ) diff --git a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala index 87386b9341..8991bc2ba8 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala @@ -36,9 +36,6 @@ object HttpCodecError { final case class MissingHeader(headerName: String) extends HeaderError { def message = s"Missing header $headerName" } - case object MissingAuthorizationHeader extends HeaderError { - def message = "Missing header Authorization" - } final case class MissingHeaders(headerNames: Chunk[String]) extends HeaderError { def message = s"Missing headers ${headerNames.mkString(", ")}" } diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 85d83a70c3..c7eeaa6b96 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -16,19 +16,16 @@ package zio.http.endpoint -import scala.annotation.nowarn -import scala.reflect.ClassTag - import zio._ - -import zio.stream.ZStream - -import zio.schema.Schema - import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ import zio.http.codec._ import zio.http.endpoint.Endpoint.{OutErrors, defaultMediaTypes} +import zio.schema.Schema +import zio.stream.ZStream + +import scala.annotation.nowarn +import scala.reflect.ClassTag /** * An [[zio.http.endpoint.Endpoint]] represents an API endpoint for the HTTP @@ -384,7 +381,9 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( case Some(HttpCodecError.CustomError("SchemaTransformationFailure", message)) if maybeUnauthedResponse.isDefined && message.endsWith(" auth required") => maybeUnauthedResponse.get - case Some(HttpCodecError.MissingAuthorizationHeader) => + case Some(HttpCodecError.MissingHeaders(headerNames)) if (headerNames.contains(Header.Authorization.name)) => + Handler.succeed(Response.unauthorized) + case Some(HttpCodecError.MissingHeader(headerName)) if headerName == Header.Authorization.name => Handler.succeed(Response.unauthorized) case Some(error) => Handler.fromFunctionZIO { (request: zio.http.Request) => From 31764330315acb32d5413df6fa2f0c8ffde88eb1 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 10 Jun 2025 14:27:31 +0200 Subject: [PATCH 07/12] revert pattern matching case --- .../src/main/scala/zio/http/endpoint/Endpoint.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index c7eeaa6b96..1a60204852 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -381,12 +381,14 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( case Some(HttpCodecError.CustomError("SchemaTransformationFailure", message)) if maybeUnauthedResponse.isDefined && message.endsWith(" auth required") => maybeUnauthedResponse.get - case Some(HttpCodecError.MissingHeaders(headerNames)) if (headerNames.contains(Header.Authorization.name)) => + case Some(HttpCodecError.MissingHeaders(headerNames)) + if headerNames.contains(Header.Authorization.name) => Handler.succeed(Response.unauthorized) case Some(HttpCodecError.MissingHeader(headerName)) if headerName == Header.Authorization.name => Handler.succeed(Response.unauthorized) - case Some(error) => + case _: Some[_] => Handler.fromFunctionZIO { (request: zio.http.Request) => + val error = cause.defects.head.asInstanceOf[HttpCodecError] val response = { val outputMediaTypes = ( @@ -399,7 +401,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( } ZIO.succeed(response) } - case None => + case None => Handler.failCause(cause) } } From eeeb70934699443b8b5d0c82651048f2fcf0f203 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 10 Jun 2025 14:28:58 +0200 Subject: [PATCH 08/12] fmt --- .../src/main/scala/zio/http/Header.scala | 21 +++++++++++-------- .../scala/zio/http/endpoint/Endpoint.scala | 13 +++++++----- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/Header.scala b/zio-http/shared/src/main/scala/zio/http/Header.scala index 09c4e7dc58..b52b168d47 100644 --- a/zio-http/shared/src/main/scala/zio/http/Header.scala +++ b/zio-http/shared/src/main/scala/zio/http/Header.scala @@ -16,20 +16,12 @@ package zio.http -import zio.Config.Secret -import zio._ -import zio.http.codec.{HttpCodecError, RichTextCodec} -import zio.http.internal.{DateEncoding, ErrorConstructor, StringSchemaCodec} -import zio.schema.Schema -import zio.schema.codec.DecodeError -import zio.schema.codec.DecodeError.ReadError -import zio.schema.validation.ValidationError - import java.net.URI import java.nio.charset.{Charset, UnsupportedCharsetException} import java.time.ZonedDateTime import java.util.Base64 import java.util.concurrent.ConcurrentHashMap + import scala.annotation.tailrec import scala.collection.mutable import scala.runtime.AbstractFunction11 @@ -37,6 +29,17 @@ import scala.util.control.NonFatal import scala.util.matching.Regex import scala.util.{Either, Failure, Success, Try} +import zio.Config.Secret +import zio._ + +import zio.schema.Schema +import zio.schema.codec.DecodeError +import zio.schema.codec.DecodeError.ReadError +import zio.schema.validation.ValidationError + +import zio.http.codec.{HttpCodecError, RichTextCodec} +import zio.http.internal.{DateEncoding, ErrorConstructor, StringSchemaCodec} + sealed trait Header { type Self <: Header def self: Self diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 1a60204852..5db5dbef2f 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -16,16 +16,19 @@ package zio.http.endpoint +import scala.annotation.nowarn +import scala.reflect.ClassTag + import zio._ + +import zio.stream.ZStream + +import zio.schema.Schema + import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ import zio.http.codec._ import zio.http.endpoint.Endpoint.{OutErrors, defaultMediaTypes} -import zio.schema.Schema -import zio.stream.ZStream - -import scala.annotation.nowarn -import scala.reflect.ClassTag /** * An [[zio.http.endpoint.Endpoint]] represents an API endpoint for the HTTP From a32d60c33d249e3a840766b59f1eb8163411b526 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 10 Jun 2025 14:47:02 +0200 Subject: [PATCH 09/12] Handle DecodingErrorHeader --- .../scala/zio/http/endpoint/Endpoint.scala | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index 5db5dbef2f..b55e77c93f 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -16,19 +16,16 @@ package zio.http.endpoint -import scala.annotation.nowarn -import scala.reflect.ClassTag - import zio._ - -import zio.stream.ZStream - -import zio.schema.Schema - import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ import zio.http.codec._ import zio.http.endpoint.Endpoint.{OutErrors, defaultMediaTypes} +import zio.schema.Schema +import zio.stream.ZStream + +import scala.annotation.nowarn +import scala.reflect.ClassTag /** * An [[zio.http.endpoint.Endpoint]] represents an API endpoint for the HTTP @@ -387,9 +384,11 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( case Some(HttpCodecError.MissingHeaders(headerNames)) if headerNames.contains(Header.Authorization.name) => Handler.succeed(Response.unauthorized) - case Some(HttpCodecError.MissingHeader(headerName)) if headerName == Header.Authorization.name => + case Some(HttpCodecError.MissingHeader(headerName)) if headerName == Header.Authorization.name => + Handler.succeed(Response.unauthorized) + case Some(HttpCodecError.DecodingErrorHeader(headerName, _)) if headerName == Header.Authorization.name => Handler.succeed(Response.unauthorized) - case _: Some[_] => + case _: Some[_] => Handler.fromFunctionZIO { (request: zio.http.Request) => val error = cause.defects.head.asInstanceOf[HttpCodecError] val response = { @@ -404,7 +403,7 @@ final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType]( } ZIO.succeed(response) } - case None => + case None => Handler.failCause(cause) } } From c9da01152c9dbb12e67e2cb0c512d22662e3bab9 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 10 Jun 2025 14:49:33 +0200 Subject: [PATCH 10/12] fmt --- .../src/main/scala/zio/http/endpoint/Endpoint.scala | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala index b55e77c93f..c3c8d19f55 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/Endpoint.scala @@ -16,16 +16,19 @@ package zio.http.endpoint +import scala.annotation.nowarn +import scala.reflect.ClassTag + import zio._ + +import zio.stream.ZStream + +import zio.schema.Schema + import zio.http.Header.Accept.MediaTypeWithQFactor import zio.http._ import zio.http.codec._ import zio.http.endpoint.Endpoint.{OutErrors, defaultMediaTypes} -import zio.schema.Schema -import zio.stream.ZStream - -import scala.annotation.nowarn -import scala.reflect.ClassTag /** * An [[zio.http.endpoint.Endpoint]] represents an API endpoint for the HTTP From 64c1ea2b962582d0c73658f25a2ec753258bab94 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 10 Jun 2025 14:51:33 +0200 Subject: [PATCH 11/12] Remove unrelated changes --- zio-http/shared/src/main/scala/zio/http/Header.scala | 1 + .../shared/src/main/scala/zio/http/codec/HttpCodecError.scala | 1 + .../src/main/scala/zio/http/codec/internal/EncoderDecoder.scala | 1 + 3 files changed, 3 insertions(+) diff --git a/zio-http/shared/src/main/scala/zio/http/Header.scala b/zio-http/shared/src/main/scala/zio/http/Header.scala index b52b168d47..dfeb61187f 100644 --- a/zio-http/shared/src/main/scala/zio/http/Header.scala +++ b/zio-http/shared/src/main/scala/zio/http/Header.scala @@ -37,6 +37,7 @@ import zio.schema.codec.DecodeError import zio.schema.codec.DecodeError.ReadError import zio.schema.validation.ValidationError +import zio.http.Header.HeaderTypeBase.Typed import zio.http.codec.{HttpCodecError, RichTextCodec} import zio.http.internal.{DateEncoding, ErrorConstructor, StringSchemaCodec} diff --git a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala index 8991bc2ba8..10bd90dfe2 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/HttpCodecError.scala @@ -23,6 +23,7 @@ import zio.{Cause, Chunk} import zio.schema.codec.DecodeError import zio.schema.validation.ValidationError +import zio.http.Header.HeaderType import zio.http.{Path, Status} sealed trait HttpCodecError extends Exception with NoStackTrace with Product with Serializable { diff --git a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala index 16148d4b11..63c315179d 100644 --- a/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala +++ b/zio-http/shared/src/main/scala/zio/http/codec/internal/EncoderDecoder.scala @@ -16,6 +16,7 @@ package zio.http.codec.internal +import scala.annotation.tailrec import scala.util.Try import zio._ From 113c4975eeaafebfa0c39e3f02f9ff90f6a45c00 Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 10 Jun 2025 15:06:28 +0200 Subject: [PATCH 12/12] Draft conversion to WWWAuthenticate --- .../scala/zio/http/endpoint/AuthType.scala | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/AuthType.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/AuthType.scala index aa5214c7a2..9b7aeea56d 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/AuthType.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/AuthType.scala @@ -16,6 +16,7 @@ sealed trait AuthType { self => AuthType { type ClientRequirement = ClientReq }, ] + def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] } object AuthType { @@ -27,28 +28,43 @@ object AuthType { case object None extends AuthType { type ClientRequirement = Unit override val codec: HeaderCodec[Unit] = HttpCodec.empty.asInstanceOf[HeaderCodec[Unit]] + + override def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] = + Option.empty } case object Basic extends AuthType { type ClientRequirement = Header.Authorization.Basic override val codec: HeaderCodec[Header.Authorization.Basic] = HeaderCodec.basicAuth + + override def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] = + Some(Header.WWWAuthenticate.Basic()) } case object Bearer extends AuthType { type ClientRequirement = Header.Authorization.Bearer override val codec: HeaderCodec[Header.Authorization.Bearer] = HeaderCodec.bearerAuth + + override def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] = + Some(Header.WWWAuthenticate.Bearer(???)) } case object Digest extends AuthType { type ClientRequirement = Header.Authorization.Digest override val codec: HeaderCodec[Header.Authorization.Digest] = HeaderCodec.digestAuth + + override def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] = + Some(Header.WWWAuthenticate.Digest(None)) } final case class Custom[ClientReq](override val codec: HttpCodec[HttpCodecType.RequestType, ClientReq]) extends AuthType { type ClientRequirement = ClientReq + + override def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] = + Some(Header.WWWAuthenticate.Unknown(???, ???, ???)) } final case class Or[ClientReq1, ClientReq2, ClientReq]( @@ -57,8 +73,10 @@ object AuthType { alternator: Alternator.WithOut[ClientReq1, ClientReq2, ClientReq], ) extends AuthType { type ClientRequirement = ClientReq - override val codec: HttpCodec[HttpCodecType.RequestType, ClientReq] = + override val codec: HttpCodec[HttpCodecType.RequestType, ClientReq] = auth1.codec.orElseEither(auth2.codec)(alternator) + override def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] = + auth1.asWWWAuthenticateHeader } final case class ScopedAuth[ClientReq]( @@ -68,6 +86,9 @@ object AuthType { type ClientRequirement = ClientReq override val codec: HttpCodec[HttpCodecType.RequestType, ClientReq] = authType.codec + override def asWWWAuthenticateHeader: Option[Header.WWWAuthenticate] = + authType.asWWWAuthenticateHeader + def scopes: List[String] = _scopes def scopes(newScopes: List[String]) = copy(_scopes = newScopes)