xref: /aosp_15_r20/external/grpc-grpc/src/ruby/bin/math_server.rb (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1*cc02d7e2SAndroid Build Coastguard Worker#!/usr/bin/env ruby
2*cc02d7e2SAndroid Build Coastguard Worker
3*cc02d7e2SAndroid Build Coastguard Worker# Copyright 2015 gRPC authors.
4*cc02d7e2SAndroid Build Coastguard Worker#
5*cc02d7e2SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*cc02d7e2SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*cc02d7e2SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*cc02d7e2SAndroid Build Coastguard Worker#
9*cc02d7e2SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*cc02d7e2SAndroid Build Coastguard Worker#
11*cc02d7e2SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*cc02d7e2SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*cc02d7e2SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*cc02d7e2SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*cc02d7e2SAndroid Build Coastguard Worker# limitations under the License.
16*cc02d7e2SAndroid Build Coastguard Worker
17*cc02d7e2SAndroid Build Coastguard Worker# Sample gRPC Ruby server that implements the Math::Calc service and helps
18*cc02d7e2SAndroid Build Coastguard Worker# validate GRPC::RpcServer as GRPC implementation using proto2 serialization.
19*cc02d7e2SAndroid Build Coastguard Worker#
20*cc02d7e2SAndroid Build Coastguard Worker# Usage: $ path/to/math_server.rb
21*cc02d7e2SAndroid Build Coastguard Worker
22*cc02d7e2SAndroid Build Coastguard Workerthis_dir = File.expand_path(File.dirname(__FILE__))
23*cc02d7e2SAndroid Build Coastguard Workerlib_dir = File.join(File.dirname(this_dir), 'lib')
24*cc02d7e2SAndroid Build Coastguard Worker$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
25*cc02d7e2SAndroid Build Coastguard Worker$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
26*cc02d7e2SAndroid Build Coastguard Worker
27*cc02d7e2SAndroid Build Coastguard Workerrequire 'forwardable'
28*cc02d7e2SAndroid Build Coastguard Workerrequire 'grpc'
29*cc02d7e2SAndroid Build Coastguard Workerrequire 'logger'
30*cc02d7e2SAndroid Build Coastguard Workerrequire 'math_services_pb'
31*cc02d7e2SAndroid Build Coastguard Workerrequire 'optparse'
32*cc02d7e2SAndroid Build Coastguard Worker
33*cc02d7e2SAndroid Build Coastguard Worker# RubyLogger defines a logger for gRPC based on the standard ruby logger.
34*cc02d7e2SAndroid Build Coastguard Workermodule RubyLogger
35*cc02d7e2SAndroid Build Coastguard Worker  def logger
36*cc02d7e2SAndroid Build Coastguard Worker    LOGGER
37*cc02d7e2SAndroid Build Coastguard Worker  end
38*cc02d7e2SAndroid Build Coastguard Worker
39*cc02d7e2SAndroid Build Coastguard Worker  LOGGER = Logger.new(STDOUT)
40*cc02d7e2SAndroid Build Coastguard Workerend
41*cc02d7e2SAndroid Build Coastguard Worker
42*cc02d7e2SAndroid Build Coastguard Worker# GRPC is the general RPC module
43*cc02d7e2SAndroid Build Coastguard Workermodule GRPC
44*cc02d7e2SAndroid Build Coastguard Worker  # Inject the noop #logger if no module-level logger method has been injected.
45*cc02d7e2SAndroid Build Coastguard Worker  extend RubyLogger
46*cc02d7e2SAndroid Build Coastguard Workerend
47*cc02d7e2SAndroid Build Coastguard Worker
48*cc02d7e2SAndroid Build Coastguard Worker# Holds state for a fibonacci series
49*cc02d7e2SAndroid Build Coastguard Workerclass Fibber
50*cc02d7e2SAndroid Build Coastguard Worker  def initialize(limit)
51*cc02d7e2SAndroid Build Coastguard Worker    fail "bad limit: got #{limit}, want limit > 0" if limit < 1
52*cc02d7e2SAndroid Build Coastguard Worker    @limit = limit
53*cc02d7e2SAndroid Build Coastguard Worker  end
54*cc02d7e2SAndroid Build Coastguard Worker
55*cc02d7e2SAndroid Build Coastguard Worker  def generator
56*cc02d7e2SAndroid Build Coastguard Worker    return enum_for(:generator) unless block_given?
57*cc02d7e2SAndroid Build Coastguard Worker    idx, current, previous = 0, 1, 1
58*cc02d7e2SAndroid Build Coastguard Worker    until idx == @limit
59*cc02d7e2SAndroid Build Coastguard Worker      if idx.zero? || idx == 1
60*cc02d7e2SAndroid Build Coastguard Worker        yield Math::Num.new(num: 1)
61*cc02d7e2SAndroid Build Coastguard Worker        idx += 1
62*cc02d7e2SAndroid Build Coastguard Worker        next
63*cc02d7e2SAndroid Build Coastguard Worker      end
64*cc02d7e2SAndroid Build Coastguard Worker      tmp = current
65*cc02d7e2SAndroid Build Coastguard Worker      current = previous + current
66*cc02d7e2SAndroid Build Coastguard Worker      previous = tmp
67*cc02d7e2SAndroid Build Coastguard Worker      yield Math::Num.new(num: current)
68*cc02d7e2SAndroid Build Coastguard Worker      idx += 1
69*cc02d7e2SAndroid Build Coastguard Worker    end
70*cc02d7e2SAndroid Build Coastguard Worker  end
71*cc02d7e2SAndroid Build Coastguard Workerend
72*cc02d7e2SAndroid Build Coastguard Worker
73*cc02d7e2SAndroid Build Coastguard Worker# A EnumeratorQueue wraps a Queue to yield the items added to it.
74*cc02d7e2SAndroid Build Coastguard Workerclass EnumeratorQueue
75*cc02d7e2SAndroid Build Coastguard Worker  extend Forwardable
76*cc02d7e2SAndroid Build Coastguard Worker  def_delegators :@q, :push
77*cc02d7e2SAndroid Build Coastguard Worker
78*cc02d7e2SAndroid Build Coastguard Worker  def initialize(sentinel)
79*cc02d7e2SAndroid Build Coastguard Worker    @q = Queue.new
80*cc02d7e2SAndroid Build Coastguard Worker    @sentinel = sentinel
81*cc02d7e2SAndroid Build Coastguard Worker  end
82*cc02d7e2SAndroid Build Coastguard Worker
83*cc02d7e2SAndroid Build Coastguard Worker  def each_item
84*cc02d7e2SAndroid Build Coastguard Worker    return enum_for(:each_item) unless block_given?
85*cc02d7e2SAndroid Build Coastguard Worker    loop do
86*cc02d7e2SAndroid Build Coastguard Worker      r = @q.pop
87*cc02d7e2SAndroid Build Coastguard Worker      break if r.equal?(@sentinel)
88*cc02d7e2SAndroid Build Coastguard Worker      fail r if r.is_a? Exception
89*cc02d7e2SAndroid Build Coastguard Worker      yield r
90*cc02d7e2SAndroid Build Coastguard Worker    end
91*cc02d7e2SAndroid Build Coastguard Worker  end
92*cc02d7e2SAndroid Build Coastguard Workerend
93*cc02d7e2SAndroid Build Coastguard Worker
94*cc02d7e2SAndroid Build Coastguard Worker# The Math::Math:: module occurs because the service has the same name as its
95*cc02d7e2SAndroid Build Coastguard Worker# package. That practice should be avoided by defining real services.
96*cc02d7e2SAndroid Build Coastguard Workerclass Calculator < Math::Math::Service
97*cc02d7e2SAndroid Build Coastguard Worker  def div(div_args, _call)
98*cc02d7e2SAndroid Build Coastguard Worker    if div_args.divisor.zero?
99*cc02d7e2SAndroid Build Coastguard Worker      # To send non-OK status handlers raise a StatusError with the code and
100*cc02d7e2SAndroid Build Coastguard Worker      # and detail they want sent as a Status.
101*cc02d7e2SAndroid Build Coastguard Worker      fail GRPC::StatusError.new(GRPC::Status::INVALID_ARGUMENT,
102*cc02d7e2SAndroid Build Coastguard Worker                                 'divisor cannot be 0')
103*cc02d7e2SAndroid Build Coastguard Worker    end
104*cc02d7e2SAndroid Build Coastguard Worker
105*cc02d7e2SAndroid Build Coastguard Worker    Math::DivReply.new(quotient: div_args.dividend / div_args.divisor,
106*cc02d7e2SAndroid Build Coastguard Worker                       remainder: div_args.dividend % div_args.divisor)
107*cc02d7e2SAndroid Build Coastguard Worker  end
108*cc02d7e2SAndroid Build Coastguard Worker
109*cc02d7e2SAndroid Build Coastguard Worker  def sum(call)
110*cc02d7e2SAndroid Build Coastguard Worker    # the requests are accesible as the Enumerator call#each_request
111*cc02d7e2SAndroid Build Coastguard Worker    nums = call.each_remote_read.collect(&:num)
112*cc02d7e2SAndroid Build Coastguard Worker    sum = nums.inject { |s, x| s + x }
113*cc02d7e2SAndroid Build Coastguard Worker    Math::Num.new(num: sum)
114*cc02d7e2SAndroid Build Coastguard Worker  end
115*cc02d7e2SAndroid Build Coastguard Worker
116*cc02d7e2SAndroid Build Coastguard Worker  def fib(fib_args, _call)
117*cc02d7e2SAndroid Build Coastguard Worker    if fib_args.limit < 1
118*cc02d7e2SAndroid Build Coastguard Worker      fail StatusError.new(Status::INVALID_ARGUMENT, 'limit must be >= 0')
119*cc02d7e2SAndroid Build Coastguard Worker    end
120*cc02d7e2SAndroid Build Coastguard Worker
121*cc02d7e2SAndroid Build Coastguard Worker    # return an Enumerator of Nums
122*cc02d7e2SAndroid Build Coastguard Worker    Fibber.new(fib_args.limit).generator
123*cc02d7e2SAndroid Build Coastguard Worker    # just return the generator, GRPC::GenericServer sends each actual response
124*cc02d7e2SAndroid Build Coastguard Worker  end
125*cc02d7e2SAndroid Build Coastguard Worker
126*cc02d7e2SAndroid Build Coastguard Worker  def div_many(requests)
127*cc02d7e2SAndroid Build Coastguard Worker    # requests is an lazy Enumerator of the requests sent by the client.
128*cc02d7e2SAndroid Build Coastguard Worker    q = EnumeratorQueue.new(self)
129*cc02d7e2SAndroid Build Coastguard Worker    t = Thread.new do
130*cc02d7e2SAndroid Build Coastguard Worker      begin
131*cc02d7e2SAndroid Build Coastguard Worker        requests.each do |req|
132*cc02d7e2SAndroid Build Coastguard Worker          GRPC.logger.info("read #{req.inspect}")
133*cc02d7e2SAndroid Build Coastguard Worker          resp = Math::DivReply.new(quotient: req.dividend / req.divisor,
134*cc02d7e2SAndroid Build Coastguard Worker                                    remainder: req.dividend % req.divisor)
135*cc02d7e2SAndroid Build Coastguard Worker          q.push(resp)
136*cc02d7e2SAndroid Build Coastguard Worker          Thread.pass  # let the internal Bidi threads run
137*cc02d7e2SAndroid Build Coastguard Worker        end
138*cc02d7e2SAndroid Build Coastguard Worker        GRPC.logger.info('finished reads')
139*cc02d7e2SAndroid Build Coastguard Worker        q.push(self)
140*cc02d7e2SAndroid Build Coastguard Worker      rescue StandardError => e
141*cc02d7e2SAndroid Build Coastguard Worker        q.push(e)  # share the exception with the enumerator
142*cc02d7e2SAndroid Build Coastguard Worker        raise e
143*cc02d7e2SAndroid Build Coastguard Worker      end
144*cc02d7e2SAndroid Build Coastguard Worker    end
145*cc02d7e2SAndroid Build Coastguard Worker    t.priority = -2  # hint that the div_many thread should not be favoured
146*cc02d7e2SAndroid Build Coastguard Worker    q.each_item
147*cc02d7e2SAndroid Build Coastguard Worker  end
148*cc02d7e2SAndroid Build Coastguard Workerend
149*cc02d7e2SAndroid Build Coastguard Worker
150*cc02d7e2SAndroid Build Coastguard Workerdef load_test_certs
151*cc02d7e2SAndroid Build Coastguard Worker  this_dir = File.expand_path(File.dirname(__FILE__))
152*cc02d7e2SAndroid Build Coastguard Worker  data_dir = File.join(File.dirname(this_dir), 'spec/testdata')
153*cc02d7e2SAndroid Build Coastguard Worker  files = ['ca.pem', 'server1.key', 'server1.pem']
154*cc02d7e2SAndroid Build Coastguard Worker  files.map { |f| File.open(File.join(data_dir, f)).read }
155*cc02d7e2SAndroid Build Coastguard Workerend
156*cc02d7e2SAndroid Build Coastguard Worker
157*cc02d7e2SAndroid Build Coastguard Workerdef test_server_creds
158*cc02d7e2SAndroid Build Coastguard Worker  certs = load_test_certs
159*cc02d7e2SAndroid Build Coastguard Worker  GRPC::Core::ServerCredentials.new(
160*cc02d7e2SAndroid Build Coastguard Worker    nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
161*cc02d7e2SAndroid Build Coastguard Workerend
162*cc02d7e2SAndroid Build Coastguard Worker
163*cc02d7e2SAndroid Build Coastguard Workerdef main
164*cc02d7e2SAndroid Build Coastguard Worker  options = {
165*cc02d7e2SAndroid Build Coastguard Worker    'host' => 'localhost:7071',
166*cc02d7e2SAndroid Build Coastguard Worker    'secure' => false
167*cc02d7e2SAndroid Build Coastguard Worker  }
168*cc02d7e2SAndroid Build Coastguard Worker  OptionParser.new do |opts|
169*cc02d7e2SAndroid Build Coastguard Worker    opts.banner = 'Usage: [--host <hostname>:<port>] [--secure|-s]'
170*cc02d7e2SAndroid Build Coastguard Worker    opts.on('--host HOST', '<hostname>:<port>') do |v|
171*cc02d7e2SAndroid Build Coastguard Worker      options['host'] = v
172*cc02d7e2SAndroid Build Coastguard Worker    end
173*cc02d7e2SAndroid Build Coastguard Worker    opts.on('-s', '--secure', 'access using test creds') do |v|
174*cc02d7e2SAndroid Build Coastguard Worker      options['secure'] = v
175*cc02d7e2SAndroid Build Coastguard Worker    end
176*cc02d7e2SAndroid Build Coastguard Worker  end.parse!
177*cc02d7e2SAndroid Build Coastguard Worker
178*cc02d7e2SAndroid Build Coastguard Worker  s = GRPC::RpcServer.new
179*cc02d7e2SAndroid Build Coastguard Worker  if options['secure']
180*cc02d7e2SAndroid Build Coastguard Worker    s.add_http2_port(options['host'], test_server_creds)
181*cc02d7e2SAndroid Build Coastguard Worker    GRPC.logger.info("... running securely on #{options['host']}")
182*cc02d7e2SAndroid Build Coastguard Worker  else
183*cc02d7e2SAndroid Build Coastguard Worker    s.add_http2_port(options['host'], :this_port_is_insecure)
184*cc02d7e2SAndroid Build Coastguard Worker    GRPC.logger.info("... running insecurely on #{options['host']}")
185*cc02d7e2SAndroid Build Coastguard Worker  end
186*cc02d7e2SAndroid Build Coastguard Worker
187*cc02d7e2SAndroid Build Coastguard Worker  s.handle(Calculator)
188*cc02d7e2SAndroid Build Coastguard Worker  s.run_till_terminated
189*cc02d7e2SAndroid Build Coastguard Workerend
190*cc02d7e2SAndroid Build Coastguard Worker
191*cc02d7e2SAndroid Build Coastguard Workermain
192