/* * * 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 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 /** * 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 - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; - (nullable instancetype)initWithFactories:(nullable NSArray> *)factories previousInterceptor:(nullable id)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 - (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