micronaut-projects / micronaut-grpc

Integration between Micronaut and GRPC
Apache License 2.0
69 stars 39 forks source link

Support `ServerInterceptors` #587

Closed jetaggart closed 1 year ago

jetaggart commented 2 years ago

Feature description

I'd like to have interceptors on a per service basis, for example if I'm using raw grpc-kotlin I can configure my server like this:

val server: Server = ServerBuilder.forPort(port)
        .addService(
            ServerInterceptors
                .intercept(RouteGuideService(features), TestInterceptor())
        )
        .build()

Is this possible? If not, how could it be added?

I attempted this:


@Factory
class GreetingFactory(private val thing: GreeterService) {
    val USER_DETAILS: Context.Key<Any> = Context.key("user_details")

    @Bean
    @Singleton
    fun greeterEndpoint(): BindableService {
        return ServerInterceptors.intercept(GreetingEndpoint(), TestInterceptor())
    }

    inner class GreetingEndpoint : GatewayServiceGrpcKt.GatewayServiceCoroutineImplBase() {
        override suspend fun sayHello(request: GatewayRequest): GatewayReply {
            return GatewayReply.newBuilder().setMessage(USER_DETAILS.get()).build()
        }
    }

    inner class TestInterceptor : ServerInterceptor {

        override fun <ReqT : Any?, RespT : Any?> interceptCall(
            call: ServerCall<ReqT, RespT>?,
            headers: Metadata?,
            next: ServerCallHandler<ReqT, RespT>?
        ): ServerCall.Listener<ReqT> {
            val context = Context.current()
                .withValue(USER_DETAILS, "user info")

            return Contexts.interceptCall(context, call, headers, next)
        }

    }
}

But get this compiler exception:

Type mismatch: inferred type is ServerServiceDefinition! but BindableService was expected

It appears the GrpcServiceBuilder class only expects BindableService and not ServerServiceDefinition which is the return type of .intercept. Should the builder look for injected ServerServiceDefinition as well and treat those as services?

https://grpc.github.io/grpc-java/javadoc/io/grpc/ServerBuilder.html

jetaggart commented 2 years ago

After asking this I realized I could just convert it to a BindableService:

    @Bean
    @Singleton
    fun greeterEndpoint(): BindableService {
        return BindableService {
            ServerInterceptors.intercept(GreetingEndpoint(), TestInterceptor())
        }
    }

I still wonder if there a more ergonomic way to do this?

graemerocher commented 2 years ago

If you have improvement ideas contributions are welcome

jetaggart commented 2 years ago

I'm not sure I'm experienced enough to contribute code but looking at the source, could you just add ServerServiceDefinition to the list of injected params here:

https://github.com/micronaut-projects/micronaut-grpc/blob/8b5e877f4377ea82482ba545e315bd2877febe4b/grpc-server-runtime/src/main/java/io/micronaut/grpc/server/GrpcServerBuilder.java#L80-L83

Then iterate and add the services here:

https://github.com/micronaut-projects/micronaut-grpc/blob/8b5e877f4377ea82482ba545e315bd2877febe4b/grpc-server-runtime/src/main/java/io/micronaut/grpc/server/GrpcServerBuilder.java#L92-L96

?