diff --git a/doc/PROTOCOL-HTTP2.md b/doc/PROTOCOL-HTTP2.md index 7f7055bb4df7b35391a7f25761ab1c28bfe6708f..7a26724c85ded4b28214845a5e5a3e84a882ab3f 100644 --- a/doc/PROTOCOL-HTTP2.md +++ b/doc/PROTOCOL-HTTP2.md @@ -44,8 +44,9 @@ Request-Headers are delivered as HTTP2 headers in HEADERS + CONTINUATION frames. * **Message-Type** → "grpc-message-type" {_type name for message schema_} * **Custom-Metadata** → Binary-Header / ASCII-Header * **Binary-Header** → {Header-Name "-bin" } {_base64 encoded value_} -* **ASCII-Header** → Header-Name {_value_} +* **ASCII-Header** → Header-Name ASCII-Value * **Header-Name** → 1\*( %x30-39 / %x61-7A / "\_" / "-") ; 0-9 a-z \_ - +* **ASCII-Value** → 1\*( %x20-%x7E ) ; space and printable ASCII HTTP2 requires that reserved headers, ones starting with ":" appear before all other headers. Additionally implementations should send **Timeout** immediately after the reserved headers and they should send the **Call-Definition** headers before sending **Custom-Metadata**. @@ -56,6 +57,23 @@ If **Timeout** is omitted a server should assume an infinite timeout. Client imp Note that HTTP2 does not allow arbitrary octet sequences for header values so binary header values must be encoded using Base64 as per https://tools.ietf.org/html/rfc4648#section-4. Implementations MUST accept padded and un-padded values and should emit un-padded values. Applications define binary headers by having their names end with "-bin". Runtime libraries use this suffix to detect binary headers and properly apply base64 encoding & decoding as headers are sent and received. +**Custom-Metadata** header order is not guaranteed to be preserved except for +values with duplicate header names. Duplicate header names may have their values +joined with "," as the delimiter and be considered semantically equivalent. +Implementations must split **Binary-Header**s on "," before decoding the +Base64-encoded values. + +**ASCII-Value** should not have leading or trailing whitespace. If it contains +leading or trailing whitespace, it may be stripped. The **ASCII-Value** +character range defined is more strict than HTTP. Implementations must not error +due to receiving an invalid **ASCII-Value** that's a valid **field-value** in +HTTP, but the precise behavior is not strictly defined: they may throw the value +away or accept the value. If accepted, care must be taken to make sure that the +application is permitted to echo the value back as metadata. For example, if the +metadata is provided to the application as a list in a request, the application +should not trigger an error by providing that same list as the metadata in the +response. + Servers may limit the size of **Request-Headers**, with a default of 8 KiB suggested. Implementations are encouraged to compute total header size like HTTP/2's `SETTINGS_MAX_HEADER_LIST_SIZE`: the sum of all header fields, for each diff --git a/examples/node/README.md b/examples/node/README.md index df681e85dd7ed0f6a094ed75fdf8d6d18ba0f794..09c56f7fa6f402cce6e1df6205ecdd1c17ebedee 100644 --- a/examples/node/README.md +++ b/examples/node/README.md @@ -4,7 +4,7 @@ gRPC in 3 minutes (Node.js) PREREQUISITES ------------- -- `node`: This requires Node 10.x or greater. +- `node`: This requires Node 0.10.x or greater. - [homebrew][] on Mac OS X. This simplifies the installation of the gRPC C core. INSTALL diff --git a/include/grpc++/support/channel_arguments.h b/include/grpc++/support/channel_arguments.h index 9957712a9652c744b91a8f5b274bc4025959518e..4da76a83edc62be1e47f6cb65645ec2b3e4a2f65 100644 --- a/include/grpc++/support/channel_arguments.h +++ b/include/grpc++/support/channel_arguments.h @@ -70,7 +70,8 @@ class ChannelArguments { void SetChannelArgs(grpc_channel_args* channel_args) const; // gRPC specific channel argument setters - /// Set target name override for SSL host name checking. + /// Set target name override for SSL host name checking. This option is for + /// testing only and should never be used in production. void SetSslTargetNameOverride(const grpc::string& name); // TODO(yangg) add flow control options /// Set the compression algorithm for the channel. diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c index 49c2cf9a19ee3d9597e30e95409dc4ca3b71eaa4..b15a9033af803bd1261fbb083ddf5fea2fe19ede 100644 --- a/src/core/client_config/subchannel.c +++ b/src/core/client_config/subchannel.c @@ -697,10 +697,25 @@ static double generate_uniform_random_number(grpc_subchannel *c) { /* Update backoff_delta and next_attempt in subchannel */ static void update_reconnect_parameters(grpc_subchannel *c) { + size_t i; gpr_int32 backoff_delta_millis, jitter; gpr_int32 max_backoff_millis = GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; double jitter_range; + + if (c->args) { + for (i = 0; i < c->args->num_args; i++) { + if (0 == strcmp(c->args->args[i].key, + "grpc.testing.fixed_reconnect_backoff")) { + GPR_ASSERT(c->args->args[i].type == GRPC_ARG_INTEGER); + c->next_attempt = gpr_time_add( + gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(c->args->args[i].value.integer, GPR_TIMESPAN)); + return; + } + } + } + backoff_delta_millis = (gpr_int32)(gpr_time_to_millis(c->backoff_delta) * GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER); diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c index bc0586d0691ff6d3e62445c9499a477eef8f5292..623a8d9233746fdb8a3629870d918fbce41267fd 100644 --- a/src/core/support/time_win32.c +++ b/src/core/support/time_win32.c @@ -66,14 +66,12 @@ gpr_timespec gpr_now(gpr_clock_type clock) { now_tv.tv_nsec = now_tb.millitm * 1000000; break; case GPR_CLOCK_MONOTONIC: + case GPR_CLOCK_PRECISE: QueryPerformanceCounter(×tamp); now_dbl = (timestamp.QuadPart - g_start_time.QuadPart) * g_time_scale; now_tv.tv_sec = (time_t)now_dbl; now_tv.tv_nsec = (int)((now_dbl - (double)now_tv.tv_sec) * 1e9); break; - case GPR_CLOCK_PRECISE: - gpr_precise_clock_now(&now_tv); - break; } return now_tv; } diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc index 584a0cf8abfc8e81eaa480ae487daa096daea58e..c11734d73749ac8a9d4bcea51d7b438f5d3ba0cf 100644 --- a/src/node/ext/channel.cc +++ b/src/node/ext/channel.cc @@ -82,7 +82,7 @@ bool ParseChannelArgs(Local<Value> args_val, return false; } grpc_channel_args *channel_args = reinterpret_cast<grpc_channel_args*>( - malloc(sizeof(channel_args))); + malloc(sizeof(grpc_channel_args))); *channel_args_ptr = channel_args; Local<Object> args_hash = Nan::To<Object>(args_val).ToLocalChecked(); Local<Array> keys = Nan::GetOwnPropertyNames(args_hash).ToLocalChecked(); diff --git a/src/node/src/client.js b/src/node/src/client.js index 3cdd55075242c750df019ff1936533d294cecd5e..d57826781d391d6872e6f00b757ee380ffa8a134 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -612,7 +612,15 @@ exports.makeClientConstructor = function(methods, serviceName) { if (!options) { options = {}; } - options['grpc.primary_user_agent'] = 'grpc-node/' + version; + /* Append the grpc-node user agent string after the application user agent + * string, and put the combination at the beginning of the user agent string + */ + if (options['grpc.primary_user_agent']) { + options['grpc.primary_user_agent'] += ' '; + } else { + options['grpc.primary_user_agent'] = ''; + } + options['grpc.primary_user_agent'] += 'grpc-node/' + version; /* Private fields use $ as a prefix instead of _ because it is an invalid * prefix of a method name */ this.$channel = new grpc.Channel(address, credentials, options); diff --git a/src/objective-c/examples/Sample/Podfile b/src/objective-c/examples/Sample/Podfile index 72308c1619294e04d890374d72fbbf4e3c3f621d..3b2f41256940c4931638bf5b422d03e08adf72f2 100644 --- a/src/objective-c/examples/Sample/Podfile +++ b/src/objective-c/examples/Sample/Podfile @@ -1,8 +1,9 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' +pod 'Protobuf', :path => "../../../../third_party/protobuf" pod 'gRPC', :path => "../../../.." -pod 'RemoteTest', :path => "../../generated_libraries/RemoteTestClient" +pod 'RemoteTest', :path => "../RemoteTestClient" target 'Sample' do end diff --git a/src/objective-c/examples/Sample/Sample/ViewController.m b/src/objective-c/examples/Sample/Sample/ViewController.m index 05bd6fa2dbc5c5ba8be85cfc8e2c7a9c89f39952..3d634a340df6dad79715fc9a91f7d0216262bf71 100644 --- a/src/objective-c/examples/Sample/Sample/ViewController.m +++ b/src/objective-c/examples/Sample/Sample/ViewController.m @@ -34,7 +34,7 @@ #import "ViewController.h" #import <GRPCClient/GRPCCall.h> -#import <GRPCClient/GRPCMethodName.h> +#import <ProtoRPC/ProtoMethod.h> #import <RemoteTest/Messages.pbobjc.h> #import <RemoteTest/Test.pbrpc.h> #import <RxLibrary/GRXWriter+Immediate.h> @@ -66,14 +66,14 @@ // Same example call using the generic gRPC client library: - GRPCMethodName *method = [[GRPCMethodName alloc] initWithPackage:@"grpc.testing" - interface:@"TestService" - method:@"UnaryCall"]; + ProtoMethod *method = [[ProtoMethod alloc] initWithPackage:@"grpc.testing" + service:@"TestService" + method:@"UnaryCall"]; - id<GRXWriter> requestsWriter = [GRXWriter writerWithValue:[request data]]; + GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]]; GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteHost - method:method + path:method.HTTPPath requestsWriter:requestsWriter]; id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) { diff --git a/src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec b/src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec deleted file mode 100644 index 23ccffe69dacd61f397e99b5b12734b6fb4ee14b..0000000000000000000000000000000000000000 --- a/src/objective-c/generated_libraries/RouteGuideClient/RouteGuide.podspec +++ /dev/null @@ -1,31 +0,0 @@ -Pod::Spec.new do |s| - s.name = "RouteGuide" - s.version = "0.0.1" - s.license = "New BSD" - - s.ios.deployment_target = "6.0" - s.osx.deployment_target = "10.8" - - # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. - s.prepare_command = <<-CMD - BINDIR=../../../../bins/$CONFIG - PROTOC=$BINDIR/protobuf/protoc - PLUGIN=$BINDIR/grpc_objective_c_plugin - $PROTOC --plugin=protoc-gen-grpc=$PLUGIN --objc_out=. --grpc_out=. *.proto - CMD - - s.subspec "Messages" do |ms| - ms.source_files = "*.pbobjc.{h,m}" - ms.header_mappings_dir = "." - ms.requires_arc = false - ms.dependency "Protobuf", "~> 3.0.0-alpha-3" - end - - s.subspec "Services" do |ss| - ss.source_files = "*.pbrpc.{h,m}" - ss.header_mappings_dir = "." - ss.requires_arc = true - ss.dependency "gRPC", "~> 0.5" - ss.dependency "#{s.name}/Messages" - end -end diff --git a/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto b/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto deleted file mode 100644 index 19592e2ebdd6fa88fb384a0305a790a8a0d4218e..0000000000000000000000000000000000000000 --- a/src/objective-c/generated_libraries/RouteGuideClient/route_guide.proto +++ /dev/null @@ -1,120 +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 routeguide; - -option objc_class_prefix = "RGD"; - -// Interface exported by the server. -service RouteGuide { - // A simple RPC. - // - // Obtains the feature at a given position. - rpc GetFeature(Point) returns (Feature) {} - - // A server-to-client streaming RPC. - // - // Obtains the Features available within the given Rectangle. Results are - // streamed rather than returned at once (e.g. in a response message with a - // repeated field), as the rectangle may cover a large area and contain a - // huge number of features. - rpc ListFeatures(Rectangle) returns (stream Feature) {} - - // A client-to-server streaming RPC. - // - // Accepts a stream of Points on a route being traversed, returning a - // RouteSummary when traversal is completed. - rpc RecordRoute(stream Point) returns (RouteSummary) {} - - // A Bidirectional streaming RPC. - // - // Accepts a stream of RouteNotes sent while a route is being traversed, - // while receiving other RouteNotes (e.g. from other users). - rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} -} - -// Points are represented as latitude-longitude pairs in the E7 representation -// (degrees multiplied by 10**7 and rounded to the nearest integer). -// Latitudes should be in the range +/- 90 degrees and longitude should be in -// the range +/- 180 degrees (inclusive). -message Point { - int32 latitude = 1; - int32 longitude = 2; -} - -// A latitude-longitude rectangle, represented as two diagonally opposite -// points "lo" and "hi". -message Rectangle { - // One corner of the rectangle. - Point lo = 1; - - // The other corner of the rectangle. - Point hi = 2; -} - -// A feature names something at a given point. -// -// If a feature could not be named, the name is empty. -message Feature { - // The name of the feature. - string name = 1; - - // The point where the feature is detected. - Point location = 2; -} - -// A RouteNote is a message sent while at a given point. -message RouteNote { - // The location from which the message is sent. - Point location = 1; - - // The message to be sent. - string message = 2; -} - -// A RouteSummary is received in response to a RecordRoute rpc. -// -// It contains the number of individual points received, the number of -// detected features, and the total distance covered as the cumulative sum of -// the distance between each point. -message RouteSummary { - // The number of points received. - int32 point_count = 1; - - // The number of known features passed while traversing the route. - int32 feature_count = 2; - - // The distance covered in metres. - int32 distance = 3; - - // The duration of the traversal in seconds. - int32 elapsed_time = 4; -} diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index 2a9b894cf631c8a46b19543e69ae8809ab7b522b..cab608d37fee7df15ad461ffe08a6501f9ea3b3c 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -3,8 +3,7 @@ platform :ios, '8.0' pod 'Protobuf', :path => "../../../third_party/protobuf" pod 'gRPC', :path => "../../.." -pod 'RemoteTest', :path => "../generated_libraries/RemoteTestClient" -pod 'RouteGuide', :path => "../generated_libraries/RouteGuideClient" +pod 'RemoteTest', :path => "RemoteTestClient" link_with 'AllTests', 'RxLibraryUnitTests', diff --git a/src/objective-c/generated_libraries/RemoteTestClient/RemoteTest.podspec b/src/objective-c/tests/RemoteTestClient/RemoteTest.podspec similarity index 100% rename from src/objective-c/generated_libraries/RemoteTestClient/RemoteTest.podspec rename to src/objective-c/tests/RemoteTestClient/RemoteTest.podspec diff --git a/src/objective-c/generated_libraries/RemoteTestClient/empty.proto b/src/objective-c/tests/RemoteTestClient/empty.proto similarity index 100% rename from src/objective-c/generated_libraries/RemoteTestClient/empty.proto rename to src/objective-c/tests/RemoteTestClient/empty.proto diff --git a/src/objective-c/generated_libraries/RemoteTestClient/messages.proto b/src/objective-c/tests/RemoteTestClient/messages.proto similarity index 100% rename from src/objective-c/generated_libraries/RemoteTestClient/messages.proto rename to src/objective-c/tests/RemoteTestClient/messages.proto diff --git a/src/objective-c/generated_libraries/RemoteTestClient/test.proto b/src/objective-c/tests/RemoteTestClient/test.proto similarity index 100% rename from src/objective-c/generated_libraries/RemoteTestClient/test.proto rename to src/objective-c/tests/RemoteTestClient/test.proto diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php index c26be607ffc98bb618fbdfac6e2b9e7cfd2a39c4..aa4de349ea7f60b3f8ae41c8ac292f8b5aa05ef6 100755 --- a/src/php/lib/Grpc/BaseStub.php +++ b/src/php/lib/Grpc/BaseStub.php @@ -51,6 +51,7 @@ class BaseStub * @param $opts array * - 'update_metadata': (optional) a callback function which takes in a * metadata array, and returns an updated metadata array + * - 'grpc.primary_user_agent': (optional) a user-agent string */ public function __construct($hostname, $opts) { @@ -64,7 +65,12 @@ class BaseStub } $package_config = json_decode( file_get_contents(dirname(__FILE__).'/../../composer.json'), true); - $opts['grpc.primary_user_agent'] = + if (!empty($opts['grpc.primary_user_agent'])) { + $opts['grpc.primary_user_agent'] .= ' '; + } else { + $opts['grpc.primary_user_agent'] = ''; + } + $opts['grpc.primary_user_agent'] .= 'grpc-php/'.$package_config['version']; $this->channel = new Channel($hostname, $opts); } diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index 6b5beb6f5d74294566a5922dae5145ce2afdb16e..40364328ee8d9a3b0209de537daf2d18348c945e 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -139,7 +139,15 @@ static const rb_data_type_t grpc_rb_md_ary_data_type = { {NULL, NULL}}, NULL, NULL, - 0}; +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + /* it is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because + * grpc_rb_call_destroy + * touches a hash object. + * TODO(yugui) Directly use st_table and call the free function earlier? + */ + 0, +#endif +}; /* Describes grpc_call struct for RTypedData */ static const rb_data_type_t grpc_call_data_type = { @@ -148,12 +156,15 @@ static const rb_data_type_t grpc_call_data_type = { {NULL, NULL}}, NULL, NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY /* it is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because * grpc_rb_call_destroy * touches a hash object. * TODO(yugui) Directly use st_table and call the free function earlier? */ - 0}; + 0, +#endif +}; /* Error code details is a hash containing text strings describing errors */ VALUE rb_error_code_details; diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c index 90afdc3fe112e9b65cc1f4073a930f34206987db..cd0b966e7269298da228ecf19ff49c137f857e47 100644 --- a/src/ruby/ext/grpc/rb_channel.c +++ b/src/ruby/ext/grpc/rb_channel.c @@ -111,7 +111,9 @@ static rb_data_type_t grpc_channel_data_type = { {grpc_rb_channel_mark, grpc_rb_channel_free, GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, NULL, NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY +#endif }; /* Allocates grpc_rb_channel instances. */ diff --git a/src/ruby/ext/grpc/rb_channel_args.c b/src/ruby/ext/grpc/rb_channel_args.c index 1ba30b69aa2dd653e2baa88b38d782b1354e0c5c..37dd981925ad5ee19b038b672feda8fa69c320a9 100644 --- a/src/ruby/ext/grpc/rb_channel_args.c +++ b/src/ruby/ext/grpc/rb_channel_args.c @@ -44,7 +44,9 @@ static rb_data_type_t grpc_rb_channel_args_data_type = { {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, NULL, NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY +#endif }; /* A callback the processes the hash key values in channel_args hash */ diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c index 0bc9eb2a97ae67bd7112dae70c4ea660fd81f4c1..a7de96d71893f61cd5bce0ce636f4df857032566 100644 --- a/src/ruby/ext/grpc/rb_completion_queue.c +++ b/src/ruby/ext/grpc/rb_completion_queue.c @@ -121,9 +121,11 @@ static rb_data_type_t grpc_rb_completion_queue_data_type = { {GRPC_RB_GC_NOT_MARKED, grpc_rb_completion_queue_destroy, GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, NULL, NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY /* cannot immediately free because grpc_rb_completion_queue_shutdown_drain * calls rb_thread_call_without_gvl. */ - 0 + 0, +#endif }; /* Allocates a completion queue. */ diff --git a/src/ruby/ext/grpc/rb_credentials.c b/src/ruby/ext/grpc/rb_credentials.c index ae757f698666c9327d191b4c438908e9e4dc7c28..486ff79f910f036524fe21b138d81267b962fef6 100644 --- a/src/ruby/ext/grpc/rb_credentials.c +++ b/src/ruby/ext/grpc/rb_credentials.c @@ -92,7 +92,10 @@ static rb_data_type_t grpc_rb_credentials_data_type = { GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, NULL, NULL, - RUBY_TYPED_FREE_IMMEDIATELY}; +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + RUBY_TYPED_FREE_IMMEDIATELY +#endif +}; /* Allocates Credential instances. Provides safe initial defaults for the instance fields. */ diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index 327fd1a4fc1b82afc8f5903d9b7b337a3f251b5c..33f48779d84655d09dbbd18c57f171e353924c14 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -55,7 +55,10 @@ static rb_data_type_t grpc_rb_timespec_data_type = { {NULL, NULL}}, NULL, NULL, - RUBY_TYPED_FREE_IMMEDIATELY}; +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + RUBY_TYPED_FREE_IMMEDIATELY +#endif +}; /* Alloc func that blocks allocation of a given object by raising an * exception. */ @@ -262,10 +265,20 @@ static void Init_grpc_time_consts() { id_tv_nsec = rb_intern("tv_nsec"); } +/* + TODO: find an alternative to ruby_vm_at_exit that is ok in Ruby 2.0 where + RUBY_TYPED_FREE_IMMEDIATELY is not defined. + + At the moment, registering a function using ruby_vm_at_exit segfaults in Ruby + 2.0. This is not an issue with the gRPC handler. More likely, this was an + in issue with 2.0 that got resolved in 2.1 and has not been backported. +*/ +#ifdef RUBY_TYPED_FREE_IMMEDIATELY static void grpc_rb_shutdown(ruby_vm_t *vm) { (void)vm; grpc_shutdown(); } +#endif /* Initialize the GRPC module structs */ @@ -285,7 +298,12 @@ VALUE sym_metadata = Qundef; void Init_grpc() { grpc_init(); + +/* TODO: find alternative to ruby_vm_at_exit that is ok in Ruby 2.0 */ +#ifdef RUBY_TYPED_FREE_IMMEDIATELY ruby_vm_at_exit(grpc_rb_shutdown); +#endif + grpc_rb_mGRPC = rb_define_module("GRPC"); grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core"); grpc_rb_sNewServerRpc = diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c index 446965886968b3779e919f14cef529d13140a4ab..ebdd7e1a346a2b49374ef533e5f4b6872d2a1d0b 100644 --- a/src/ruby/ext/grpc/rb_server.c +++ b/src/ruby/ext/grpc/rb_server.c @@ -101,11 +101,14 @@ static const rb_data_type_t grpc_rb_server_data_type = { {NULL, NULL}}, NULL, NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY /* It is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because the free function would block * and we might want to unlock GVL * TODO(yugui) Unlock GVL? */ - 0}; + 0, +#endif +}; /* Allocates grpc_rb_server instances. */ static VALUE grpc_rb_server_alloc(VALUE cls) { diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c index ea4d0d864ecf7c3233eae43b4ce47bfe155342f4..de57585e0baecef5185f8128eee3a46b34e5cccd 100644 --- a/src/ruby/ext/grpc/rb_server_credentials.c +++ b/src/ruby/ext/grpc/rb_server_credentials.c @@ -91,7 +91,9 @@ static const rb_data_type_t grpc_rb_server_credentials_data_type = { {grpc_rb_server_credentials_mark, grpc_rb_server_credentials_free, GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, NULL, NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY +#endif }; /* Allocates ServerCredential instances. diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb index d9cb924735610396ac610d50a4fd451d0d4743a4..e80d24edc9b487993c368ee693d61b8685d91c64 100644 --- a/src/ruby/lib/grpc/generic/active_call.rb +++ b/src/ruby/lib/grpc/generic/active_call.rb @@ -199,11 +199,7 @@ module GRPC # marshalled. def remote_send(req, marshalled = false) GRPC.logger.debug("sending #{req}, marshalled? #{marshalled}") - if marshalled - payload = req - else - payload = @marshal.call(req) - end + payload = marshalled ? req : @marshal.call(req) @call.run_batch(@cq, self, INFINITE_FUTURE, SEND_MESSAGE => payload) end @@ -417,7 +413,9 @@ module GRPC # @return [Enumerator, nil] a response Enumerator def bidi_streamer(requests, **kw, &blk) start_call(**kw) unless @started - bd = BidiCall.new(@call, @cq, @marshal, @unmarshal) + bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, + metadata_tag: @metadata_tag) + @metadata_tag = nil # run_on_client ensures metadata is read bd.run_on_client(requests, @op_notifier, &blk) end diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb index 9dbbb74caff191cce3fcf31cdf52d1b949f4a0be..6b9b7856933cb94dd86144b4f80ad9cc0b904f43 100644 --- a/src/ruby/lib/grpc/generic/bidi_call.rb +++ b/src/ruby/lib/grpc/generic/bidi_call.rb @@ -56,7 +56,8 @@ module GRPC # the call # @param marshal [Function] f(obj)->string that marshal requests # @param unmarshal [Function] f(string)->obj that unmarshals responses - def initialize(call, q, marshal, unmarshal) + # @param metadata_tag [Object] tag object used to collect metadata + def initialize(call, q, marshal, unmarshal, metadata_tag: nil) fail(ArgumentError, 'not a call') unless call.is_a? Core::Call unless q.is_a? Core::CompletionQueue fail(ArgumentError, 'not a CompletionQueue') @@ -67,6 +68,7 @@ module GRPC @op_notifier = nil # signals completion on clients @readq = Queue.new @unmarshal = unmarshal + @metadata_tag = metadata_tag end # Begins orchestration of the Bidi stream for a client sending requests. @@ -113,6 +115,18 @@ module GRPC @op_notifier.notify(self) end + # performs a read using @call.run_batch, ensures metadata is set up + def read_using_run_batch + ops = { RECV_MESSAGE => nil } + ops[RECV_INITIAL_METADATA] = nil unless @metadata_tag.nil? + batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE, ops) + unless @metadata_tag.nil? + @call.metadata = batch_result.metadata + @metadata_tag = nil + end + batch_result + end + # each_queued_msg yields each message on this instances readq # # - messages are added to the readq by #read_loop @@ -169,9 +183,7 @@ module GRPC loop do GRPC.logger.debug("bidi-read-loop: #{count}") count += 1 - # TODO: ensure metadata is read if available, currently it's not - batch_result = @call.run_batch(@cq, read_tag, INFINITE_FUTURE, - RECV_MESSAGE => nil) + batch_result = read_using_run_batch # handle the next message if batch_result.message.nil? diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 228c500672b859499596e8e769e0f40929ba71df..0e318bd53b8fb5e693a40af13cc8d35f1783ed79 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -418,11 +418,11 @@ module GRPC an_rpc = @server.request_call(@cq, loop_tag, INFINITE_FUTURE) break if (!an_rpc.nil?) && an_rpc.call.nil? - c = new_active_server_call(an_rpc) - unless c.nil? - mth = an_rpc.method.to_sym - @pool.schedule(c) do |call| - rpc_descs[mth].run_server_method(call, rpc_handlers[mth]) + active_call = new_active_server_call(an_rpc) + unless active_call.nil? + @pool.schedule(active_call) do |ac| + c, mth = ac + rpc_descs[mth].run_server_method(c, rpc_handlers[mth]) end end rescue Core::CallError, RuntimeError => e @@ -442,6 +442,7 @@ module GRPC # allow the metadata to be accessed from the call handle_call_tag = Object.new an_rpc.call.metadata = an_rpc.metadata # attaches md to call for handlers + GRPC.logger.debug("call md is #{an_rpc.metadata}") connect_md = nil unless @connect_md_proc.nil? connect_md = @connect_md_proc.call(an_rpc.method, an_rpc.metadata) @@ -454,9 +455,11 @@ module GRPC # Create the ActiveCall GRPC.logger.info("deadline is #{an_rpc.deadline}; (now=#{Time.now})") rpc_desc = rpc_descs[an_rpc.method.to_sym] - ActiveCall.new(an_rpc.call, @cq, - rpc_desc.marshal_proc, rpc_desc.unmarshal_proc(:input), - an_rpc.deadline) + c = ActiveCall.new(an_rpc.call, @cq, + rpc_desc.marshal_proc, rpc_desc.unmarshal_proc(:input), + an_rpc.deadline) + mth = an_rpc.method.to_sym + [c, mth] end protected diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb index 1388685734a3d51635992c6cfe3fc7aba39f02cd..b84cd43090656523dfb85c54c762e55fbe685b7a 100755 --- a/src/ruby/pb/test/client.rb +++ b/src/ruby/pb/test/client.rb @@ -46,6 +46,7 @@ $LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir) require 'optparse' +require 'logger' require 'grpc' require 'googleauth' @@ -59,6 +60,22 @@ require 'signet/ssl_config' AUTH_ENV = Google::Auth::CredentialsLoader::ENV_VAR +# RubyLogger defines a logger for gRPC based on the standard ruby logger. +module RubyLogger + def logger + LOGGER + end + + LOGGER = Logger.new(STDOUT) + LOGGER.level = Logger::INFO +end + +# GRPC is the general RPC module +module GRPC + # Inject the noop #logger if no module-level logger method has been injected. + extend RubyLogger +end + # AssertionError is use to indicate interop test failures. class AssertionError < RuntimeError; end diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb index 25c1b1e9e64e7f06a5a2f60e8f6c93f61cfbe2d6..67877a191fc96505f5040cd826568235921a439a 100755 --- a/src/ruby/pb/test/server.rb +++ b/src/ruby/pb/test/server.rb @@ -45,6 +45,7 @@ $LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir) require 'forwardable' +require 'logger' require 'optparse' require 'grpc' @@ -53,6 +54,60 @@ require 'test/proto/empty' require 'test/proto/messages' require 'test/proto/test_services' +# DebugIsTruncated extends the default Logger to truncate debug messages +class DebugIsTruncated < Logger + def debug(s) + super(truncate(s, 1024)) + end + + # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>: + # + # 'Once upon a time in a world far far away'.truncate(27) + # # => "Once upon a time in a wo..." + # + # Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break: + # + # 'Once upon a time in a world far far away'.truncate(27, separator: ' ') + # # => "Once upon a time in a..." + # + # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/) + # # => "Once upon a time in a..." + # + # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...") + # for a total length not exceeding <tt>length</tt>: + # + # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)') + # # => "And they f... (continued)" + def truncate(s, truncate_at, options = {}) + return s unless s.length > truncate_at + omission = options[:omission] || '...' + with_extra_room = truncate_at - omission.length + stop = \ + if options[:separator] + rindex(options[:separator], with_extra_room) || with_extra_room + else + with_extra_room + end + "#{s[0, stop]}#{omission}" + end +end + +# RubyLogger defines a logger for gRPC based on the standard ruby logger. +module RubyLogger + def logger + LOGGER + end + + LOGGER = DebugIsTruncated.new(STDOUT) + LOGGER.level = Logger::WARN +end + +# GRPC is the general RPC module +module GRPC + # Inject the noop #logger if no module-level logger method has been injected. + extend RubyLogger +end + # loads the certificates by the test server. def load_test_certs this_dir = File.expand_path(File.dirname(__FILE__)) @@ -113,7 +168,7 @@ class TestTarget < Grpc::Testing::TestService::Service def streaming_input_call(call) sizes = call.each_remote_read.map { |x| x.payload.body.length } - sum = sizes.inject { |s, x| s + x } + sum = sizes.inject(0) { |s, x| s + x } StreamingInputCallResponse.new(aggregated_payload_size: sum) end diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb index 9bc82638c75fd3091e655b531035ca20234951cd..322566b7842fe316a446fd228dd9b735587a9fcb 100644 --- a/src/ruby/spec/pb/health/checker_spec.rb +++ b/src/ruby/spec/pb/health/checker_spec.rb @@ -31,6 +31,7 @@ require 'grpc' require 'grpc/health/v1alpha/health' require 'grpc/health/checker' require 'open3' +require 'tmpdir' def can_run_codegen_check system('which grpc_ruby_plugin') && system('which protoc') diff --git a/test/core/client_config/lb_policies_test.c b/test/core/client_config/lb_policies_test.c index e44930ed53512d9184ae087ba98f29aaf18cbf01..0218b8f07fbc37cf4dfa674d53cdfd91df4a3584 100644 --- a/test/core/client_config/lb_policies_test.c +++ b/test/core/client_config/lb_policies_test.c @@ -303,7 +303,8 @@ int *perform_request(servers_fixture *f, grpc_channel *client, grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL)); s_idx = -1; - while ((ev = grpc_completion_queue_next(f->cq, n_millis_time(s_idx == -1 ? 3000 : 200), NULL)) + while ((ev = grpc_completion_queue_next( + f->cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1), NULL)) .type != GRPC_QUEUE_TIMEOUT) { GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); read_tag = ((int)(gpr_intptr)ev.tag); @@ -315,6 +316,7 @@ int *perform_request(servers_fixture *f, grpc_channel *client, s_idx = read_tag - 1000; s_valid[s_idx] = 1; connection_sequence[iter_num] = s_idx; + break; } else if (read_tag == 1) { gpr_log(GPR_DEBUG, "client timed out"); GPR_ASSERT(ev.success); @@ -453,6 +455,8 @@ void run_spec(const test_spec *spec) { int *actual_connection_sequence; request_data rdata; servers_fixture *f; + grpc_channel_args args; + grpc_arg arg; rdata.call_details = gpr_malloc(sizeof(grpc_call_details) * spec->num_servers); f = setup_servers("127.0.0.1", &rdata, spec->num_servers); @@ -462,7 +466,14 @@ void run_spec(const test_spec *spec) { f->num_servers, ",", NULL); gpr_asprintf(&client_hostport, "ipv4:%s?lb_policy=round_robin", servers_hostports_str); - client = grpc_insecure_channel_create(client_hostport, NULL, NULL); + + arg.type = GRPC_ARG_INTEGER; + arg.key = "grpc.testing.fixed_reconnect_backoff"; + arg.value.integer = 100; + args.num_args = 1; + args.args = &arg; + + client = grpc_insecure_channel_create(client_hostport, &args, NULL); gpr_log(GPR_INFO, "Testing '%s' with servers=%s client=%s", spec->description, servers_hostports_str, client_hostport); diff --git a/tools/codegen/core/gen_chttp2_stream_lists.py b/tools/codegen/core/gen_chttp2_stream_lists.py deleted file mode 100644 index b28b04f643122b019e912540f228c8ed20be9eeb..0000000000000000000000000000000000000000 --- a/tools/codegen/core/gen_chttp2_stream_lists.py +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/tools/http2_interop/http2_interop.test b/tools/http2_interop/http2_interop.test deleted file mode 100755 index 0700763dc3386b03bbbf19ef4c241702ccf187ad..0000000000000000000000000000000000000000 Binary files a/tools/http2_interop/http2_interop.test and /dev/null differ diff --git a/tools/jenkins/run_interop.sh b/tools/jenkins/run_interop.sh index 5dd477ed77ac2ee927fff38eaaeefc3f8bb7e869..a424aea7fc47641fefd5a39c96d987ea6e569a0a 100755 --- a/tools/jenkins/run_interop.sh +++ b/tools/jenkins/run_interop.sh @@ -34,4 +34,4 @@ set -ex # Enter the gRPC repo root cd $(dirname $0)/../.. -tools/run_tests/run_interop_tests.py -l all -s all --cloud_to_prod --cloud_to_prod_auth --use_docker -t -j 12 $@ || true +tools/run_tests/run_interop_tests.py -l all -s all --cloud_to_prod --cloud_to_prod_auth --use_docker --http2_interop -t -j 12 $@ || true diff --git a/tools/run_tests/port_server.py b/tools/run_tests/port_server.py index 3b85486ebfe599732f38f1db2756db9b03edc2e4..14e82b601eadbef7062397a2c33c791bff43318c 100755 --- a/tools/run_tests/port_server.py +++ b/tools/run_tests/port_server.py @@ -42,7 +42,7 @@ import time # increment this number whenever making a change to ensure that # the changes are picked up by running CI servers # note that all changes must be backwards compatible -_MY_VERSION = 5 +_MY_VERSION = 7 if len(sys.argv) == 2 and sys.argv[1] == 'dump_version': diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py index 1686942b0ae9eb2ce467922fa75c7027ed60ed14..2634164a2150ea0a7e1cc06d40e9b17f6eab53ee 100755 --- a/tools/run_tests/run_interop_tests.py +++ b/tools/run_tests/run_interop_tests.py @@ -624,7 +624,9 @@ try: docker_image=docker_images.get(str(language))) jobs.append(test_job) - if args.http2_interop: + # TODO(carl-mastrangelo): Currently prod TLS terminators aren't spec compliant. Reenable + # this once a better solution is in place. + if args.http2_interop and False: for test_case in _HTTP2_TEST_CASES: test_job = cloud_to_prod_jobspec(http2Interop, test_case, docker_image=docker_images.get(str(http2Interop))) @@ -660,6 +662,9 @@ try: if args.http2_interop: for test_case in _HTTP2_TEST_CASES: + if server_name == "go": + # TODO(carl-mastrangelo): Reenable after https://github.com/grpc/grpc-go/issues/434 + continue test_job = cloud_to_cloud_jobspec(http2Interop, test_case, server_name, diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 85c73d6fbd20eb9dca23061d3227e8d2c3c5523c..ab2b71b80e0cb8c27fcd9f3cefbedb9fd247d22b 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -749,7 +749,8 @@ def _start_port_server(port_server_port): running = False if running: current_version = int(subprocess.check_output( - [sys.executable, 'tools/run_tests/port_server.py', 'dump_version'])) + [sys.executable, os.path.abspath('tools/run_tests/port_server.py'), + 'dump_version'])) print 'my port server is version %d' % current_version running = (version >= current_version) if not running: @@ -760,13 +761,18 @@ def _start_port_server(port_server_port): fd, logfile = tempfile.mkstemp() os.close(fd) print 'starting port_server, with log file %s' % logfile - args = [sys.executable, 'tools/run_tests/port_server.py', '-p', '%d' % port_server_port, '-l', logfile] + args = [sys.executable, os.path.abspath('tools/run_tests/port_server.py'), + '-p', '%d' % port_server_port, '-l', logfile] env = dict(os.environ) env['BUILD_ID'] = 'pleaseDontKillMeJenkins' if platform.system() == 'Windows': + # Working directory of port server needs to be outside of Jenkins + # workspace to prevent file lock issues. + tempdir = tempfile.mkdtemp() port_server = subprocess.Popen( args, env=env, + cwd=tempdir, creationflags = 0x00000008, # detached process close_fds=True) else: