blob: 5e2b7e701df1d70b771534ad1438bd15dcd8039f [file] [log] [blame]
Tim Emiola3acf05a2015-01-13 07:55:34 -08001#!/usr/bin/env ruby
2
Craig Tiller06059952015-02-18 08:34:56 -08003# Copyright 2015, Google Inc.
nnoble097ef9b2014-12-01 17:06:10 -08004# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met:
9#
10# * Redistributions of source code must retain the above copyright
11# notice, this list of conditions and the following disclaimer.
12# * Redistributions in binary form must reproduce the above
13# copyright notice, this list of conditions and the following disclaimer
14# in the documentation and/or other materials provided with the
15# distribution.
16# * Neither the name of Google Inc. nor the names of its
17# contributors may be used to endorse or promote products derived from
18# this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
nnoble097ef9b2014-12-01 17:06:10 -080032# Sample gRPC Ruby server that implements the Math::Calc service and helps
33# validate GRPC::RpcServer as GRPC implementation using proto2 serialization.
34#
35# Usage: $ path/to/math_server.rb
36
37this_dir = File.expand_path(File.dirname(__FILE__))
38lib_dir = File.join(File.dirname(this_dir), 'lib')
39$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
40$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
41
42require 'forwardable'
43require 'grpc'
temiola0f0a6bc2015-01-07 18:43:40 -080044require 'math_services'
nnoble0c475f02014-12-05 15:37:39 -080045require 'optparse'
nnoble097ef9b2014-12-01 17:06:10 -080046
47# Holds state for a fibonacci series
48class Fibber
nnoble097ef9b2014-12-01 17:06:10 -080049 def initialize(limit)
Tim Emiolae2860c52015-01-16 02:58:41 -080050 fail "bad limit: got #{limit}, want limit > 0" if limit < 1
nnoble097ef9b2014-12-01 17:06:10 -080051 @limit = limit
52 end
53
54 def generator
55 return enum_for(:generator) unless block_given?
56 idx, current, previous = 0, 1, 1
57 until idx == @limit
Aggelos Avgerinos57e7dc82015-05-09 13:08:03 +030058 if idx.zero? || idx == 1
Tim Emiolae2860c52015-01-16 02:58:41 -080059 yield Math::Num.new(num: 1)
nnoble097ef9b2014-12-01 17:06:10 -080060 idx += 1
61 next
62 end
63 tmp = current
64 current = previous + current
65 previous = tmp
Tim Emiolae2860c52015-01-16 02:58:41 -080066 yield Math::Num.new(num: current)
nnoble097ef9b2014-12-01 17:06:10 -080067 idx += 1
68 end
69 end
70end
71
72# A EnumeratorQueue wraps a Queue to yield the items added to it.
73class EnumeratorQueue
74 extend Forwardable
75 def_delegators :@q, :push
76
77 def initialize(sentinel)
78 @q = Queue.new
79 @sentinel = sentinel
80 end
81
82 def each_item
83 return enum_for(:each_item) unless block_given?
84 loop do
85 r = @q.pop
86 break if r.equal?(@sentinel)
Tim Emiolae2860c52015-01-16 02:58:41 -080087 fail r if r.is_a? Exception
nnoble097ef9b2014-12-01 17:06:10 -080088 yield r
89 end
90 end
nnoble097ef9b2014-12-01 17:06:10 -080091end
92
93# The Math::Math:: module occurs because the service has the same name as its
94# package. That practice should be avoided by defining real services.
95class Calculator < Math::Math::Service
Tim Emiolae2860c52015-01-16 02:58:41 -080096 def div(div_args, _call)
Aggelos Avgerinos57e7dc82015-05-09 13:08:03 +030097 if div_args.divisor.zero?
nnoble097ef9b2014-12-01 17:06:10 -080098 # To send non-OK status handlers raise a StatusError with the code and
99 # and detail they want sent as a Status.
Tim Emiolae2860c52015-01-16 02:58:41 -0800100 fail GRPC::StatusError.new(GRPC::Status::INVALID_ARGUMENT,
101 'divisor cannot be 0')
nnoble097ef9b2014-12-01 17:06:10 -0800102 end
103
Tim Emiolae2860c52015-01-16 02:58:41 -0800104 Math::DivReply.new(quotient: div_args.dividend / div_args.divisor,
105 remainder: div_args.dividend % div_args.divisor)
nnoble097ef9b2014-12-01 17:06:10 -0800106 end
107
108 def sum(call)
109 # the requests are accesible as the Enumerator call#each_request
Tim Emiolae2860c52015-01-16 02:58:41 -0800110 nums = call.each_remote_read.collect(&:num)
111 sum = nums.inject { |s, x| s + x }
112 Math::Num.new(num: sum)
nnoble097ef9b2014-12-01 17:06:10 -0800113 end
114
Tim Emiolae2860c52015-01-16 02:58:41 -0800115 def fib(fib_args, _call)
nnoble097ef9b2014-12-01 17:06:10 -0800116 if fib_args.limit < 1
Tim Emiolae2860c52015-01-16 02:58:41 -0800117 fail StatusError.new(Status::INVALID_ARGUMENT, 'limit must be >= 0')
nnoble097ef9b2014-12-01 17:06:10 -0800118 end
119
120 # return an Enumerator of Nums
Tim Emiolae2860c52015-01-16 02:58:41 -0800121 Fibber.new(fib_args.limit).generator
nnoble097ef9b2014-12-01 17:06:10 -0800122 # just return the generator, GRPC::GenericServer sends each actual response
123 end
124
125 def div_many(requests)
126 # requests is an lazy Enumerator of the requests sent by the client.
127 q = EnumeratorQueue.new(self)
128 t = Thread.new do
129 begin
130 requests.each do |req|
Nick Gauthierf233d962015-05-20 14:02:50 -0400131 GRPC.logger.info("read #{req.inspect}")
Tim Emiolae2860c52015-01-16 02:58:41 -0800132 resp = Math::DivReply.new(quotient: req.dividend / req.divisor,
133 remainder: req.dividend % req.divisor)
nnoble097ef9b2014-12-01 17:06:10 -0800134 q.push(resp)
Tim Emiolae2860c52015-01-16 02:58:41 -0800135 Thread.pass # let the internal Bidi threads run
nnoble097ef9b2014-12-01 17:06:10 -0800136 end
Nick Gauthierf233d962015-05-20 14:02:50 -0400137 GRPC.logger.info('finished reads')
nnoble097ef9b2014-12-01 17:06:10 -0800138 q.push(self)
139 rescue StandardError => e
140 q.push(e) # share the exception with the enumerator
141 raise e
142 end
143 end
144 t.priority = -2 # hint that the div_many thread should not be favoured
145 q.each_item
146 end
nnoble097ef9b2014-12-01 17:06:10 -0800147end
148
nnoble0c475f02014-12-05 15:37:39 -0800149def load_test_certs
150 this_dir = File.expand_path(File.dirname(__FILE__))
151 data_dir = File.join(File.dirname(this_dir), 'spec/testdata')
152 files = ['ca.pem', 'server1.key', 'server1.pem']
153 files.map { |f| File.open(File.join(data_dir, f)).read }
154end
155
156def test_server_creds
157 certs = load_test_certs
Tim Emiola73a540a2015-08-28 18:56:17 -0700158 GRPC::Core::ServerCredentials.new(
159 nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
nnoble0c475f02014-12-05 15:37:39 -0800160end
161
nnoble097ef9b2014-12-01 17:06:10 -0800162def main
nnoble0c475f02014-12-05 15:37:39 -0800163 options = {
164 'host' => 'localhost:7071',
165 'secure' => false
166 }
167 OptionParser.new do |opts|
temiola0f0a6bc2015-01-07 18:43:40 -0800168 opts.banner = 'Usage: [--host <hostname>:<port>] [--secure|-s]'
169 opts.on('--host HOST', '<hostname>:<port>') do |v|
nnoble0c475f02014-12-05 15:37:39 -0800170 options['host'] = v
171 end
172 opts.on('-s', '--secure', 'access using test creds') do |v|
Tim Emiolae2860c52015-01-16 02:58:41 -0800173 options['secure'] = v
nnoble0c475f02014-12-05 15:37:39 -0800174 end
175 end.parse!
176
Tim Emiola0ce8edc2015-03-05 15:17:30 -0800177 s = GRPC::RpcServer.new
nnoble0c475f02014-12-05 15:37:39 -0800178 if options['secure']
Tim Emiola0ce8edc2015-03-05 15:17:30 -0800179 s.add_http2_port(options['host'], test_server_creds)
Nick Gauthierf233d962015-05-20 14:02:50 -0400180 GRPC.logger.info("... running securely on #{options['host']}")
nnoble0c475f02014-12-05 15:37:39 -0800181 else
nnoble0c475f02014-12-05 15:37:39 -0800182 s.add_http2_port(options['host'])
Nick Gauthierf233d962015-05-20 14:02:50 -0400183 GRPC.logger.info("... running insecurely on #{options['host']}")
nnoble097ef9b2014-12-01 17:06:10 -0800184 end
185
nnoble097ef9b2014-12-01 17:06:10 -0800186 s.handle(Calculator)
Tim Emiola321871e2015-04-16 12:56:11 -0700187 s.run_till_terminated
nnoble097ef9b2014-12-01 17:06:10 -0800188end
189
Craig Tiller190d3602015-02-18 09:23:38 -0800190main