| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 | # Copyright 2014, Google Inc.# All rights reserved.## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions are# met:##     * Redistributions of source code must retain the above copyright# notice, this list of conditions and the following disclaimer.#     * Redistributions in binary form must reproduce the above# copyright notice, this list of conditions and the following disclaimer# in the documentation and/or other materials provided with the# distribution.#     * Neither the name of Google Inc. nor the names of its# contributors may be used to endorse or promote products derived from# this software without specific prior written permission.## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.require 'grpc'require 'spec_helper'include GRPC::Core::CompletionTypeinclude GRPC::Coredef load_test_certs  test_root = File.join(File.dirname(__FILE__), 'testdata')  files = ['ca.pem', 'server1.key', 'server1.pem']  files.map { |f| File.open(File.join(test_root, f)).read }endshared_context 'setup: tags' do  before(:example) do    @server_finished_tag = Object.new    @client_finished_tag = Object.new    @client_metadata_tag = Object.new    @server_tag = Object.new    @tag = Object.new  end  def deadline    Time.now + 2  end  def expect_next_event_on(queue, type, tag)    ev = queue.pluck(tag, deadline)    if type.nil?      expect(ev).to be_nil    else      expect(ev).to_not be_nil      expect(ev.type).to be(type)    end    ev  end  def server_allows_client_to_proceed    @server.request_call(@server_tag)    ev = @server_queue.pluck(@server_tag, deadline)    expect(ev).not_to be_nil    expect(ev.type).to be(SERVER_RPC_NEW)    server_call = ev.call    server_call.server_accept(@server_queue, @server_finished_tag)    server_call.server_end_initial_metadata    server_call  end  def server_responds_with(server_call, reply_text)    reply = ByteBuffer.new(reply_text)    server_call.start_read(@server_tag)    ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)    expect(ev.type).to be(READ)    server_call.start_write(reply, @server_tag)    ev = @server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)    expect(ev).not_to be_nil    expect(ev.type).to be(WRITE_ACCEPTED)  end  def client_sends(call, sent = 'a message')    req = ByteBuffer.new(sent)    call.start_write(req, @tag)    ev = @client_queue.pluck(@tag, TimeConsts::INFINITE_FUTURE)    expect(ev).not_to be_nil    expect(ev.type).to be(WRITE_ACCEPTED)    sent  end  def new_client_call    @ch.create_call('/method', 'localhost', deadline)  endendshared_examples 'basic GRPC message delivery is OK' do  include_context 'setup: tags'  it 'servers receive requests from clients and start responding' do    reply = ByteBuffer.new('the server payload')    call = new_client_call    call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)    # check the server rpc new was received    # @server.request_call(@server_tag)    # ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)    # accept the call    # server_call = ev.call    # server_call.server_accept(@server_queue, @server_finished_tag)    # server_call.server_end_initial_metadata    server_call = server_allows_client_to_proceed    # client sends a message    msg = client_sends(call)    # confirm the server can read the inbound message    server_call.start_read(@server_tag)    ev = expect_next_event_on(@server_queue, READ, @server_tag)    expect(ev.result.to_s).to eq(msg)    #  the server response    server_call.start_write(reply, @server_tag)    expect_next_event_on(@server_queue, WRITE_ACCEPTED, @server_tag)  end  it 'responses written by servers are received by the client' do    call = new_client_call    call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)    server_call = server_allows_client_to_proceed    client_sends(call)    server_responds_with(server_call, 'server_response')    call.start_read(@tag)    ev = expect_next_event_on(@client_queue, READ, @tag)    expect(ev.result.to_s).to eq('server_response')  end  it 'servers can ignore a client write and send a status' do    call = new_client_call    call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)    # check the server rpc new was received    @server.request_call(@server_tag)    ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)    expect(ev.tag).to be(@server_tag)    # accept the call - need to do this to sent status.    server_call = ev.call    server_call.server_accept(@server_queue, @server_finished_tag)    server_call.server_end_initial_metadata    server_call.start_write_status(StatusCodes::NOT_FOUND, 'not found',                                   @server_tag)    # Client sends some data    client_sends(call)    # client gets an empty response for the read, preceeded by some metadata.    call.start_read(@tag)    expect_next_event_on(@client_queue, CLIENT_METADATA_READ,                         @client_metadata_tag)    ev = expect_next_event_on(@client_queue, READ, @tag)    expect(ev.tag).to be(@tag)    expect(ev.result.to_s).to eq('')    # finally, after client sends writes_done, they get the finished.    call.writes_done(@tag)    expect_next_event_on(@client_queue, FINISH_ACCEPTED, @tag)    ev = expect_next_event_on(@client_queue, FINISHED, @client_finished_tag)    expect(ev.result.code).to eq(StatusCodes::NOT_FOUND)  end  it 'completes calls by sending status to client and server' do    call = new_client_call    call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)    server_call = server_allows_client_to_proceed    client_sends(call)    server_responds_with(server_call, 'server_response')    server_call.start_write_status(10_101, 'status code is 10101', @server_tag)    # first the client says writes are done    call.start_read(@tag)    expect_next_event_on(@client_queue, READ, @tag)    call.writes_done(@tag)    # but nothing happens until the server sends a status    expect_next_event_on(@server_queue, FINISH_ACCEPTED, @server_tag)    ev = expect_next_event_on(@server_queue, FINISHED, @server_finished_tag)    expect(ev.result).to be_a(Struct::Status)    # client gets FINISHED    expect_next_event_on(@client_queue, FINISH_ACCEPTED, @tag)    ev = expect_next_event_on(@client_queue, FINISHED, @client_finished_tag)    expect(ev.result.details).to eq('status code is 10101')    expect(ev.result.code).to eq(10_101)  endendshared_examples 'GRPC metadata delivery works OK' do  include_context 'setup: tags'  describe 'from client => server' do    before(:example) do      n = 7  # arbitrary number of metadata      diff_keys_fn = proc { |i| [sprintf('k%d', i), sprintf('v%d', i)] }      diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]      null_vals_fn = proc { |i| [sprintf('k%d', i), sprintf('v\0%d', i)] }      null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]      same_keys_fn = proc { |i| [sprintf('k%d', i), [sprintf('v%d', i)] * n] }      same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]      symbol_key = { a_key: 'a val' }      @valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]      @bad_keys = []      @bad_keys << { Object.new => 'a value' }      @bad_keys << { 1 => 'a value' }    end    it 'raises an exception if a metadata key is invalid' do      @bad_keys.each do |md|        call = new_client_call        expect { call.add_metadata(md) }.to raise_error      end    end    it 'sends all the metadata pairs when keys and values are valid' do      @valid_metadata.each do |md|        call = new_client_call        call.add_metadata(md)        # Client begins a call OK        call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)        # ... server has all metadata available even though the client did not        # send a write        @server.request_call(@server_tag)        ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)        replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]        result = ev.result.metadata        expect(result.merge(replace_symbols)).to eq(result)      end    end  end  describe 'from server => client' do    before(:example) do      n = 7  # arbitrary number of metadata      diff_keys_fn = proc { |i| [sprintf('k%d', i), sprintf('v%d', i)] }      diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]      null_vals_fn = proc { |i| [sprintf('k%d', i), sprintf('v\0%d', i)] }      null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]      same_keys_fn = proc { |i| [sprintf('k%d', i), [sprintf('v%d', i)] * n] }      same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]      symbol_key = { a_key: 'a val' }      @valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]      @bad_keys = []      @bad_keys << { Object.new => 'a value' }      @bad_keys << { 1 => 'a value' }    end    it 'raises an exception if a metadata key is invalid' do      @bad_keys.each do |md|        call = new_client_call        call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)        # server gets the invocation        @server.request_call(@server_tag)        ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)        expect { ev.call.add_metadata(md) }.to raise_error      end    end    it 'sends a hash that contains the status when no metadata is added' do      call = new_client_call      call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)      # server gets the invocation      @server.request_call(@server_tag)      ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)      server_call = ev.call      # ... server accepts the call without adding metadata      server_call.server_accept(@server_queue, @server_finished_tag)      server_call.server_end_initial_metadata      # there is the HTTP status metadata, though there should not be any      # TODO: update this with the bug number to be resolved      ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ,                                @client_metadata_tag)      expect(ev.result).to eq(':status' => '200')    end    it 'sends all the pairs and status:200 when keys and values are valid' do      @valid_metadata.each do |md|        call = new_client_call        call.invoke(@client_queue, @client_metadata_tag, @client_finished_tag)        # server gets the invocation        @server.request_call(@server_tag)        ev = expect_next_event_on(@server_queue, SERVER_RPC_NEW, @server_tag)        server_call = ev.call        # ... server adds metadata and accepts the call        server_call.add_metadata(md)        server_call.server_accept(@server_queue, @server_finished_tag)        server_call.server_end_initial_metadata        # Now the client can read the metadata        ev = expect_next_event_on(@client_queue, CLIENT_METADATA_READ,                                  @client_metadata_tag)        replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]        replace_symbols[':status'] = '200'        expect(ev.result).to eq(replace_symbols)      end    end  endenddescribe 'the http client/server' do  before(:example) do    server_host = '0.0.0.0:0'    @client_queue = GRPC::Core::CompletionQueue.new    @server_queue = GRPC::Core::CompletionQueue.new    @server = GRPC::Core::Server.new(@server_queue, nil)    server_port = @server.add_http2_port(server_host)    @server.start    @ch = Channel.new("0.0.0.0:#{server_port}", nil)  end  after(:example) do    @ch.close    @server.close  end  it_behaves_like 'basic GRPC message delivery is OK' do  end  it_behaves_like 'GRPC metadata delivery works OK' do  endenddescribe 'the secure http client/server' do  before(:example) do    certs = load_test_certs    server_host = 'localhost:0'    @client_queue = GRPC::Core::CompletionQueue.new    @server_queue = GRPC::Core::CompletionQueue.new    server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])    @server = GRPC::Core::Server.new(@server_queue, nil, server_creds)    server_port = @server.add_http2_port(server_host, true)    @server.start    args = { Channel::SSL_TARGET => 'foo.test.google.com' }    @ch = Channel.new("0.0.0.0:#{server_port}", args,                      GRPC::Core::Credentials.new(certs[0], nil, nil))  end  after(:example) do    @server.close  end  it_behaves_like 'basic GRPC message delivery is OK' do  end  it_behaves_like 'GRPC metadata delivery works OK' do  endend
 |