Skip to content
Snippets Groups Projects
Commit 7e7911f7 authored by Tim Emiola's avatar Tim Emiola
Browse files

Updates the module name in the idiomatic and stub layers

parent 409e6c80
No related branches found
No related tags found
No related merge requests found
Showing
with 1663 additions and 1682 deletions
...@@ -41,4 +41,4 @@ require 'grpc/generic/service' ...@@ -41,4 +41,4 @@ require 'grpc/generic/service'
require 'grpc/generic/rpc_server' require 'grpc/generic/rpc_server'
# alias GRPC # alias GRPC
GRPC = Google::RPC GRPC = GRPC
...@@ -30,39 +30,37 @@ ...@@ -30,39 +30,37 @@
require 'faraday' require 'faraday'
require 'grpc/auth/signet' require 'grpc/auth/signet'
module Google module GRPC
module RPC # Module Auth provides classes that provide Google-specific authentication
# Module Auth provides classes that provide Google-specific authentication # used to access Google gRPC services.
# used to access Google gRPC services. module Auth
module Auth # Extends Signet::OAuth2::Client so that the auth token is obtained from
# Extends Signet::OAuth2::Client so that the auth token is obtained from # the GCE metadata server.
# the GCE metadata server. class GCECredentials < Signet::OAuth2::Client
class GCECredentials < Signet::OAuth2::Client COMPUTE_AUTH_TOKEN_URI = 'http://metadata/computeMetadata/v1/'\
COMPUTE_AUTH_TOKEN_URI = 'http://metadata/computeMetadata/v1/'\ 'instance/service-accounts/default/token'
'instance/service-accounts/default/token' COMPUTE_CHECK_URI = 'http://metadata.google.internal'
COMPUTE_CHECK_URI = 'http://metadata.google.internal'
# Detect if this appear to be a GCE instance, by checking if metadata # Detect if this appear to be a GCE instance, by checking if metadata
# is available # is available
def self.on_gce?(options = {}) def self.on_gce?(options = {})
c = options[:connection] || Faraday.default_connection c = options[:connection] || Faraday.default_connection
resp = c.get(COMPUTE_CHECK_URI) resp = c.get(COMPUTE_CHECK_URI)
return false unless resp.status == 200 return false unless resp.status == 200
return false unless resp.headers.key?('Metadata-Flavor') return false unless resp.headers.key?('Metadata-Flavor')
return resp.headers['Metadata-Flavor'] == 'Google' return resp.headers['Metadata-Flavor'] == 'Google'
rescue Faraday::ConnectionFailed rescue Faraday::ConnectionFailed
return false return false
end end
# Overrides the super class method to change how access tokens are # Overrides the super class method to change how access tokens are
# fetched. # fetched.
def fetch_access_token(options = {}) def fetch_access_token(options = {})
c = options[:connection] || Faraday.default_connection c = options[:connection] || Faraday.default_connection
c.headers = { 'Metadata-Flavor' => 'Google' } c.headers = { 'Metadata-Flavor' => 'Google' }
resp = c.get(COMPUTE_AUTH_TOKEN_URI) resp = c.get(COMPUTE_AUTH_TOKEN_URI)
Signet::OAuth2.parse_credentials(resp.body, Signet::OAuth2.parse_credentials(resp.body,
resp.headers['content-type']) resp.headers['content-type'])
end
end end
end end
end end
......
...@@ -39,29 +39,27 @@ def read_json_key(json_key_io) ...@@ -39,29 +39,27 @@ def read_json_key(json_key_io)
[json_key['private_key'], json_key['client_email']] [json_key['private_key'], json_key['client_email']]
end end
module Google module GRPC
module RPC # Module Auth provides classes that provide Google-specific authentication
# Module Auth provides classes that provide Google-specific authentication # used to access Google gRPC services.
# used to access Google gRPC services. module Auth
module Auth # Authenticates requests using Google's Service Account credentials.
# Authenticates requests using Google's Service Account credentials. # (cf https://developers.google.com/accounts/docs/OAuth2ServiceAccount)
# (cf https://developers.google.com/accounts/docs/OAuth2ServiceAccount) class ServiceAccountCredentials < Signet::OAuth2::Client
class ServiceAccountCredentials < Signet::OAuth2::Client TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token' AUDIENCE = TOKEN_CRED_URI
AUDIENCE = TOKEN_CRED_URI
# Initializes a ServiceAccountCredentials. # Initializes a ServiceAccountCredentials.
# #
# @param scope [string|array] the scope(s) to access # @param scope [string|array] the scope(s) to access
# @param json_key_io [IO] an IO from which the JSON key can be read # @param json_key_io [IO] an IO from which the JSON key can be read
def initialize(scope, json_key_io) def initialize(scope, json_key_io)
private_key, client_email = read_json_key(json_key_io) private_key, client_email = read_json_key(json_key_io)
super(token_credential_uri: TOKEN_CRED_URI, super(token_credential_uri: TOKEN_CRED_URI,
audience: AUDIENCE, audience: AUDIENCE,
scope: scope, scope: scope,
issuer: client_email, issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key)) signing_key: OpenSSL::PKey::RSA.new(private_key))
end
end end
end end
end end
......
...@@ -27,16 +27,17 @@ ...@@ -27,16 +27,17 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Google require 'grpc'
module RPC
module Core # GRPC contains the General RPC module.
# Event is a class defined in the c extension module GRPC
# module Core
# Here, we add an inspect method. # Event is a class defined in the c extension
class Event #
def inspect # Here, we add an inspect method.
"<#{self.class}: type:#{type}, tag:#{tag} result:#{result}>" class Event
end def inspect
"<#{self.class}: type:#{type}, tag:#{tag} result:#{result}>"
end end
end end
end end
......
...@@ -29,44 +29,43 @@ ...@@ -29,44 +29,43 @@
require 'grpc' require 'grpc'
module Google # GRPC contains the General RPC module.
module RPC module GRPC
module Core module Core
# TimeConsts is a module from the C extension. # TimeConsts is a module from the C extension.
#
# Here it's re-opened to add a utility func.
module TimeConsts
# Converts a time delta to an absolute deadline.
# #
# Here it's re-opened to add a utility func. # Assumes timeish is a relative time, and converts its to an absolute,
module TimeConsts # with following exceptions:
# Converts a time delta to an absolute deadline. #
# # * if timish is one of the TimeConsts.TimeSpec constants the value is
# Assumes timeish is a relative time, and converts its to an absolute, # preserved.
# with following exceptions: # * timish < 0 => TimeConsts.INFINITE_FUTURE
# # * timish == 0 => TimeConsts.ZERO
# * if timish is one of the TimeConsts.TimeSpec constants the value is #
# preserved. # @param timeish [Number|TimeSpec]
# * timish < 0 => TimeConsts.INFINITE_FUTURE # @return timeish [Number|TimeSpec]
# * timish == 0 => TimeConsts.ZERO def from_relative_time(timeish)
# if timeish.is_a? TimeSpec
# @param timeish [Number|TimeSpec] timeish
# @return timeish [Number|TimeSpec] elsif timeish.nil?
def from_relative_time(timeish) TimeConsts::ZERO
if timeish.is_a? TimeSpec elsif !timeish.is_a? Numeric
timeish fail(TypeError,
elsif timeish.nil? "Cannot make an absolute deadline from #{timeish.inspect}")
TimeConsts::ZERO elsif timeish < 0
elsif !timeish.is_a? Numeric TimeConsts::INFINITE_FUTURE
fail(TypeError, elsif timeish == 0
"Cannot make an absolute deadline from #{timeish.inspect}") TimeConsts::ZERO
elsif timeish < 0 else
TimeConsts::INFINITE_FUTURE Time.now + timeish
elsif timeish == 0
TimeConsts::ZERO
else
Time.now + timeish
end
end end
module_function :from_relative_time
end end
module_function :from_relative_time
end end
end end
end end
...@@ -29,35 +29,33 @@ ...@@ -29,35 +29,33 @@
require 'grpc' require 'grpc'
module Google # GRPC contains the General RPC module.
# Google::RPC contains the General RPC module. module GRPC
module RPC # OutOfTime is an exception class that indicates that an RPC exceeded its
# OutOfTime is an exception class that indicates that an RPC exceeded its # deadline.
# deadline. OutOfTime = Class.new(StandardError)
OutOfTime = Class.new(StandardError)
# BadStatus is an exception class that indicates that an error occurred at # BadStatus is an exception class that indicates that an error occurred at
# either end of a GRPC connection. When raised, it indicates that a status # either end of a GRPC connection. When raised, it indicates that a status
# error should be returned to the other end of a GRPC connection; when # error should be returned to the other end of a GRPC connection; when
# caught it means that this end received a status error. # caught it means that this end received a status error.
class BadStatus < StandardError class BadStatus < StandardError
attr_reader :code, :details attr_reader :code, :details
# @param code [Numeric] the status code # @param code [Numeric] the status code
# @param details [String] the details of the exception # @param details [String] the details of the exception
def initialize(code, details = 'unknown cause') def initialize(code, details = 'unknown cause')
super("#{code}:#{details}") super("#{code}:#{details}")
@code = code @code = code
@details = details @details = details
end end
# Converts the exception to a GRPC::Status for use in the networking # Converts the exception to a GRPC::Status for use in the networking
# wrapper layer. # wrapper layer.
# #
# @return [Status] with the same code and details # @return [Status] with the same code and details
def to_status def to_status
Status.new(code, details) Status.new(code, details)
end
end end
end end
end end
This diff is collapsed.
...@@ -36,186 +36,184 @@ def assert_event_type(ev, want) ...@@ -36,186 +36,184 @@ def assert_event_type(ev, want)
fail("Unexpected rpc event: got #{got}, want #{want}") unless got == want fail("Unexpected rpc event: got #{got}, want #{want}") unless got == want
end end
module Google # GRPC contains the General RPC module.
# Google::RPC contains the General RPC module. module GRPC
module RPC # The BiDiCall class orchestrates exection of a BiDi stream on a client or
# The BiDiCall class orchestrates exection of a BiDi stream on a client or # server.
# server. class BidiCall
class BidiCall include Core::CompletionType
include Core::CompletionType include Core::StatusCodes
include Core::StatusCodes include Core::TimeConsts
include Core::TimeConsts
# Creates a BidiCall. # Creates a BidiCall.
# #
# BidiCall should only be created after a call is accepted. That means # BidiCall should only be created after a call is accepted. That means
# different things on a client and a server. On the client, the call is # different things on a client and a server. On the client, the call is
# accepted after call.invoke. On the server, this is after call.accept. # accepted after call.invoke. On the server, this is after call.accept.
# #
# #initialize cannot determine if the call is accepted or not; so if a # #initialize cannot determine if the call is accepted or not; so if a
# call that's not accepted is used here, the error won't be visible until # call that's not accepted is used here, the error won't be visible until
# the BidiCall#run is called. # the BidiCall#run is called.
# #
# deadline is the absolute deadline for the call. # deadline is the absolute deadline for the call.
# #
# @param call [Call] the call used by the ActiveCall # @param call [Call] the call used by the ActiveCall
# @param q [CompletionQueue] the completion queue used to accept # @param q [CompletionQueue] the completion queue used to accept
# the call # the call
# @param marshal [Function] f(obj)->string that marshal requests # @param marshal [Function] f(obj)->string that marshal requests
# @param unmarshal [Function] f(string)->obj that unmarshals responses # @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [Fixnum] the deadline for the call to complete # @param deadline [Fixnum] the deadline for the call to complete
# @param finished_tag [Object] the object used as the call's finish tag, # @param finished_tag [Object] the object used as the call's finish tag,
def initialize(call, q, marshal, unmarshal, deadline, finished_tag) def initialize(call, q, marshal, unmarshal, deadline, finished_tag)
fail(ArgumentError, 'not a call') unless call.is_a? Core::Call fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
unless q.is_a? Core::CompletionQueue unless q.is_a? Core::CompletionQueue
fail(ArgumentError, 'not a CompletionQueue') fail(ArgumentError, 'not a CompletionQueue')
end
@call = call
@cq = q
@deadline = deadline
@finished_tag = finished_tag
@marshal = marshal
@readq = Queue.new
@unmarshal = unmarshal
end end
@call = call
@cq = q
@deadline = deadline
@finished_tag = finished_tag
@marshal = marshal
@readq = Queue.new
@unmarshal = unmarshal
end
# Begins orchestration of the Bidi stream for a client sending requests. # Begins orchestration of the Bidi stream for a client sending requests.
# #
# The method either returns an Enumerator of the responses, or accepts a # The method either returns an Enumerator of the responses, or accepts a
# block that can be invoked with each response. # block that can be invoked with each response.
# #
# @param requests the Enumerable of requests to send # @param requests the Enumerable of requests to send
# @return an Enumerator of requests to yield # @return an Enumerator of requests to yield
def run_on_client(requests, &blk) def run_on_client(requests, &blk)
enq_th = start_write_loop(requests) enq_th = start_write_loop(requests)
loop_th = start_read_loop loop_th = start_read_loop
replies = each_queued_msg replies = each_queued_msg
return replies if blk.nil? return replies if blk.nil?
replies.each { |r| blk.call(r) } replies.each { |r| blk.call(r) }
enq_th.join enq_th.join
loop_th.join loop_th.join
end end
# Begins orchestration of the Bidi stream for a server generating replies. # Begins orchestration of the Bidi stream for a server generating replies.
# #
# N.B. gen_each_reply is a func(Enumerable<Requests>) # N.B. gen_each_reply is a func(Enumerable<Requests>)
# #
# It takes an enumerable of requests as an arg, in case there is a # It takes an enumerable of requests as an arg, in case there is a
# relationship between the stream of requests and the stream of replies. # relationship between the stream of requests and the stream of replies.
# #
# This does not mean that must necessarily be one. E.g, the replies # This does not mean that must necessarily be one. E.g, the replies
# produced by gen_each_reply could ignore the received_msgs # produced by gen_each_reply could ignore the received_msgs
# #
# @param gen_each_reply [Proc] generates the BiDi stream replies. # @param gen_each_reply [Proc] generates the BiDi stream replies.
def run_on_server(gen_each_reply) def run_on_server(gen_each_reply)
replys = gen_each_reply.call(each_queued_msg) replys = gen_each_reply.call(each_queued_msg)
enq_th = start_write_loop(replys, is_client: false) enq_th = start_write_loop(replys, is_client: false)
loop_th = start_read_loop loop_th = start_read_loop
loop_th.join loop_th.join
enq_th.join enq_th.join
end end
private private
END_OF_READS = :end_of_reads END_OF_READS = :end_of_reads
END_OF_WRITES = :end_of_writes END_OF_WRITES = :end_of_writes
# each_queued_msg yields each message on this instances readq # each_queued_msg yields each message on this instances readq
# #
# - messages are added to the readq by #read_loop # - messages are added to the readq by #read_loop
# - iteration ends when the instance itself is added # - iteration ends when the instance itself is added
def each_queued_msg def each_queued_msg
return enum_for(:each_queued_msg) unless block_given? return enum_for(:each_queued_msg) unless block_given?
count = 0 count = 0
loop do loop do
logger.debug("each_queued_msg: msg##{count}") logger.debug("each_queued_msg: msg##{count}")
count += 1 count += 1
req = @readq.pop req = @readq.pop
throw req if req.is_a? StandardError throw req if req.is_a? StandardError
break if req.equal?(END_OF_READS) break if req.equal?(END_OF_READS)
yield req yield req
end
end end
end
# during bidi-streaming, read the requests to send from a separate thread # during bidi-streaming, read the requests to send from a separate thread
# read so that read_loop does not block waiting for requests to read. # read so that read_loop does not block waiting for requests to read.
def start_write_loop(requests, is_client: true) def start_write_loop(requests, is_client: true)
Thread.new do # TODO: run on a thread pool Thread.new do # TODO: run on a thread pool
write_tag = Object.new write_tag = Object.new
begin begin
count = 0 count = 0
requests.each do |req| requests.each do |req|
count += 1 count += 1
payload = @marshal.call(req) payload = @marshal.call(req)
@call.start_write(Core::ByteBuffer.new(payload), write_tag) @call.start_write(Core::ByteBuffer.new(payload), write_tag)
ev = @cq.pluck(write_tag, INFINITE_FUTURE) ev = @cq.pluck(write_tag, INFINITE_FUTURE)
begin begin
assert_event_type(ev, WRITE_ACCEPTED) assert_event_type(ev, WRITE_ACCEPTED)
ensure ensure
ev.close ev.close
end
end end
if is_client end
@call.writes_done(write_tag) if is_client
ev = @cq.pluck(write_tag, INFINITE_FUTURE) @call.writes_done(write_tag)
begin ev = @cq.pluck(write_tag, INFINITE_FUTURE)
assert_event_type(ev, FINISH_ACCEPTED) begin
ensure assert_event_type(ev, FINISH_ACCEPTED)
ev.close ensure
end ev.close
logger.debug("bidi-client: sent #{count} reqs, waiting to finish")
ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
begin
assert_event_type(ev, FINISHED)
ensure
ev.close
end
logger.debug('bidi-client: finished received')
end end
rescue StandardError => e logger.debug("bidi-client: sent #{count} reqs, waiting to finish")
logger.warn('bidi: write_loop failed') ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
logger.warn(e) begin
assert_event_type(ev, FINISHED)
ensure
ev.close
end
logger.debug('bidi-client: finished received')
end end
rescue StandardError => e
logger.warn('bidi: write_loop failed')
logger.warn(e)
end end
end end
end
# starts the read loop # starts the read loop
def start_read_loop def start_read_loop
Thread.new do Thread.new do
begin begin
read_tag = Object.new read_tag = Object.new
count = 0 count = 0
# queue the initial read before beginning the loop
loop do
logger.debug("waiting for read #{count}")
count += 1
@call.start_read(read_tag)
ev = @cq.pluck(read_tag, INFINITE_FUTURE)
begin
assert_event_type(ev, READ)
# handle the next event. # queue the initial read before beginning the loop
if ev.result.nil? loop do
@readq.push(END_OF_READS) logger.debug("waiting for read #{count}")
logger.debug('done reading!') count += 1
break @call.start_read(read_tag)
end ev = @cq.pluck(read_tag, INFINITE_FUTURE)
begin
assert_event_type(ev, READ)
# push the latest read onto the queue and continue reading # handle the next event.
logger.debug("received req: #{ev.result}") if ev.result.nil?
res = @unmarshal.call(ev.result.to_s) @readq.push(END_OF_READS)
@readq.push(res) logger.debug('done reading!')
ensure break
ev.close
end end
end
rescue StandardError => e # push the latest read onto the queue and continue reading
logger.warn('bidi: read_loop failed') logger.debug("received req: #{ev.result}")
logger.warn(e) res = @unmarshal.call(ev.result.to_s)
@readq.push(e) # let each_queued_msg terminate with this error @readq.push(res)
ensure
ev.close
end
end end
rescue StandardError => e
logger.warn('bidi: read_loop failed')
logger.warn(e)
@readq.push(e) # let each_queued_msg terminate with this error
end end
end end
end end
......
This diff is collapsed.
...@@ -29,123 +29,122 @@ ...@@ -29,123 +29,122 @@
require 'grpc/grpc' require 'grpc/grpc'
module Google # GRPC contains the General RPC module.
module RPC module GRPC
# RpcDesc is a Descriptor of an RPC method. # RpcDesc is a Descriptor of an RPC method.
class RpcDesc < Struct.new(:name, :input, :output, :marshal_method, class RpcDesc < Struct.new(:name, :input, :output, :marshal_method,
:unmarshal_method) :unmarshal_method)
include Core::StatusCodes include Core::StatusCodes
# Used to wrap a message class to indicate that it needs to be streamed. # Used to wrap a message class to indicate that it needs to be streamed.
class Stream class Stream
attr_accessor :type attr_accessor :type
def initialize(type) def initialize(type)
@type = type @type = type
end
end end
end
# @return [Proc] { |instance| marshalled(instance) } # @return [Proc] { |instance| marshalled(instance) }
def marshal_proc def marshal_proc
proc { |o| o.class.method(marshal_method).call(o).to_s } proc { |o| o.class.method(marshal_method).call(o).to_s }
end end
# @param [:input, :output] target determines whether to produce the an # @param [:input, :output] target determines whether to produce the an
# unmarshal Proc for the rpc input parameter or # unmarshal Proc for the rpc input parameter or
# its output parameter # its output parameter
# #
# @return [Proc] An unmarshal proc { |marshalled(instance)| instance } # @return [Proc] An unmarshal proc { |marshalled(instance)| instance }
def unmarshal_proc(target) def unmarshal_proc(target)
fail ArgumentError unless [:input, :output].include?(target) fail ArgumentError unless [:input, :output].include?(target)
unmarshal_class = method(target).call unmarshal_class = method(target).call
unmarshal_class = unmarshal_class.type if unmarshal_class.is_a? Stream unmarshal_class = unmarshal_class.type if unmarshal_class.is_a? Stream
proc { |o| unmarshal_class.method(unmarshal_method).call(o) } proc { |o| unmarshal_class.method(unmarshal_method).call(o) }
end end
def run_server_method(active_call, mth) def run_server_method(active_call, mth)
# While a server method is running, it might be cancelled, its deadline # While a server method is running, it might be cancelled, its deadline
# might be reached, the handler could throw an unknown error, or a # might be reached, the handler could throw an unknown error, or a
# well-behaved handler could throw a StatusError. # well-behaved handler could throw a StatusError.
if request_response? if request_response?
req = active_call.remote_read req = active_call.remote_read
resp = mth.call(req, active_call.single_req_view) resp = mth.call(req, active_call.single_req_view)
active_call.remote_send(resp) active_call.remote_send(resp)
elsif client_streamer? elsif client_streamer?
resp = mth.call(active_call.multi_req_view) resp = mth.call(active_call.multi_req_view)
active_call.remote_send(resp) active_call.remote_send(resp)
elsif server_streamer? elsif server_streamer?
req = active_call.remote_read req = active_call.remote_read
replys = mth.call(req, active_call.single_req_view) replys = mth.call(req, active_call.single_req_view)
replys.each { |r| active_call.remote_send(r) } replys.each { |r| active_call.remote_send(r) }
else # is a bidi_stream else # is a bidi_stream
active_call.run_server_bidi(mth) active_call.run_server_bidi(mth)
end
send_status(active_call, OK, 'OK')
rescue BadStatus => e
# this is raised by handlers that want GRPC to send an application
# error code and detail message.
logger.debug("app err: #{active_call}, status:#{e.code}:#{e.details}")
send_status(active_call, e.code, e.details)
rescue Core::CallError => e
# This is raised by GRPC internals but should rarely, if ever happen.
# Log it, but don't notify the other endpoint..
logger.warn("failed call: #{active_call}\n#{e}")
rescue OutOfTime
# This is raised when active_call#method.call exceeeds the deadline
# event. Send a status of deadline exceeded
logger.warn("late call: #{active_call}")
send_status(active_call, DEADLINE_EXCEEDED, 'late')
rescue Core::EventError => e
# This is raised by GRPC internals but should rarely, if ever happen.
# Log it, but don't notify the other endpoint..
logger.warn("failed call: #{active_call}\n#{e}")
rescue StandardError => e
# This will usuaally be an unhandled error in the handling code.
# Send back a UNKNOWN status to the client
logger.warn("failed handler: #{active_call}; sending status:UNKNOWN")
logger.warn(e)
send_status(active_call, UNKNOWN, 'no reason given')
end end
send_status(active_call, OK, 'OK')
rescue BadStatus => e
# this is raised by handlers that want GRPC to send an application
# error code and detail message.
logger.debug("app err: #{active_call}, status:#{e.code}:#{e.details}")
send_status(active_call, e.code, e.details)
rescue Core::CallError => e
# This is raised by GRPC internals but should rarely, if ever happen.
# Log it, but don't notify the other endpoint..
logger.warn("failed call: #{active_call}\n#{e}")
rescue OutOfTime
# This is raised when active_call#method.call exceeeds the deadline
# event. Send a status of deadline exceeded
logger.warn("late call: #{active_call}")
send_status(active_call, DEADLINE_EXCEEDED, 'late')
rescue Core::EventError => e
# This is raised by GRPC internals but should rarely, if ever happen.
# Log it, but don't notify the other endpoint..
logger.warn("failed call: #{active_call}\n#{e}")
rescue StandardError => e
# This will usuaally be an unhandled error in the handling code.
# Send back a UNKNOWN status to the client
logger.warn("failed handler: #{active_call}; sending status:UNKNOWN")
logger.warn(e)
send_status(active_call, UNKNOWN, 'no reason given')
end
def assert_arity_matches(mth) def assert_arity_matches(mth)
if request_response? || server_streamer? if request_response? || server_streamer?
if mth.arity != 2 if mth.arity != 2
fail arity_error(mth, 2, "should be #{mth.name}(req, call)") fail arity_error(mth, 2, "should be #{mth.name}(req, call)")
end end
else else
if mth.arity != 1 if mth.arity != 1
fail arity_error(mth, 1, "should be #{mth.name}(call)") fail arity_error(mth, 1, "should be #{mth.name}(call)")
end
end end
end end
end
def request_response? def request_response?
!input.is_a?(Stream) && !output.is_a?(Stream) !input.is_a?(Stream) && !output.is_a?(Stream)
end end
def client_streamer? def client_streamer?
input.is_a?(Stream) && !output.is_a?(Stream) input.is_a?(Stream) && !output.is_a?(Stream)
end end
def server_streamer? def server_streamer?
!input.is_a?(Stream) && output.is_a?(Stream) !input.is_a?(Stream) && output.is_a?(Stream)
end end
def bidi_streamer? def bidi_streamer?
input.is_a?(Stream) && output.is_a?(Stream) input.is_a?(Stream) && output.is_a?(Stream)
end end
def arity_error(mth, want, msg) def arity_error(mth, want, msg)
"##{mth.name}: bad arg count; got:#{mth.arity}, want:#{want}, #{msg}" "##{mth.name}: bad arg count; got:#{mth.arity}, want:#{want}, #{msg}"
end end
def send_status(active_client, code, details) def send_status(active_client, code, details)
details = 'Not sure why' if details.nil? details = 'Not sure why' if details.nil?
active_client.send_status(code, details) active_client.send_status(code, details)
rescue StandardError => e rescue StandardError => e
logger.warn("Could not send status #{code}:#{details}") logger.warn("Could not send status #{code}:#{details}")
logger.warn(e) logger.warn(e)
end
end end
end end
end end
This diff is collapsed.
...@@ -48,188 +48,186 @@ class String ...@@ -48,188 +48,186 @@ class String
end end
end end
module Google # GRPC contains the General RPC module.
# Google::RPC contains the General RPC module. module GRPC
module RPC # Provides behaviour used to implement schema-derived service classes.
# Provides behaviour used to implement schema-derived service classes. #
# # Is intended to be used to support both client and server
# Is intended to be used to support both client and server # IDL-schema-derived servers.
# IDL-schema-derived servers. module GenericService
module GenericService # Used to indicate that a name has already been specified
# Used to indicate that a name has already been specified class DuplicateRpcName < StandardError
class DuplicateRpcName < StandardError def initialize(name)
def initialize(name) super("rpc (#{name}) is already defined")
super("rpc (#{name}) is already defined")
end
end end
end
# Provides a simple DSL to describe RPC services. # Provides a simple DSL to describe RPC services.
#
# E.g, a Maths service that uses the serializable messages DivArgs,
# DivReply and Num might define its endpoint uses the following way:
#
# rpc :div DivArgs, DivReply # single request, single response
# rpc :sum stream(Num), Num # streamed input, single response
# rpc :fib FibArgs, stream(Num) # single request, streamed response
# rpc :div_many stream(DivArgs), stream(DivReply)
# # streamed req and resp
#
# Each 'rpc' adds an RpcDesc to classes including this module, and
# #assert_rpc_descs_have_methods is used to ensure the including class
# provides methods with signatures that support all the descriptors.
module Dsl
# This configures the method names that the serializable message
# implementation uses to marshal and unmarshal messages.
# #
# E.g, a Maths service that uses the serializable messages DivArgs, # - unmarshal_class method must be a class method on the serializable
# DivReply and Num might define its endpoint uses the following way: # message type that takes a string (byte stream) and produces and object
# #
# rpc :div DivArgs, DivReply # single request, single response # - marshal_class_method is called on a serializable message instance
# rpc :sum stream(Num), Num # streamed input, single response # and produces a serialized string.
# rpc :fib FibArgs, stream(Num) # single request, streamed response
# rpc :div_many stream(DivArgs), stream(DivReply)
# # streamed req and resp
# #
# Each 'rpc' adds an RpcDesc to classes including this module, and # The Dsl verifies that the types in the descriptor have both the
# #assert_rpc_descs_have_methods is used to ensure the including class # unmarshal and marshal methods.
# provides methods with signatures that support all the descriptors. attr_writer(:marshal_class_method, :unmarshal_class_method)
module Dsl
# This configures the method names that the serializable message
# implementation uses to marshal and unmarshal messages.
#
# - unmarshal_class method must be a class method on the serializable
# message type that takes a string (byte stream) and produces and object
#
# - marshal_class_method is called on a serializable message instance
# and produces a serialized string.
#
# The Dsl verifies that the types in the descriptor have both the
# unmarshal and marshal methods.
attr_writer(:marshal_class_method, :unmarshal_class_method)
# This allows configuration of the service name.
attr_accessor(:service_name)
# Adds an RPC spec.
#
# Takes the RPC name and the classes representing the types to be
# serialized, and adds them to the including classes rpc_desc hash.
#
# input and output should both have the methods #marshal and #unmarshal
# that are responsible for writing and reading an object instance from a
# byte buffer respectively.
#
# @param name [String] the name of the rpc
# @param input [Object] the input parameter's class
# @param output [Object] the output parameter's class
def rpc(name, input, output)
fail(DuplicateRpcName, name) if rpc_descs.key? name
assert_can_marshal(input)
assert_can_marshal(output)
rpc_descs[name] = RpcDesc.new(name, input, output,
marshal_class_method,
unmarshal_class_method)
end
def inherited(subclass) # This allows configuration of the service name.
# Each subclass should have a distinct class variable with its own attr_accessor(:service_name)
# rpc_descs
subclass.rpc_descs.merge!(rpc_descs)
subclass.service_name = service_name
end
# the name of the instance method used to marshal events to a byte # Adds an RPC spec.
# stream. #
def marshal_class_method # Takes the RPC name and the classes representing the types to be
@marshal_class_method ||= :marshal # serialized, and adds them to the including classes rpc_desc hash.
end #
# input and output should both have the methods #marshal and #unmarshal
# that are responsible for writing and reading an object instance from a
# byte buffer respectively.
#
# @param name [String] the name of the rpc
# @param input [Object] the input parameter's class
# @param output [Object] the output parameter's class
def rpc(name, input, output)
fail(DuplicateRpcName, name) if rpc_descs.key? name
assert_can_marshal(input)
assert_can_marshal(output)
rpc_descs[name] = RpcDesc.new(name, input, output,
marshal_class_method,
unmarshal_class_method)
end
# the name of the class method used to unmarshal from a byte stream. def inherited(subclass)
def unmarshal_class_method # Each subclass should have a distinct class variable with its own
@unmarshal_class_method ||= :unmarshal # rpc_descs
end subclass.rpc_descs.merge!(rpc_descs)
subclass.service_name = service_name
end
def assert_can_marshal(cls) # the name of the instance method used to marshal events to a byte
cls = cls.type if cls.is_a? RpcDesc::Stream # stream.
mth = unmarshal_class_method def marshal_class_method
unless cls.methods.include? mth @marshal_class_method ||= :marshal
fail(ArgumentError, "#{cls} needs #{cls}.#{mth}") end
end
mth = marshal_class_method # the name of the class method used to unmarshal from a byte stream.
return if cls.methods.include? mth def unmarshal_class_method
@unmarshal_class_method ||= :unmarshal
end
def assert_can_marshal(cls)
cls = cls.type if cls.is_a? RpcDesc::Stream
mth = unmarshal_class_method
unless cls.methods.include? mth
fail(ArgumentError, "#{cls} needs #{cls}.#{mth}") fail(ArgumentError, "#{cls} needs #{cls}.#{mth}")
end end
mth = marshal_class_method
return if cls.methods.include? mth
fail(ArgumentError, "#{cls} needs #{cls}.#{mth}")
end
# @param cls [Class] the class of a serializable type # @param cls [Class] the class of a serializable type
# @return cls wrapped in a RpcDesc::Stream # @return cls wrapped in a RpcDesc::Stream
def stream(cls) def stream(cls)
assert_can_marshal(cls) assert_can_marshal(cls)
RpcDesc::Stream.new(cls) RpcDesc::Stream.new(cls)
end end
# the RpcDescs defined for this GenericService, keyed by name. # the RpcDescs defined for this GenericService, keyed by name.
def rpc_descs def rpc_descs
@rpc_descs ||= {} @rpc_descs ||= {}
end end
# Creates a rpc client class with methods for accessing the methods # Creates a rpc client class with methods for accessing the methods
# currently in rpc_descs. # currently in rpc_descs.
def rpc_stub_class def rpc_stub_class
descs = rpc_descs descs = rpc_descs
route_prefix = service_name route_prefix = service_name
Class.new(ClientStub) do Class.new(ClientStub) do
# @param host [String] the host the stub connects to # @param host [String] the host the stub connects to
# @param kw [KeywordArgs] the channel arguments, plus any optional # @param kw [KeywordArgs] the channel arguments, plus any optional
# args for configuring the client's channel # args for configuring the client's channel
def initialize(host, **kw) def initialize(host, **kw)
super(host, Core::CompletionQueue.new, **kw) super(host, Core::CompletionQueue.new, **kw)
end end
# Used define_method to add a method for each rpc_desc. Each method # Used define_method to add a method for each rpc_desc. Each method
# calls the base class method for the given descriptor. # calls the base class method for the given descriptor.
descs.each_pair do |name, desc| descs.each_pair do |name, desc|
mth_name = name.to_s.underscore.to_sym mth_name = name.to_s.underscore.to_sym
marshal = desc.marshal_proc marshal = desc.marshal_proc
unmarshal = desc.unmarshal_proc(:output) unmarshal = desc.unmarshal_proc(:output)
route = "/#{route_prefix}/#{name}" route = "/#{route_prefix}/#{name}"
if desc.request_response? if desc.request_response?
define_method(mth_name) do |req, deadline = nil| define_method(mth_name) do |req, deadline = nil|
logger.debug("calling #{@host}:#{route}") logger.debug("calling #{@host}:#{route}")
request_response(route, req, marshal, unmarshal, deadline) request_response(route, req, marshal, unmarshal, deadline)
end end
elsif desc.client_streamer? elsif desc.client_streamer?
define_method(mth_name) do |reqs, deadline = nil| define_method(mth_name) do |reqs, deadline = nil|
logger.debug("calling #{@host}:#{route}") logger.debug("calling #{@host}:#{route}")
client_streamer(route, reqs, marshal, unmarshal, deadline) client_streamer(route, reqs, marshal, unmarshal, deadline)
end end
elsif desc.server_streamer? elsif desc.server_streamer?
define_method(mth_name) do |req, deadline = nil, &blk| define_method(mth_name) do |req, deadline = nil, &blk|
logger.debug("calling #{@host}:#{route}") logger.debug("calling #{@host}:#{route}")
server_streamer(route, req, marshal, unmarshal, deadline, server_streamer(route, req, marshal, unmarshal, deadline,
&blk) &blk)
end end
else # is a bidi_stream else # is a bidi_stream
define_method(mth_name) do |reqs, deadline = nil, &blk| define_method(mth_name) do |reqs, deadline = nil, &blk|
logger.debug("calling #{@host}:#{route}") logger.debug("calling #{@host}:#{route}")
bidi_streamer(route, reqs, marshal, unmarshal, deadline, &blk) bidi_streamer(route, reqs, marshal, unmarshal, deadline, &blk)
end
end end
end end
end end
end end
end
# Asserts that the appropriate methods are defined for each added rpc # Asserts that the appropriate methods are defined for each added rpc
# spec. Is intended to aid verifying that server classes are correctly # spec. Is intended to aid verifying that server classes are correctly
# implemented. # implemented.
def assert_rpc_descs_have_methods def assert_rpc_descs_have_methods
rpc_descs.each_pair do |m, spec| rpc_descs.each_pair do |m, spec|
mth_name = m.to_s.underscore.to_sym mth_name = m.to_s.underscore.to_sym
unless instance_methods.include?(mth_name) unless instance_methods.include?(mth_name)
fail "#{self} does not provide instance method '#{mth_name}'" fail "#{self} does not provide instance method '#{mth_name}'"
end
spec.assert_arity_matches(instance_method(mth_name))
end end
spec.assert_arity_matches(instance_method(mth_name))
end end
end end
end
def self.included(o) def self.included(o)
o.extend(Dsl) o.extend(Dsl)
# Update to the use the service name including module. Proivde a default # Update to the use the service name including module. Proivde a default
# that can be nil e,g. when modules are declared dynamically. # that can be nil e,g. when modules are declared dynamically.
return unless o.service_name.nil? return unless o.service_name.nil?
if o.name.nil? if o.name.nil?
o.service_name = 'GenericService' o.service_name = 'GenericService'
else
modules = o.name.split('::')
if modules.length > 2
o.service_name = modules[modules.length - 2]
else else
modules = o.name.split('::') o.service_name = modules.first
if modules.length > 2
o.service_name = modules[modules.length - 2]
else
o.service_name = modules.first
end
end end
end end
end end
......
...@@ -35,6 +35,6 @@ Logging.logger.root.appenders = Logging.appenders.stdout ...@@ -35,6 +35,6 @@ Logging.logger.root.appenders = Logging.appenders.stdout
Logging.logger.root.level = :info Logging.logger.root.level = :info
# TODO: provide command-line configuration for logging # TODO: provide command-line configuration for logging
Logging.logger['Google::RPC'].level = :debug Logging.logger['GRPC'].level = :debug
Logging.logger['Google::RPC::ActiveCall'].level = :info Logging.logger['GRPC::ActiveCall'].level = :info
Logging.logger['Google::RPC::BidiCall'].level = :info Logging.logger['GRPC::BidiCall'].level = :info
...@@ -27,9 +27,7 @@ ...@@ -27,9 +27,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Google # GRPC contains the General RPC module.
# Google::RPC contains the General RPC module. module GRPC
module RPC VERSION = '0.0.1'
VERSION = '0.0.1'
end
end end
...@@ -36,9 +36,9 @@ require 'faraday' ...@@ -36,9 +36,9 @@ require 'faraday'
require 'grpc/auth/compute_engine' require 'grpc/auth/compute_engine'
require 'spec_helper' require 'spec_helper'
describe Google::RPC::Auth::GCECredentials do describe GRPC::Auth::GCECredentials do
MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token' MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token'
GCECredentials = Google::RPC::Auth::GCECredentials GCECredentials = GRPC::Auth::GCECredentials
before(:example) do before(:example) do
@client = GCECredentials.new @client = GCECredentials.new
......
...@@ -38,7 +38,7 @@ require 'multi_json' ...@@ -38,7 +38,7 @@ require 'multi_json'
require 'openssl' require 'openssl'
require 'spec_helper' require 'spec_helper'
describe Google::RPC::Auth::ServiceAccountCredentials do describe GRPC::Auth::ServiceAccountCredentials do
before(:example) do before(:example) do
@key = OpenSSL::PKey::RSA.new(2048) @key = OpenSSL::PKey::RSA.new(2048)
cred_json = { cred_json = {
...@@ -49,7 +49,7 @@ describe Google::RPC::Auth::ServiceAccountCredentials do ...@@ -49,7 +49,7 @@ describe Google::RPC::Auth::ServiceAccountCredentials do
type: 'service_account' type: 'service_account'
} }
cred_json_text = MultiJson.dump(cred_json) cred_json_text = MultiJson.dump(cred_json)
@client = Google::RPC::Auth::ServiceAccountCredentials.new( @client = GRPC::Auth::ServiceAccountCredentials.new(
'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.profile',
StringIO.new(cred_json_text)) StringIO.new(cred_json_text))
end end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment