-
-
Couldn't load subscription status.
- Fork 466
Redesigned model #1916
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redesigned model #1916
Conversation
|
|
||
| // Http app that is accessible only via a jwt token | ||
| def user: UHttpApp = Http.collect[Request] { case Method.GET -> !! / "user" / name / "greet" => | ||
| def user: HttpRoute[Any, Nothing] = Http.collect[Request] { case Method.GET -> !! / "user" / name / "greet" => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can keep the old type aliases to avoid the updating code??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these old aliases are conflicting with the new ones:
type HttpRoute[-R, +Err] = Http[R, Err, Request, Response]
type App[-R] = HttpRoute[R, Response]Probably the HttpRoute name is not very good either anymore (I defined it when it was Route[R, Err, Req, Resp]).
So we should review all the names together. I don't like much these old aliases but maybe it is more important to keep them for people who already use them.
| private def plainTextApp(response: Response) = | ||
| Http.fromHExit(HExit.succeed(response)).whenPathEq(plaintextPath) | ||
| private def plainTextApp(response: Response): HttpRoute[Any, Nothing] = | ||
| Handler.response(response).toRoute.whenPathEq(plaintextPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toRoute, some old temrinology still exists??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
although I think .toRoute was a nicer name than .toHttp (and Route vs Handler is a better distinction than Http vs Handler which does not make much sense to me)
|
|
||
| import zio.http.HExit.Effect | ||
| import zio.{Cause, Trace, ZIO} | ||
| import zio.{Cause, Tag, Trace, ZEnvironment, ZIO, ZLayer} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we going to be able to replace this in favor of zio.Exit with appropriate optimizations applied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened a ticket about it few days ago. Should we do it in this PR now that 2.0.6 is out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I think we would be able to simply replace it with Exit now)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed it here, with some workaround to override flatMap/map/mapBoth (also opened zio/zio#7714 but it creates stack safety issues in streams)
| type RequestHandlerMiddleware[-R, +Err] = HandlerMiddleware[R, Err, Request, Response, Request, Response] | ||
| type AppHandlerMiddleware[-R] = RequestHandlerMiddleware[R, Response] | ||
|
|
||
| type HttpRoute[-R, +Err] = Http[R, Err, Request, Response] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vigoo Let's not use any Route terminology, and let's minimize name changes (so keep HttpApp instead of App, etc.).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Old names for reference:
type HttpApp[-R, +E] = Http[R, E, Request, Response]
type UHttpApp = HttpApp[Any, Nothing]
type RHttpApp[-R] = HttpApp[R, Throwable]
type EHttpApp = HttpApp[Any, Throwable]
type UHttp[-A, +B] = Http[Any, Nothing, A, B]
type ResponseZIO[-R, +E] = ZIO[R, E, Response]
type UMiddleware[+AIn, -BIn, -AOut, +BOut] = Middleware[Any, Nothing, AIn, BIn, AOut, BOut]There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so the old HttpApp is what currently is HttpRoute. If that's HttpApp we still need to call somehow the "final" thing, which fixes the Err to Response - that's what I called App[R]. Do you have a better idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we just don't need type aliases for that and use HttpApp[R, Response] in the code.
|
One impact of this change is that previously interrupting a ZIO handing a request would close the connection, not returning a response. I was using this behavior in https://github.com/jamesward/easyracer |
I did not intentionally change this, but not sure if it was by design. @jdegoes what do you think? |
|
@vigoo I think interruption maps most directly to immediate close. Can we do that in a followup? |
|
I've filed a bug to track: #2022 |
|
@jamesward thanks! |
Redesigned core model
Https with different ways to authenticate #1627This is my second attempt to fix the core problem described in #1871 (the previous was #1882).
The primary changes
Http[R, E, A, B]type intoHandlerandRouteHExitcan no longer represent "unhandled input"Responseas its error channelDetails
Handler and Route
Handler
The old
Http[R, E, A, B]type represented something likeA => ZIO[R, Option[E], B]in which it combined transformation from input to output with possibly not handling the given input ("routing") and defined powerful combinators to express complex routes and transformations with it.In the new model we define:
as a possibly effectful transformation from
IntoOutwith no possibility to not handle an input (other than failing). SoHandleris basically aIn => ZIO[R, Err, Out]function.(Note that in the real implementation I'm still using
HExiton both theHandlerandRouterlevel to make it possible to avoid the ZIO runtime in some cases, but that is just an optimization.)All the constructors and operators from
Httpthat are not are depending on partial results are implemented onHandler(so nocollect,orElse,++, etc).Route
The remaining features of the old
Httptype are moved into the typeRoute:Routecorresponds to something likeIn => ZIO[R, Err, Option[Handler[R, Err, In, Out]]]: it can either evaluate to aHandler, or state that it is not handling the request, or fail, and it can do this in an effectful way if needed. Route has the "usual" zio-http constructors likecollectand they can be combined with++.A
Handlercan always be converted to aRoute(.toRoute). ARoutecan be converted to aHandlerby providing a default route (.toHandler(default)).The zio-http server itself requires
App[R]which isRoute[R, Response, Request, Response]. AHttpRoute[R, Err]can be converted to this using the default error reporting withroute.withDefaultErrorResponse.Implementation notes
HandlerandRouteis now implemented with direct executable encoding unlike the previousHttptype. It was easier to experiment this wayHExitcan no longer representEmpty(just like in my previous PR). I kept this limitation from the previous attempt to make sureHandlercan never represent unhandled state. AsRoutestill needs to represent it and I did not want to wrap everything in option, I'm representing it withHExit.succeed(null)and all methods directly returning suchHExits are protected byzio.Unsafe.Middlewares
The old "middleware" concept which was a
Http => Httpfunction is slightly modified in this model.First of all we define
HandlerAspectandRouteAspectas transformers of the two types:These are equivalent to
ZIOAspectand they have the same expression power:applyd (@@) to a handler or route>>>(andThen)No other generic combinators are defined on this level to keep it simple.
On top of these we reimplement all the "non-generic" middlewares as either handler or router aspects. Only those need to be router which need access to the routing itself. For example:
metrics, to be able to count unhandled casesCORSbecause it adds new routesdropTrailingSlashbecause it "doubles" the allowed routesallowandallowZIObecause they dynamically change the routingEverything else (of the predefined middlewares in zio-http) turns out to be expressible as handler aspects.
To make it more approachable, most are defined using the
HttpRouteMiddlewareandRequestHandlerMiddlewaretype aliases, and most of them are implemented directly as functions on handlers, instead of composing very generic middlewares.All these "middlewares", both handler and route aspect based, are collected to the
Middlewareobject as before to make it more backward compatible.Applying middlewares
The only thing remaining is to define how these middlewares are applied in a complex application, and this is where the new model must solve the reported linked issues.
I think by having this well defined, type level separation between routes and handlers the middleware application rule can be simple enough to not be surprising for users (but this is the weakest point of the whole change, of course):
With the actually used type aliases:
RequestHandlerMiddleware[R, E] to aRequestHandler[R, E]gives back aRequestHandler[R, E]`HttpRouteMiddleware[R, E] to aHttpRoute[R, E]gives back a newHttpRoute[R, E]`RequestHandlerMiddleware[R, E] to aHttpRoute[R, E]gives back a newHttpRoute[R, E]` with no change in the routing logic, and in case it routes to a handler, the given handler gets modified with the middleware.More generally:
Routerequires that theIntype is not changed by the aspectResults
This model solves the linked issues (added unit tests for them), and it does not really reduce the power of zio-http - including effectful routing etc. that were mentioned in the linked issue.
Freedom of combining middlewares via various combinators has been limited but I did it intentionally.
We can also rename either
HandlerorRouteback toHttpbut I wanted to have fresh names to make it easier to discuss the new model.Some boilerplate (
.toRouteconversions etc) could be reduced by adding more operators or implicit conversions but I'm not sure about it - and for now I wanted to keep everything as clear as possible for discussion.All existing tests are passing.
Remaining: