rpc_server_spec.rb 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. # Copyright 2015, 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. def load_test_certs
  31. test_root = File.join(File.dirname(File.dirname(__FILE__)), 'testdata')
  32. files = ['ca.pem', 'server1.key', 'server1.pem']
  33. files.map { |f| File.open(File.join(test_root, f)).read }
  34. end
  35. # A test message
  36. class EchoMsg
  37. def self.marshal(_o)
  38. ''
  39. end
  40. def self.unmarshal(_o)
  41. EchoMsg.new
  42. end
  43. end
  44. # A test service with no methods.
  45. class EmptyService
  46. include GRPC::GenericService
  47. end
  48. # A test service without an implementation.
  49. class NoRpcImplementation
  50. include GRPC::GenericService
  51. rpc :an_rpc, EchoMsg, EchoMsg
  52. end
  53. # A test service with an echo implementation.
  54. class EchoService
  55. include GRPC::GenericService
  56. rpc :an_rpc, EchoMsg, EchoMsg
  57. attr_reader :received_md
  58. def initialize(**kw)
  59. @trailing_metadata = kw
  60. @received_md = []
  61. end
  62. def an_rpc(req, call)
  63. GRPC.logger.info('echo service received a request')
  64. call.output_metadata.update(@trailing_metadata)
  65. @received_md << call.metadata unless call.metadata.nil?
  66. req
  67. end
  68. end
  69. EchoStub = EchoService.rpc_stub_class
  70. # A test service with an implementation that fails with BadStatus
  71. class FailingService
  72. include GRPC::GenericService
  73. rpc :an_rpc, EchoMsg, EchoMsg
  74. attr_reader :details, :code, :md
  75. def initialize(_default_var = 'ignored')
  76. @details = 'app error'
  77. @code = 101
  78. @md = { failed_method: 'an_rpc' }
  79. end
  80. def an_rpc(_req, _call)
  81. fail GRPC::BadStatus.new(@code, @details, **@md)
  82. end
  83. end
  84. FailingStub = FailingService.rpc_stub_class
  85. # A slow test service.
  86. class SlowService
  87. include GRPC::GenericService
  88. rpc :an_rpc, EchoMsg, EchoMsg
  89. attr_reader :received_md, :delay
  90. def initialize(_default_var = 'ignored')
  91. @delay = 0.25
  92. @received_md = []
  93. end
  94. def an_rpc(req, call)
  95. GRPC.logger.info("starting a slow #{@delay} rpc")
  96. sleep @delay
  97. @received_md << call.metadata unless call.metadata.nil?
  98. req # send back the req as the response
  99. end
  100. end
  101. SlowStub = SlowService.rpc_stub_class
  102. describe GRPC::RpcServer do
  103. RpcServer = GRPC::RpcServer
  104. StatusCodes = GRPC::Core::StatusCodes
  105. before(:each) do
  106. @method = 'an_rpc_method'
  107. @pass = 0
  108. @fail = 1
  109. @noop = proc { |x| x }
  110. @server_queue = GRPC::Core::CompletionQueue.new
  111. server_host = '0.0.0.0:0'
  112. @server = GRPC::Core::Server.new(@server_queue, nil)
  113. server_port = @server.add_http2_port(server_host)
  114. @host = "localhost:#{server_port}"
  115. @ch = GRPC::Core::Channel.new(@host, nil)
  116. end
  117. after(:each) do
  118. @server.close
  119. end
  120. describe '#new' do
  121. it 'can be created with just some args' do
  122. opts = { a_channel_arg: 'an_arg' }
  123. blk = proc do
  124. RpcServer.new(**opts)
  125. end
  126. expect(&blk).not_to raise_error
  127. end
  128. it 'can be created with a default deadline' do
  129. opts = { a_channel_arg: 'an_arg', deadline: 5 }
  130. blk = proc do
  131. RpcServer.new(**opts)
  132. end
  133. expect(&blk).not_to raise_error
  134. end
  135. it 'can be created with a completion queue override' do
  136. opts = {
  137. a_channel_arg: 'an_arg',
  138. completion_queue_override: @server_queue
  139. }
  140. blk = proc do
  141. RpcServer.new(**opts)
  142. end
  143. expect(&blk).not_to raise_error
  144. end
  145. it 'cannot be created with a bad completion queue override' do
  146. blk = proc do
  147. opts = {
  148. a_channel_arg: 'an_arg',
  149. completion_queue_override: Object.new
  150. }
  151. RpcServer.new(**opts)
  152. end
  153. expect(&blk).to raise_error
  154. end
  155. it 'cannot be created with invalid ServerCredentials' do
  156. blk = proc do
  157. opts = {
  158. a_channel_arg: 'an_arg',
  159. creds: Object.new
  160. }
  161. RpcServer.new(**opts)
  162. end
  163. expect(&blk).to raise_error
  164. end
  165. it 'can be created with a server override' do
  166. opts = { a_channel_arg: 'an_arg', server_override: @server }
  167. blk = proc do
  168. RpcServer.new(**opts)
  169. end
  170. expect(&blk).not_to raise_error
  171. end
  172. it 'cannot be created with a bad server override' do
  173. blk = proc do
  174. opts = {
  175. a_channel_arg: 'an_arg',
  176. server_override: Object.new
  177. }
  178. RpcServer.new(**opts)
  179. end
  180. expect(&blk).to raise_error
  181. end
  182. end
  183. describe '#stopped?' do
  184. before(:each) do
  185. opts = { a_channel_arg: 'an_arg', poll_period: 1.5 }
  186. @srv = RpcServer.new(**opts)
  187. end
  188. after(:each) do
  189. @srv.stop
  190. end
  191. it 'starts out false' do
  192. expect(@srv.stopped?).to be(false)
  193. end
  194. it 'stays false after a #stop is called before #run' do
  195. @srv.stop
  196. expect(@srv.stopped?).to be(false)
  197. end
  198. it 'stays false after the server starts running', server: true do
  199. @srv.handle(EchoService)
  200. t = Thread.new { @srv.run }
  201. @srv.wait_till_running
  202. expect(@srv.stopped?).to be(false)
  203. @srv.stop
  204. t.join
  205. end
  206. it 'is true after a running server is stopped', server: true do
  207. @srv.handle(EchoService)
  208. t = Thread.new { @srv.run }
  209. @srv.wait_till_running
  210. @srv.stop
  211. expect(@srv.stopped?).to be(true)
  212. t.join
  213. end
  214. end
  215. describe '#running?' do
  216. it 'starts out false' do
  217. opts = { a_channel_arg: 'an_arg', server_override: @server }
  218. r = RpcServer.new(**opts)
  219. expect(r.running?).to be(false)
  220. end
  221. it 'is false if run is called with no services registered', server: true do
  222. opts = {
  223. a_channel_arg: 'an_arg',
  224. poll_period: 2,
  225. server_override: @server
  226. }
  227. r = RpcServer.new(**opts)
  228. r.run
  229. expect(r.running?).to be(false)
  230. r.stop
  231. end
  232. it 'is true after run is called with a registered service' do
  233. opts = {
  234. a_channel_arg: 'an_arg',
  235. poll_period: 2.5,
  236. server_override: @server
  237. }
  238. r = RpcServer.new(**opts)
  239. r.handle(EchoService)
  240. t = Thread.new { r.run }
  241. r.wait_till_running
  242. expect(r.running?).to be(true)
  243. r.stop
  244. t.join
  245. end
  246. end
  247. describe '#handle' do
  248. before(:each) do
  249. @opts = { a_channel_arg: 'an_arg', poll_period: 1 }
  250. @srv = RpcServer.new(**@opts)
  251. end
  252. after(:each) do
  253. @srv.stop
  254. end
  255. it 'raises if #run has already been called' do
  256. @srv.handle(EchoService)
  257. t = Thread.new { @srv.run }
  258. @srv.wait_till_running
  259. expect { @srv.handle(EchoService) }.to raise_error
  260. @srv.stop
  261. t.join
  262. end
  263. it 'raises if the server has been run and stopped' do
  264. @srv.handle(EchoService)
  265. t = Thread.new { @srv.run }
  266. @srv.wait_till_running
  267. @srv.stop
  268. t.join
  269. expect { @srv.handle(EchoService) }.to raise_error
  270. end
  271. it 'raises if the service does not include GenericService ' do
  272. expect { @srv.handle(Object) }.to raise_error
  273. end
  274. it 'raises if the service does not declare any rpc methods' do
  275. expect { @srv.handle(EmptyService) }.to raise_error
  276. end
  277. it 'raises if the service does not define its rpc methods' do
  278. expect { @srv.handle(NoRpcImplementation) }.to raise_error
  279. end
  280. it 'raises if a handler method is already registered' do
  281. @srv.handle(EchoService)
  282. expect { r.handle(EchoService) }.to raise_error
  283. end
  284. end
  285. describe '#run' do
  286. let(:client_opts) { { channel_override: @ch } }
  287. let(:marshal) { EchoService.rpc_descs[:an_rpc].marshal_proc }
  288. let(:unmarshal) { EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output) }
  289. context 'with no connect_metadata' do
  290. before(:each) do
  291. server_opts = {
  292. server_override: @server,
  293. completion_queue_override: @server_queue,
  294. poll_period: 1
  295. }
  296. @srv = RpcServer.new(**server_opts)
  297. end
  298. after(:each) do
  299. @srv.stop
  300. end
  301. it 'should return NOT_FOUND status on unknown methods', server: true do
  302. @srv.handle(EchoService)
  303. t = Thread.new { @srv.run }
  304. @srv.wait_till_running
  305. req = EchoMsg.new
  306. blk = proc do
  307. cq = GRPC::Core::CompletionQueue.new
  308. stub = GRPC::ClientStub.new(@host, cq, **client_opts)
  309. stub.request_response('/unknown', req, marshal, unmarshal)
  310. end
  311. expect(&blk).to raise_error GRPC::BadStatus
  312. @srv.stop
  313. t.join
  314. end
  315. it 'should handle multiple sequential requests', server: true do
  316. @srv.handle(EchoService)
  317. t = Thread.new { @srv.run }
  318. @srv.wait_till_running
  319. req = EchoMsg.new
  320. n = 5 # arbitrary
  321. stub = EchoStub.new(@host, **client_opts)
  322. n.times { expect(stub.an_rpc(req)).to be_a(EchoMsg) }
  323. @srv.stop
  324. t.join
  325. end
  326. it 'should receive metadata sent as rpc keyword args', server: true do
  327. service = EchoService.new
  328. @srv.handle(service)
  329. t = Thread.new { @srv.run }
  330. @srv.wait_till_running
  331. req = EchoMsg.new
  332. stub = EchoStub.new(@host, **client_opts)
  333. expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
  334. wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
  335. expect(service.received_md).to eq(wanted_md)
  336. @srv.stop
  337. t.join
  338. end
  339. it 'should receive metadata if a deadline is specified', server: true do
  340. service = SlowService.new
  341. @srv.handle(service)
  342. t = Thread.new { @srv.run }
  343. @srv.wait_till_running
  344. req = EchoMsg.new
  345. stub = SlowStub.new(@host, **client_opts)
  346. deadline = service.delay + 1.0 # wait for long enough
  347. expect(stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
  348. wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
  349. expect(service.received_md).to eq(wanted_md)
  350. @srv.stop
  351. t.join
  352. end
  353. it 'should not receive metadata if the client times out', server: true do
  354. service = SlowService.new
  355. @srv.handle(service)
  356. t = Thread.new { @srv.run }
  357. @srv.wait_till_running
  358. req = EchoMsg.new
  359. stub = SlowStub.new(@host, **client_opts)
  360. deadline = 0.1 # too short for SlowService to respond
  361. blk = proc { stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2') }
  362. expect(&blk).to raise_error GRPC::BadStatus
  363. wanted_md = []
  364. expect(service.received_md).to eq(wanted_md)
  365. @srv.stop
  366. t.join
  367. end
  368. it 'should handle cancellation correctly', server: true do
  369. service = SlowService.new
  370. @srv.handle(service)
  371. t = Thread.new { @srv.run }
  372. @srv.wait_till_running
  373. req = EchoMsg.new
  374. stub = SlowStub.new(@host, **client_opts)
  375. op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
  376. Thread.new do # cancel the call
  377. sleep 0.1
  378. op.cancel
  379. end
  380. expect { op.execute }.to raise_error GRPC::Cancelled
  381. @srv.stop
  382. t.join
  383. end
  384. it 'should receive updated metadata', server: true do
  385. service = EchoService.new
  386. @srv.handle(service)
  387. t = Thread.new { @srv.run }
  388. @srv.wait_till_running
  389. req = EchoMsg.new
  390. client_opts[:update_metadata] = proc do |md|
  391. md[:k1] = 'updated-v1'
  392. md
  393. end
  394. stub = EchoStub.new(@host, **client_opts)
  395. expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
  396. wanted_md = [{ 'k1' => 'updated-v1', 'k2' => 'v2',
  397. 'jwt_aud_uri' => "https://#{@host}/EchoService" }]
  398. expect(service.received_md).to eq(wanted_md)
  399. @srv.stop
  400. t.join
  401. end
  402. it 'should handle multiple parallel requests', server: true do
  403. @srv.handle(EchoService)
  404. t = Thread.new { @srv.run }
  405. @srv.wait_till_running
  406. req, q = EchoMsg.new, Queue.new
  407. n = 5 # arbitrary
  408. threads = [t]
  409. n.times do
  410. threads << Thread.new do
  411. stub = EchoStub.new(@host, **client_opts)
  412. q << stub.an_rpc(req)
  413. end
  414. end
  415. n.times { expect(q.pop).to be_a(EchoMsg) }
  416. @srv.stop
  417. threads.each(&:join)
  418. end
  419. it 'should return UNAVAILABLE on too many jobs', server: true do
  420. opts = {
  421. a_channel_arg: 'an_arg',
  422. server_override: @server,
  423. completion_queue_override: @server_queue,
  424. pool_size: 1,
  425. poll_period: 1,
  426. max_waiting_requests: 0
  427. }
  428. alt_srv = RpcServer.new(**opts)
  429. alt_srv.handle(SlowService)
  430. t = Thread.new { alt_srv.run }
  431. alt_srv.wait_till_running
  432. req = EchoMsg.new
  433. n = 5 # arbitrary, use as many to ensure the server pool is exceeded
  434. threads = []
  435. one_failed_as_unavailable = false
  436. n.times do
  437. threads << Thread.new do
  438. stub = SlowStub.new(@host, **client_opts)
  439. begin
  440. stub.an_rpc(req)
  441. rescue GRPC::BadStatus => e
  442. one_failed_as_unavailable = e.code == StatusCodes::UNAVAILABLE
  443. end
  444. end
  445. end
  446. threads.each(&:join)
  447. alt_srv.stop
  448. t.join
  449. expect(one_failed_as_unavailable).to be(true)
  450. end
  451. end
  452. context 'with connect metadata' do
  453. let(:test_md_proc) do
  454. proc do |mth, md|
  455. res = md.clone
  456. res['method'] = mth
  457. res['connect_k1'] = 'connect_v1'
  458. res
  459. end
  460. end
  461. before(:each) do
  462. server_opts = {
  463. server_override: @server,
  464. completion_queue_override: @server_queue,
  465. poll_period: 1,
  466. connect_md_proc: test_md_proc
  467. }
  468. @srv = RpcServer.new(**server_opts)
  469. end
  470. after(:each) do
  471. @srv.stop
  472. end
  473. it 'should send connect metadata to the client', server: true do
  474. service = EchoService.new
  475. @srv.handle(service)
  476. t = Thread.new { @srv.run }
  477. @srv.wait_till_running
  478. req = EchoMsg.new
  479. stub = EchoStub.new(@host, **client_opts)
  480. op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
  481. expect(op.metadata).to be nil
  482. expect(op.execute).to be_a(EchoMsg)
  483. wanted_md = {
  484. 'k1' => 'v1',
  485. 'k2' => 'v2',
  486. 'method' => '/EchoService/an_rpc',
  487. 'connect_k1' => 'connect_v1'
  488. }
  489. expect(op.metadata).to eq(wanted_md)
  490. @srv.stop
  491. t.join
  492. end
  493. end
  494. context 'with trailing metadata' do
  495. before(:each) do
  496. server_opts = {
  497. server_override: @server,
  498. completion_queue_override: @server_queue,
  499. poll_period: 1
  500. }
  501. @srv = RpcServer.new(**server_opts)
  502. end
  503. after(:each) do
  504. @srv.stop
  505. end
  506. it 'should be added to BadStatus when requests fail', server: true do
  507. service = FailingService.new
  508. @srv.handle(service)
  509. t = Thread.new { @srv.run }
  510. @srv.wait_till_running
  511. req = EchoMsg.new
  512. stub = FailingStub.new(@host, **client_opts)
  513. blk = proc { stub.an_rpc(req) }
  514. # confirm it raise the expected error
  515. expect(&blk).to raise_error GRPC::BadStatus
  516. # call again and confirm exception contained the trailing metadata.
  517. begin
  518. blk.call
  519. rescue GRPC::BadStatus => e
  520. expect(e.code).to eq(service.code)
  521. expect(e.details).to eq(service.details)
  522. expect(e.metadata).to eq(service.md)
  523. end
  524. @srv.stop
  525. t.join
  526. end
  527. it 'should be received by the client', server: true do
  528. wanted_trailers = { 'k1' => 'out_v1', 'k2' => 'out_v2' }
  529. service = EchoService.new(k1: 'out_v1', k2: 'out_v2')
  530. @srv.handle(service)
  531. t = Thread.new { @srv.run }
  532. @srv.wait_till_running
  533. req = EchoMsg.new
  534. stub = EchoStub.new(@host, **client_opts)
  535. op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
  536. expect(op.metadata).to be nil
  537. expect(op.execute).to be_a(EchoMsg)
  538. expect(op.metadata).to eq(wanted_trailers)
  539. @srv.stop
  540. t.join
  541. end
  542. end
  543. end
  544. end