1# Copyright 2015 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15require 'spec_helper'
16
17include GRPC::Core
18
19shared_context 'setup: tags' do
20  let(:sent_message) { 'sent message' }
21  let(:reply_text) { 'the reply' }
22
23  def deadline
24    Time.now + 5
25  end
26
27  def server_allows_client_to_proceed(metadata = {})
28    recvd_rpc = @server.request_call
29    expect(recvd_rpc).to_not eq nil
30    server_call = recvd_rpc.call
31    ops = { CallOps::SEND_INITIAL_METADATA => metadata }
32    server_batch = server_call.run_batch(ops)
33    expect(server_batch.send_metadata).to be true
34    server_call
35  end
36
37  def new_client_call
38    @ch.create_call(nil, nil, '/method', nil, deadline)
39  end
40
41  def ok_status
42    Struct::Status.new(StatusCodes::OK, 'OK')
43  end
44end
45
46shared_examples 'basic GRPC message delivery is OK' do
47  include GRPC::Core
48  include_context 'setup: tags'
49
50  context 'the test channel' do
51    it 'should have a target' do
52      expect(@ch.target).to be_a(String)
53    end
54  end
55
56  context 'a client call' do
57    it 'should have a peer' do
58      expect(new_client_call.peer).to be_a(String)
59    end
60  end
61
62  it 'calls have peer info' do
63    call = new_client_call
64    expect(call.peer).to be_a(String)
65  end
66
67  it 'servers receive requests from clients and can respond' do
68    call = new_client_call
69    server_call = nil
70
71    server_thread = Thread.new do
72      server_call = server_allows_client_to_proceed
73    end
74
75    client_ops = {
76      CallOps::SEND_INITIAL_METADATA => {},
77      CallOps::SEND_MESSAGE => sent_message,
78      CallOps::SEND_CLOSE_FROM_CLIENT => nil
79    }
80    client_batch = call.run_batch(client_ops)
81    expect(client_batch.send_metadata).to be true
82    expect(client_batch.send_message).to be true
83    expect(client_batch.send_close).to be true
84
85    # confirm the server can read the inbound message
86    server_thread.join
87    server_ops = {
88      CallOps::RECV_MESSAGE => nil,
89      CallOps::RECV_CLOSE_ON_SERVER => nil,
90      CallOps::SEND_STATUS_FROM_SERVER => ok_status
91    }
92    server_batch = server_call.run_batch(server_ops)
93    expect(server_batch.message).to eq(sent_message)
94    expect(server_batch.send_close).to be true
95    expect(server_batch.send_status).to be true
96
97    # finish the call
98    final_client_batch = call.run_batch(
99      CallOps::RECV_INITIAL_METADATA => nil,
100      CallOps::RECV_STATUS_ON_CLIENT => nil)
101    expect(final_client_batch.metadata).to eq({})
102    expect(final_client_batch.status.code).to eq(0)
103  end
104
105  it 'responses written by servers are received by the client' do
106    call = new_client_call
107    server_call = nil
108
109    server_thread = Thread.new do
110      server_call = server_allows_client_to_proceed
111    end
112
113    client_ops = {
114      CallOps::SEND_INITIAL_METADATA => {},
115      CallOps::SEND_MESSAGE => sent_message,
116      CallOps::SEND_CLOSE_FROM_CLIENT => nil
117    }
118    client_batch = call.run_batch(client_ops)
119    expect(client_batch.send_metadata).to be true
120    expect(client_batch.send_message).to be true
121    expect(client_batch.send_close).to be true
122
123    # confirm the server can read the inbound message
124    server_thread.join
125    server_ops = {
126      CallOps::RECV_MESSAGE => nil,
127      CallOps::RECV_CLOSE_ON_SERVER => nil,
128      CallOps::SEND_MESSAGE => reply_text,
129      CallOps::SEND_STATUS_FROM_SERVER => ok_status
130    }
131    server_batch = server_call.run_batch(server_ops)
132    expect(server_batch.message).to eq(sent_message)
133    expect(server_batch.send_close).to be true
134    expect(server_batch.send_message).to be true
135    expect(server_batch.send_status).to be true
136
137    # finish the call
138    final_client_batch = call.run_batch(
139      CallOps::RECV_INITIAL_METADATA => nil,
140      CallOps::RECV_MESSAGE => nil,
141      CallOps::RECV_STATUS_ON_CLIENT => nil)
142    expect(final_client_batch.metadata).to eq({})
143    expect(final_client_batch.message).to eq(reply_text)
144    expect(final_client_batch.status.code).to eq(0)
145  end
146
147  it 'compressed messages can be sent and received' do
148    call = new_client_call
149    server_call = nil
150    long_request_str = '0' * 2000
151    long_response_str = '1' * 2000
152    md = { 'grpc-internal-encoding-request' => 'gzip' }
153
154    server_thread = Thread.new do
155      server_call = server_allows_client_to_proceed(md)
156    end
157
158    client_ops = {
159      CallOps::SEND_INITIAL_METADATA => md,
160      CallOps::SEND_MESSAGE => long_request_str,
161      CallOps::SEND_CLOSE_FROM_CLIENT => nil
162    }
163    client_batch = call.run_batch(client_ops)
164    expect(client_batch.send_metadata).to be true
165    expect(client_batch.send_message).to be true
166    expect(client_batch.send_close).to be true
167
168    # confirm the server can read the inbound message
169    server_thread.join
170    server_ops = {
171      CallOps::RECV_MESSAGE => nil,
172      CallOps::RECV_CLOSE_ON_SERVER => nil,
173      CallOps::SEND_MESSAGE => long_response_str,
174      CallOps::SEND_STATUS_FROM_SERVER => ok_status
175    }
176    server_batch = server_call.run_batch(server_ops)
177    expect(server_batch.message).to eq(long_request_str)
178    expect(server_batch.send_close).to be true
179    expect(server_batch.send_message).to be true
180    expect(server_batch.send_status).to be true
181
182    client_ops = {
183      CallOps::RECV_INITIAL_METADATA => nil,
184      CallOps::RECV_MESSAGE => nil,
185      CallOps::RECV_STATUS_ON_CLIENT => nil
186    }
187    final_client_batch = call.run_batch(client_ops)
188    expect(final_client_batch.metadata).to eq({})
189    expect(final_client_batch.message).to eq long_response_str
190    expect(final_client_batch.status.code).to eq(0)
191  end
192
193  it 'servers can ignore a client write and send a status' do
194    call = new_client_call
195    server_call = nil
196
197    server_thread = Thread.new do
198      server_call = server_allows_client_to_proceed
199    end
200
201    client_ops = {
202      CallOps::SEND_INITIAL_METADATA => {},
203      CallOps::SEND_MESSAGE => sent_message,
204      CallOps::SEND_CLOSE_FROM_CLIENT => nil
205    }
206    client_batch = call.run_batch(client_ops)
207    expect(client_batch.send_metadata).to be true
208    expect(client_batch.send_message).to be true
209    expect(client_batch.send_close).to be true
210
211    # confirm the server can read the inbound message
212    the_status = Struct::Status.new(StatusCodes::OK, 'OK')
213    server_thread.join
214    server_ops = {
215      CallOps::SEND_STATUS_FROM_SERVER => the_status
216    }
217    server_batch = server_call.run_batch(server_ops)
218    expect(server_batch.message).to eq nil
219    expect(server_batch.send_status).to be true
220
221    final_client_batch = call.run_batch(
222      CallOps::RECV_INITIAL_METADATA => nil,
223      CallOps::RECV_STATUS_ON_CLIENT => nil)
224    expect(final_client_batch.metadata).to eq({})
225    expect(final_client_batch.status.code).to eq(0)
226  end
227
228  it 'completes calls by sending status to client and server' do
229    call = new_client_call
230    server_call = nil
231
232    server_thread = Thread.new do
233      server_call = server_allows_client_to_proceed
234    end
235
236    client_ops = {
237      CallOps::SEND_INITIAL_METADATA => {},
238      CallOps::SEND_MESSAGE => sent_message
239    }
240    client_batch = call.run_batch(client_ops)
241    expect(client_batch.send_metadata).to be true
242    expect(client_batch.send_message).to be true
243
244    # confirm the server can read the inbound message and respond
245    the_status = Struct::Status.new(StatusCodes::OK, 'OK', {})
246    server_thread.join
247    server_ops = {
248      CallOps::RECV_MESSAGE => nil,
249      CallOps::SEND_MESSAGE => reply_text,
250      CallOps::SEND_STATUS_FROM_SERVER => the_status
251    }
252    server_batch = server_call.run_batch(server_ops)
253    expect(server_batch.message).to eq sent_message
254    expect(server_batch.send_status).to be true
255    expect(server_batch.send_message).to be true
256
257    # confirm the client can receive the server response and status.
258    client_ops = {
259      CallOps::SEND_CLOSE_FROM_CLIENT => nil,
260      CallOps::RECV_INITIAL_METADATA => nil,
261      CallOps::RECV_MESSAGE => nil,
262      CallOps::RECV_STATUS_ON_CLIENT => nil
263    }
264    final_client_batch = call.run_batch(client_ops)
265    expect(final_client_batch.send_close).to be true
266    expect(final_client_batch.message).to eq reply_text
267    expect(final_client_batch.status).to eq the_status
268
269    # confirm the server can receive the client close.
270    server_ops = {
271      CallOps::RECV_CLOSE_ON_SERVER => nil
272    }
273    final_server_batch = server_call.run_batch(server_ops)
274    expect(final_server_batch.send_close).to be true
275  end
276
277  def client_cancel_test(cancel_proc, expected_code,
278                         expected_details)
279    call = new_client_call
280    server_call = nil
281
282    server_thread = Thread.new do
283      server_call = server_allows_client_to_proceed
284    end
285
286    client_ops = {
287      CallOps::SEND_INITIAL_METADATA => {},
288      CallOps::RECV_INITIAL_METADATA => nil
289    }
290    client_batch = call.run_batch(client_ops)
291    expect(client_batch.send_metadata).to be true
292    expect(client_batch.metadata).to eq({})
293
294    cancel_proc.call(call)
295
296    server_thread.join
297    server_ops = {
298      CallOps::RECV_CLOSE_ON_SERVER => nil
299    }
300    server_batch = server_call.run_batch(server_ops)
301    expect(server_batch.send_close).to be true
302
303    client_ops = {
304      CallOps::RECV_STATUS_ON_CLIENT => {}
305    }
306    client_batch = call.run_batch(client_ops)
307
308    expect(client_batch.status.code).to be expected_code
309    expect(client_batch.status.details).to eq expected_details
310  end
311
312  it 'clients can cancel a call on the server' do
313    expected_code = StatusCodes::CANCELLED
314    expected_details = 'Cancelled'
315    cancel_proc = proc { |call| call.cancel }
316    client_cancel_test(cancel_proc, expected_code, expected_details)
317  end
318
319  it 'cancel_with_status unknown status' do
320    code = StatusCodes::UNKNOWN
321    details = 'test unknown reason'
322    cancel_proc = proc { |call| call.cancel_with_status(code, details) }
323    client_cancel_test(cancel_proc, code, details)
324  end
325
326  it 'cancel_with_status unknown status' do
327    code = StatusCodes::FAILED_PRECONDITION
328    details = 'test failed precondition reason'
329    cancel_proc = proc { |call| call.cancel_with_status(code, details) }
330    client_cancel_test(cancel_proc, code, details)
331  end
332end
333
334shared_examples 'GRPC metadata delivery works OK' do
335  include_context 'setup: tags'
336
337  describe 'from client => server' do
338    before(:example) do
339      n = 7  # arbitrary number of metadata
340      diff_keys_fn = proc { |i| [format('k%d', i), format('v%d', i)] }
341      diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]
342      null_vals_fn = proc { |i| [format('k%d', i), format('v\0%d', i)] }
343      null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]
344      same_keys_fn = proc { |i| [format('k%d', i), [format('v%d', i)] * n] }
345      same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]
346      symbol_key = { a_key: 'a val' }
347      @valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]
348      @bad_keys = []
349      @bad_keys << { Object.new => 'a value' }
350      @bad_keys << { 1 => 'a value' }
351    end
352
353    it 'raises an exception if a metadata key is invalid' do
354      @bad_keys.each do |md|
355        call = new_client_call
356        client_ops = {
357          CallOps::SEND_INITIAL_METADATA => md
358        }
359        blk = proc do
360          call.run_batch(client_ops)
361        end
362        expect(&blk).to raise_error
363      end
364    end
365
366    it 'sends all the metadata pairs when keys and values are valid' do
367      @valid_metadata.each do |md|
368        recvd_rpc = nil
369        rcv_thread = Thread.new do
370          recvd_rpc = @server.request_call
371        end
372
373        call = new_client_call
374        client_ops = {
375          CallOps::SEND_INITIAL_METADATA => md,
376          CallOps::SEND_CLOSE_FROM_CLIENT => nil
377        }
378        client_batch = call.run_batch(client_ops)
379        expect(client_batch.send_metadata).to be true
380
381        # confirm the server can receive the client metadata
382        rcv_thread.join
383        expect(recvd_rpc).to_not eq nil
384        recvd_md = recvd_rpc.metadata
385        replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
386        expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
387
388        # finish the call
389        final_server_batch = recvd_rpc.call.run_batch(
390          CallOps::RECV_CLOSE_ON_SERVER => nil,
391          CallOps::SEND_INITIAL_METADATA => nil,
392          CallOps::SEND_STATUS_FROM_SERVER => ok_status)
393        expect(final_server_batch.send_close).to be(true)
394        expect(final_server_batch.send_metadata).to be(true)
395        expect(final_server_batch.send_status).to be(true)
396
397        final_client_batch = call.run_batch(
398          CallOps::RECV_INITIAL_METADATA => nil,
399          CallOps::RECV_STATUS_ON_CLIENT => nil)
400        expect(final_client_batch.metadata).to eq({})
401        expect(final_client_batch.status.code).to eq(0)
402      end
403    end
404  end
405
406  describe 'from server => client' do
407    before(:example) do
408      n = 7  # arbitrary number of metadata
409      diff_keys_fn = proc { |i| [format('k%d', i), format('v%d', i)] }
410      diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]
411      null_vals_fn = proc { |i| [format('k%d', i), format('v\0%d', i)] }
412      null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]
413      same_keys_fn = proc { |i| [format('k%d', i), [format('v%d', i)] * n] }
414      same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]
415      symbol_key = { a_key: 'a val' }
416      @valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]
417      @bad_keys = []
418      @bad_keys << { Object.new => 'a value' }
419      @bad_keys << { 1 => 'a value' }
420    end
421
422    it 'raises an exception if a metadata key is invalid' do
423      @bad_keys.each do |md|
424        recvd_rpc = nil
425        rcv_thread = Thread.new do
426          recvd_rpc = @server.request_call
427        end
428
429        call = new_client_call
430        # client signals that it's done sending metadata to allow server to
431        # respond
432        client_ops = {
433          CallOps::SEND_INITIAL_METADATA => nil
434        }
435        call.run_batch(client_ops)
436
437        # server gets the invocation
438        rcv_thread.join
439        expect(recvd_rpc).to_not eq nil
440        server_ops = {
441          CallOps::SEND_INITIAL_METADATA => md
442        }
443        blk = proc do
444          recvd_rpc.call.run_batch(server_ops)
445        end
446        expect(&blk).to raise_error
447
448        # cancel the call so the server can shut down immediately
449        call.cancel
450      end
451    end
452
453    it 'sends an empty hash if no metadata is added' do
454      recvd_rpc = nil
455      rcv_thread = Thread.new do
456        recvd_rpc = @server.request_call
457      end
458
459      call = new_client_call
460      # client signals that it's done sending metadata to allow server to
461      # respond
462      client_ops = {
463        CallOps::SEND_INITIAL_METADATA => nil,
464        CallOps::SEND_CLOSE_FROM_CLIENT => nil
465      }
466      client_batch = call.run_batch(client_ops)
467      expect(client_batch.send_metadata).to be true
468      expect(client_batch.send_close).to be true
469
470      # server gets the invocation but sends no metadata back
471      rcv_thread.join
472      expect(recvd_rpc).to_not eq nil
473      server_call = recvd_rpc.call
474      server_ops = {
475        # receive close and send status to finish the call
476        CallOps::RECV_CLOSE_ON_SERVER => nil,
477        CallOps::SEND_INITIAL_METADATA => nil,
478        CallOps::SEND_STATUS_FROM_SERVER => ok_status
479      }
480      srv_batch = server_call.run_batch(server_ops)
481      expect(srv_batch.send_close).to be true
482      expect(srv_batch.send_metadata).to be true
483      expect(srv_batch.send_status).to be true
484
485      # client receives nothing as expected
486      client_ops = {
487        CallOps::RECV_INITIAL_METADATA => nil,
488        # receive status to finish the call
489        CallOps::RECV_STATUS_ON_CLIENT => nil
490      }
491      final_client_batch = call.run_batch(client_ops)
492      expect(final_client_batch.metadata).to eq({})
493      expect(final_client_batch.status.code).to eq(0)
494    end
495
496    it 'sends all the pairs when keys and values are valid' do
497      @valid_metadata.each do |md|
498        recvd_rpc = nil
499        rcv_thread = Thread.new do
500          recvd_rpc = @server.request_call
501        end
502
503        call = new_client_call
504        # client signals that it's done sending metadata to allow server to
505        # respond
506        client_ops = {
507          CallOps::SEND_INITIAL_METADATA => nil,
508          CallOps::SEND_CLOSE_FROM_CLIENT => nil
509        }
510        client_batch = call.run_batch(client_ops)
511        expect(client_batch.send_metadata).to be true
512        expect(client_batch.send_close).to be true
513
514        # server gets the invocation but sends no metadata back
515        rcv_thread.join
516        expect(recvd_rpc).to_not eq nil
517        server_call = recvd_rpc.call
518        server_ops = {
519          CallOps::RECV_CLOSE_ON_SERVER => nil,
520          CallOps::SEND_INITIAL_METADATA => md,
521          CallOps::SEND_STATUS_FROM_SERVER => ok_status
522        }
523        srv_batch = server_call.run_batch(server_ops)
524        expect(srv_batch.send_close).to be true
525        expect(srv_batch.send_metadata).to be true
526        expect(srv_batch.send_status).to be true
527
528        # client receives nothing as expected
529        client_ops = {
530          CallOps::RECV_INITIAL_METADATA => nil,
531          CallOps::RECV_STATUS_ON_CLIENT => nil
532        }
533        final_client_batch = call.run_batch(client_ops)
534        replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
535        expect(final_client_batch.metadata).to eq(replace_symbols)
536        expect(final_client_batch.status.code).to eq(0)
537      end
538    end
539  end
540end
541
542describe 'the http client/server' do
543  before(:example) do
544    server_host = '0.0.0.0:0'
545    @server = new_core_server_for_testing(nil)
546    server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
547    @server.start
548    @ch = Channel.new("0.0.0.0:#{server_port}", nil, :this_channel_is_insecure)
549  end
550
551  after(:example) do
552    @ch.close
553    @server.shutdown_and_notify(deadline)
554    @server.close
555  end
556
557  it_behaves_like 'basic GRPC message delivery is OK' do
558  end
559
560  it_behaves_like 'GRPC metadata delivery works OK' do
561  end
562end
563
564describe 'the secure http client/server' do
565  include_context 'setup: tags'
566
567  def load_test_certs
568    test_root = File.join(File.dirname(__FILE__), 'testdata')
569    files = ['ca.pem', 'server1.key', 'server1.pem']
570    files.map { |f| File.open(File.join(test_root, f)).read }
571  end
572
573  before(:example) do
574    certs = load_test_certs
575    server_host = '0.0.0.0:0'
576    server_creds = GRPC::Core::ServerCredentials.new(
577      nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
578    @server = new_core_server_for_testing(nil)
579    server_port = @server.add_http2_port(server_host, server_creds)
580    @server.start
581    args = { Channel::SSL_TARGET => 'foo.test.google.fr' }
582    @ch = Channel.new("0.0.0.0:#{server_port}", args,
583                      GRPC::Core::ChannelCredentials.new(certs[0], nil, nil))
584  end
585
586  after(:example) do
587    @server.shutdown_and_notify(deadline)
588    @server.close
589  end
590
591  it_behaves_like 'basic GRPC message delivery is OK' do
592  end
593
594  it_behaves_like 'GRPC metadata delivery works OK' do
595  end
596
597  def credentials_update_test(creds_update_md)
598    auth_proc = proc { creds_update_md }
599    call_creds = GRPC::Core::CallCredentials.new(auth_proc)
600
601    initial_md_key = 'k2'
602    initial_md_val = 'v2'
603    initial_md = { initial_md_key => initial_md_val }
604    expected_md = creds_update_md.clone
605    fail 'bad test param' unless expected_md[initial_md_key].nil?
606    expected_md[initial_md_key] = initial_md_val
607
608    recvd_rpc = nil
609    rcv_thread = Thread.new do
610      recvd_rpc = @server.request_call
611    end
612
613    call = new_client_call
614    call.set_credentials! call_creds
615
616    client_batch = call.run_batch(
617      CallOps::SEND_INITIAL_METADATA => initial_md,
618      CallOps::SEND_CLOSE_FROM_CLIENT => nil)
619    expect(client_batch.send_metadata).to be true
620    expect(client_batch.send_close).to be true
621
622    # confirm the server can receive the client metadata
623    rcv_thread.join
624    expect(recvd_rpc).to_not eq nil
625    recvd_md = recvd_rpc.metadata
626    replace_symbols = Hash[expected_md.each_pair.collect { |x, y| [x.to_s, y] }]
627    expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
628
629    credentials_update_test_finish_call(call, recvd_rpc.call)
630  end
631
632  def credentials_update_test_finish_call(client_call, server_call)
633    final_server_batch = server_call.run_batch(
634      CallOps::RECV_CLOSE_ON_SERVER => nil,
635      CallOps::SEND_INITIAL_METADATA => nil,
636      CallOps::SEND_STATUS_FROM_SERVER => ok_status)
637    expect(final_server_batch.send_close).to be(true)
638    expect(final_server_batch.send_metadata).to be(true)
639    expect(final_server_batch.send_status).to be(true)
640
641    final_client_batch = client_call.run_batch(
642      CallOps::RECV_INITIAL_METADATA => nil,
643      CallOps::RECV_STATUS_ON_CLIENT => nil)
644    expect(final_client_batch.metadata).to eq({})
645    expect(final_client_batch.status.code).to eq(0)
646  end
647
648  it 'modifies metadata with CallCredentials' do
649    credentials_update_test('k1' => 'updated-v1')
650  end
651
652  it 'modifies large metadata with CallCredentials' do
653    val_array = %w(
654      '00000000000000000000000000000000000000000000000000000000000000',
655      '11111111111111111111111111111111111111111111111111111111111111',
656    )
657    md = {
658      k3: val_array,
659      k4: '0000000000000000000000000000000000000000000000000000000000',
660      keeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeey5: 'v1'
661    }
662    credentials_update_test(md)
663  end
664end
665