blob: a063aa043a97e99b043b6614bc6ac58044e67203 [file] [log] [blame]
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/**
* \file GRPCInterceptor.h
* API for interceptors implementation. This feature is currently EXPERIMENTAL
and is subject to
* breaking changes without prior notice.
*
* The interceptors in the gRPC system forms a chain. When a call is made by the
user, each
* interceptor on the chain has chances to react to events of the call and make
necessary
* modifications to the call's parameters, data, metadata, or flow.
*
* \verbatim
-----------
| GRPCCall2 |
-----------
|
|
--------------------------
| GRPCInterceptorManager 1 |
--------------------------
| GRPCInterceptor 1 |
--------------------------
|
...
|
--------------------------
| GRPCInterceptorManager N |
--------------------------
| GRPCInterceptor N |
--------------------------
|
|
------------------
| GRPCCallInternal |
------------------
\endverbatim
*
* The chain of interceptors is initialized when the corresponding GRPCCall2
object or proto call
* object (GRPCUnaryProtoCall and GRPCStreamingProtoCall) is initialized. The
initialization of the
* chain is controlled by the property interceptorFactories in the callOptions
parameter of the
* corresponding call object. Property interceptorFactories is an array of
* id<GRPCInterceptorFactory> objects provided by the user. When a call object
is initialized, each
* interceptor factory generates an interceptor object for the call. gRPC
internally links the
* interceptors with each other and with the actual call object. The order of
the interceptors in
* the chain is exactly the same as the order of factory objects in
interceptorFactories property.
* All requests (start, write, finish, cancel, receive next) initiated by the
user will be processed
* in the order of interceptors, and all responses (initial metadata, data,
trailing metadata, write
* data done) are processed in the reverse order.
*
* Each interceptor in the interceptor chain should behave as a user of the next
interceptor, and at
* the same time behave as a call to the previous interceptor. Therefore
interceptor implementations
* must follow the state transition of gRPC calls and must also forward events
that are consistent
* with the current state of the next/previous interceptor. They should also
make sure that the
* events they forwarded to the next and previous interceptors will, in the end,
make the neighbour
* interceptor terminate correctly and reaches "finished" state. The diagram
below shows the state
* transitions. Any event not appearing on the diagram means the event is not
permitted for that
* particular state.
*
* \verbatim
writeData
receiveNextMessages
didReceiveInitialMetadata
didReceiveData
didWriteData receiveNextmessages
writeData ----- ----- ----
didReceiveInitialMetadata receiveNextMessages | | | | | |
didReceiveData | V | V | V didWriteData
------------- start --------- finish ------------
| initialized | -----> | started | --------> | half-close |
------------- --------- ------------
| | |
| | didClose | didClose
|cancel | cancel | cancel
| V |
| ---------- |
--------------> | finished | <--------------
----------
| ^ writeData
| | finish
------ cancel
receiveNextMessages
\endverbatim
*
* An interceptor must forward responses to its previous interceptor in the
order of initial
* metadata, message(s), and trailing metadata. Forwarding responses out of this
order (e.g.
* forwarding a message before initial metadata) is not allowed.
*
* Events of requests and responses are dispatched to interceptor objects using
the interceptor's
* dispatch queue. The dispatch queue should be serial queue to make sure the
events are processed
* in order. Interceptor implementations must derive from GRPCInterceptor class.
The class makes
* some basic implementation of all methods responding to an event of a call. If
an interceptor does
* not care about a particular event, it can use the basic implementation of the
GRPCInterceptor
* class, which simply forward the event to the next or previous interceptor in
the chain.
*
* The interceptor object should be unique for each call since the call context
is not passed to the
* interceptor object in a call event. However, the interceptors can be
implemented to share states
* by receiving state sharing object from the factory upon construction.
*/
#import "GRPCCall.h"
#import "GRPCDispatchable.h"
NS_ASSUME_NONNULL_BEGIN
@class GRPCInterceptorManager;
@class GRPCInterceptor;
@class GRPCRequestOptions;
@class GRPCCallOptions;
@protocol GRPCResponseHandler;
/**
* The GRPCInterceptorInterface defines the request events that can occur to an
* interceptor.
*/
@protocol GRPCInterceptorInterface <NSObject, GRPCDispatchable>
/**
* To start the call. This method will only be called once for each instance.
*/
- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions
callOptions:(GRPCCallOptions *)callOptions;
/**
* To write data to the call.
*/
- (void)writeData:(id)data;
/**
* To finish the stream of requests.
*/
- (void)finish;
/**
* To cancel the call.
*/
- (void)cancel;
/**
* To indicate the call that the previous interceptor is ready to receive more
* messages.
*/
- (void)receiveNextMessages:(NSUInteger)numberOfMessages;
@end
/**
* An interceptor factory object is used to create interceptor object for the
* call at the call start time.
*/
@protocol GRPCInterceptorFactory
/**
* Create an interceptor object. gRPC uses the returned object as the
* interceptor for the current call
*/
- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager;
@end
/**
* GRPCInterceptorManager is a helper class to forward messages between the
* interceptors. The interceptor manager object retains reference to the next
* and previous interceptor object in the interceptor chain, and forward
* corresponding events to them.
*
* All methods except the initializer of the class can only be called on the
* manager's dispatch queue. Since the manager's dispatch queue targets
* corresponding interceptor's dispatch queue, it is also safe to call the
* manager's methods in the corresponding interceptor instance's methods that
* implement GRPCInterceptorInterface.
*
* When an interceptor is shutting down, it must invoke -shutDown method of its
* corresponding manager so that references to other interceptors can be
* released and proper clean-up is made.
*/
@interface GRPCInterceptorManager : NSObject <GRPCInterceptorInterface, GRPCResponseHandler>
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (nullable instancetype)initWithFactories:(nullable NSArray<id<GRPCInterceptorFactory>> *)factories
previousInterceptor:(nullable id<GRPCResponseHandler>)previousInterceptor
transportID:(GRPCTransportID)transportID;
/**
* Notify the manager that the interceptor has shut down and the manager should
* release references to other interceptors and stop forwarding
* requests/responses.
*/
- (void)shutDown;
// Methods to forward GRPCInterceptorInterface calls to the next interceptor
/** Notify the next interceptor in the chain to start the call and pass
* arguments */
- (void)startNextInterceptorWithRequest:(GRPCRequestOptions *)requestOptions
callOptions:(GRPCCallOptions *)callOptions;
/** Pass a message to be sent to the next interceptor in the chain */
- (void)writeNextInterceptorWithData:(id)data;
/** Notify the next interceptor in the chain to finish the call */
- (void)finishNextInterceptor;
/** Notify the next interceptor in the chain to cancel the call */
- (void)cancelNextInterceptor;
/** Notify the next interceptor in the chain to receive more messages */
- (void)receiveNextInterceptorMessages:(NSUInteger)numberOfMessages;
// Methods to forward GRPCResponseHandler callbacks to the previous object
/** Forward initial metadata to the previous interceptor in the chain */
- (void)forwardPreviousInterceptorWithInitialMetadata:(nullable NSDictionary *)initialMetadata;
/** Forward a received message to the previous interceptor in the chain */
- (void)forwardPreviousInterceptorWithData:(nullable id)data;
/** Forward call close and trailing metadata to the previous interceptor in the
* chain */
- (void)forwardPreviousInterceptorCloseWithTrailingMetadata:
(nullable NSDictionary *)trailingMetadata
error:(nullable NSError *)error;
/** Forward write completion to the previous interceptor in the chain */
- (void)forwardPreviousInterceptorDidWriteData;
@end
/**
* Base class for a gRPC interceptor. The implementation of the base class
* provides default behavior of an interceptor, which is simply forward a
* request/callback to the next/previous interceptor in the chain. The base
* class implementation uses the same dispatch queue for both requests and
* callbacks.
*
* An interceptor implementation should inherit from this base class and
* initialize the base class with [super
* initWithInterceptorManager:dispatchQueue:] for the default implementation to
* function properly.
*/
@interface GRPCInterceptor : NSObject <GRPCInterceptorInterface, GRPCResponseHandler>
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
/**
* Initialize the interceptor with the next interceptor in the chain, and
* provide the dispatch queue that this interceptor's methods are dispatched
* onto.
*/
- (nullable instancetype)initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
dispatchQueue:(dispatch_queue_t)dispatchQueue;
// Default implementation of GRPCInterceptorInterface
- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions
callOptions:(GRPCCallOptions *)callOptions;
- (void)writeData:(id)data;
- (void)finish;
- (void)cancel;
- (void)receiveNextMessages:(NSUInteger)numberOfMessages;
// Default implementation of GRPCResponeHandler
- (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata;
- (void)didReceiveData:(id)data;
- (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata
error:(nullable NSError *)error;
- (void)didWriteData;
@end
NS_ASSUME_NONNULL_END