rpc_server_spec.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. # Copyright 2014, Google Inc.
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met:
  7. #
  8. # * Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # * Redistributions in binary form must reproduce the above
  11. # copyright notice, this list of conditions and the following disclaimer
  12. # in the documentation and/or other materials provided with the
  13. # distribution.
  14. # * Neither the name of Google Inc. nor the names of its
  15. # contributors may be used to endorse or promote products derived from
  16. # this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. require 'grpc'
  30. require 'xray/thread_dump_signal_handler'
  31. def load_test_certs
  32. test_root = File.join(File.dirname(File.dirname(__FILE__)), 'testdata')
  33. files = ['ca.pem', 'server1.key', 'server1.pem']
  34. files.map { |f| File.open(File.join(test_root, f)).read }
  35. end
  36. # A test message
  37. class EchoMsg
  38. def self.marshal(_o)
  39. ''
  40. end
  41. def self.unmarshal(_o)
  42. EchoMsg.new
  43. end
  44. end
  45. # A test service with no methods.
  46. class EmptyService
  47. include GRPC::GenericService
  48. end
  49. # A test service without an implementation.
  50. class NoRpcImplementation
  51. include GRPC::GenericService
  52. rpc :an_rpc, EchoMsg, EchoMsg
  53. end
  54. # A test service with an implementation.
  55. class EchoService
  56. include GRPC::GenericService
  57. rpc :an_rpc, EchoMsg, EchoMsg
  58. def initialize(_default_var = 'ignored')
  59. end
  60. def an_rpc(req, _call)
  61. logger.info('echo service received a request')
  62. req
  63. end
  64. end
  65. EchoStub = EchoService.rpc_stub_class
  66. # A slow test service.
  67. class SlowService
  68. include GRPC::GenericService
  69. rpc :an_rpc, EchoMsg, EchoMsg
  70. def initialize(_default_var = 'ignored')
  71. end
  72. def an_rpc(req, _call)
  73. delay = 0.25
  74. logger.info("starting a slow #{delay} rpc")
  75. sleep delay
  76. req # send back the req as the response
  77. end
  78. end
  79. SlowStub = SlowService.rpc_stub_class
  80. describe GRPC::RpcServer do
  81. RpcServer = GRPC::RpcServer
  82. StatusCodes = GRPC::Core::StatusCodes
  83. before(:each) do
  84. @method = 'an_rpc_method'
  85. @pass = 0
  86. @fail = 1
  87. @noop = proc { |x| x }
  88. @server_queue = GRPC::Core::CompletionQueue.new
  89. server_host = '0.0.0.0:0'
  90. @server = GRPC::Core::Server.new(@server_queue, nil)
  91. server_port = @server.add_http2_port(server_host)
  92. @host = "localhost:#{server_port}"
  93. @ch = GRPC::Core::Channel.new(@host, nil)
  94. end
  95. after(:each) do
  96. @server.close
  97. end
  98. describe '#new' do
  99. it 'can be created with just some args' do
  100. opts = { a_channel_arg: 'an_arg' }
  101. blk = proc do
  102. RpcServer.new(**opts)
  103. end
  104. expect(&blk).not_to raise_error
  105. end
  106. it 'can be created with a default deadline' do
  107. opts = { a_channel_arg: 'an_arg', deadline: 5 }
  108. blk = proc do
  109. RpcServer.new(**opts)
  110. end
  111. expect(&blk).not_to raise_error
  112. end
  113. it 'can be created with a completion queue override' do
  114. opts = {
  115. a_channel_arg: 'an_arg',
  116. completion_queue_override: @server_queue
  117. }
  118. blk = proc do
  119. RpcServer.new(**opts)
  120. end
  121. expect(&blk).not_to raise_error
  122. end
  123. it 'cannot be created with a bad completion queue override' do
  124. blk = proc do
  125. opts = {
  126. a_channel_arg: 'an_arg',
  127. completion_queue_override: Object.new
  128. }
  129. RpcServer.new(**opts)
  130. end
  131. expect(&blk).to raise_error
  132. end
  133. it 'cannot be created with invalid ServerCredentials' do
  134. blk = proc do
  135. opts = {
  136. a_channel_arg: 'an_arg',
  137. creds: Object.new
  138. }
  139. RpcServer.new(**opts)
  140. end
  141. expect(&blk).to raise_error
  142. end
  143. it 'can be created with the creds as valid ServerCedentials' do
  144. certs = load_test_certs
  145. server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
  146. blk = proc do
  147. opts = {
  148. a_channel_arg: 'an_arg',
  149. creds: server_creds
  150. }
  151. RpcServer.new(**opts)
  152. end
  153. expect(&blk).to_not raise_error
  154. end
  155. it 'can be created with a server override' do
  156. opts = { a_channel_arg: 'an_arg', server_override: @server }
  157. blk = proc do
  158. RpcServer.new(**opts)
  159. end
  160. expect(&blk).not_to raise_error
  161. end
  162. it 'cannot be created with a bad server override' do
  163. blk = proc do
  164. opts = {
  165. a_channel_arg: 'an_arg',
  166. server_override: Object.new
  167. }
  168. RpcServer.new(**opts)
  169. end
  170. expect(&blk).to raise_error
  171. end
  172. end
  173. describe '#stopped?' do
  174. before(:each) do
  175. opts = { a_channel_arg: 'an_arg', poll_period: 1 }
  176. @srv = RpcServer.new(**opts)
  177. end
  178. it 'starts out false' do
  179. expect(@srv.stopped?).to be(false)
  180. end
  181. it 'stays false after a #stop is called before #run' do
  182. @srv.stop
  183. expect(@srv.stopped?).to be(false)
  184. end
  185. it 'stays false after the server starts running' do
  186. @srv.handle(EchoService)
  187. t = Thread.new { @srv.run }
  188. @srv.wait_till_running
  189. expect(@srv.stopped?).to be(false)
  190. @srv.stop
  191. t.join
  192. end
  193. it 'is true after a running server is stopped' do
  194. @srv.handle(EchoService)
  195. t = Thread.new { @srv.run }
  196. @srv.wait_till_running
  197. @srv.stop
  198. expect(@srv.stopped?).to be(true)
  199. t.join
  200. end
  201. end
  202. describe '#running?' do
  203. it 'starts out false' do
  204. opts = { a_channel_arg: 'an_arg', server_override: @server }
  205. r = RpcServer.new(**opts)
  206. expect(r.running?).to be(false)
  207. end
  208. it 'is false after run is called with no services registered' do
  209. opts = {
  210. a_channel_arg: 'an_arg',
  211. poll_period: 1,
  212. server_override: @server
  213. }
  214. r = RpcServer.new(**opts)
  215. r.run
  216. expect(r.running?).to be(false)
  217. end
  218. it 'is true after run is called with a registered service' do
  219. opts = {
  220. a_channel_arg: 'an_arg',
  221. poll_period: 1,
  222. server_override: @server
  223. }
  224. r = RpcServer.new(**opts)
  225. r.handle(EchoService)
  226. t = Thread.new { r.run }
  227. r.wait_till_running
  228. expect(r.running?).to be(true)
  229. r.stop
  230. t.join
  231. end
  232. end
  233. describe '#handle' do
  234. before(:each) do
  235. @opts = { a_channel_arg: 'an_arg', poll_period: 1 }
  236. @srv = RpcServer.new(**@opts)
  237. end
  238. it 'raises if #run has already been called' do
  239. @srv.handle(EchoService)
  240. t = Thread.new { @srv.run }
  241. @srv.wait_till_running
  242. expect { @srv.handle(EchoService) }.to raise_error
  243. @srv.stop
  244. t.join
  245. end
  246. it 'raises if the server has been run and stopped' do
  247. @srv.handle(EchoService)
  248. t = Thread.new { @srv.run }
  249. @srv.wait_till_running
  250. @srv.stop
  251. t.join
  252. expect { @srv.handle(EchoService) }.to raise_error
  253. end
  254. it 'raises if the service does not include GenericService ' do
  255. expect { @srv.handle(Object) }.to raise_error
  256. end
  257. it 'raises if the service does not declare any rpc methods' do
  258. expect { @srv.handle(EmptyService) }.to raise_error
  259. end
  260. it 'raises if the service does not define its rpc methods' do
  261. expect { @srv.handle(NoRpcImplementation) }.to raise_error
  262. end
  263. it 'raises if a handler method is already registered' do
  264. @srv.handle(EchoService)
  265. expect { r.handle(EchoService) }.to raise_error
  266. end
  267. end
  268. describe '#run' do
  269. before(:each) do
  270. @client_opts = {
  271. channel_override: @ch
  272. }
  273. @marshal = EchoService.rpc_descs[:an_rpc].marshal_proc
  274. @unmarshal = EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output)
  275. server_opts = {
  276. server_override: @server,
  277. completion_queue_override: @server_queue,
  278. poll_period: 1
  279. }
  280. @srv = RpcServer.new(**server_opts)
  281. end
  282. describe 'when running' do
  283. it 'should return NOT_FOUND status for requests on unknown methods' do
  284. @srv.handle(EchoService)
  285. t = Thread.new { @srv.run }
  286. @srv.wait_till_running
  287. req = EchoMsg.new
  288. blk = proc do
  289. cq = GRPC::Core::CompletionQueue.new
  290. stub = GRPC::ClientStub.new(@host, cq, **@client_opts)
  291. stub.request_response('/unknown', req, @marshal, @unmarshal)
  292. end
  293. expect(&blk).to raise_error GRPC::BadStatus
  294. @srv.stop
  295. t.join
  296. end
  297. it 'should obtain responses for multiple sequential requests' do
  298. @srv.handle(EchoService)
  299. t = Thread.new { @srv.run }
  300. @srv.wait_till_running
  301. req = EchoMsg.new
  302. n = 5 # arbitrary
  303. stub = EchoStub.new(@host, **@client_opts)
  304. n.times { expect(stub.an_rpc(req)).to be_a(EchoMsg) }
  305. @srv.stop
  306. t.join
  307. end
  308. it 'should obtain responses for multiple parallel requests' do
  309. @srv.handle(EchoService)
  310. Thread.new { @srv.run }
  311. @srv.wait_till_running
  312. req, q = EchoMsg.new, Queue.new
  313. n = 5 # arbitrary
  314. threads = []
  315. n.times do
  316. threads << Thread.new do
  317. stub = EchoStub.new(@host, **@client_opts)
  318. q << stub.an_rpc(req)
  319. end
  320. end
  321. n.times { expect(q.pop).to be_a(EchoMsg) }
  322. @srv.stop
  323. threads.each(&:join)
  324. end
  325. it 'should return UNAVAILABLE status if there too many jobs' do
  326. opts = {
  327. a_channel_arg: 'an_arg',
  328. server_override: @server,
  329. completion_queue_override: @server_queue,
  330. pool_size: 1,
  331. poll_period: 1,
  332. max_waiting_requests: 0
  333. }
  334. alt_srv = RpcServer.new(**opts)
  335. alt_srv.handle(SlowService)
  336. Thread.new { alt_srv.run }
  337. alt_srv.wait_till_running
  338. req = EchoMsg.new
  339. n = 5 # arbitrary, use as many to ensure the server pool is exceeded
  340. threads = []
  341. one_failed_as_unavailable = false
  342. n.times do
  343. threads << Thread.new do
  344. stub = SlowStub.new(@host, **@client_opts)
  345. begin
  346. stub.an_rpc(req)
  347. rescue GRPC::BadStatus => e
  348. one_failed_as_unavailable = e.code == StatusCodes::UNAVAILABLE
  349. end
  350. end
  351. end
  352. threads.each(&:join)
  353. alt_srv.stop
  354. expect(one_failed_as_unavailable).to be(true)
  355. end
  356. end
  357. end
  358. end