| Tim Emiola | 3acf05a | 2015-01-13 07:55:34 -0800 | [diff] [blame] | 1 | #!/usr/bin/env ruby | 
 | 2 |  | 
| Craig Tiller | 0605995 | 2015-02-18 08:34:56 -0800 | [diff] [blame] | 3 | # Copyright 2015, Google Inc. | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 4 | # 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 |  | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 32 | # 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 |  | 
 | 37 | this_dir = File.expand_path(File.dirname(__FILE__)) | 
 | 38 | lib_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 |  | 
 | 42 | require 'forwardable' | 
 | 43 | require 'grpc' | 
| Tim Emiola | 81d950a | 2015-08-28 16:57:28 -0700 | [diff] [blame] | 44 | require 'logger' | 
| temiola | 0f0a6bc | 2015-01-07 18:43:40 -0800 | [diff] [blame] | 45 | require 'math_services' | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 46 | require 'optparse' | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 47 |  | 
| Tim Emiola | 81d950a | 2015-08-28 16:57:28 -0700 | [diff] [blame] | 48 | # RubyLogger defines a logger for gRPC based on the standard ruby logger. | 
 | 49 | module RubyLogger | 
 | 50 |   def logger | 
 | 51 |     LOGGER | 
 | 52 |   end | 
 | 53 |  | 
 | 54 |   LOGGER = Logger.new(STDOUT) | 
 | 55 | end | 
 | 56 |  | 
 | 57 | # GRPC is the general RPC module | 
 | 58 | module GRPC | 
 | 59 |   # Inject the noop #logger if no module-level logger method has been injected. | 
 | 60 |   extend RubyLogger | 
 | 61 | end | 
 | 62 |  | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 63 | # Holds state for a fibonacci series | 
 | 64 | class Fibber | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 65 |   def initialize(limit) | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 66 |     fail "bad limit: got #{limit}, want limit > 0" if limit < 1 | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 67 |     @limit = limit | 
 | 68 |   end | 
 | 69 |  | 
 | 70 |   def generator | 
 | 71 |     return enum_for(:generator) unless block_given? | 
 | 72 |     idx, current, previous = 0, 1, 1 | 
 | 73 |     until idx == @limit | 
| Aggelos Avgerinos | 57e7dc8 | 2015-05-09 13:08:03 +0300 | [diff] [blame] | 74 |       if idx.zero? || idx == 1 | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 75 |         yield Math::Num.new(num: 1) | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 76 |         idx += 1 | 
 | 77 |         next | 
 | 78 |       end | 
 | 79 |       tmp = current | 
 | 80 |       current = previous + current | 
 | 81 |       previous = tmp | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 82 |       yield Math::Num.new(num: current) | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 83 |       idx += 1 | 
 | 84 |     end | 
 | 85 |   end | 
 | 86 | end | 
 | 87 |  | 
 | 88 | # A EnumeratorQueue wraps a Queue to yield the items added to it. | 
 | 89 | class EnumeratorQueue | 
 | 90 |   extend Forwardable | 
 | 91 |   def_delegators :@q, :push | 
 | 92 |  | 
 | 93 |   def initialize(sentinel) | 
 | 94 |     @q = Queue.new | 
 | 95 |     @sentinel = sentinel | 
 | 96 |   end | 
 | 97 |  | 
 | 98 |   def each_item | 
 | 99 |     return enum_for(:each_item) unless block_given? | 
 | 100 |     loop do | 
 | 101 |       r = @q.pop | 
 | 102 |       break if r.equal?(@sentinel) | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 103 |       fail r if r.is_a? Exception | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 104 |       yield r | 
 | 105 |     end | 
 | 106 |   end | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 107 | end | 
 | 108 |  | 
 | 109 | # The Math::Math:: module occurs because the service has the same name as its | 
 | 110 | # package. That practice should be avoided by defining real services. | 
 | 111 | class Calculator < Math::Math::Service | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 112 |   def div(div_args, _call) | 
| Aggelos Avgerinos | 57e7dc8 | 2015-05-09 13:08:03 +0300 | [diff] [blame] | 113 |     if div_args.divisor.zero? | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 114 |       # To send non-OK status handlers raise a StatusError with the code and | 
 | 115 |       # and detail they want sent as a Status. | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 116 |       fail GRPC::StatusError.new(GRPC::Status::INVALID_ARGUMENT, | 
 | 117 |                                  'divisor cannot be 0') | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 118 |     end | 
 | 119 |  | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 120 |     Math::DivReply.new(quotient: div_args.dividend / div_args.divisor, | 
 | 121 |                        remainder: div_args.dividend % div_args.divisor) | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 122 |   end | 
 | 123 |  | 
 | 124 |   def sum(call) | 
 | 125 |     # the requests are accesible as the Enumerator call#each_request | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 126 |     nums = call.each_remote_read.collect(&:num) | 
 | 127 |     sum = nums.inject { |s, x| s + x } | 
 | 128 |     Math::Num.new(num: sum) | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 129 |   end | 
 | 130 |  | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 131 |   def fib(fib_args, _call) | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 132 |     if fib_args.limit < 1 | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 133 |       fail StatusError.new(Status::INVALID_ARGUMENT, 'limit must be >= 0') | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 134 |     end | 
 | 135 |  | 
 | 136 |     # return an Enumerator of Nums | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 137 |     Fibber.new(fib_args.limit).generator | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 138 |     # just return the generator, GRPC::GenericServer sends each actual response | 
 | 139 |   end | 
 | 140 |  | 
 | 141 |   def div_many(requests) | 
 | 142 |     # requests is an lazy Enumerator of the requests sent by the client. | 
 | 143 |     q = EnumeratorQueue.new(self) | 
 | 144 |     t = Thread.new do | 
 | 145 |       begin | 
 | 146 |         requests.each do |req| | 
| Nick Gauthier | f233d96 | 2015-05-20 14:02:50 -0400 | [diff] [blame] | 147 |           GRPC.logger.info("read #{req.inspect}") | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 148 |           resp = Math::DivReply.new(quotient: req.dividend / req.divisor, | 
 | 149 |                                     remainder: req.dividend % req.divisor) | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 150 |           q.push(resp) | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 151 |           Thread.pass  # let the internal Bidi threads run | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 152 |         end | 
| Nick Gauthier | f233d96 | 2015-05-20 14:02:50 -0400 | [diff] [blame] | 153 |         GRPC.logger.info('finished reads') | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 154 |         q.push(self) | 
 | 155 |       rescue StandardError => e | 
 | 156 |         q.push(e)  # share the exception with the enumerator | 
 | 157 |         raise e | 
 | 158 |       end | 
 | 159 |     end | 
 | 160 |     t.priority = -2  # hint that the div_many thread should not be favoured | 
 | 161 |     q.each_item | 
 | 162 |   end | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 163 | end | 
 | 164 |  | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 165 | def load_test_certs | 
 | 166 |   this_dir = File.expand_path(File.dirname(__FILE__)) | 
 | 167 |   data_dir = File.join(File.dirname(this_dir), 'spec/testdata') | 
 | 168 |   files = ['ca.pem', 'server1.key', 'server1.pem'] | 
 | 169 |   files.map { |f| File.open(File.join(data_dir, f)).read } | 
 | 170 | end | 
 | 171 |  | 
 | 172 | def test_server_creds | 
 | 173 |   certs = load_test_certs | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 174 |   GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2]) | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 175 | end | 
 | 176 |  | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 177 | def main | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 178 |   options = { | 
 | 179 |     'host' => 'localhost:7071', | 
 | 180 |     'secure' => false | 
 | 181 |   } | 
 | 182 |   OptionParser.new do |opts| | 
| temiola | 0f0a6bc | 2015-01-07 18:43:40 -0800 | [diff] [blame] | 183 |     opts.banner = 'Usage: [--host <hostname>:<port>] [--secure|-s]' | 
 | 184 |     opts.on('--host HOST', '<hostname>:<port>') do |v| | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 185 |       options['host'] = v | 
 | 186 |     end | 
 | 187 |     opts.on('-s', '--secure', 'access using test creds') do |v| | 
| Tim Emiola | e2860c5 | 2015-01-16 02:58:41 -0800 | [diff] [blame] | 188 |       options['secure'] = v | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 189 |     end | 
 | 190 |   end.parse! | 
 | 191 |  | 
| Tim Emiola | 0ce8edc | 2015-03-05 15:17:30 -0800 | [diff] [blame] | 192 |   s = GRPC::RpcServer.new | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 193 |   if options['secure'] | 
| Tim Emiola | 0ce8edc | 2015-03-05 15:17:30 -0800 | [diff] [blame] | 194 |     s.add_http2_port(options['host'], test_server_creds) | 
| Nick Gauthier | f233d96 | 2015-05-20 14:02:50 -0400 | [diff] [blame] | 195 |     GRPC.logger.info("... running securely on #{options['host']}") | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 196 |   else | 
| nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 197 |     s.add_http2_port(options['host']) | 
| Nick Gauthier | f233d96 | 2015-05-20 14:02:50 -0400 | [diff] [blame] | 198 |     GRPC.logger.info("... running insecurely on #{options['host']}") | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 199 |   end | 
 | 200 |  | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 201 |   s.handle(Calculator) | 
| Tim Emiola | 321871e | 2015-04-16 12:56:11 -0700 | [diff] [blame] | 202 |   s.run_till_terminated | 
| nnoble | 097ef9b | 2014-12-01 17:06:10 -0800 | [diff] [blame] | 203 | end | 
 | 204 |  | 
| Craig Tiller | 190d360 | 2015-02-18 09:23:38 -0800 | [diff] [blame] | 205 | main |