aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ruby
diff options
context:
space:
mode:
authorGravatar Michael Lumish <mlumish@google.com>2015-12-15 11:43:05 -0800
committerGravatar Michael Lumish <mlumish@google.com>2015-12-15 11:43:05 -0800
commitda61668122b63fc18d64231dede700139234bc75 (patch)
tree3b6fef704fd0e3670c0aa308dd6c24704c4f8c94 /src/ruby
parent960870549a31a354b63823319640a628a2fd1767 (diff)
parent5c8c3e78a5b2cd6fb8504b48bac7b7f030cce787 (diff)
Merge pull request #4454 from grpc/master
Re-cutting the 0.12 release from master.
Diffstat (limited to 'src/ruby')
-rwxr-xr-xsrc/ruby/bin/apis/pubsub_demo.rb22
-rwxr-xr-xsrc/ruby/bin/math.proto80
-rwxr-xr-xsrc/ruby/bin/math.rb29
-rwxr-xr-xsrc/ruby/bin/math_services.rb29
-rw-r--r--src/ruby/ext/grpc/extconf.rb4
-rw-r--r--src/ruby/ext/grpc/rb_call.c25
-rw-r--r--src/ruby/ext/grpc/rb_call.h6
-rw-r--r--src/ruby/ext/grpc/rb_call_credentials.c312
-rw-r--r--src/ruby/ext/grpc/rb_call_credentials.h46
-rw-r--r--src/ruby/ext/grpc/rb_channel_credentials.c64
-rw-r--r--src/ruby/ext/grpc/rb_grpc.c6
-rwxr-xr-xsrc/ruby/grpc.gemspec1
-rw-r--r--src/ruby/lib/grpc/generic/client_stub.rb82
-rw-r--r--src/ruby/lib/grpc/generic/rpc_server.rb2
-rw-r--r--src/ruby/pb/README.md2
-rwxr-xr-xsrc/ruby/pb/generate_proto_ruby.sh51
-rw-r--r--src/ruby/pb/grpc/health/v1alpha/health.proto50
-rwxr-xr-xsrc/ruby/pb/test/client.rb21
-rw-r--r--src/ruby/pb/test/proto/messages.rb2
-rwxr-xr-xsrc/ruby/pb/test/server.rb2
-rw-r--r--src/ruby/spec/call_credentials_spec.rb57
-rw-r--r--src/ruby/spec/call_spec.rb9
-rw-r--r--src/ruby/spec/channel_credentials_spec.rb38
-rw-r--r--src/ruby/spec/client_server_spec.rb29
-rw-r--r--src/ruby/spec/generic/client_stub_spec.rb57
-rw-r--r--src/ruby/spec/generic/rpc_server_spec.rb19
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*)(&params));
+}
+
+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 }