grpc-go: How to tie data to the lifetime of the underlying transport?

42 views
Skip to first unread message

Benoit Sigoure

unread,
May 19, 2017, 6:23:08 PM5/19/17
to grpc-io
Hi there,

let’s say I have the following interface:

service S {
rpc Foo(FooRequest) returns (FooResponse);
}

In Foo(), I authenticate the client using a user/password passed as
metadata before executing the FooRequest. Authenticating the client
each time is expensive, so I want to remember somewhere that I already
authenticated this client (for this discussion let’s ignore security
considerations like caching policy etc). I can’t return a session ID
or something like that to the client, I just receive the user/pass
each time.

My thinking was to cache this information somewhere in the transport,
but there’s a small twist: I need to clean up something whenever the
transport goes down. I can’t tie this to the Stream because this is
not a streaming RPC so each call to Foo() has its own single-use
stream in the context. I can get access to the transport
(*transport.http2Server), but I can’t access its private shutdownChan
to wait for the transport to be closed.

So what’s the best way (if any) to tie something to the lifetime to
the transport?

--
Benoit "tsuna" Sigoure

Eric Anderson

unread,
May 22, 2017, 4:33:06 PM5/22/17
to Benoit Sigoure, grpc-io
On Fri, May 19, 2017 at 3:22 PM, Benoit Sigoure <tsun...@gmail.com> wrote:
In Foo(), I authenticate the client using a user/password passed as
metadata before executing the FooRequest.  Authenticating the client
each time is expensive, so I want to remember somewhere that I already
authenticated this client (for this discussion let’s ignore security
considerations like caching policy etc).

I'd suggest caching the authentication result and check the cache each request. The client is not aware when a new connection is used and binding the authentication to the connection breaks some proxying models. We purposefully do not support this.

(Now, if you needed some information to make the cache usages faster, that we could entertain. But each request must be authenticated separately except for transport-level authentication, like TLS client certificates.)

I can’t return a session ID
or something like that to the client, I just receive the user/pass
each time.

Well, you can, but gRPC won't help the client to pass that value in the future. It would generally be discouraged.

My thinking was to cache this information somewhere in the transport,
but there’s a small twist: I need to clean up something whenever the
transport goes down.  I can’t tie this to the Stream because this is
not a streaming RPC so each call to Foo() has its own single-use
stream in the context.  I can get access to the transport
(*transport.http2Server), but I can’t access its private shutdownChan
to wait for the transport to be closed.

So what’s the best way (if any) to tie something to the lifetime to
the transport?

What sort of thing do you need to clean up? Like removing an ID from a map?

Benoit Sigoure

unread,
May 22, 2017, 5:58:41 PM5/22/17
to Eric Anderson, grpc-io
I ended up finding a solution BTW. I’m (ab)using the stats.Handler
interface. TagConn is invoked each time a connection is created, so I
can stick my session cache in the context (and the cool thing is that
each Stream’s context is derived form the context returned by
TagConn). Then in HandleConn, when the event is ConnEnd, I can handle
the connection going down.

On Mon, May 22, 2017 at 1:32 PM, 'Eric Anderson' via grpc.io
<grp...@googlegroups.com> wrote:
> On Fri, May 19, 2017 at 3:22 PM, Benoit Sigoure <tsun...@gmail.com> wrote:
>>
>> In Foo(), I authenticate the client using a user/password passed as
>> metadata before executing the FooRequest. Authenticating the client
>> each time is expensive, so I want to remember somewhere that I already
>> authenticated this client (for this discussion let’s ignore security
>> considerations like caching policy etc).
>
>
> I'd suggest caching the authentication result and check the cache each
> request. The client is not aware when a new connection is used and binding
> the authentication to the connection breaks some proxying models.

It’s fine that the client is unaware or that proxy would mess things
up in between. The client is always sending their credentials with
each all as metadata, so in the worst case I have to re-authenticate
the client from scratch if there was a transport-level problem (be it
a connection issue or a proxy in between not keeping the connection
open).

>> I can’t return a session ID
>> or something like that to the client, I just receive the user/pass
>> each time.
>
>
> Well, you can, but gRPC won't help the client to pass that value in the
> future. It would generally be discouraged.

What I meant by “I can’t” is that the client always just sends its
credentials, period. I can’t return a cookie-like value to the client
and hope it would send it back with subsequent requests, because I’m
not writing the code of the client and I can’t assume that the client
will send anything other than just their credentials.

>> My thinking was to cache this information somewhere in the transport,
>> but there’s a small twist: I need to clean up something whenever the
>> transport goes down. I can’t tie this to the Stream because this is
>> not a streaming RPC so each call to Foo() has its own single-use
>> stream in the context. I can get access to the transport
>> (*transport.http2Server), but I can’t access its private shutdownChan
>> to wait for the transport to be closed.
>>
>> So what’s the best way (if any) to tie something to the lifetime to
>> the transport?
>
>
> What sort of thing do you need to clean up? Like removing an ID from a map?

Something like that… Does it matter what I need to do? I need to
releases some resources I allocate during authentication, the details
don’t really matter IMO, just some cleanup that needs to happen.

--
Benoit "tsuna" Sigoure
Reply all
Reply to author
Forward
0 new messages