blob: 166990f8c6c2b9776fe4b5f96ece794835c4877d [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