grpc / grpc-web

gRPC for Web Clients
https://grpc.io
Apache License 2.0
8.56k stars 762 forks source link

Access metadata in StreamInterceptor response #1200

Open alexdohm opened 2 years ago

alexdohm commented 2 years ago

I've looked through the existing tickets and am still lost on how to access the response headers in a streamInterceptor. I need to update a JWT token if an Authorization header is set in a stream response.

My code uses both unary and streaming calls, and unary calls are working successfully with promise based interceptor and promise-base client with reading and setting headers in both request and response. Based on #988 stream interceptors are only meant for a normal callback-based client and interceptor. Using #766 I implemented a stream interceptor and in my golang server side code, I tried setting the metadata header as a test and sending the stream :

header := metadata.Pairs("Authorization", "testing auth header")
stream.SendHeader(header)
err = stream.Send(row)

By inspecting the http response, I see that the header is set, however the 'metadata' event is never intercepted and 'data' returns my proto response, but I have no access to the header / metadata.

InterceptedStream.prototype.on = function(eventType, callback) {
    console.log("event type", eventType)
    if (eventType === 'data') {
        const newCallback = (response) => {
            console.log("stream interceptor response", response) //possible to access metadata here?
            // const auth = response.getMetadata().authorization //does not exist
            callback(response);
        };
        this.stream.on(eventType, newCallback);
    } else if (eventType === 'metadata') {
        const metaCallback = (response) => {
            console.log("metadata stream", response) //or I can access metadata here but this is never called
            callback(response);
        };
        this.stream.on(eventType, metaCallback);
    } else {
        this.stream.on(eventType, callback);
    }
    return this;
};
return new InterceptedStream(invoker(request));

I've noticed that I have access to a promise based client in my autogenerated files for the stream, but I'm not sure how that would be integrated with a stream interceptor, or if I need to use a unary interceptor with the stream in some way.

options are defined as such

let options = {
    'unaryInterceptors': [new UnaryInterceptor()],
    'streamInterceptors': [new StreamInterceptor()]
};
Vue.prototype.$interceptOptions = options

and I link my streaming call as such:

 this.machines = new MachineClient(grpcApiUrl(), null, this.$interceptOptions);

I'm using grpc-web v.1.2.1

sampajano commented 2 years ago

Thanks for the question!

I'm curious if in your testing, are you able to get the 'metadata' event callback without using the interceptor?


I'm not exactly sure how to generate a 'metadata' message on the server. But i've tested with a 'status' interceptor with metadata, and it seems to work for me.

So here, similar to the code below: https://github.com/grpc/grpc-web/blob/8c5502186445e35002697f4bd8d1b820abdbed5d/net/grpc/gateway/examples/echo/commonjs-example/client.js#L41-L47

I've added a new block to intercept the 'status' callback, where it prints out and replaces the metadata.

    } else if (eventType == 'status') {
      const newCallback = (status) => {
        console.log(['Old metadata: ', status.metadata]);
        callback({metadata: {'new_metadata': 'value'}});
      };
      this.stream.on('status', newCallback);
    }

.. and that seems to work..

And so far i'm not able to tell any reason why 'metadata' callback would behave differently than 'data' or 'status'..