diff options
author | 2015-12-15 11:43:05 -0800 | |
---|---|---|
committer | 2015-12-15 11:43:05 -0800 | |
commit | da61668122b63fc18d64231dede700139234bc75 (patch) | |
tree | 3b6fef704fd0e3670c0aa308dd6c24704c4f8c94 /src/ruby | |
parent | 960870549a31a354b63823319640a628a2fd1767 (diff) | |
parent | 5c8c3e78a5b2cd6fb8504b48bac7b7f030cce787 (diff) |
Merge pull request #4454 from grpc/master
Re-cutting the 0.12 release from master.
Diffstat (limited to 'src/ruby')
26 files changed, 682 insertions, 363 deletions
diff --git a/src/ruby/bin/apis/pubsub_demo.rb b/src/ruby/bin/apis/pubsub_demo.rb index 003e91a6b3..143ecc7a8f 100755 --- a/src/ruby/bin/apis/pubsub_demo.rb +++ b/src/ruby/bin/apis/pubsub_demo.rb @@ -32,7 +32,6 @@ # pubsub_demo demos accesses the Google PubSub API via its gRPC interface # # $ GOOGLE_APPLICATION_CREDENTIALS=<path_to_service_account_key_file> \ -# SSL_CERT_FILE=<path/to/ssl/certs> \ # path/to/pubsub_demo.rb \ # [--action=<chosen_demo_action> ] # @@ -55,18 +54,9 @@ require 'google/protobuf/empty' require 'tech/pubsub/proto/pubsub' require 'tech/pubsub/proto/pubsub_services' -# loads the certificates used to access the test server securely. -def load_prod_cert - fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil? - p "loading prod certs from #{ENV['SSL_CERT_FILE']}" - File.open(ENV['SSL_CERT_FILE']) do |f| - return f.read - end -end - # creates a SSL Credentials from the production certificates. def ssl_creds - GRPC::Core::ChannelCredentials.new(load_prod_cert) + GRPC::Core::ChannelCredentials.new() end # Builds the metadata authentication update proc. @@ -80,8 +70,9 @@ def publisher_stub(opts) address = "#{opts.host}:#{opts.port}" stub_clz = Tech::Pubsub::PublisherService::Stub # shorter GRPC.logger.info("... access PublisherService at #{address}") - stub_clz.new(address, - creds: ssl_creds, update_metadata: auth_proc(opts), + call_creds = GRPC::Core::CallCredentials.new(auth_proc(opts)) + combined_creds = ssl_creds.compose(call_creds) + stub_clz.new(address, creds: combined_creds, GRPC::Core::Channel::SSL_TARGET => opts.host) end @@ -90,8 +81,9 @@ def subscriber_stub(opts) address = "#{opts.host}:#{opts.port}" stub_clz = Tech::Pubsub::SubscriberService::Stub # shorter GRPC.logger.info("... access SubscriberService at #{address}") - stub_clz.new(address, - creds: ssl_creds, update_metadata: auth_proc(opts), + call_creds = GRPC::Core::CallCredentials.new(auth_proc(opts)) + combined_creds = ssl_creds.compose(call_creds) + stub_clz.new(address, creds: combined_creds, GRPC::Core::Channel::SSL_TARGET => opts.host) end diff --git a/src/ruby/bin/math.proto b/src/ruby/bin/math.proto deleted file mode 100755 index 311e148c02..0000000000 --- a/src/ruby/bin/math.proto +++ /dev/null @@ -1,80 +0,0 @@ - -// Copyright 2015, 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. - -syntax = "proto3"; - -package math; - -message DivArgs { - int64 dividend = 1; - int64 divisor = 2; -} - -message DivReply { - int64 quotient = 1; - int64 remainder = 2; -} - -message FibArgs { - int64 limit = 1; -} - -message Num { - int64 num = 1; -} - -message FibReply { - int64 count = 1; -} - -service Math { - // Div divides args.dividend by args.divisor and returns the quotient and - // remainder. - rpc Div (DivArgs) returns (DivReply) { - } - - // DivMany accepts an arbitrary number of division args from the client stream - // and sends back the results in the reply stream. The stream continues until - // the client closes its end; the server does the same after sending all the - // replies. The stream ends immediately if either end aborts. - rpc DivMany (stream DivArgs) returns (stream DivReply) { - } - - // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib - // generates up to limit numbers; otherwise it continues until the call is - // canceled. Unlike Fib above, Fib has no final FibReply. - rpc Fib (FibArgs) returns (stream Num) { - } - - // Sum sums a stream of numbers, returning the final result once the stream - // is closed. - rpc Sum (stream Num) returns (Num) { - } -} diff --git a/src/ruby/bin/math.rb b/src/ruby/bin/math.rb index 323993ed43..60429a1505 100755 --- a/src/ruby/bin/math.rb +++ b/src/ruby/bin/math.rb @@ -1,32 +1,3 @@ -# Copyright 2015, 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # source: math.proto diff --git a/src/ruby/bin/math_services.rb b/src/ruby/bin/math_services.rb index cf58a53913..2d482129c2 100755 --- a/src/ruby/bin/math_services.rb +++ b/src/ruby/bin/math_services.rb @@ -1,32 +1,3 @@ -# Copyright 2015, 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # Source: math.proto for package 'math' diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb index 7972272e2d..db9385e961 100644 --- a/src/ruby/ext/grpc/extconf.rb +++ b/src/ruby/ext/grpc/extconf.rb @@ -94,6 +94,10 @@ else end $CFLAGS << ' -I' + File.join(grpc_root, 'include') $LDFLAGS << ' -L' + grpc_lib_dir + if grpc_config == 'gcov' + $CFLAGS << ' -O0 -fprofile-arcs -ftest-coverage' + $LDFLAGS << ' -fprofile-arcs -ftest-coverage -rdynamic' + end raise 'gpr not found' unless have_library('gpr', 'gpr_now') raise 'grpc not found' unless have_library('grpc', 'grpc_channel_destroy') end diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index 40364328ee..1647d9b484 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -39,6 +39,7 @@ #include <grpc/support/alloc.h> #include "rb_byte_buffer.h" +#include "rb_call_credentials.h" #include "rb_completion_queue.h" #include "rb_grpc.h" @@ -279,6 +280,26 @@ static VALUE grpc_rb_call_set_write_flag(VALUE self, VALUE write_flag) { return rb_ivar_set(self, id_write_flag, write_flag); } +/* + call-seq: + call.set_credentials call_credentials + + Sets credentials on a call */ +static VALUE grpc_rb_call_set_credentials(VALUE self, VALUE credentials) { + grpc_call *call = NULL; + grpc_call_credentials *creds; + grpc_call_error err; + TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call); + creds = grpc_rb_get_wrapped_call_credentials(credentials); + err = grpc_call_set_credentials(call, creds); + if (err != GRPC_CALL_OK) { + rb_raise(grpc_rb_eCallError, + "grpc_call_set_credentials failed with %s (code=%d)", + grpc_call_error_detail_of(err), err); + } + return Qnil; +} + /* grpc_rb_md_ary_fill_hash_cb is the hash iteration callback used to fill grpc_metadata_array. @@ -347,7 +368,7 @@ static int grpc_rb_md_ary_capacity_hash_cb(VALUE key, VALUE val, /* grpc_rb_md_ary_convert converts a ruby metadata hash into a grpc_metadata_array. */ -static void grpc_rb_md_ary_convert(VALUE md_ary_hash, +void grpc_rb_md_ary_convert(VALUE md_ary_hash, grpc_metadata_array *md_ary) { VALUE md_ary_obj = Qnil; if (md_ary_hash == Qnil) { @@ -795,6 +816,8 @@ void Init_grpc_call() { rb_define_method(grpc_rb_cCall, "write_flag", grpc_rb_call_get_write_flag, 0); rb_define_method(grpc_rb_cCall, "write_flag=", grpc_rb_call_set_write_flag, 1); + rb_define_method(grpc_rb_cCall, "set_credentials!", + grpc_rb_call_set_credentials, 1); /* Ids used to support call attributes */ id_metadata = rb_intern("metadata"); diff --git a/src/ruby/ext/grpc/rb_call.h b/src/ruby/ext/grpc/rb_call.h index 1d2fbc3580..24adb3477b 100644 --- a/src/ruby/ext/grpc/rb_call.h +++ b/src/ruby/ext/grpc/rb_call.h @@ -50,6 +50,12 @@ const char* grpc_call_error_detail_of(grpc_call_error err); /* Converts a metadata array to a hash. */ VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary); +/* grpc_rb_md_ary_convert converts a ruby metadata hash into + a grpc_metadata_array. +*/ +void grpc_rb_md_ary_convert(VALUE md_ary_hash, + grpc_metadata_array *md_ary); + /* grpc_rb_eCallError is the ruby class of the exception thrown during call operations. */ extern VALUE grpc_rb_eCallError; diff --git a/src/ruby/ext/grpc/rb_call_credentials.c b/src/ruby/ext/grpc/rb_call_credentials.c new file mode 100644 index 0000000000..acc5472799 --- /dev/null +++ b/src/ruby/ext/grpc/rb_call_credentials.c @@ -0,0 +1,312 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#include "rb_call_credentials.h" + +#include <ruby/ruby.h> +#include <ruby/thread.h> + +#include <grpc/grpc.h> +#include <grpc/grpc_security.h> + +#include "rb_call.h" +#include "rb_grpc.h" + +/* grpc_rb_cCallCredentials is the ruby class that proxies + * grpc_call_credentials */ +static VALUE grpc_rb_cCallCredentials = Qnil; + +/* grpc_rb_call_credentials wraps a grpc_call_credentials. It provides a peer + * ruby object, 'mark' to minimize copying when a credential is created from + * ruby. */ +typedef struct grpc_rb_call_credentials { + /* Holder of ruby objects involved in contructing the credentials */ + VALUE mark; + + /* The actual credentials */ + grpc_call_credentials *wrapped; +} grpc_rb_call_credentials; + +typedef struct callback_params { + VALUE get_metadata; + grpc_auth_metadata_context context; + void *user_data; + grpc_credentials_plugin_metadata_cb callback; +} callback_params; + +static VALUE grpc_rb_call_credentials_callback(VALUE callback_args) { + VALUE result = rb_hash_new(); + VALUE metadata = rb_funcall(rb_ary_entry(callback_args, 0), rb_intern("call"), + 1, rb_ary_entry(callback_args, 1)); + rb_hash_aset(result, rb_str_new2("metadata"), metadata); + rb_hash_aset(result, rb_str_new2("status"), INT2NUM(GRPC_STATUS_OK)); + rb_hash_aset(result, rb_str_new2("details"), rb_str_new2("")); + return result; +} + +static VALUE grpc_rb_call_credentials_callback_rescue(VALUE args, + VALUE exception_object) { + VALUE result = rb_hash_new(); + rb_hash_aset(result, rb_str_new2("metadata"), Qnil); + /* Currently only gives the exception class name. It should be possible get + more details */ + rb_hash_aset(result, rb_str_new2("status"), + INT2NUM(GRPC_STATUS_PERMISSION_DENIED)); + rb_hash_aset(result, rb_str_new2("details"), + rb_str_new2(rb_obj_classname(exception_object))); + return result; +} + +static void *grpc_rb_call_credentials_callback_with_gil(void *param) { + callback_params *const params = (callback_params *)param; + VALUE auth_uri = rb_str_new_cstr(params->context.service_url); + /* Pass the arguments to the proc in a hash, which currently only has they key + 'auth_uri' */ + VALUE callback_args = rb_ary_new(); + VALUE args = rb_hash_new(); + VALUE result; + grpc_metadata_array md_ary; + grpc_status_code status; + VALUE details; + char *error_details; + grpc_metadata_array_init(&md_ary); + rb_hash_aset(args, ID2SYM(rb_intern("jwt_aud_uri")), auth_uri); + rb_ary_push(callback_args, params->get_metadata); + rb_ary_push(callback_args, args); + result = rb_rescue(grpc_rb_call_credentials_callback, callback_args, + grpc_rb_call_credentials_callback_rescue, Qnil); + // Both callbacks return a hash, so result should be a hash + grpc_rb_md_ary_convert(rb_hash_aref(result, rb_str_new2("metadata")), &md_ary); + status = NUM2INT(rb_hash_aref(result, rb_str_new2("status"))); + details = rb_hash_aref(result, rb_str_new2("details")); + error_details = StringValueCStr(details); + params->callback(params->user_data, md_ary.metadata, md_ary.count, status, + error_details); + grpc_metadata_array_destroy(&md_ary); + + return NULL; +} + +static void grpc_rb_call_credentials_plugin_get_metadata( + void *state, grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, void *user_data) { + callback_params params; + params.get_metadata = (VALUE)state; + params.context = context; + params.user_data = user_data; + params.callback = cb; + + rb_thread_call_with_gvl(grpc_rb_call_credentials_callback_with_gil, + (void*)(¶ms)); +} + +static void grpc_rb_call_credentials_plugin_destroy(void *state) { + // Not sure what needs to be done here +} + +/* Destroys the credentials instances. */ +static void grpc_rb_call_credentials_free(void *p) { + grpc_rb_call_credentials *wrapper; + if (p == NULL) { + return; + } + wrapper = (grpc_rb_call_credentials *)p; + + /* Delete the wrapped object if the mark object is Qnil, which indicates that + * no other object is the actual owner. */ + if (wrapper->wrapped != NULL && wrapper->mark == Qnil) { + grpc_call_credentials_release(wrapper->wrapped); + wrapper->wrapped = NULL; + } + + xfree(p); +} + +/* Protects the mark object from GC */ +static void grpc_rb_call_credentials_mark(void *p) { + grpc_rb_call_credentials *wrapper = NULL; + if (p == NULL) { + return; + } + wrapper = (grpc_rb_call_credentials *)p; + + /* If it's not already cleaned up, mark the mark object */ + if (wrapper->mark != Qnil) { + rb_gc_mark(wrapper->mark); + } +} + +static rb_data_type_t grpc_rb_call_credentials_data_type = { + "grpc_call_credentials", + {grpc_rb_call_credentials_mark, grpc_rb_call_credentials_free, + GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, + NULL, + NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + RUBY_TYPED_FREE_IMMEDIATELY +#endif +}; + +/* Allocates CallCredentials instances. + Provides safe initial defaults for the instance fields. */ +static VALUE grpc_rb_call_credentials_alloc(VALUE cls) { + grpc_rb_call_credentials *wrapper = ALLOC(grpc_rb_call_credentials); + wrapper->wrapped = NULL; + wrapper->mark = Qnil; + return TypedData_Wrap_Struct(cls, &grpc_rb_call_credentials_data_type, wrapper); +} + +/* Creates a wrapping object for a given call credentials. This should only be + * called with grpc_call_credentials objects that are not already associated + * with any Ruby object */ +VALUE grpc_rb_wrap_call_credentials(grpc_call_credentials *c) { + VALUE rb_wrapper; + grpc_rb_call_credentials *wrapper; + if (c == NULL) { + return Qnil; + } + rb_wrapper = grpc_rb_call_credentials_alloc(grpc_rb_cCallCredentials); + TypedData_Get_Struct(rb_wrapper, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, wrapper); + wrapper->wrapped = c; + return rb_wrapper; +} + +/* Clones CallCredentials instances. + Gives CallCredentials a consistent implementation of Ruby's object copy/dup + protocol. */ +static VALUE grpc_rb_call_credentials_init_copy(VALUE copy, VALUE orig) { + grpc_rb_call_credentials *orig_cred = NULL; + grpc_rb_call_credentials *copy_cred = NULL; + + if (copy == orig) { + return copy; + } + + /* Raise an error if orig is not a credentials object or a subclass. */ + if (TYPE(orig) != T_DATA || + RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_call_credentials_free) { + rb_raise(rb_eTypeError, "not a %s", + rb_obj_classname(grpc_rb_cCallCredentials)); + } + + TypedData_Get_Struct(orig, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, orig_cred); + TypedData_Get_Struct(copy, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, copy_cred); + + /* use ruby's MEMCPY to make a byte-for-byte copy of the credentials + * wrapper object. */ + MEMCPY(copy_cred, orig_cred, grpc_rb_call_credentials, 1); + return copy; +} + +/* The attribute used on the mark object to hold the callback */ +static ID id_callback; + +/* + call-seq: + creds = Credentials.new auth_proc + proc: (required) Proc that generates auth metadata + Initializes CallCredential instances. */ +static VALUE grpc_rb_call_credentials_init(VALUE self, VALUE proc) { + grpc_rb_call_credentials *wrapper = NULL; + grpc_call_credentials *creds = NULL; + grpc_metadata_credentials_plugin plugin; + + TypedData_Get_Struct(self, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, wrapper); + + plugin.get_metadata = grpc_rb_call_credentials_plugin_get_metadata; + plugin.destroy = grpc_rb_call_credentials_plugin_destroy; + if (!rb_obj_is_proc(proc)) { + rb_raise(rb_eTypeError, "Argument to CallCredentials#new must be a proc"); + return Qnil; + } + plugin.state = (void*)proc; + plugin.type = ""; + + creds = grpc_metadata_credentials_create_from_plugin(plugin, NULL); + if (creds == NULL) { + rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); + return Qnil; + } + + wrapper->wrapped = creds; + rb_ivar_set(self, id_callback, proc); + + return self; +} + +static VALUE grpc_rb_call_credentials_compose(int argc, VALUE *argv, + VALUE self) { + grpc_call_credentials *creds; + grpc_call_credentials *other; + if (argc == 0) { + return self; + } + creds = grpc_rb_get_wrapped_call_credentials(self); + for (int i = 0; i < argc; i++) { + other = grpc_rb_get_wrapped_call_credentials(argv[i]); + creds = grpc_composite_call_credentials_create(creds, other, NULL); + } + return grpc_rb_wrap_call_credentials(creds); +} + +void Init_grpc_call_credentials() { + grpc_rb_cCallCredentials = + rb_define_class_under(grpc_rb_mGrpcCore, "CallCredentials", rb_cObject); + + /* Allocates an object managed by the ruby runtime */ + rb_define_alloc_func(grpc_rb_cCallCredentials, + grpc_rb_call_credentials_alloc); + + /* Provides a ruby constructor and support for dup/clone. */ + rb_define_method(grpc_rb_cCallCredentials, "initialize", + grpc_rb_call_credentials_init, 1); + rb_define_method(grpc_rb_cCallCredentials, "initialize_copy", + grpc_rb_call_credentials_init_copy, 1); + rb_define_method(grpc_rb_cCallCredentials, "compose", + grpc_rb_call_credentials_compose, -1); + + id_callback = rb_intern("__callback"); +} + +/* Gets the wrapped grpc_call_credentials from the ruby wrapper */ +grpc_call_credentials *grpc_rb_get_wrapped_call_credentials(VALUE v) { + grpc_rb_call_credentials *wrapper = NULL; + TypedData_Get_Struct(v, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, + wrapper); + return wrapper->wrapped; +} diff --git a/src/ruby/ext/grpc/rb_call_credentials.h b/src/ruby/ext/grpc/rb_call_credentials.h new file mode 100644 index 0000000000..5350a8f7ff --- /dev/null +++ b/src/ruby/ext/grpc/rb_call_credentials.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#ifndef GRPC_RB_CALL_CREDENTIALS_H_ +#define GRPC_RB_CALL_CREDENTIALS_H_ + +#include <ruby/ruby.h> + +#include <grpc/grpc_security.h> + +/* Initializes the ruby CallCredentials class. */ +void Init_grpc_call_credentials(); + +grpc_call_credentials* grpc_rb_get_wrapped_call_credentials(VALUE v); + +#endif /* GRPC_RB_CALL_CREDENTIALS_H_ */ diff --git a/src/ruby/ext/grpc/rb_channel_credentials.c b/src/ruby/ext/grpc/rb_channel_credentials.c index 072a6f54ab..3530081c6b 100644 --- a/src/ruby/ext/grpc/rb_channel_credentials.c +++ b/src/ruby/ext/grpc/rb_channel_credentials.c @@ -37,7 +37,9 @@ #include <grpc/grpc.h> #include <grpc/grpc_security.h> +#include <grpc/support/log.h> +#include "rb_call_credentials.h" #include "rb_grpc.h" /* grpc_rb_cChannelCredentials is the ruby class that proxies @@ -107,6 +109,22 @@ static VALUE grpc_rb_channel_credentials_alloc(VALUE cls) { return TypedData_Wrap_Struct(cls, &grpc_rb_channel_credentials_data_type, wrapper); } +/* Creates a wrapping object for a given channel credentials. This should only + * be called with grpc_channel_credentials objects that are not already + * associated with any Ruby object. */ +VALUE grpc_rb_wrap_channel_credentials(grpc_channel_credentials *c) { + VALUE rb_wrapper; + grpc_rb_channel_credentials *wrapper; + if (c == NULL) { + return Qnil; + } + rb_wrapper = grpc_rb_channel_credentials_alloc(grpc_rb_cChannelCredentials); + TypedData_Get_Struct(rb_wrapper, grpc_rb_channel_credentials, + &grpc_rb_channel_credentials_data_type, wrapper); + wrapper->wrapped = c; + return rb_wrapper; +} + /* Clones ChannelCredentials instances. Gives ChannelCredentials a consistent implementation of Ruby's object copy/dup protocol. */ @@ -148,11 +166,13 @@ static ID id_pem_cert_chain; /* call-seq: - creds1 = Credentials.new(pem_root_certs) + creds1 = Credentials.new() + ... + creds2 = Credentials.new(pem_root_certs) ... - creds2 = Credentials.new(pem_root_certs, pem_private_key, + creds3 = Credentials.new(pem_root_certs, pem_private_key, pem_cert_chain) - pem_root_certs: (required) PEM encoding of the server root certificate + pem_root_certs: (optional) PEM encoding of the server root certificate pem_private_key: (optional) PEM encoding of the client's private key pem_cert_chain: (optional) PEM encoding of the client's cert chain Initializes Credential instances. */ @@ -163,26 +183,23 @@ static VALUE grpc_rb_channel_credentials_init(int argc, VALUE *argv, VALUE self) grpc_rb_channel_credentials *wrapper = NULL; grpc_channel_credentials *creds = NULL; grpc_ssl_pem_key_cert_pair key_cert_pair; + const char *pem_root_certs_cstr = NULL; MEMZERO(&key_cert_pair, grpc_ssl_pem_key_cert_pair, 1); - /* TODO: Remove mandatory arg when we support default roots. */ - /* "12" == 1 mandatory arg, 2 (credentials) is optional */ - rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key, + /* "03" == no mandatory arg, 3 optional */ + rb_scan_args(argc, argv, "03", &pem_root_certs, &pem_private_key, &pem_cert_chain); TypedData_Get_Struct(self, grpc_rb_channel_credentials, &grpc_rb_channel_credentials_data_type, wrapper); - if (pem_root_certs == Qnil) { - rb_raise(rb_eRuntimeError, - "could not create a credential: nil pem_root_certs"); - return Qnil; + if (pem_root_certs != Qnil) { + pem_root_certs_cstr = RSTRING_PTR(pem_root_certs); } if (pem_private_key == Qnil && pem_cert_chain == Qnil) { - creds = - grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL, NULL); + creds = grpc_ssl_credentials_create(pem_root_certs_cstr, NULL, NULL); } else { key_cert_pair.private_key = RSTRING_PTR(pem_private_key); key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain); - creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), + creds = grpc_ssl_credentials_create(pem_root_certs_cstr, &key_cert_pair, NULL); } if (creds == NULL) { @@ -199,6 +216,25 @@ static VALUE grpc_rb_channel_credentials_init(int argc, VALUE *argv, VALUE self) return self; } +static VALUE grpc_rb_channel_credentials_compose(int argc, VALUE *argv, + VALUE self) { + grpc_channel_credentials *creds; + grpc_call_credentials *other; + if (argc == 0) { + return self; + } + creds = grpc_rb_get_wrapped_channel_credentials(self); + for (int i = 0; i < argc; i++) { + other = grpc_rb_get_wrapped_call_credentials(argv[i]); + creds = grpc_composite_channel_credentials_create(creds, other, NULL); + if (creds == NULL) { + rb_raise(rb_eRuntimeError, + "Failed to compose channel and call credentials"); + } + } + return grpc_rb_wrap_channel_credentials(creds); +} + void Init_grpc_channel_credentials() { grpc_rb_cChannelCredentials = rb_define_class_under(grpc_rb_mGrpcCore, "ChannelCredentials", rb_cObject); @@ -212,6 +248,8 @@ void Init_grpc_channel_credentials() { grpc_rb_channel_credentials_init, -1); rb_define_method(grpc_rb_cChannelCredentials, "initialize_copy", grpc_rb_channel_credentials_init_copy, 1); + rb_define_method(grpc_rb_cChannelCredentials, "compose", + grpc_rb_channel_credentials_compose, -1); id_pem_cert_chain = rb_intern("__pem_cert_chain"); id_pem_private_key = rb_intern("__pem_private_key"); diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index 7c7c2d3440..a752a5f879 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -41,6 +41,7 @@ #include <grpc/grpc.h> #include <grpc/support/time.h> #include "rb_call.h" +#include "rb_call_credentials.h" #include "rb_channel.h" #include "rb_channel_credentials.h" #include "rb_completion_queue.h" @@ -91,7 +92,7 @@ static ID id_tv_sec; static ID id_tv_nsec; /** - * grpc_rb_time_timeval creates a time_eval from a ruby time object. + * grpc_rb_time_timeval creates a timeval from a ruby time object. * * This func is copied from ruby source, MRI/source/time.c, which is published * under the same license as the ruby.h, on which the entire extensions is @@ -137,7 +138,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { d += 1; f -= 1; } - t.tv_sec = (time_t)f; + t.tv_sec = (gpr_int64)f; if (f != t.tv_sec) { rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT_VALUE(time)); @@ -318,6 +319,7 @@ void Init_grpc() { Init_grpc_channel(); Init_grpc_completion_queue(); Init_grpc_call(); + Init_grpc_call_credentials(); Init_grpc_channel_credentials(); Init_grpc_server(); Init_grpc_server_credentials(); diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec index 61cf18cf54..363abe9a46 100755 --- a/src/ruby/grpc.gemspec +++ b/src/ruby/grpc.gemspec @@ -39,6 +39,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake-compiler', '~> 0.9' s.add_development_dependency 'rspec', '~> 3.2' s.add_development_dependency 'rubocop', '~> 0.30.0' + s.add_development_dependency 'signet', '~>0.6.0' s.extensions = %w(ext/grpc/extconf.rb) end diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb index 90aaa026ec..13100a614c 100644 --- a/src/ruby/lib/grpc/generic/client_stub.rb +++ b/src/ruby/lib/grpc/generic/client_stub.rb @@ -57,21 +57,6 @@ module GRPC Core::Channel.new(host, kw, creds) end - def self.update_with_jwt_aud_uri(a_hash, host, method) - last_slash_idx, res = method.rindex('/'), a_hash.clone - return res if last_slash_idx.nil? - service_name = method[0..(last_slash_idx - 1)] - res[:jwt_aud_uri] = "https://#{host}#{service_name}" - res - end - - # check_update_metadata is used by #initialize verify that it's a Proc. - def self.check_update_metadata(update_metadata) - return update_metadata if update_metadata.nil? - fail(TypeError, '!is_a?Proc') unless update_metadata.is_a?(Proc) - update_metadata - end - # Allows users of the stub to modify the propagate mask. # # This is an advanced feature for use when making calls to another gRPC @@ -99,29 +84,21 @@ module GRPC # - :timeout # when present, this is the default timeout used for calls # - # - :update_metadata - # when present, this a func that takes a hash and returns a hash - # it can be used to update metadata, i.e, remove, or amend - # metadata values. - # # @param host [String] the host the stub connects to # @param q [Core::CompletionQueue] used to wait for events # @param channel_override [Core::Channel] a pre-created channel # @param timeout [Number] the default timeout to use in requests # @param creds [Core::ChannelCredentials] the channel credentials - # @param update_metadata a func that updates metadata as described above # @param kw [KeywordArgs]the channel arguments def initialize(host, q, channel_override: nil, timeout: nil, creds: nil, propagate_mask: nil, - update_metadata: nil, **kw) fail(TypeError, '!CompletionQueue') unless q.is_a?(Core::CompletionQueue) @queue = q @ch = ClientStub.setup_channel(channel_override, host, creds, **kw) - @update_metadata = ClientStub.check_update_metadata(update_metadata) alt_host = kw[Core::Channel::SSL_TARGET] @host = alt_host.nil? ? host : alt_host @propagate_mask = propagate_mask @@ -166,6 +143,8 @@ module GRPC # @param deadline [Time] (optional) the time the request should complete # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @param return_op [true|false] return an Operation if true # @return [Object] the response received from the server def request_response(method, req, marshal, unmarshal, @@ -173,19 +152,20 @@ module GRPC timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.request_response(req, **md) unless return_op + parent: parent, + credentials: credentials) + return c.request_response(req, **kw) unless return_op # return the operation view of the active_call; define #execute as a # new method for this instance that invokes #request_response. op = c.operation op.define_singleton_method(:execute) do - c.request_response(req, **md) + c.request_response(req, **kw) end op end @@ -234,25 +214,28 @@ module GRPC # @param return_op [true|false] return an Operation if true # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @return [Object|Operation] the response received from the server def client_streamer(method, requests, marshal, unmarshal, deadline: nil, timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.client_streamer(requests, **md) unless return_op + parent: parent, + credentials: credentials) + return c.client_streamer(requests, **kw) unless return_op # return the operation view of the active_call; define #execute as a # new method for this instance that invokes #client_streamer. op = c.operation op.define_singleton_method(:execute) do - c.client_streamer(requests, **md) + c.client_streamer(requests, **kw) end op end @@ -309,6 +292,8 @@ module GRPC # @param return_op [true|false]return an Operation if true # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @param blk [Block] when provided, is executed for each response # @return [Enumerator|Operation|nil] as discussed above def server_streamer(method, req, marshal, unmarshal, @@ -316,20 +301,21 @@ module GRPC timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw, &blk) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.server_streamer(req, **md, &blk) unless return_op + parent: parent, + credentials: credentials) + return c.server_streamer(req, **kw, &blk) unless return_op # return the operation view of the active_call; define #execute # as a new method for this instance that invokes #server_streamer op = c.operation op.define_singleton_method(:execute) do - c.server_streamer(req, **md, &blk) + c.server_streamer(req, **kw, &blk) end op end @@ -424,6 +410,8 @@ module GRPC # @param deadline [Time] (optional) the time the request should complete # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @param return_op [true|false] return an Operation if true # @param blk [Block] when provided, is executed for each response # @return [Enumerator|nil|Operation] as discussed above @@ -432,36 +420,28 @@ module GRPC timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw, &blk) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.bidi_streamer(requests, **md, &blk) unless return_op + parent: parent, + credentials: credentials) + + return c.bidi_streamer(requests, **kw, &blk) unless return_op # return the operation view of the active_call; define #execute # as a new method for this instance that invokes #bidi_streamer op = c.operation op.define_singleton_method(:execute) do - c.bidi_streamer(requests, **md, &blk) + c.bidi_streamer(requests, **kw, &blk) end op end private - def update_metadata(kw, method) - return kw if @update_metadata.nil? - just_jwt_uri = self.class.update_with_jwt_aud_uri({}, @host, method) - updated = @update_metadata.call(just_jwt_uri) - - # keys should be lowercase - updated = Hash[updated.each_pair.map { |k, v| [k.downcase, v] }] - kw.merge(updated) - end - # Creates a new active stub # # @param method [string] the method being called. @@ -473,7 +453,8 @@ module GRPC def new_active_call(method, marshal, unmarshal, deadline: nil, timeout: nil, - parent: nil) + parent: nil, + credentials: nil) if deadline.nil? deadline = from_relative_time(timeout.nil? ? @timeout : timeout) end @@ -483,6 +464,7 @@ module GRPC method, nil, # host use nil, deadline) + call.set_credentials credentials unless credentials.nil? ActiveCall.new(call, @queue, marshal, unmarshal, deadline, started: false) end end diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 0e318bd53b..410156ff03 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -48,6 +48,8 @@ module GRPC return false when 'TERM' return false + when nil + return true end end true diff --git a/src/ruby/pb/README.md b/src/ruby/pb/README.md index 84644e1098..e04aef185c 100644 --- a/src/ruby/pb/README.md +++ b/src/ruby/pb/README.md @@ -20,7 +20,7 @@ re-generate the surface. ```bash $ # (from this directory) -$ protoc -I . grpc/health/v1alpha/health.proto \ +$ protoc -I ../../proto ../../proto/grpc/health/v1alpha/health.proto \ --grpc_out=. \ --ruby_out=. \ --plugin=protoc-gen-grpc=`which grpc_ruby_plugin` diff --git a/src/ruby/pb/generate_proto_ruby.sh b/src/ruby/pb/generate_proto_ruby.sh new file mode 100755 index 0000000000..576b1c08d3 --- /dev/null +++ b/src/ruby/pb/generate_proto_ruby.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# Copyright 2015, 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. + +# Regenerates gRPC service stubs from proto files. +set +e +cd $(dirname $0)/../../.. + +PROTOC=bins/opt/protobuf/protoc +PLUGIN=protoc-gen-grpc=bins/opt/grpc_ruby_plugin + +$PROTOC -I src/proto src/proto/grpc/health/v1alpha/health.proto \ + --grpc_out=src/ruby/pb \ + --ruby_out=src/ruby/pb \ + --plugin=$PLUGIN + +$PROTOC -I . test/proto/{messages,test,empty}.proto \ + --grpc_out=src/ruby/pb \ + --ruby_out=src/ruby/pb \ + --plugin=$PLUGIN + +$PROTOC -I src/proto/math src/proto/math/math.proto \ + --grpc_out=src/ruby/bin \ + --ruby_out=src/ruby/bin \ + --plugin=$PLUGIN diff --git a/src/ruby/pb/grpc/health/v1alpha/health.proto b/src/ruby/pb/grpc/health/v1alpha/health.proto deleted file mode 100644 index d31df1e0a7..0000000000 --- a/src/ruby/pb/grpc/health/v1alpha/health.proto +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015, 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. - -syntax = "proto3"; - -package grpc.health.v1alpha; - -message HealthCheckRequest { - string host = 1; - string service = 2; -} - -message HealthCheckResponse { - enum ServingStatus { - UNKNOWN = 0; - SERVING = 1; - NOT_SERVING = 2; - } - ServingStatus status = 1; -} - -service Health { - rpc Check(HealthCheckRequest) returns (HealthCheckResponse); -}
\ No newline at end of file diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb index 30550d6cc0..6cc616e5cb 100755 --- a/src/ruby/pb/test/client.rb +++ b/src/ruby/pb/test/client.rb @@ -93,13 +93,6 @@ def load_test_certs files.map { |f| File.open(File.join(data_dir, f)).read } end -# loads the certificates used to access the test server securely. -def load_prod_cert - fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil? - GRPC.logger.info("loading prod certs from #{ENV['SSL_CERT_FILE']}") - File.open(ENV['SSL_CERT_FILE']).read -end - # creates SSL Credentials from the test certificates. def test_creds certs = load_test_certs @@ -108,8 +101,7 @@ end # creates SSL Credentials from the production certificates. def prod_creds - cert_text = load_prod_cert - GRPC::Core::ChannelCredentials.new(cert_text) + GRPC::Core::ChannelCredentials.new() end # creates the SSL Credentials. @@ -132,7 +124,8 @@ def create_stub(opts) if wants_creds.include?(opts.test_case) unless opts.oauth_scope.nil? auth_creds = Google::Auth.get_application_default(opts.oauth_scope) - stub_opts[:update_metadata] = auth_creds.updater_proc + call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc) + stub_opts[:creds] = stub_opts[:creds].compose call_creds end end @@ -141,12 +134,14 @@ def create_stub(opts) kw = auth_creds.updater_proc.call({}) # gives as an auth token # use a metadata update proc that just adds the auth token. - stub_opts[:update_metadata] = proc { |md| md.merge(kw) } + call_creds = GRPC::Core::CallCredentials.new(proc { |md| md.merge(kw) }) + stub_opts[:creds] = stub_opts[:creds].compose call_creds end if opts.test_case == 'jwt_token_creds' # don't use a scope auth_creds = Google::Auth.get_application_default - stub_opts[:update_metadata] = auth_creds.updater_proc + call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc) + stub_opts[:creds] = stub_opts[:creds].compose call_creds end GRPC.logger.info("... connecting securely to #{address}") @@ -160,7 +155,7 @@ end # produces a string of null chars (\0) of length l. def nulls(l) fail 'requires #{l} to be +ve' if l < 0 - [].pack('x' * l).force_encoding('utf-8') + [].pack('x' * l).force_encoding('ascii-8bit') end # a PingPongPlayer implements the ping pong bidi test. diff --git a/src/ruby/pb/test/proto/messages.rb b/src/ruby/pb/test/proto/messages.rb index 9b7f977285..5222c9824a 100644 --- a/src/ruby/pb/test/proto/messages.rb +++ b/src/ruby/pb/test/proto/messages.rb @@ -6,7 +6,7 @@ require 'google/protobuf' Google::Protobuf::DescriptorPool.generated_pool.build do add_message "grpc.testing.Payload" do optional :type, :enum, 1, "grpc.testing.PayloadType" - optional :body, :string, 2 + optional :body, :bytes, 2 end add_message "grpc.testing.EchoStatus" do optional :code, :int32, 1 diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb index 67877a191f..851e815222 100755 --- a/src/ruby/pb/test/server.rb +++ b/src/ruby/pb/test/server.rb @@ -126,7 +126,7 @@ end # produces a string of null chars (\0) of length l. def nulls(l) fail 'requires #{l} to be +ve' if l < 0 - [].pack('x' * l).force_encoding('utf-8') + [].pack('x' * l).force_encoding('ascii-8bit') end # A EnumeratorQueue wraps a Queue yielding the items added to it via each_item. diff --git a/src/ruby/spec/call_credentials_spec.rb b/src/ruby/spec/call_credentials_spec.rb new file mode 100644 index 0000000000..32a0ad44b7 --- /dev/null +++ b/src/ruby/spec/call_credentials_spec.rb @@ -0,0 +1,57 @@ +# Copyright 2015, 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' + +describe GRPC::Core::CallCredentials do + CallCredentials = GRPC::Core::CallCredentials + + let(:auth_proc) { proc { { 'plugin_key' => 'plugin_value' } } } + + describe '#new' do + it 'can successfully create a CallCredentials from a proc' do + expect { CallCredentials.new(auth_proc) }.not_to raise_error + end + end + + describe '#compose' do + it 'can compose with another CallCredentials' do + creds1 = CallCredentials.new(auth_proc) + creds2 = CallCredentials.new(auth_proc) + expect { creds1.compose creds2 }.not_to raise_error + end + + it 'can compose with multiple CallCredentials' do + creds1 = CallCredentials.new(auth_proc) + creds2 = CallCredentials.new(auth_proc) + creds3 = CallCredentials.new(auth_proc) + expect { creds1.compose(creds2, creds3) }.not_to raise_error + end + end +end diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb index dd3c45f754..6629570fba 100644 --- a/src/ruby/spec/call_spec.rb +++ b/src/ruby/spec/call_spec.rb @@ -144,6 +144,15 @@ describe GRPC::Core::Call do end end + describe '#set_credentials!' do + it 'can set a valid CallCredentials object' do + call = make_test_call + auth_proc = proc { { 'plugin_key' => 'plugin_value' } } + creds = GRPC::Core::CallCredentials.new auth_proc + expect { call.set_credentials! creds }.not_to raise_error + end + end + def make_test_call @ch.create_call(client_queue, nil, nil, 'dummy_method', nil, deadline) end diff --git a/src/ruby/spec/channel_credentials_spec.rb b/src/ruby/spec/channel_credentials_spec.rb index b2bdf7032e..0bcfc752a0 100644 --- a/src/ruby/spec/channel_credentials_spec.rb +++ b/src/ruby/spec/channel_credentials_spec.rb @@ -31,6 +31,7 @@ require 'grpc' describe GRPC::Core::ChannelCredentials do ChannelCredentials = GRPC::Core::ChannelCredentials + CallCredentials = GRPC::Core::CallCredentials def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') @@ -54,10 +55,43 @@ describe GRPC::Core::ChannelCredentials do expect { ChannelCredentials.new(root_cert) }.not_to raise_error end - it 'cannot be constructed with a nil server roots' do + it 'can be constructed with a nil server roots' do _, client_key, client_chain = load_test_certs blk = proc { ChannelCredentials.new(nil, client_key, client_chain) } - expect(&blk).to raise_error + expect(&blk).not_to raise_error + end + + it 'can be constructed with no params' do + blk = proc { ChannelCredentials.new(nil) } + expect(&blk).not_to raise_error + end + end + + describe '#compose' do + it 'can compose with a CallCredentials' do + certs = load_test_certs + channel_creds = ChannelCredentials.new(*certs) + auth_proc = proc { { 'plugin_key' => 'plugin_value' } } + call_creds = CallCredentials.new auth_proc + expect { channel_creds.compose call_creds }.not_to raise_error + end + + it 'can compose with multiple CallCredentials' do + certs = load_test_certs + channel_creds = ChannelCredentials.new(*certs) + auth_proc = proc { { 'plugin_key' => 'plugin_value' } } + call_creds1 = CallCredentials.new auth_proc + call_creds2 = CallCredentials.new auth_proc + expect do + channel_creds.compose(call_creds1, call_creds2) + end.not_to raise_error + end + + it 'cannot compose with ChannelCredentials' do + certs = load_test_certs + channel_creds1 = ChannelCredentials.new(*certs) + channel_creds2 = ChannelCredentials.new(*certs) + expect { channel_creds1.compose channel_creds2 }.to raise_error(TypeError) end end end diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index 734f176e94..7cce2076c9 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -413,6 +413,8 @@ describe 'the http client/server' do end describe 'the secure http client/server' do + include_context 'setup: tags' + def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') files = ['ca.pem', 'server1.key', 'server1.pem'] @@ -443,4 +445,31 @@ describe 'the secure http client/server' do it_behaves_like 'GRPC metadata delivery works OK' do end + + it 'modifies metadata with CallCredentials' do + auth_proc = proc { { 'k1' => 'updated-v1' } } + call_creds = GRPC::Core::CallCredentials.new(auth_proc) + md = { 'k2' => 'v2' } + expected_md = { 'k1' => 'updated-v1', 'k2' => 'v2' } + recvd_rpc = nil + rcv_thread = Thread.new do + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + end + + call = new_client_call + call.set_credentials! call_creds + client_ops = { + CallOps::SEND_INITIAL_METADATA => md + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_metadata).to be true + + # confirm the server can receive the client metadata + rcv_thread.join + expect(recvd_rpc).to_not eq nil + recvd_md = recvd_rpc.metadata + replace_symbols = Hash[expected_md.each_pair.collect { |x, y| [x.to_s, y] }] + expect(recvd_md).to eq(recvd_md.merge(replace_symbols)) + end end diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index da5bc6c9e5..40550230dd 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -145,34 +145,6 @@ describe 'ClientStub' do th.join end - it 'should update the sent metadata with a provided metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_request_response(@sent_msg, @resp, @pass, - k1: 'updated-v1', k2: 'v2') - update_md = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - expect(get_response(stub)).to eq(@resp) - th.join - end - - it 'should downcase the keys provided by the metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_request_response(@sent_msg, @resp, @pass, - k1: 'downcased-key-v1', k2: 'v2') - update_md = proc do |md| - md[:K1] = 'downcased-key-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - expect(get_response(stub)).to eq(@resp) - th.join - end - it 'should send a request when configured using an override channel' do server_port = create_test_server alt_host = "localhost:#{server_port}" @@ -241,20 +213,6 @@ describe 'ClientStub' do th.join end - it 'should update the sent metadata with a provided metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_client_streamer(@sent_msgs, @resp, @pass, - k1: 'updated-v1', k2: 'v2') - update_md = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - expect(get_response(stub)).to eq(@resp) - th.join - end - it 'should raise an error if the status is not ok' do server_port = create_test_server host = "localhost:#{server_port}" @@ -323,21 +281,6 @@ describe 'ClientStub' do expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus) th.join end - - it 'should update the sent metadata with a provided metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_server_streamer(@sent_msg, @replys, @pass, - k1: 'updated-v1', k2: 'v2') - update_md = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - e = get_responses(stub) - expect(e.collect { |r| r }).to eq(@replys) - th.join - end end describe 'without a call operation' do diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb index efe07f734e..d95a021311 100644 --- a/src/ruby/spec/generic/rpc_server_spec.rb +++ b/src/ruby/spec/generic/rpc_server_spec.rb @@ -422,25 +422,6 @@ describe GRPC::RpcServer do t.join end - it 'should receive updated metadata', server: true do - service = EchoService.new - @srv.handle(service) - t = Thread.new { @srv.run } - @srv.wait_till_running - req = EchoMsg.new - client_opts[:update_metadata] = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = EchoStub.new(@host, **client_opts) - expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg) - wanted_md = [{ 'k1' => 'updated-v1', 'k2' => 'v2', - 'jwt_aud_uri' => "https://#{@host}/EchoService" }] - check_md(wanted_md, service.received_md) - @srv.stop - t.join - end - it 'should handle multiple parallel requests', server: true do @srv.handle(EchoService) t = Thread.new { @srv.run } |