improbable-eng / grpc-web

gRPC Web implementation for Golang and TypeScript
Apache License 2.0
4.39k stars 436 forks source link

Golang Grpc Web Server UnaryInterceptor is not triggered by grpc-web Client #1127

Closed nolanrsherman closed 2 years ago

nolanrsherman commented 2 years ago

Description

I have a golang based server that is serving a grpc server via http and the standard grpc server on tcp. The issue I am having is that I would like to do some logging using grpc.UnaryInterceptor, and that works fine for standard grpc clients. But, for some reason grpc-web clients do not trigger the interceptor function.

Expectation

Grpc Server Interceptors will execute for requests created by both types of clients (grpc, grpc-web).

What actually happens

Grpc Server Interceptors only execute for requests by grpc (via tcp) clients.

Code Examples

package main

func main()  {
    log.Info("application starting...")
    // Background context
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    //logger
    zapLogger, err := zap.NewDevelopment()
    if err != nil {
        log.Fatalln(err)
    }
    // Register http gRPC server handler
    mux := runtime.NewServeMux()

    // Add new grpc server
    rpcServer := grpc.NewServer(
        grpc.Creds(insecure.NewCredentials()),
        grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
            grpc_zap.StreamServerInterceptor(zapLogger),
        )),
        grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
            grpc_zap.UnaryServerInterceptor(zapLogger),
        )),
    )

    // A function that just register multiple services to the rpcServer and mux for grpc-web
    registerServices(ctx, rpcServer, mux)

    // wrap server for grpc-web support
    wrappedGrpc := grpcweb.WrapServer(
        rpcServer,
        grpcweb.WithOriginFunc(func(origin string) bool { return true }),
    )
    handleGrpcWeb := http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
        if wrappedGrpc.IsAcceptableGrpcCorsRequest(req) || wrappedGrpc.IsGrpcWebRequest(req) {
            wrappedGrpc.ServeHTTP(resp, req)
            return
        }
        // Fall back to other servers.
        mux.ServeHTTP(resp, req)

    })

    // Run Http and gRPC servers on seperate routines
    errorChannel := make(chan error)
    go func() {
        log.Info("http server listening on port ", 9090)
        // Start HTTP server (and proxy calls to gRPC server endpoint)
        errorChannel <- http.ListenAndServe(":"+9090, handleGrpcWeb)
    }()
    go func() {
        lis, err := net.Listen("tcp", ":"+8433)
        if err != nil {
            log.Fatalf("failed to listen: %v", err)
        }
        log.Info("gRPC server listening on port ", 8433)
        errorChannel <- rpcServer.Serve(lis)
    }()
    // Return if either of them return an error
    serveError := <-errorChannel
    log.Info("a fatal error occured while serving. Attempting a graceful stop...", 8433)
    rpcServer.GracefulStop() //graceful stop for gRPC
    // no graceful stop for http yet TODO
    return serveError
}

func registerServices(ctx context.Context, rpcServer *grpc.Server, mux *runtime.ServeMux, mDatabase *mongo.Database) {

    someService := &somethingService{} //implements a grpc server
    someService.RegisterSomethingServiceServer(rpcServer, someService)
    err := someService.RegisterSomethingServiceHandlerServer(ctx, mux, someService)
    if err != nil {
        panic(err)
    }

}
nolanrsherman commented 2 years ago

After debugging into this, I found that this is actually just when grpc-gateway is used, not grpcweb.