http4s / http4s-grpc

https://http4s.github.io/http4s-grpc/
MIT License
40 stars 5 forks source link

Allow server implementation to access extra attributes #144

Open i10416 opened 3 weeks ago

i10416 commented 3 weeks ago

Current method signature: (t:T, ctx: Headers) lacks ability to get attributes from outside.

Imagine when you implement an auth interceptor that put a user id to request's attributes, how can you get the user id in service implementation?

val routes = TestService.toRoutes(impl)
val withAuth = HttpRoutes[IO] { req =>
    OptionT.liftF(decodeJWT(req.headers.get(ci"Authorization").head)).flatMap {
      claims => routes.run(req.withAttributes(userIdKey, claims.sub))
    }
}

I have two (similar) idea to implement this feature.

The first idea, which draws inspiration from Rust's tonic, is to define gRPC Request type to carry extra data and generated method signature becomes t: Request[T].

trait Request[T] {
  def message: T
  def headers: Headers // For accuracy, we should us e grpc MetadataMap type instead of http.Headers
  def attributes: Vault
}

// ...
  def unaryToUnary[F[_]: Temporal, A, B](
      decode: Decoder[A],
      encode: Encoder[B],
      serviceName: String,
      methodName: String,
  )( // Stuff we apply at invocation
-     f: (A, Headers) => F[B]
+.    f: (Request[A]) => F[B]
 ): HttpRoutes[F] = HttpRoutes.of[F] {

The second idea is to just add Vault type to f arguments.

// ...
  def unaryToUnary[F[_]: Temporal, A, B](
      decode: Decoder[A],
      encode: Encoder[B],
      serviceName: String,
      methodName: String,
  )( // Stuff we apply at invocation
-     f: (A, Headers) => F[B]
+.    f: (A, Headers, Vault) => F[B]
 ): HttpRoutes[F] = HttpRoutes.of[F] {
i10416 commented 3 weeks ago

I found another approach at fs2-grpc. https://github.com/typelevel/fs2-grpc/issues/668