1# Copyright 2017 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14require_relative 'interceptor_registry'
15
16# GRPC contains the General RPC module.
17module GRPC
18  ##
19  # Base class for interception in GRPC
20  #
21  class Interceptor
22    ##
23    # @param [Hash] options A hash of options that will be used
24    #   by the interceptor. This is an EXPERIMENTAL API.
25    #
26    def initialize(options = {})
27      @options = options || {}
28    end
29  end
30
31  ##
32  # ClientInterceptor allows for wrapping outbound gRPC client stub requests.
33  # This is an EXPERIMENTAL API.
34  #
35  class ClientInterceptor < Interceptor
36    ##
37    # Intercept a unary request response call
38    #
39    # @param [Object] request
40    # @param [GRPC::ActiveCall] call
41    # @param [String] method
42    # @param [Hash] metadata
43    #
44    def request_response(request: nil, call: nil, method: nil, metadata: nil)
45      GRPC.logger.debug "Intercepting request response method #{method}" \
46        " for request #{request} with call #{call} and metadata: #{metadata}"
47      yield
48    end
49
50    ##
51    # Intercept a client streaming call
52    #
53    # @param [Enumerable] requests
54    # @param [GRPC::ActiveCall] call
55    # @param [String] method
56    # @param [Hash] metadata
57    #
58    def client_streamer(requests: nil, call: nil, method: nil, metadata: nil)
59      GRPC.logger.debug "Intercepting client streamer method #{method}" \
60       " for requests #{requests} with call #{call} and metadata: #{metadata}"
61      yield
62    end
63
64    ##
65    # Intercept a server streaming call
66    #
67    # @param [Object] request
68    # @param [GRPC::ActiveCall] call
69    # @param [String] method
70    # @param [Hash] metadata
71    #
72    def server_streamer(request: nil, call: nil, method: nil, metadata: nil)
73      GRPC.logger.debug "Intercepting server streamer method #{method}" \
74        " for request #{request} with call #{call} and metadata: #{metadata}"
75      yield
76    end
77
78    ##
79    # Intercept a BiDi streaming call
80    #
81    # @param [Enumerable] requests
82    # @param [GRPC::ActiveCall] call
83    # @param [String] method
84    # @param [Hash] metadata
85    #
86    def bidi_streamer(requests: nil, call: nil, method: nil, metadata: nil)
87      GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
88        " for requests #{requests} with call #{call} and metadata: #{metadata}"
89      yield
90    end
91  end
92
93  ##
94  # ServerInterceptor allows for wrapping gRPC server execution handling.
95  # This is an EXPERIMENTAL API.
96  #
97  class ServerInterceptor < Interceptor
98    ##
99    # Intercept a unary request response call.
100    #
101    # @param [Object] request
102    # @param [GRPC::ActiveCall::SingleReqView] call
103    # @param [Method] method
104    #
105    def request_response(request: nil, call: nil, method: nil)
106      GRPC.logger.debug "Intercepting request response method #{method}" \
107        " for request #{request} with call #{call}"
108      yield
109    end
110
111    ##
112    # Intercept a client streaming call
113    #
114    # @param [GRPC::ActiveCall::MultiReqView] call
115    # @param [Method] method
116    #
117    def client_streamer(call: nil, method: nil)
118      GRPC.logger.debug "Intercepting client streamer method #{method}" \
119        " with call #{call}"
120      yield
121    end
122
123    ##
124    # Intercept a server streaming call
125    #
126    # @param [Object] request
127    # @param [GRPC::ActiveCall::SingleReqView] call
128    # @param [Method] method
129    #
130    def server_streamer(request: nil, call: nil, method: nil)
131      GRPC.logger.debug "Intercepting server streamer method #{method}" \
132        " for request #{request} with call #{call}"
133      yield
134    end
135
136    ##
137    # Intercept a BiDi streaming call
138    #
139    # @param [Enumerable<Object>] requests
140    # @param [GRPC::ActiveCall::MultiReqView] call
141    # @param [Method] method
142    #
143    def bidi_streamer(requests: nil, call: nil, method: nil)
144      GRPC.logger.debug "Intercepting bidi streamer method #{method}" \
145        " for requests #{requests} with call #{call}"
146      yield
147    end
148  end
149
150  ##
151  # Represents the context in which an interceptor runs. Used to provide an
152  # injectable mechanism for handling interception. This is an EXPERIMENTAL API.
153  #
154  class InterceptionContext
155    ##
156    # @param interceptors [Array<GRPC::Interceptor>]
157    #
158    def initialize(interceptors = [])
159      @interceptors = interceptors.dup
160    end
161
162    ##
163    # Intercept the call and fire out to interceptors in a FIFO execution.
164    # This is an EXPERIMENTAL API.
165    #
166    # @param [Symbol] type The request type
167    # @param [Hash] args The arguments for the call
168    #
169    def intercept!(type, args = {})
170      return yield if @interceptors.none?
171
172      i = @interceptors.pop
173      return yield unless i
174
175      i.send(type, **args) do
176        if @interceptors.any?
177          intercept!(type, args) do
178            yield
179          end
180        else
181          yield
182        end
183      end
184    end
185  end
186end
187