blob: 3b62c1b3ec03fe07fe97da95c99a4464755e5bd9 [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.
*
*/
/**
* 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.
*
*
* -----------
* | GRPCCall2 |
* -----------
* |
* |
* --------------------------
* | GRPCInterceptorManager 1 |
* --------------------------
* | GRPCInterceptor 1 |
* --------------------------
* |
* ...
* |
* --------------------------
* | GRPCInterceptorManager N |
* --------------------------
* | GRPCInterceptor N |
* --------------------------
* |
* |
* ------------------
* | GRPCCallInternal |
* ------------------
*
* 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.
*
* 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
*
* 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"
NS_ASSUME_NONNULL_BEGIN
@class GRPCInterceptorManager;
@class GRPCInterceptor;
/**
* The GRPCInterceptorInterface defines the request events that can occur to an interceptr.
*/
@protocol GRPCInterceptorInterface<NSObject>
/**
* The queue on which all methods of this interceptor should be dispatched on. The queue must be a
* serial queue.
*/
@property(readonly) dispatch_queue_t requestDispatchQueue;
/**
* 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 should be 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
/**
* The interceptor manager object retains reference to the next and previous interceptor object in
* the interceptor chain, and forward corresponding events to them. When a call terminates, it must
* invoke shutDown method of its corresponding manager so that references to other interceptors can
* be released.
*/
@interface GRPCInterceptorManager : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype) new NS_UNAVAILABLE;
- (nullable instancetype)initWithNextInterceptor:(id<GRPCInterceptorInterface>)nextInterceptor
NS_DESIGNATED_INITIALIZER;
/** Set the previous interceptor in the chain. Can only be set once. */
- (void)setPreviousInterceptor:(id<GRPCResponseHandler>)previousInterceptor;
/** Indicate shutdown of the interceptor; release the reference to other interceptors */
- (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
requestDispatchQueue:(dispatch_queue_t)requestDispatchQueue
responseDispatchQueue:(dispatch_queue_t)responseDispatchQueue
NS_DESIGNATED_INITIALIZER;
// 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