Skip to content

Conversation

mogol
Copy link
Contributor

@mogol mogol commented May 3, 2018

Fixes #79


Draft implementation for interceptors #79 to support analysis of metadata (e.g. authorization)

abstract class Interceptor {
  GrpcError handle(ServiceCall call);
}

Any feedback is appreciated.

@ghost ghost mentioned this pull request May 9, 2018
Copy link
Contributor

@sigurdm sigurdm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi German Saprykin
Thanks for the contribution!
I have a bunch of comments and questions but the basic approach is good I think.

await harness.fromServer.done;
});

test('Server returns error if interceptor blocks request', () async {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it ends up being implicit from the other tests, but perhaps add an explicit test of the interceptor not blocking the request?


GrpcError _applyInterceptors() {
for (final interceptor in _interceptors) {
final error = interceptor.handle(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to take into account if the interceptor throws and handle that gracefully somehow?

class ServerHandler extends ServiceCall {
final ServerTransportStream _stream;
final Service Function(String service) _serviceLookup;
final Iterable<Interceptor> _interceptors;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps store this as a List.
If you end up storing a custom iterator, it might have confusing behaviour for repeated iterations.

Timer _timeoutTimer;

ServerHandler(this._serviceLookup, this._stream);
ServerHandler(this._serviceLookup, this._stream, this._interceptors);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would break the public api. Not neccesarily a bad thing as we are pre 1.0.
Perhaps turn it into a named argument.
Anyway we need an update to the pubspec.yaml version string.

import 'call.dart';

abstract class Interceptor {
GrpcError handle(ServiceCall call);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a way to silently intercept without returning an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is use case?
What's better way to implement? It could be

  • GrpcError.custom, check error code and sink stream without sending the error.
  • Exception, on any exception silently sink stream

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the use-case - it just seemed more general.
I have not used the interceptors for other GRpc implementations so I'm a bit unsure how they are used...

One way would be to make a response class with an enum. Something like:

enum InterceptorResponseType { error, ok, block}
class InterceptorResponse {
  final InterceptorResponseType type;
  final GrpcError error;

  InterceptorResponse.ok() : type = InterceptorResponseType.ok;
  InterceptorResponse.error(this.error) : type = InterceptorResponseType.error;
  InterceptorResponce.block() : type =  InterceptorResponseType.block;
}


import 'call.dart';

abstract class Interceptor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// Create a server for the given [services].
Server(List<Service> services) {
Server(List<Service> services,
{Iterable<Interceptor> interceptors = const []})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a type annotation to the list literal:
const <Interceptor>[]

/// Listens for incoming RPCs, dispatching them to the right [Service] handler.
class Server {
final Map<String, Service> _services = {};
final Iterable<Interceptor> _interceptors;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here I think a List is preferable.


test('Server returns error if interceptor blocks request', () async {
GrpcError interceptorHandler(ServiceCall call) {
return new GrpcError.unauthenticated('Request is unauthenticated');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make it more realistic: branch on the method name here.

import 'call.dart';

abstract class Interceptor {
GrpcError handle(ServiceCall call);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to make this async, such that we can do general async handling in the interceptor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds interesting, looks like it will require a refactoring, old code is fully synchronous.
I tried to return Future and mark functions async. UT started to fail.
@sigurdm Don't you mind to do it in separate PR?

Should Interceptor return always Future<GrpcError> in this case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point that the current handling is synchronous. Yes - that should be pushed to another PR.
Yes in that case it would return Future

@thelinuxfoundation
Copy link

Thank you for your pull request. Before we can look at your contribution, we need to ensure all contributors are covered by a Contributor License Agreement.

After the following items are addressed, please respond with a new comment here, and the automated system will re-verify.

Regards,
CLA GitHub bot

@mogol
Copy link
Contributor Author

mogol commented Jun 29, 2018

Test failures are on dart:dev, it seems not because of changes in this pr

@mit-mit
Copy link
Collaborator

mit-mit commented Jul 2, 2018

Yes, looks like the test failures are also happening in master. Filed #91 to track.

Copy link
Contributor

@sigurdm sigurdm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes looks good!
Just a few typos


/// A gRPC Interceptor.
///
/// Interceptor is called before correspoding [ServiceMethod] invocation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typos:
Interceptor is called before its => An interceptor is called before the corresponding
correspoding => corresponding

If interceptor returns [GrpcError] => If the interceptor returns a [GrpcError]
If interceptor returns => If the interceptor returns
correspoding => the corresponding

import 'call.dart';

abstract class Interceptor {
GrpcError handle(ServiceCall call);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point that the current handling is synchronous. Yes - that should be pushed to another PR.
Yes in that case it would return Future

import 'call.dart';

abstract class Interceptor {
GrpcError handle(ServiceCall call);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the use-case - it just seemed more general.
I have not used the interceptors for other GRpc implementations so I'm a bit unsure how they are used...

One way would be to make a response class with an enum. Something like:

enum InterceptorResponseType { error, ok, block}
class InterceptorResponse {
  final InterceptorResponseType type;
  final GrpcError error;

  InterceptorResponse.ok() : type = InterceptorResponseType.ok;
  InterceptorResponse.error(this.error) : type = InterceptorResponseType.error;
  InterceptorResponce.block() : type =  InterceptorResponseType.block;
}

/// If the interceptor returns a [GrpcError], the error will be returned as a response and [ServiceMethod] wouldn't be called.
/// If the interceptor throws [Exception], [GrpcError.internal] with exception.toString() will be returned.
/// If the interceptor returns null, the corresponding [ServiceMethod] of [Service] will be called.
typedef Interceptor = GrpcError Function(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sigurdm I have checked go implementation for interceptors

https://github.com/grpc/grpc-go/blob/a3e99ebee0a498c3093e9741d09c579f0fc34a7f/server.go#L1020

From my understanding it has only two cases - no-error and error,
if there is an error, write a status and stop processing a request.
If there is no error, continue processing.

Imo it doesn't make sense to add this feature (I mean silent blocking) now.
Also I am not sure it conforms to grpc standard or not.

What do you think? Is it required?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No let's stick with error/no-error!

@mogol
Copy link
Contributor Author

mogol commented Jul 4, 2018

Is anything else required to be done in this PR?

@sigurdm
Copy link
Contributor

sigurdm commented Jul 4, 2018

No I don't think so. Thanks!
We need to fix the build issues. Then I'll merge this.

@mit-mit
Copy link
Collaborator

mit-mit commented Jul 9, 2018

Merging now that the build is passing. Thanks @mogol for the contribution!

@mit-mit mit-mit merged commit 847a362 into grpc:master Jul 9, 2018
@mogol mogol deleted the interceptor branch July 12, 2018 02:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants