diff --git a/.gitignore b/.gitignore index 502483f4569..ca61bda124c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ dist/ *.egg # Node installation output -node_modules/ +^node_modules src/node/extension_binary/ # gcov coverage data diff --git a/BUILD b/BUILD index f8e7661ad46..b69c9404113 100644 --- a/BUILD +++ b/BUILD @@ -1136,6 +1136,8 @@ cc_library( "src/compiler/csharp_generator.h", "src/compiler/csharp_generator_helpers.h", "src/compiler/generator_helpers.h", + "src/compiler/node_generator.h", + "src/compiler/node_generator_helpers.h", "src/compiler/objective_c_generator.h", "src/compiler/objective_c_generator_helpers.h", "src/compiler/python_generator.h", @@ -1145,6 +1147,7 @@ cc_library( "src/compiler/ruby_generator_string-inl.h", "src/compiler/cpp_generator.cc", "src/compiler/csharp_generator.cc", + "src/compiler/node_generator.cc", "src/compiler/objective_c_generator.cc", "src/compiler/python_generator.cc", "src/compiler/ruby_generator.cc", @@ -1666,6 +1669,18 @@ cc_binary( ) +cc_binary( + name = "grpc_node_plugin", + srcs = [ + "src/compiler/node_plugin.cc", + ], + deps = [ + "//external:protobuf_compiler", + ":grpc_plugin_support", + ], +) + + cc_binary( name = "grpc_objective_c_plugin", srcs = [ diff --git a/Makefile b/Makefile index 386c4a79b34..3b7dbd9638a 100644 --- a/Makefile +++ b/Makefile @@ -779,7 +779,7 @@ endif .SECONDARY = %.pb.h %.pb.cc -PROTOC_PLUGINS = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin +PROTOC_PLUGINS = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_node_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin ifeq ($(DEP_MISSING),) all: static shared plugins dep_error: @@ -1013,6 +1013,7 @@ generic_end2end_test: $(BINDIR)/$(CONFIG)/generic_end2end_test grpc_cli: $(BINDIR)/$(CONFIG)/grpc_cli grpc_cpp_plugin: $(BINDIR)/$(CONFIG)/grpc_cpp_plugin grpc_csharp_plugin: $(BINDIR)/$(CONFIG)/grpc_csharp_plugin +grpc_node_plugin: $(BINDIR)/$(CONFIG)/grpc_node_plugin grpc_objective_c_plugin: $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin grpc_python_plugin: $(BINDIR)/$(CONFIG)/grpc_python_plugin grpc_ruby_plugin: $(BINDIR)/$(CONFIG)/grpc_ruby_plugin @@ -2245,6 +2246,8 @@ else $(Q) $(INSTALL) -d $(prefix)/bin $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(prefix)/bin/grpc_csharp_plugin $(Q) $(INSTALL) -d $(prefix)/bin + $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_node_plugin $(prefix)/bin/grpc_node_plugin + $(Q) $(INSTALL) -d $(prefix)/bin $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(prefix)/bin/grpc_objective_c_plugin $(Q) $(INSTALL) -d $(prefix)/bin $(Q) $(INSTALL) $(BINDIR)/$(CONFIG)/grpc_python_plugin $(prefix)/bin/grpc_python_plugin @@ -3611,6 +3614,7 @@ endif LIBGRPC_PLUGIN_SUPPORT_SRC = \ src/compiler/cpp_generator.cc \ src/compiler/csharp_generator.cc \ + src/compiler/node_generator.cc \ src/compiler/objective_c_generator.cc \ src/compiler/python_generator.cc \ src/compiler/ruby_generator.cc \ @@ -10545,6 +10549,37 @@ ifneq ($(NO_DEPS),true) endif +GRPC_NODE_PLUGIN_SRC = \ + src/compiler/node_plugin.cc \ + +GRPC_NODE_PLUGIN_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GRPC_NODE_PLUGIN_SRC)))) + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. + +$(BINDIR)/$(CONFIG)/grpc_node_plugin: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/grpc_node_plugin: $(PROTOBUF_DEP) $(GRPC_NODE_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + $(E) "[HOSTLD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(HOST_LDXX) $(HOST_LDFLAGS) $(GRPC_NODE_PLUGIN_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(HOST_LDLIBSXX) $(HOST_LDLIBS_PROTOC) $(HOST_LDLIBS) $(HOST_LDLIBS_PROTOC) -o $(BINDIR)/$(CONFIG)/grpc_node_plugin + +endif + +$(OBJDIR)/$(CONFIG)/src/compiler/node_plugin.o: $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a + +deps_grpc_node_plugin: $(GRPC_NODE_PLUGIN_OBJS:.o=.dep) + +ifneq ($(NO_DEPS),true) +-include $(GRPC_NODE_PLUGIN_OBJS:.o=.dep) +endif + + GRPC_OBJECTIVE_C_PLUGIN_SRC = \ src/compiler/objective_c_plugin.cc \ diff --git a/build.yaml b/build.yaml index 7248753bca7..6c00b42a1e7 100644 --- a/build.yaml +++ b/build.yaml @@ -938,6 +938,8 @@ libs: - src/compiler/csharp_generator.h - src/compiler/csharp_generator_helpers.h - src/compiler/generator_helpers.h + - src/compiler/node_generator.h + - src/compiler/node_generator_helpers.h - src/compiler/objective_c_generator.h - src/compiler/objective_c_generator_helpers.h - src/compiler/python_generator.h @@ -948,6 +950,7 @@ libs: src: - src/compiler/cpp_generator.cc - src/compiler/csharp_generator.cc + - src/compiler/node_generator.cc - src/compiler/objective_c_generator.cc - src/compiler/python_generator.cc - src/compiler/ruby_generator.cc @@ -2582,6 +2585,15 @@ targets: secure: false vs_config_type: Application vs_project_guid: '{3C813052-A49A-4662-B90A-1ADBEC7EE453}' +- name: grpc_node_plugin + build: protoc + language: c++ + src: + - src/compiler/node_plugin.cc + deps: + - grpc_plugin_support + secure: false + vs_config_type: Application - name: grpc_objective_c_plugin build: protoc language: c++ diff --git a/examples/node/greeter_client.js b/examples/node/greeter_client.js index 2820acbbb71..7125c2fec5b 100644 --- a/examples/node/greeter_client.js +++ b/examples/node/greeter_client.js @@ -31,22 +31,30 @@ * */ -var PROTO_PATH = __dirname + '/../protos/helloworld.proto'; - var grpc = require('grpc'); -var hello_proto = grpc.load(PROTO_PATH).helloworld; + +var hello_messages = require('./helloworld_pb'); +var hello_service = require('./helloworld_grpc_pb'); function main() { - var client = new hello_proto.Greeter('localhost:50051', - grpc.credentials.createInsecure()); + var client = new hello_service.GreeterClient('localhost:50051', + grpc.credentials.createInsecure()); var user; if (process.argv.length >= 3) { user = process.argv[2]; } else { user = 'world'; } - client.sayHello({name: user}, function(err, response) { - console.log('Greeting:', response.message); + + var request = new hello_messages.HelloRequest(); + request.setName(user); + + client.sayHello(request, function(err, response) { + if (err) { + debugger; + throw err; + } + console.log('Greeting:', response.getMessage()); }); } diff --git a/examples/node/greeter_server.js b/examples/node/greeter_server.js index e7ad51f6009..a4aebf6d09d 100644 --- a/examples/node/greeter_server.js +++ b/examples/node/greeter_server.js @@ -31,16 +31,18 @@ * */ -var PROTO_PATH = __dirname + '/../protos/helloworld.proto'; - var grpc = require('grpc'); -var hello_proto = grpc.load(PROTO_PATH).helloworld; + +var hello_messages = require('./helloworld_pb'); +var hello_service = require('./helloworld_grpc_pb'); /** * Implements the SayHello RPC method. */ function sayHello(call, callback) { - callback(null, {message: 'Hello ' + call.request.name}); + var reply = new hello_messages.HelloReply(); + reply.setMessage("Hello " + call.request.getName()); + callback(null, reply); } /** @@ -49,7 +51,7 @@ function sayHello(call, callback) { */ function main() { var server = new grpc.Server(); - server.addProtoService(hello_proto.Greeter.service, {sayHello: sayHello}); + server.addService(hello_service.GreeterService, {sayHello: sayHello}); server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); server.start(); } diff --git a/examples/node/helloworld_grpc_pb.js b/examples/node/helloworld_grpc_pb.js new file mode 100644 index 00000000000..3d070d7de08 --- /dev/null +++ b/examples/node/helloworld_grpc_pb.js @@ -0,0 +1,39 @@ +// GENERATED CODE -- DO NOT EDIT! + +var grpc = require('grpc'); +var helloworld_pb = require('./helloworld_pb.js'); + +function serialize_HelloReply(arg) { + if (!(arg instanceof helloworld_pb.HelloReply)) { + throw new Error('Expected argument of type HelloReply'); + } + return new Buffer(arg.serializeBinary()); +} +function deserialize_HelloReply(buffer_arg) { + return helloworld_pb.HelloReply.deserializeBinary(new Uint8Array(buffer_arg)); +} +function serialize_HelloRequest(arg) { + if (!(arg instanceof helloworld_pb.HelloRequest)) { + throw new Error('Expected argument of type HelloRequest'); + } + return new Buffer(arg.serializeBinary()); +} +function deserialize_HelloRequest(buffer_arg) { + return helloworld_pb.HelloRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +var GreeterService = exports.GreeterService = { + sayHello: { + path: '/helloworld.Greeter/SayHello', + requestStream: false, + responseStream: false, + requestType: helloworld_pb.HelloRequest, + responseType: helloworld_pb.HelloReply, + requestSerialize: serialize_HelloRequest, + requestDeserialize: deserialize_HelloRequest, + responseSerialize: serialize_HelloReply, + responseDeserialize: deserialize_HelloReply, + }, +}; + +exports.GreeterClient = grpc.makeGenericClientConstructor(GreeterService); diff --git a/examples/node/helloworld_pb.js b/examples/node/helloworld_pb.js new file mode 100644 index 00000000000..6405bd90f10 --- /dev/null +++ b/examples/node/helloworld_pb.js @@ -0,0 +1,332 @@ +/** + * @fileoverview + * @enhanceable + * @public + */ +// GENERATED CODE -- DO NOT EDIT! + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +goog.exportSymbol('proto.helloworld.HelloReply', null, global); +goog.exportSymbol('proto.helloworld.HelloRequest', null, global); + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.helloworld.HelloRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.helloworld.HelloRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.helloworld.HelloRequest.displayName = 'proto.helloworld.HelloRequest'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.helloworld.HelloRequest.prototype.toObject = function(opt_includeInstance) { + return proto.helloworld.HelloRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.helloworld.HelloRequest} msg The msg instance to transform. + * @return {!Object} + */ +proto.helloworld.HelloRequest.toObject = function(includeInstance, msg) { + var f, obj = { + name: msg.getName() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.helloworld.HelloRequest} + */ +proto.helloworld.HelloRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.helloworld.HelloRequest; + return proto.helloworld.HelloRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.helloworld.HelloRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.helloworld.HelloRequest} + */ +proto.helloworld.HelloRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setName(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Class method variant: serializes the given message to binary data + * (in protobuf wire format), writing to the given BinaryWriter. + * @param {!proto.helloworld.HelloRequest} message + * @param {!jspb.BinaryWriter} writer + */ +proto.helloworld.HelloRequest.serializeBinaryToWriter = function(message, writer) { + message.serializeBinaryToWriter(writer); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.helloworld.HelloRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + this.serializeBinaryToWriter(writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format), + * writing to the given BinaryWriter. + * @param {!jspb.BinaryWriter} writer + */ +proto.helloworld.HelloRequest.prototype.serializeBinaryToWriter = function (writer) { + var f = undefined; + f = this.getName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * Creates a deep clone of this proto. No data is shared with the original. + * @return {!proto.helloworld.HelloRequest} The clone. + */ +proto.helloworld.HelloRequest.prototype.cloneMessage = function() { + return /** @type {!proto.helloworld.HelloRequest} */ (jspb.Message.cloneMessage(this)); +}; + + +/** + * optional string name = 1; + * @return {string} + */ +proto.helloworld.HelloRequest.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldProto3(this, 1, "")); +}; + + +/** @param {string} value */ +proto.helloworld.HelloRequest.prototype.setName = function(value) { + jspb.Message.setField(this, 1, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.helloworld.HelloReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.helloworld.HelloReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.helloworld.HelloReply.displayName = 'proto.helloworld.HelloReply'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.helloworld.HelloReply.prototype.toObject = function(opt_includeInstance) { + return proto.helloworld.HelloReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.helloworld.HelloReply} msg The msg instance to transform. + * @return {!Object} + */ +proto.helloworld.HelloReply.toObject = function(includeInstance, msg) { + var f, obj = { + message: msg.getMessage() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.helloworld.HelloReply} + */ +proto.helloworld.HelloReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.helloworld.HelloReply; + return proto.helloworld.HelloReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.helloworld.HelloReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.helloworld.HelloReply} + */ +proto.helloworld.HelloReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setMessage(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Class method variant: serializes the given message to binary data + * (in protobuf wire format), writing to the given BinaryWriter. + * @param {!proto.helloworld.HelloReply} message + * @param {!jspb.BinaryWriter} writer + */ +proto.helloworld.HelloReply.serializeBinaryToWriter = function(message, writer) { + message.serializeBinaryToWriter(writer); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.helloworld.HelloReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + this.serializeBinaryToWriter(writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format), + * writing to the given BinaryWriter. + * @param {!jspb.BinaryWriter} writer + */ +proto.helloworld.HelloReply.prototype.serializeBinaryToWriter = function (writer) { + var f = undefined; + f = this.getMessage(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * Creates a deep clone of this proto. No data is shared with the original. + * @return {!proto.helloworld.HelloReply} The clone. + */ +proto.helloworld.HelloReply.prototype.cloneMessage = function() { + return /** @type {!proto.helloworld.HelloReply} */ (jspb.Message.cloneMessage(this)); +}; + + +/** + * optional string message = 1; + * @return {string} + */ +proto.helloworld.HelloReply.prototype.getMessage = function() { + return /** @type {string} */ (jspb.Message.getFieldProto3(this, 1, "")); +}; + + +/** @param {string} value */ +proto.helloworld.HelloReply.prototype.setMessage = function(value) { + jspb.Message.setField(this, 1, value); +}; + + +goog.object.extend(exports, proto.helloworld); diff --git a/examples/node/package.json b/examples/node/package.json index d135df2464b..49ab74d3181 100644 --- a/examples/node/package.json +++ b/examples/node/package.json @@ -4,6 +4,7 @@ "dependencies": { "async": "^1.5.2", "grpc": "0.13.0", + "google-protobuf": "*", "lodash": "^4.6.1", "minimist": "^1.2.0" } diff --git a/grpc.gemspec b/grpc.gemspec index f83b7d35872..a9f0f681df0 100755 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -24,10 +24,6 @@ Gem::Specification.new do |s| s.files += Dir.glob('include/grpc/**/*') s.test_files = Dir.glob('src/ruby/spec/**/*') s.bindir = 'src/ruby/bin' - %w(math noproto).each do |b| - s.executables += ["#{b}_client.rb", "#{b}_server.rb"] - end - s.executables += %w(grpc_ruby_interop_client grpc_ruby_interop_server) s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb ) s.platform = Gem::Platform::RUBY diff --git a/package.json b/package.json index 72731c02450..5ed7f363d37 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "lib": "src/node/src" }, "scripts": { - "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js", + "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js --exclude-path=src/node/.jshintignore", "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint", "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json", "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test", @@ -35,6 +35,7 @@ "devDependencies": { "async": "^1.5.0", "google-auth-library": "^0.9.2", + "google-protobuf": "^3.0.0-alpha.5", "istanbul": "^0.3.21", "jsdoc": "^3.3.2", "jshint": "^2.5.0", @@ -72,5 +73,25 @@ "binding.gyp" ], "main": "src/node/index.js", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "jshintConfig" : { + "bitwise": true, + "curly": true, + "eqeqeq": true, + "esnext": true, + "freeze": true, + "immed": true, + "indent": 2, + "latedef": "nofunc", + "maxlen": 80, + "mocha": true, + "newcap": true, + "node": true, + "noarg": true, + "quotmark": "single", + "strict": true, + "trailing": true, + "undef": true, + "unused": "vars" + } } diff --git a/package.xml b/package.xml index d192ebde2cc..3ae810df5eb 100644 --- a/package.xml +++ b/package.xml @@ -10,7 +10,7 @@ grpc-packages@google.com yes - 2016-03-01 + 2016-04-19 0.14.0 @@ -22,7 +22,7 @@ BSD -- Increase unit test code coverage #5225 +- destroy grpc_byte_buffer after startBatch #6096 @@ -996,8 +996,8 @@ Update to wrap gRPC C Core version 0.10.0 - 0.14.0 - 0.14.0 + 0.8.1 + 0.8.1 beta @@ -1009,5 +1009,20 @@ Update to wrap gRPC C Core version 0.10.0 - Increase unit test code coverage #5225 + + + 0.14.0 + 0.14.0 + + + beta + beta + + 2016-04-19 + BSD + +- destroy grpc_byte_buffer after startBatch #6096 + + diff --git a/requirements.txt b/requirements.txt index e3208e63556..0ec0e75b762 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ # GRPC Python setup requirements +coverage>=4.0 +cython>=0.23 enum34>=1.0.4 futures>=2.2.0 -cython>=0.23 -coverage>=4.0 +protobuf>=3.0.0a3 six>=1.10 -wheel>=0.29 +wheel>=0.29 \ No newline at end of file diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index b1336993067..9319c419349 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -86,7 +86,7 @@ void PrintIncludes(Printer *printer, const std::vector& headers, c } } -grpc::string GetHeaderPrologue(File *file, const Parameters ¶ms) { +grpc::string GetHeaderPrologue(File *file, const Parameters & /*params*/) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -96,6 +96,7 @@ grpc::string GetHeaderPrologue(File *file, const Parameters ¶ms) { vars["filename"] = file->filename(); vars["filename_identifier"] = FilenameIdentifier(file->filename()); vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = file->message_header_ext(); printer->Print(vars, "// Generated by the gRPC protobuf plugin.\n"); printer->Print(vars, @@ -104,7 +105,7 @@ grpc::string GetHeaderPrologue(File *file, const Parameters ¶ms) { printer->Print(vars, "#ifndef GRPC_$filename_identifier$__INCLUDED\n"); printer->Print(vars, "#define GRPC_$filename_identifier$__INCLUDED\n"); printer->Print(vars, "\n"); - printer->Print(vars, "#include \"$filename_base$.pb.h\"\n"); + printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); printer->Print(vars, "\n"); } return output; @@ -794,8 +795,7 @@ grpc::string GetHeaderServices(File *file, return output; } -grpc::string GetHeaderEpilogue(File *file, - const Parameters ¶ms) { +grpc::string GetHeaderEpilogue(File *file, const Parameters & /*params*/) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -821,8 +821,7 @@ grpc::string GetHeaderEpilogue(File *file, return output; } -grpc::string GetSourcePrologue(File *file, - const Parameters ¶ms) { +grpc::string GetSourcePrologue(File *file, const Parameters & /*params*/) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -831,13 +830,16 @@ grpc::string GetSourcePrologue(File *file, vars["filename"] = file->filename(); vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = file->message_header_ext(); + vars["service_header_ext"] = file->service_header_ext(); printer->Print(vars, "// Generated by the gRPC protobuf plugin.\n"); printer->Print(vars, "// If you make any local change, they will be lost.\n"); printer->Print(vars, "// source: $filename$\n\n"); - printer->Print(vars, "#include \"$filename_base$.pb.h\"\n"); - printer->Print(vars, "#include \"$filename_base$.grpc.pb.h\"\n"); + printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); + printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n"); + printer->Print(vars, file->additional_headers().c_str()); printer->Print(vars, "\n"); } return output; @@ -1180,8 +1182,7 @@ grpc::string GetSourceServices(File *file, return output; } -grpc::string GetSourceEpilogue(File *file, - const Parameters ¶ms) { +grpc::string GetSourceEpilogue(File *file, const Parameters & /*params*/) { grpc::string temp; if (!file->package().empty()) { diff --git a/src/compiler/cpp_generator.h b/src/compiler/cpp_generator.h index 99a60a2eaec..953ddfd569e 100644 --- a/src/compiler/cpp_generator.h +++ b/src/compiler/cpp_generator.h @@ -106,8 +106,11 @@ struct File { virtual grpc::string filename() const = 0; virtual grpc::string filename_without_ext() const = 0; + virtual grpc::string message_header_ext() const = 0; + virtual grpc::string service_header_ext() const = 0; virtual grpc::string package() const = 0; virtual std::vector package_parts() const = 0; + virtual grpc::string additional_headers() const = 0; virtual int service_count() const = 0; virtual std::unique_ptr service(int i) const = 0; diff --git a/src/compiler/cpp_plugin.cc b/src/compiler/cpp_plugin.cc index f703c6453d5..e321c64639f 100644 --- a/src/compiler/cpp_plugin.cc +++ b/src/compiler/cpp_plugin.cc @@ -120,11 +120,16 @@ class ProtoBufFile : public grpc_cpp_generator::File { return grpc_generator::StripProto(filename()); } + grpc::string message_header_ext() const { return ".pb.h"; } + grpc::string service_header_ext() const { return ".grpc.pb.h"; } + grpc::string package() const { return file_->package(); } std::vector package_parts() const { return grpc_generator::tokenize(package(), "."); } + grpc::string additional_headers() const { return ""; } + int service_count() const { return file_->service_count(); }; std::unique_ptr service(int i) const { return std::unique_ptr ( diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc index 69e2738d53d..4def6c5e31f 100644 --- a/src/compiler/csharp_generator.cc +++ b/src/compiler/csharp_generator.cc @@ -350,10 +350,12 @@ void GenerateServerClass(Printer* out, const ServiceDescriptor *service) { void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { out->Print("// client stub\n"); + out->Print("#pragma warning disable 0618\n"); out->Print( "public class $name$ : ClientBase<$name$>, $interface$\n", "name", GetClientClassName(service), "interface", GetClientInterfaceName(service)); + out->Print("#pragma warning restore 0618\n"); out->Print("{\n"); out->Indent(); @@ -480,10 +482,12 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service, bool use_server_class) { out->Print( "// creates service definition that can be registered with a server\n"); + out->Print("#pragma warning disable 0618\n"); out->Print( "public static ServerServiceDefinition BindService($interface$ serviceImpl)\n", "interface", use_server_class ? GetServerClassName(service) : GetServerInterfaceName(service)); + out->Print("#pragma warning restore 0618\n"); out->Print("{\n"); out->Indent(); diff --git a/src/compiler/generator_helpers.h b/src/compiler/generator_helpers.h index e1bb66a8753..3ed0500efc5 100644 --- a/src/compiler/generator_helpers.h +++ b/src/compiler/generator_helpers.h @@ -52,6 +52,16 @@ inline bool StripSuffix(grpc::string *filename, const grpc::string &suffix) { return false; } +inline bool StripPrefix(grpc::string *name, const grpc::string &prefix) { + if (name->length() >= prefix.length()) { + if (name->substr(0, prefix.size()) == prefix) { + *name = name->substr(prefix.size()); + return true; + } + } + return false; +} + inline grpc::string StripProto(grpc::string filename) { if (!StripSuffix(&filename, ".protodevel")) { StripSuffix(&filename, ".proto"); diff --git a/src/compiler/node_generator.cc b/src/compiler/node_generator.cc new file mode 100644 index 00000000000..822622cccf9 --- /dev/null +++ b/src/compiler/node_generator.cc @@ -0,0 +1,277 @@ +/* + * + * Copyright 2016, 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 + +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" +#include "src/compiler/node_generator_helpers.h" + +using grpc::protobuf::FileDescriptor; +using grpc::protobuf::ServiceDescriptor; +using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::Descriptor; +using grpc::protobuf::io::Printer; +using grpc::protobuf::io::StringOutputStream; +using std::map; + +namespace grpc_node_generator { +namespace { + +// Returns the alias we assign to the module of the given .proto filename +// when importing. Copied entirely from +// github:google/protobuf/src/google/protobuf/compiler/js/js_generator.cc#L154 +grpc::string ModuleAlias(const grpc::string filename) { + // This scheme could technically cause problems if a file includes any 2 of: + // foo/bar_baz.proto + // foo_bar_baz.proto + // foo_bar/baz.proto + // + // We'll worry about this problem if/when we actually see it. This name isn't + // exposed to users so we can change it later if we need to. + grpc::string basename = grpc_generator::StripProto(filename); + basename = grpc_generator::StringReplace(basename, "-", "$"); + basename = grpc_generator::StringReplace(basename, "/", "_"); + return basename + "_pb"; +} + +// Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript +// message file foo/bar/baz.js +grpc::string GetJSMessageFilename(const grpc::string& filename) { + grpc::string name = filename; + return grpc_generator::StripProto(name) + "_pb.js"; +} + +// Given a filename like foo/bar/baz.proto, returns the root directory +// path ../../ +grpc::string GetRootPath(const grpc::string& filename) { + size_t slashes = std::count(filename.begin(), filename.end(), '/'); + if (slashes == 0) { + return "./"; + } + grpc::string result = ""; + for (size_t i = 0; i < slashes; i++) { + result += "../"; + } + return result; +} + +// Return the relative path to load to_file from the directory containing +// from_file, assuming that both paths are relative to the same directory +grpc::string GetRelativePath(const grpc::string& from_file, + const grpc::string& to_file) { + return GetRootPath(from_file) + to_file; +} + +/* Finds all message types used in all services in the file, and returns them + * as a map of fully qualified message type name to message descriptor */ +map GetAllMessages(const FileDescriptor *file) { + map message_types; + for (int service_num = 0; service_num < file->service_count(); service_num++) { + const ServiceDescriptor* service = file->service(service_num); + for (int method_num = 0; method_num < service->method_count(); method_num++) { + const MethodDescriptor* method = service->method(method_num); + const Descriptor* input_type = method->input_type(); + const Descriptor* output_type = method->output_type(); + message_types[input_type->name()] = input_type; + message_types[output_type->name()] = output_type; + } + } + return message_types; +} + +grpc::string MessageIdentifierName(const grpc::string& name) { + return grpc_generator::StringReplace(name, ".", "_"); +} + +grpc::string NodeObjectPath(const Descriptor *descriptor) { + grpc::string module_alias = ModuleAlias(descriptor->file()->name()); + grpc::string name = descriptor->name(); + grpc_generator::StripPrefix(&name, descriptor->file()->package() + "."); + return module_alias + "." + name; +} + +// Prints out the message serializer and deserializer functions +void PrintMessageTransformer(const Descriptor *descriptor, Printer *out) { + map template_vars; + template_vars["identifier_name"] = MessageIdentifierName(descriptor->name()); + template_vars["name"] = descriptor->name(); + template_vars["node_name"] = NodeObjectPath(descriptor); + // Print the serializer + out->Print(template_vars, "function serialize_$identifier_name$(arg) {\n"); + out->Indent(); + out->Print(template_vars, "if (!(arg instanceof $node_name$)) {\n"); + out->Indent(); + out->Print(template_vars, + "throw new Error('Expected argument of type $name$');\n"); + out->Outdent(); + out->Print("}\n"); + out->Print("return new Buffer(arg.serializeBinary());\n"); + out->Outdent(); + out->Print("}\n\n"); + + // Print the deserializer + out->Print(template_vars, + "function deserialize_$identifier_name$(buffer_arg) {\n"); + out->Indent(); + out->Print( + template_vars, + "return $node_name$.deserializeBinary(new Uint8Array(buffer_arg));\n"); + out->Outdent(); + out->Print("}\n\n"); +} + +void PrintMethod(const MethodDescriptor *method, Printer *out) { + const Descriptor *input_type = method->input_type(); + const Descriptor *output_type = method->output_type(); + map vars; + vars["service_name"] = method->service()->full_name(); + vars["name"] = method->name(); + vars["input_type"] = NodeObjectPath(input_type); + vars["input_type_id"] = MessageIdentifierName(input_type->name()); + vars["output_type"] = NodeObjectPath(output_type); + vars["output_type_id"] = MessageIdentifierName(output_type->name()); + vars["client_stream"] = method->client_streaming() ? "true" : "false"; + vars["server_stream"] = method->server_streaming() ? "true" : "false"; + out->Print("{\n"); + out->Indent(); + out->Print(vars, "path: '/$service_name$/$name$',\n"); + out->Print(vars, "requestStream: $client_stream$,\n"); + out->Print(vars, "responseStream: $server_stream$,\n"); + out->Print(vars, "requestType: $input_type$,\n"); + out->Print(vars, "responseType: $output_type$,\n"); + out->Print(vars, "requestSerialize: serialize_$input_type_id$,\n"); + out->Print(vars, "requestDeserialize: deserialize_$input_type_id$,\n"); + out->Print(vars, "responseSerialize: serialize_$output_type_id$,\n"); + out->Print(vars, "responseDeserialize: deserialize_$output_type_id$,\n"); + out->Outdent(); + out->Print("}"); +} + +// Prints out the service descriptor object +void PrintService(const ServiceDescriptor *service, Printer *out) { + map template_vars; + template_vars["name"] = service->name(); + out->Print(template_vars, "var $name$Service = exports.$name$Service = {\n"); + out->Indent(); + for (int i = 0; i < service->method_count(); i++) { + grpc::string method_name = grpc_generator::LowercaseFirstLetter( + service->method(i)->name()); + out->Print("$method_name$: ", + "method_name", method_name); + PrintMethod(service->method(i), out); + out->Print(",\n"); + } + out->Outdent(); + out->Print("};\n\n"); + out->Print(template_vars, "exports.$name$Client = " + "grpc.makeGenericClientConstructor($name$Service);\n"); +} + +} + +grpc::string GetImports(const FileDescriptor *file) { + grpc::string output; + { + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + + if (file->service_count() == 0) { + return output; + } + + out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n"); + + out.Print("'use strict';\n"); + + out.Print("var grpc = require('grpc');\n"); + if (file->message_type_count() > 0) { + grpc::string file_path = GetRelativePath(file->name(), + GetJSMessageFilename( + file->name())); + out.Print("var $module_alias$ = require('$file_path$');\n", + "module_alias", ModuleAlias(file->name()), + "file_path", file_path); + } + + for (int i = 0; i < file->dependency_count(); i++) { + grpc::string file_path = GetRelativePath( + file->name(), GetJSMessageFilename(file->dependency(i)->name())); + out.Print("var $module_alias$ = require('$file_path$');\n", + "module_alias", ModuleAlias(file->dependency(i)->name()), + "file_path", file_path); + } + out.Print("\n"); + } + return output; +} + +grpc::string GetTransformers(const FileDescriptor *file) { + grpc::string output; + { + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + + if (file->service_count() == 0) { + return output; + } + + map messages = GetAllMessages(file); + for (std::map::iterator it = + messages.begin(); + it != messages.end(); it++) { + PrintMessageTransformer(it->second, &out); + } + out.Print("\n"); + } + return output; +} + +grpc::string GetServices(const FileDescriptor *file) { + grpc::string output; + { + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + + if (file->service_count() == 0) { + return output; + } + + for (int i = 0; i < file->service_count(); i++) { + PrintService(file->service(i), &out); + } + } + return output; +} + +} // namespace grpc_node_generator diff --git a/src/compiler/node_generator.h b/src/compiler/node_generator.h new file mode 100644 index 00000000000..249a0d011f8 --- /dev/null +++ b/src/compiler/node_generator.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2016, 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_INTERNAL_COMPILER_NODE_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H + +#include "src/compiler/config.h" + +namespace grpc_node_generator { + +grpc::string GetImports(const grpc::protobuf::FileDescriptor *file); + +grpc::string GetTransformers(const grpc::protobuf::FileDescriptor *file); + +grpc::string GetServices(const grpc::protobuf::FileDescriptor *file); + +} // namespace grpc_node_generator + +#endif // GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H diff --git a/src/compiler/node_generator_helpers.h b/src/compiler/node_generator_helpers.h new file mode 100644 index 00000000000..f41a2bcf59a --- /dev/null +++ b/src/compiler/node_generator_helpers.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2016, 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_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H + +#include + +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" + +namespace grpc_node_generator { + +inline grpc::string GetJSServiceFilename(const grpc::string& filename) { + return grpc_generator::StripProto(filename) + "_grpc_pb.js"; +} + +} // namespace grpc_node_generator + +#endif // GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H diff --git a/src/compiler/node_plugin.cc b/src/compiler/node_plugin.cc new file mode 100644 index 00000000000..ac5ced35589 --- /dev/null +++ b/src/compiler/node_plugin.cc @@ -0,0 +1,77 @@ +/* + * + * 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. + * + */ + +// Generates Node gRPC service interface out of Protobuf IDL. + +#include + +#include "src/compiler/config.h" +#include "src/compiler/node_generator.h" +#include "src/compiler/node_generator_helpers.h" + +using grpc_node_generator::GetImports; +using grpc_node_generator::GetJSServiceFilename; +using grpc_node_generator::GetServices; +using grpc_node_generator::GetTransformers; + +class NodeGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + NodeGrpcGenerator() {} + ~NodeGrpcGenerator() {} + + bool Generate(const grpc::protobuf::FileDescriptor *file, + const grpc::string ¶meter, + grpc::protobuf::compiler::GeneratorContext *context, + grpc::string *error) const { + grpc::string code = GetImports(file) + + GetTransformers(file) + + GetServices(file); + if (code.size() == 0) { + return true; + } + + // Get output file name + grpc::string file_name = GetJSServiceFilename(file->name()); + + std::unique_ptr output( + context->Open(file_name)); + grpc::protobuf::io::CodedOutputStream coded_out(output.get()); + coded_out.WriteRaw(code.data(), code.size()); + return true; + } +}; + +int main(int argc, char *argv[]) { + NodeGrpcGenerator generator; + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/src/core/lib/transport/metadata.c b/src/core/lib/transport/metadata.c index 779efbb97d5..5847ec9053d 100644 --- a/src/core/lib/transport/metadata.c +++ b/src/core/lib/transport/metadata.c @@ -386,10 +386,18 @@ grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *buf, size_t length) { for (s = shard->strs[idx]; s; s = s->bucket_next) { if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length && 0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) { - GRPC_MDSTR_REF((grpc_mdstr *)s); - gpr_mu_unlock(&shard->mu); - GPR_TIMER_END("grpc_mdstr_from_buffer", 0); - return (grpc_mdstr *)s; + if (gpr_atm_full_fetch_add(&s->refcnt, 1) == 0) { + /* If we get here, we've added a ref to something that was about to + * die - drop it immediately. + * The *only* possible path here (given the shard mutex) should be to + * drop from one ref back to zero - assert that with a CAS */ + GPR_ASSERT(gpr_atm_rel_cas(&s->refcnt, 1, 0)); + /* and treat this as if we were never here... sshhh */ + } else { + gpr_mu_unlock(&shard->mu); + GPR_TIMER_END("grpc_mdstr_from_buffer", 0); + return (grpc_mdstr *)s; + } } } @@ -397,7 +405,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *buf, size_t length) { if (length + 1 < GPR_SLICE_INLINED_SIZE) { /* string data goes directly into the slice */ s = gpr_malloc(sizeof(internal_string)); - gpr_atm_rel_store(&s->refcnt, 2); + gpr_atm_rel_store(&s->refcnt, 1); s->slice.refcount = NULL; memcpy(s->slice.data.inlined.bytes, buf, length); s->slice.data.inlined.bytes[length] = 0; @@ -406,7 +414,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *buf, size_t length) { /* string data goes after the internal_string header, and we +1 for null terminator */ s = gpr_malloc(sizeof(internal_string) + length + 1); - gpr_atm_rel_store(&s->refcnt, 2); + gpr_atm_rel_store(&s->refcnt, 1); s->refcount.ref = slice_ref; s->refcount.unref = slice_unref; s->slice.refcount = &s->refcount; @@ -675,20 +683,19 @@ const char *grpc_mdstr_as_c_string(grpc_mdstr *s) { grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) { internal_string *s = (internal_string *)gs; if (is_mdstr_static(gs)) return gs; - GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) != 0); + GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) > 0); return gs; } void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) { internal_string *s = (internal_string *)gs; if (is_mdstr_static(gs)) return; - if (2 == gpr_atm_full_fetch_add(&s->refcnt, -1)) { + if (1 == gpr_atm_full_fetch_add(&s->refcnt, -1)) { strtab_shard *shard = &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)]; gpr_mu_lock(&shard->mu); - if (1 == gpr_atm_no_barrier_load(&s->refcnt)) { - internal_destroy_string(shard, s); - } + GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt)); + internal_destroy_string(shard, s); gpr_mu_unlock(&shard->mu); } } diff --git a/src/csharp/Grpc.Core/Version.cs b/src/csharp/Grpc.Core/Version.cs index 8a26bd83623..f5c44fd0984 100644 --- a/src/csharp/Grpc.Core/Version.cs +++ b/src/csharp/Grpc.Core/Version.cs @@ -33,5 +33,6 @@ using System.Reflection; -// The current version of gRPC C#. [assembly: AssemblyVersion(Grpc.Core.VersionInfo.CurrentAssemblyVersion)] +[assembly: AssemblyFileVersion(Grpc.Core.VersionInfo.CurrentAssemblyFileVersion)] +[assembly: AssemblyInformationalVersion(Grpc.Core.VersionInfo.CurrentVersion)] diff --git a/src/csharp/Grpc.Core/VersionInfo.cs b/src/csharp/Grpc.Core/VersionInfo.cs index 9014a13f400..f7a9cb9c1cb 100644 --- a/src/csharp/Grpc.Core/VersionInfo.cs +++ b/src/csharp/Grpc.Core/VersionInfo.cs @@ -35,13 +35,20 @@ namespace Grpc.Core { /// /// Provides info about current version of gRPC. + /// See https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/ + /// for rationale about assembly versioning. /// public static class VersionInfo { /// - /// Current version of gRPC C# assemblies + /// Current AssemblyVersion attribute of gRPC C# assemblies /// - public const string CurrentAssemblyVersion = "0.14.0.0"; + public const string CurrentAssemblyVersion = "1.0.0.0"; + + /// + /// Current AssemblyFileVersion of gRPC C# assemblies + /// + public const string CurrentAssemblyFileVersion = "0.14.0.0"; /// /// Current version of gRPC C# diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs index f3bb0d1cdcb..1a6482df90e 100644 --- a/src/csharp/Grpc.Examples/MathGrpc.cs +++ b/src/csharp/Grpc.Examples/MathGrpc.cs @@ -103,7 +103,9 @@ namespace Math { } // client stub + #pragma warning disable 0618 public class MathClient : ClientBase, IMathClient + #pragma warning restore 0618 { public MathClient(Channel channel) : base(channel) { @@ -167,7 +169,9 @@ namespace Math { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(IMath serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_Div, serviceImpl.Div) @@ -177,7 +181,9 @@ namespace Math { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(MathBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_Div, serviceImpl.Div) diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs index 72e11cca3a1..e7f779753d7 100644 --- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs +++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs @@ -56,7 +56,9 @@ namespace Grpc.Health.V1 { } // client stub + #pragma warning disable 0618 public class HealthClient : ClientBase, IHealthClient + #pragma warning restore 0618 { public HealthClient(Channel channel) : base(channel) { @@ -96,14 +98,18 @@ namespace Grpc.Health.V1 { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(IHealth serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_Check, serviceImpl.Check).Build(); } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(HealthBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_Check, serviceImpl.Check).Build(); diff --git a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs index cc01ae91a14..11c1572c194 100644 --- a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs @@ -72,7 +72,9 @@ namespace Grpc.Testing { } // client stub + #pragma warning disable 0618 public class MetricsServiceClient : ClientBase, IMetricsServiceClient + #pragma warning restore 0618 { public MetricsServiceClient(Channel channel) : base(channel) { @@ -120,7 +122,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(IMetricsService serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_GetAllGauges, serviceImpl.GetAllGauges) @@ -128,7 +132,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(MetricsServiceBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_GetAllGauges, serviceImpl.GetAllGauges) diff --git a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs index 46b16cf202d..18cf0672e30 100644 --- a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs @@ -71,7 +71,9 @@ namespace Grpc.Testing { } // client stub + #pragma warning disable 0618 public class BenchmarkServiceClient : ClientBase, IBenchmarkServiceClient + #pragma warning restore 0618 { public BenchmarkServiceClient(Channel channel) : base(channel) { @@ -119,7 +121,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(IBenchmarkService serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_UnaryCall, serviceImpl.UnaryCall) @@ -127,7 +131,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(BenchmarkServiceBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_UnaryCall, serviceImpl.UnaryCall) @@ -241,7 +247,9 @@ namespace Grpc.Testing { } // client stub + #pragma warning disable 0618 public class WorkerServiceClient : ClientBase, IWorkerServiceClient + #pragma warning restore 0618 { public WorkerServiceClient(Channel channel) : base(channel) { @@ -313,7 +321,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(IWorkerService serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_RunServer, serviceImpl.RunServer) @@ -323,7 +333,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(WorkerServiceBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_RunServer, serviceImpl.RunServer) diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs index 31746cbe715..3b915f6df12 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs @@ -138,7 +138,9 @@ namespace Grpc.Testing { } // client stub + #pragma warning disable 0618 public class TestServiceClient : ClientBase, ITestServiceClient + #pragma warning restore 0618 { public TestServiceClient(Channel channel) : base(channel) { @@ -226,7 +228,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(ITestService serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_EmptyCall, serviceImpl.EmptyCall) @@ -238,7 +242,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(TestServiceBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_EmptyCall, serviceImpl.EmptyCall) @@ -303,7 +309,9 @@ namespace Grpc.Testing { } // client stub + #pragma warning disable 0618 public class UnimplementedServiceClient : ClientBase, IUnimplementedServiceClient + #pragma warning restore 0618 { public UnimplementedServiceClient(Channel channel) : base(channel) { @@ -343,14 +351,18 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(IUnimplementedService serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build(); } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(UnimplementedServiceBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build(); @@ -429,7 +441,9 @@ namespace Grpc.Testing { } // client stub + #pragma warning disable 0618 public class ReconnectServiceClient : ClientBase, IReconnectServiceClient + #pragma warning restore 0618 { public ReconnectServiceClient(Channel channel) : base(channel) { @@ -485,7 +499,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(IReconnectService serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_Start, serviceImpl.Start) @@ -493,7 +509,9 @@ namespace Grpc.Testing { } // creates service definition that can be registered with a server + #pragma warning disable 0618 public static ServerServiceDefinition BindService(ReconnectServiceBase serviceImpl) + #pragma warning restore 0618 { return ServerServiceDefinition.CreateBuilder(__ServiceName) .AddMethod(__Method_Start, serviceImpl.Start) diff --git a/src/node/.gitignore b/src/node/.gitignore deleted file mode 100644 index e3fbd98336e..00000000000 --- a/src/node/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build -node_modules diff --git a/src/node/.jshintignore b/src/node/.jshintignore new file mode 100644 index 00000000000..0a73e1e2b6b --- /dev/null +++ b/src/node/.jshintignore @@ -0,0 +1 @@ +**/*_pb.js \ No newline at end of file diff --git a/src/node/.jshintrc b/src/node/.jshintrc deleted file mode 100644 index 8237e0d2b64..00000000000 --- a/src/node/.jshintrc +++ /dev/null @@ -1,28 +0,0 @@ -{ - "bitwise": true, - "curly": true, - "eqeqeq": true, - "esnext": true, - "freeze": true, - "immed": true, - "indent": 2, - "latedef": "nofunc", - "maxlen": 80, - "newcap": true, - "node": true, - "noarg": true, - "quotmark": "single", - "strict": true, - "trailing": true, - "undef": true, - "unused": "vars", - "globals": { - /* Mocha-provided globals */ - "describe": false, - "it": false, - "before": false, - "beforeEach": false, - "after": false, - "afterEach": false - } -} diff --git a/src/node/test/math/math_grpc_pb.js b/src/node/test/math/math_grpc_pb.js new file mode 100644 index 00000000000..083ed669137 --- /dev/null +++ b/src/node/test/math/math_grpc_pb.js @@ -0,0 +1,99 @@ +// GENERATED CODE -- DO NOT EDIT! + +'use strict'; +var grpc = require('grpc'); +var math_pb = require('./math_pb.js'); + +function serialize_DivArgs(arg) { + if (!(arg instanceof math_pb.DivArgs)) { + throw new Error('Expected argument of type DivArgs'); + } + return new Buffer(arg.serializeBinary()); +} + +function deserialize_DivArgs(buffer_arg) { + return math_pb.DivArgs.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_DivReply(arg) { + if (!(arg instanceof math_pb.DivReply)) { + throw new Error('Expected argument of type DivReply'); + } + return new Buffer(arg.serializeBinary()); +} + +function deserialize_DivReply(buffer_arg) { + return math_pb.DivReply.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_FibArgs(arg) { + if (!(arg instanceof math_pb.FibArgs)) { + throw new Error('Expected argument of type FibArgs'); + } + return new Buffer(arg.serializeBinary()); +} + +function deserialize_FibArgs(buffer_arg) { + return math_pb.FibArgs.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_Num(arg) { + if (!(arg instanceof math_pb.Num)) { + throw new Error('Expected argument of type Num'); + } + return new Buffer(arg.serializeBinary()); +} + +function deserialize_Num(buffer_arg) { + return math_pb.Num.deserializeBinary(new Uint8Array(buffer_arg)); +} + + +var MathService = exports.MathService = { + div: { + path: '/math.Math/Div', + requestStream: false, + responseStream: false, + requestType: math_pb.DivArgs, + responseType: math_pb.DivReply, + requestSerialize: serialize_DivArgs, + requestDeserialize: deserialize_DivArgs, + responseSerialize: serialize_DivReply, + responseDeserialize: deserialize_DivReply, + }, + divMany: { + path: '/math.Math/DivMany', + requestStream: true, + responseStream: true, + requestType: math_pb.DivArgs, + responseType: math_pb.DivReply, + requestSerialize: serialize_DivArgs, + requestDeserialize: deserialize_DivArgs, + responseSerialize: serialize_DivReply, + responseDeserialize: deserialize_DivReply, + }, + fib: { + path: '/math.Math/Fib', + requestStream: false, + responseStream: true, + requestType: math_pb.FibArgs, + responseType: math_pb.Num, + requestSerialize: serialize_FibArgs, + requestDeserialize: deserialize_FibArgs, + responseSerialize: serialize_Num, + responseDeserialize: deserialize_Num, + }, + sum: { + path: '/math.Math/Sum', + requestStream: true, + responseStream: false, + requestType: math_pb.Num, + responseType: math_pb.Num, + requestSerialize: serialize_Num, + requestDeserialize: deserialize_Num, + responseSerialize: serialize_Num, + responseDeserialize: deserialize_Num, + }, +}; + +exports.MathClient = grpc.makeGenericClientConstructor(MathService); diff --git a/src/node/test/math/math_pb.js b/src/node/test/math/math_pb.js new file mode 100644 index 00000000000..3489143bec5 --- /dev/null +++ b/src/node/test/math/math_pb.js @@ -0,0 +1,866 @@ +/** + * @fileoverview + * @enhanceable + * @public + */ +// GENERATED CODE -- DO NOT EDIT! + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +goog.exportSymbol('proto.math.DivArgs', null, global); +goog.exportSymbol('proto.math.DivReply', null, global); +goog.exportSymbol('proto.math.FibArgs', null, global); +goog.exportSymbol('proto.math.FibReply', null, global); +goog.exportSymbol('proto.math.Num', null, global); + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.math.DivArgs = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.math.DivArgs, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.math.DivArgs.displayName = 'proto.math.DivArgs'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.math.DivArgs.prototype.toObject = function(opt_includeInstance) { + return proto.math.DivArgs.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.math.DivArgs} msg The msg instance to transform. + * @return {!Object} + */ +proto.math.DivArgs.toObject = function(includeInstance, msg) { + var f, obj = { + dividend: msg.getDividend(), + divisor: msg.getDivisor() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.math.DivArgs} + */ +proto.math.DivArgs.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.math.DivArgs; + return proto.math.DivArgs.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.math.DivArgs} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.math.DivArgs} + */ +proto.math.DivArgs.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setDividend(value); + break; + case 2: + var value = /** @type {number} */ (reader.readInt64()); + msg.setDivisor(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Class method variant: serializes the given message to binary data + * (in protobuf wire format), writing to the given BinaryWriter. + * @param {!proto.math.DivArgs} message + * @param {!jspb.BinaryWriter} writer + */ +proto.math.DivArgs.serializeBinaryToWriter = function(message, writer) { + message.serializeBinaryToWriter(writer); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.math.DivArgs.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + this.serializeBinaryToWriter(writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format), + * writing to the given BinaryWriter. + * @param {!jspb.BinaryWriter} writer + */ +proto.math.DivArgs.prototype.serializeBinaryToWriter = function (writer) { + var f = undefined; + f = this.getDividend(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } + f = this.getDivisor(); + if (f !== 0) { + writer.writeInt64( + 2, + f + ); + } +}; + + +/** + * Creates a deep clone of this proto. No data is shared with the original. + * @return {!proto.math.DivArgs} The clone. + */ +proto.math.DivArgs.prototype.cloneMessage = function() { + return /** @type {!proto.math.DivArgs} */ (jspb.Message.cloneMessage(this)); +}; + + +/** + * optional int64 dividend = 1; + * @return {number} + */ +proto.math.DivArgs.prototype.getDividend = function() { + return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0)); +}; + + +/** @param {number} value */ +proto.math.DivArgs.prototype.setDividend = function(value) { + jspb.Message.setField(this, 1, value); +}; + + +/** + * optional int64 divisor = 2; + * @return {number} + */ +proto.math.DivArgs.prototype.getDivisor = function() { + return /** @type {number} */ (jspb.Message.getFieldProto3(this, 2, 0)); +}; + + +/** @param {number} value */ +proto.math.DivArgs.prototype.setDivisor = function(value) { + jspb.Message.setField(this, 2, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.math.DivReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.math.DivReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.math.DivReply.displayName = 'proto.math.DivReply'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.math.DivReply.prototype.toObject = function(opt_includeInstance) { + return proto.math.DivReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.math.DivReply} msg The msg instance to transform. + * @return {!Object} + */ +proto.math.DivReply.toObject = function(includeInstance, msg) { + var f, obj = { + quotient: msg.getQuotient(), + remainder: msg.getRemainder() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.math.DivReply} + */ +proto.math.DivReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.math.DivReply; + return proto.math.DivReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.math.DivReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.math.DivReply} + */ +proto.math.DivReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setQuotient(value); + break; + case 2: + var value = /** @type {number} */ (reader.readInt64()); + msg.setRemainder(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Class method variant: serializes the given message to binary data + * (in protobuf wire format), writing to the given BinaryWriter. + * @param {!proto.math.DivReply} message + * @param {!jspb.BinaryWriter} writer + */ +proto.math.DivReply.serializeBinaryToWriter = function(message, writer) { + message.serializeBinaryToWriter(writer); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.math.DivReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + this.serializeBinaryToWriter(writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format), + * writing to the given BinaryWriter. + * @param {!jspb.BinaryWriter} writer + */ +proto.math.DivReply.prototype.serializeBinaryToWriter = function (writer) { + var f = undefined; + f = this.getQuotient(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } + f = this.getRemainder(); + if (f !== 0) { + writer.writeInt64( + 2, + f + ); + } +}; + + +/** + * Creates a deep clone of this proto. No data is shared with the original. + * @return {!proto.math.DivReply} The clone. + */ +proto.math.DivReply.prototype.cloneMessage = function() { + return /** @type {!proto.math.DivReply} */ (jspb.Message.cloneMessage(this)); +}; + + +/** + * optional int64 quotient = 1; + * @return {number} + */ +proto.math.DivReply.prototype.getQuotient = function() { + return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0)); +}; + + +/** @param {number} value */ +proto.math.DivReply.prototype.setQuotient = function(value) { + jspb.Message.setField(this, 1, value); +}; + + +/** + * optional int64 remainder = 2; + * @return {number} + */ +proto.math.DivReply.prototype.getRemainder = function() { + return /** @type {number} */ (jspb.Message.getFieldProto3(this, 2, 0)); +}; + + +/** @param {number} value */ +proto.math.DivReply.prototype.setRemainder = function(value) { + jspb.Message.setField(this, 2, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.math.FibArgs = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.math.FibArgs, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.math.FibArgs.displayName = 'proto.math.FibArgs'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.math.FibArgs.prototype.toObject = function(opt_includeInstance) { + return proto.math.FibArgs.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.math.FibArgs} msg The msg instance to transform. + * @return {!Object} + */ +proto.math.FibArgs.toObject = function(includeInstance, msg) { + var f, obj = { + limit: msg.getLimit() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.math.FibArgs} + */ +proto.math.FibArgs.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.math.FibArgs; + return proto.math.FibArgs.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.math.FibArgs} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.math.FibArgs} + */ +proto.math.FibArgs.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setLimit(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Class method variant: serializes the given message to binary data + * (in protobuf wire format), writing to the given BinaryWriter. + * @param {!proto.math.FibArgs} message + * @param {!jspb.BinaryWriter} writer + */ +proto.math.FibArgs.serializeBinaryToWriter = function(message, writer) { + message.serializeBinaryToWriter(writer); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.math.FibArgs.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + this.serializeBinaryToWriter(writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format), + * writing to the given BinaryWriter. + * @param {!jspb.BinaryWriter} writer + */ +proto.math.FibArgs.prototype.serializeBinaryToWriter = function (writer) { + var f = undefined; + f = this.getLimit(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } +}; + + +/** + * Creates a deep clone of this proto. No data is shared with the original. + * @return {!proto.math.FibArgs} The clone. + */ +proto.math.FibArgs.prototype.cloneMessage = function() { + return /** @type {!proto.math.FibArgs} */ (jspb.Message.cloneMessage(this)); +}; + + +/** + * optional int64 limit = 1; + * @return {number} + */ +proto.math.FibArgs.prototype.getLimit = function() { + return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0)); +}; + + +/** @param {number} value */ +proto.math.FibArgs.prototype.setLimit = function(value) { + jspb.Message.setField(this, 1, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.math.Num = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.math.Num, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.math.Num.displayName = 'proto.math.Num'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.math.Num.prototype.toObject = function(opt_includeInstance) { + return proto.math.Num.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.math.Num} msg The msg instance to transform. + * @return {!Object} + */ +proto.math.Num.toObject = function(includeInstance, msg) { + var f, obj = { + num: msg.getNum() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.math.Num} + */ +proto.math.Num.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.math.Num; + return proto.math.Num.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.math.Num} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.math.Num} + */ +proto.math.Num.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setNum(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Class method variant: serializes the given message to binary data + * (in protobuf wire format), writing to the given BinaryWriter. + * @param {!proto.math.Num} message + * @param {!jspb.BinaryWriter} writer + */ +proto.math.Num.serializeBinaryToWriter = function(message, writer) { + message.serializeBinaryToWriter(writer); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.math.Num.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + this.serializeBinaryToWriter(writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format), + * writing to the given BinaryWriter. + * @param {!jspb.BinaryWriter} writer + */ +proto.math.Num.prototype.serializeBinaryToWriter = function (writer) { + var f = undefined; + f = this.getNum(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } +}; + + +/** + * Creates a deep clone of this proto. No data is shared with the original. + * @return {!proto.math.Num} The clone. + */ +proto.math.Num.prototype.cloneMessage = function() { + return /** @type {!proto.math.Num} */ (jspb.Message.cloneMessage(this)); +}; + + +/** + * optional int64 num = 1; + * @return {number} + */ +proto.math.Num.prototype.getNum = function() { + return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0)); +}; + + +/** @param {number} value */ +proto.math.Num.prototype.setNum = function(value) { + jspb.Message.setField(this, 1, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.math.FibReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.math.FibReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.math.FibReply.displayName = 'proto.math.FibReply'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.math.FibReply.prototype.toObject = function(opt_includeInstance) { + return proto.math.FibReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.math.FibReply} msg The msg instance to transform. + * @return {!Object} + */ +proto.math.FibReply.toObject = function(includeInstance, msg) { + var f, obj = { + count: msg.getCount() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.math.FibReply} + */ +proto.math.FibReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.math.FibReply; + return proto.math.FibReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.math.FibReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.math.FibReply} + */ +proto.math.FibReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setCount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Class method variant: serializes the given message to binary data + * (in protobuf wire format), writing to the given BinaryWriter. + * @param {!proto.math.FibReply} message + * @param {!jspb.BinaryWriter} writer + */ +proto.math.FibReply.serializeBinaryToWriter = function(message, writer) { + message.serializeBinaryToWriter(writer); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.math.FibReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + this.serializeBinaryToWriter(writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the message to binary data (in protobuf wire format), + * writing to the given BinaryWriter. + * @param {!jspb.BinaryWriter} writer + */ +proto.math.FibReply.prototype.serializeBinaryToWriter = function (writer) { + var f = undefined; + f = this.getCount(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } +}; + + +/** + * Creates a deep clone of this proto. No data is shared with the original. + * @return {!proto.math.FibReply} The clone. + */ +proto.math.FibReply.prototype.cloneMessage = function() { + return /** @type {!proto.math.FibReply} */ (jspb.Message.cloneMessage(this)); +}; + + +/** + * optional int64 count = 1; + * @return {number} + */ +proto.math.FibReply.prototype.getCount = function() { + return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0)); +}; + + +/** @param {number} value */ +proto.math.FibReply.prototype.setCount = function(value) { + jspb.Message.setField(this, 1, value); +}; + + +goog.object.extend(exports, proto.math); diff --git a/src/node/test/math/math_server.js b/src/node/test/math/math_server.js index 9f67c52ab0c..fa05ed01654 100644 --- a/src/node/test/math/math_server.js +++ b/src/node/test/math/math_server.js @@ -34,8 +34,8 @@ 'use strict'; var grpc = require('../..'); -var math = grpc.load(__dirname + '/../../../proto/math/math.proto').math; - +var grpcMath = require('./math_grpc_pb'); +var math = require('./math_pb'); /** * Server function for division. Provides the /Math/DivMany and /Math/Div @@ -46,14 +46,16 @@ var math = grpc.load(__dirname + '/../../../proto/math/math.proto').math; */ function mathDiv(call, cb) { var req = call.request; + var divisor = req.getDivisor(); + var dividend = req.getDividend(); // Unary + is explicit coersion to integer - if (+req.divisor === 0) { + if (req.getDivisor() === 0) { cb(new Error('cannot divide by zero')); } else { - cb(null, { - quotient: req.dividend / req.divisor, - remainder: req.dividend % req.divisor - }); + var response = new math.DivReply(); + response.setQuotient(Math.floor(dividend / divisor)); + response.setRemainder(dividend % divisor); + cb(null, response); } } @@ -67,7 +69,9 @@ function mathFib(stream) { // Here, call is a standard writable Node object Stream var previous = 0, current = 1; for (var i = 0; i < stream.request.limit; i++) { - stream.write({num: current}); + var response = new math.Num(); + response.setNum(current); + stream.write(response); var temp = current; current += previous; previous = temp; @@ -85,22 +89,26 @@ function mathSum(call, cb) { // Here, call is a standard readable Node object Stream var sum = 0; call.on('data', function(data) { - sum += (+data.num); + sum += data.getNum(); }); call.on('end', function() { - cb(null, {num: sum}); + var response = new math.Num(); + response.setNum(sum); + cb(null, response); }); } function mathDivMany(stream) { stream.on('data', function(div_args) { - if (+div_args.divisor === 0) { + var divisor = div_args.getDivisor(); + var dividend = div_args.getDividend(); + if (divisor === 0) { stream.emit('error', new Error('cannot divide by zero')); } else { - stream.write({ - quotient: div_args.dividend / div_args.divisor, - remainder: div_args.dividend % div_args.divisor - }); + var response = new math.DivReply(); + response.setQuotient(Math.floor(dividend / divisor)); + response.setRemainder(dividend % divisor); + stream.write(response); } }); stream.on('end', function() { @@ -110,7 +118,7 @@ function mathDivMany(stream) { function getMathServer() { var server = new grpc.Server(); - server.addProtoService(math.Math.service, { + server.addService(grpcMath.MathService, { div: mathDiv, fib: mathFib, sum: mathSum, diff --git a/src/node/test/math/node_modules/grpc.js b/src/node/test/math/node_modules/grpc.js new file mode 100644 index 00000000000..17c8fd96d9b --- /dev/null +++ b/src/node/test/math/node_modules/grpc.js @@ -0,0 +1,37 @@ +/* + * + * Copyright 2016, 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. + * + */ + +/* This exists solely to allow the generated code to import the grpc module + * without using a relative path */ + +module.exports = require('../../..'); diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js index 3d446105364..34c16e070b2 100644 --- a/src/node/test/math_client_test.js +++ b/src/node/test/math_client_test.js @@ -36,7 +36,8 @@ var assert = require('assert'); var grpc = require('..'); -var math = grpc.load(__dirname + '/../../proto/math/math.proto').math; +var math = require('./math/math_pb'); +var MathClient = require('./math/math_grpc_pb').MathClient; /** * Client to use to make requests to a running server. @@ -55,35 +56,41 @@ describe('Math client', function() { var port_num = server.bind('0.0.0.0:0', grpc.ServerCredentials.createInsecure()); server.start(); - math_client = new math.Math('localhost:' + port_num, - grpc.credentials.createInsecure()); + math_client = new MathClient('localhost:' + port_num, + grpc.credentials.createInsecure()); done(); }); after(function() { server.forceShutdown(); }); it('should handle a single request', function(done) { - var arg = {dividend: 7, divisor: 4}; + var arg = new math.DivArgs(); + arg.setDividend(7); + arg.setDivisor(4); math_client.div(arg, function handleDivResult(err, value) { assert.ifError(err); - assert.equal(value.quotient, 1); - assert.equal(value.remainder, 3); + assert.equal(value.getQuotient(), 1); + assert.equal(value.getRemainder(), 3); done(); }); }); it('should handle an error from a unary request', function(done) { - var arg = {dividend: 7, divisor: 0}; + var arg = new math.DivArgs(); + arg.setDividend(7); + arg.setDivisor(0); math_client.div(arg, function handleDivResult(err, value) { assert(err); done(); }); }); it('should handle a server streaming request', function(done) { - var call = math_client.fib({limit: 7}); + var arg = new math.FibArgs(); + arg.setLimit(7); + var call = math_client.fib(arg); var expected_results = [1, 1, 2, 3, 5, 8, 13]; var next_expected = 0; call.on('data', function checkResponse(value) { - assert.equal(value.num, expected_results[next_expected]); + assert.equal(value.getNum(), expected_results[next_expected]); next_expected += 1; }); call.on('status', function checkStatus(status) { @@ -94,10 +101,12 @@ describe('Math client', function() { it('should handle a client streaming request', function(done) { var call = math_client.sum(function handleSumResult(err, value) { assert.ifError(err); - assert.equal(value.num, 21); + assert.equal(value.getNum(), 21); }); for (var i = 0; i < 7; i++) { - call.write({'num': i}); + var arg = new math.Num(); + arg.setNum(i); + call.write(arg); } call.end(); call.on('status', function checkStatus(status) { @@ -107,8 +116,8 @@ describe('Math client', function() { }); it('should handle a bidirectional streaming request', function(done) { function checkResponse(index, value) { - assert.equal(value.quotient, index); - assert.equal(value.remainder, 1); + assert.equal(value.getQuotient(), index); + assert.equal(value.getRemainder(), 1); } var call = math_client.divMany(); var response_index = 0; @@ -117,7 +126,10 @@ describe('Math client', function() { response_index += 1; }); for (var i = 0; i < 7; i++) { - call.write({dividend: 2 * i + 1, divisor: 2}); + var arg = new math.DivArgs(); + arg.setDividend(2 * i + 1); + arg.setDivisor(2); + call.write(arg); } call.end(); call.on('status', function checkStatus(status) { @@ -131,7 +143,10 @@ describe('Math client', function() { assert.fail(value, undefined, 'Unexpected data response on failing call', '!='); }); - call.write({dividend: 7, divisor: 0}); + var arg = new math.DivArgs(); + arg.setDividend(7); + arg.setDivisor(0); + call.write(arg); call.end(); call.on('error', function checkStatus(status) { done(); diff --git a/src/python/.gitignore b/src/python/.gitignore new file mode 100644 index 00000000000..f158efa4bf1 --- /dev/null +++ b/src/python/.gitignore @@ -0,0 +1 @@ +gens/ diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py index 9e745701c14..295dab2d274 100644 --- a/src/python/grpcio/commands.py +++ b/src/python/grpcio/commands.py @@ -50,6 +50,9 @@ from setuptools.command import test import support PYTHON_STEM = os.path.dirname(os.path.abspath(__file__)) +GRPC_STEM = os.path.abspath(PYTHON_STEM + '../../../../') +PROTO_STEM = os.path.join(GRPC_STEM, 'src', 'proto') +PROTO_GEN_STEM = os.path.join(GRPC_STEM, 'src', 'python', 'gens') CONF_PY_ADDENDUM = """ extensions.append('sphinx.ext.napoleon') @@ -157,30 +160,45 @@ class BuildProtoModules(setuptools.Command): if not self.grpc_python_plugin_command: raise CommandError('could not find grpc_python_plugin ' '(protoc plugin for GRPC Python)') + + if not os.path.exists(PROTO_GEN_STEM): + os.makedirs(PROTO_GEN_STEM) + include_regex = re.compile(self.include) exclude_regex = re.compile(self.exclude) if self.exclude else None paths = [] - root_directory = PYTHON_STEM - for walk_root, directories, filenames in os.walk(root_directory): + for walk_root, directories, filenames in os.walk(PROTO_STEM): for filename in filenames: path = os.path.join(walk_root, filename) if include_regex.match(path) and not ( exclude_regex and exclude_regex.match(path)): paths.append(path) - command = [ - self.protoc_command, - '--plugin=protoc-gen-python-grpc={}'.format( - self.grpc_python_plugin_command), - '-I {}'.format(root_directory), - '--python_out={}'.format(root_directory), - '--python-grpc_out={}'.format(root_directory), - ] + paths - try: - subprocess.check_output(' '.join(command), cwd=root_directory, shell=True, - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - raise CommandError('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format( - command, e.message, e.output)) + + # TODO(kpayson): It would be nice to do this in a batch command, + # but we currently have name conflicts in src/proto + for path in paths: + command = [ + self.protoc_command, + '--plugin=protoc-gen-python-grpc={}'.format( + self.grpc_python_plugin_command), + '-I {}'.format(GRPC_STEM), + '--python_out={}'.format(PROTO_GEN_STEM), + '--python-grpc_out={}'.format(PROTO_GEN_STEM), + ] + [path] + try: + subprocess.check_output(' '.join(command), cwd=PYTHON_STEM, shell=True, + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + sys.stderr.write( + 'warning: Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format( + command, e.message, e.output)) + + # Generated proto directories dont include __init__.py, but + # these are needed for python package resolution + for walk_root, _, _ in os.walk(PROTO_GEN_STEM): + if walk_root != PROTO_GEN_STEM: + path = os.path.join(walk_root, '__init__.py') + open(path, 'a').close() class BuildProjectMetadata(setuptools.Command): diff --git a/src/python/grpcio/tests/interop/_insecure_interop_test.py b/src/python/grpcio/tests/interop/_insecure_interop_test.py index 00b49aba374..91519b6fba2 100644 --- a/src/python/grpcio/tests/interop/_insecure_interop_test.py +++ b/src/python/grpcio/tests/interop/_insecure_interop_test.py @@ -32,11 +32,11 @@ import unittest from grpc.beta import implementations +from src.proto.grpc.testing import test_pb2 from tests.interop import _interop_test_case from tests.interop import methods from tests.interop import server -from tests.interop import test_pb2 class InsecureInteropTest( diff --git a/src/python/grpcio/tests/interop/_secure_interop_test.py b/src/python/grpcio/tests/interop/_secure_interop_test.py index 86d7e433512..c61547b9778 100644 --- a/src/python/grpcio/tests/interop/_secure_interop_test.py +++ b/src/python/grpcio/tests/interop/_secure_interop_test.py @@ -32,11 +32,11 @@ import unittest from grpc.beta import implementations +from src.proto.grpc.testing import test_pb2 from tests.interop import _interop_test_case from tests.interop import methods from tests.interop import resources -from tests.interop import test_pb2 from tests.unit.beta import test_utilities diff --git a/src/python/grpcio/tests/interop/client.py b/src/python/grpcio/tests/interop/client.py index 1d10d7e45de..db29eb4aa76 100644 --- a/src/python/grpcio/tests/interop/client.py +++ b/src/python/grpcio/tests/interop/client.py @@ -33,10 +33,10 @@ import argparse from oauth2client import client as oauth2client_client from grpc.beta import implementations +from src.proto.grpc.testing import test_pb2 from tests.interop import methods from tests.interop import resources -from tests.interop import test_pb2 from tests.unit.beta import test_utilities _ONE_DAY_IN_SECONDS = 60 * 60 * 24 diff --git a/src/python/grpcio/tests/interop/empty.proto b/src/python/grpcio/tests/interop/empty.proto deleted file mode 100644 index 6d0eb937d67..00000000000 --- a/src/python/grpcio/tests/interop/empty.proto +++ /dev/null @@ -1,43 +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.testing; - -// An empty message that you can re-use to avoid defining duplicated empty -// messages in your project. A typical example is to use it as argument or the -// return value of a service API. For instance: -// -// service Foo { -// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; -// }; -// -message Empty {} diff --git a/src/python/grpcio/tests/interop/messages.proto b/src/python/grpcio/tests/interop/messages.proto deleted file mode 100644 index 193b6c41712..00000000000 --- a/src/python/grpcio/tests/interop/messages.proto +++ /dev/null @@ -1,167 +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. - -// Message definitions to be used by integration test service definitions. - -syntax = "proto3"; - -package grpc.testing; - -// The type of payload that should be returned. -enum PayloadType { - // Compressable text format. - COMPRESSABLE = 0; - - // Uncompressable binary format. - UNCOMPRESSABLE = 1; - - // Randomly chosen from all other formats defined in this enum. - RANDOM = 2; -} - -// Compression algorithms -enum CompressionType { - // No compression - NONE = 0; - GZIP = 1; - DEFLATE = 2; -} - -// A block of data, to simply increase gRPC message size. -message Payload { - // The type of data in body. - PayloadType type = 1; - // Primary contents of payload. - bytes body = 2; -} - -// A protobuf representation for grpc status. This is used by test -// clients to specify a status that the server should attempt to return. -message EchoStatus { - int32 code = 1; - string message = 2; -} - -// Unary request. -message SimpleRequest { - // Desired payload type in the response from the server. - // If response_type is RANDOM, server randomly chooses one from other formats. - PayloadType response_type = 1; - - // Desired payload size in the response from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. - int32 response_size = 2; - - // Optional input payload sent along with the request. - Payload payload = 3; - - // Whether SimpleResponse should include username. - bool fill_username = 4; - - // Whether SimpleResponse should include OAuth scope. - bool fill_oauth_scope = 5; - - // Compression algorithm to be used by the server for the response (stream) - CompressionType response_compression = 6; - - // Whether server should return a given status - EchoStatus response_status = 7; -} - -// Unary response, as configured by the request. -message SimpleResponse { - // Payload to increase message size. - Payload payload = 1; - // The user the request came from, for verifying authentication was - // successful when the client expected it. - string username = 2; - // OAuth scope. - string oauth_scope = 3; -} - -// Client-streaming request. -message StreamingInputCallRequest { - // Optional input payload sent along with the request. - Payload payload = 1; - - // Not expecting any payload from the response. -} - -// Client-streaming response. -message StreamingInputCallResponse { - // Aggregated size of payloads received from the client. - int32 aggregated_payload_size = 1; -} - -// Configuration for a particular response. -message ResponseParameters { - // Desired payload sizes in responses from the server. - // If response_type is COMPRESSABLE, this denotes the size before compression. - int32 size = 1; - - // Desired interval between consecutive responses in the response stream in - // microseconds. - int32 interval_us = 2; -} - -// Server-streaming request. -message StreamingOutputCallRequest { - // Desired payload type in the response from the server. - // If response_type is RANDOM, the payload from each response in the stream - // might be of different types. This is to simulate a mixed type of payload - // stream. - PayloadType response_type = 1; - - // Configuration for each expected response message. - repeated ResponseParameters response_parameters = 2; - - // Optional input payload sent along with the request. - Payload payload = 3; - - // Compression algorithm to be used by the server for the response (stream) - CompressionType response_compression = 6; - - // Whether server should return a given status - EchoStatus response_status = 7; -} - -// Server-streaming response, as configured by the request and parameters. -message StreamingOutputCallResponse { - // Payload to increase response size. - Payload payload = 1; -} - -// For reconnect interop test only. -// Server tells client whether its reconnects are following the spec and the -// reconnect backoffs it saw. -message ReconnectInfo { - bool passed = 1; - repeated int32 backoff_ms = 2; -} diff --git a/src/python/grpcio/tests/interop/methods.py b/src/python/grpcio/tests/interop/methods.py index 03810338ed2..67862ed7d37 100644 --- a/src/python/grpcio/tests/interop/methods.py +++ b/src/python/grpcio/tests/interop/methods.py @@ -42,9 +42,9 @@ from oauth2client import client as oauth2client_client from grpc.framework.common import cardinality from grpc.framework.interfaces.face import face -from tests.interop import empty_pb2 -from tests.interop import messages_pb2 -from tests.interop import test_pb2 +from src.proto.grpc.testing import empty_pb2 +from src.proto.grpc.testing import messages_pb2 +from src.proto.grpc.testing import test_pb2 _TIMEOUT = 7 diff --git a/src/python/grpcio/tests/interop/server.py b/src/python/grpcio/tests/interop/server.py index 6dd55f008c3..ab2c3c708f4 100644 --- a/src/python/grpcio/tests/interop/server.py +++ b/src/python/grpcio/tests/interop/server.py @@ -34,10 +34,10 @@ import logging import time from grpc.beta import implementations +from src.proto.grpc.testing import test_pb2 from tests.interop import methods from tests.interop import resources -from tests.interop import test_pb2 _ONE_DAY_IN_SECONDS = 60 * 60 * 24 diff --git a/src/python/grpcio/tests/interop/test.proto b/src/python/grpcio/tests/interop/test.proto deleted file mode 100644 index 9feecc02785..00000000000 --- a/src/python/grpcio/tests/interop/test.proto +++ /dev/null @@ -1,86 +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. - -// An integration test service that covers all the method signature permutations -// of unary/streaming requests/responses. - -syntax = "proto3"; - -import "tests/interop/empty.proto"; -import "tests/interop/messages.proto"; - -package grpc.testing; - -// A simple service to test the various types of RPCs and experiment with -// performance with various types of payload. -service TestService { - // One empty request followed by one empty response. - rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); - - // One request followed by one response. - rpc UnaryCall(SimpleRequest) returns (SimpleResponse); - - // One request followed by a sequence of responses (streamed download). - // The server returns the payload with client desired type and sizes. - rpc StreamingOutputCall(StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); - - // A sequence of requests followed by one response (streamed upload). - // The server returns the aggregated size of client payload as the result. - rpc StreamingInputCall(stream StreamingInputCallRequest) - returns (StreamingInputCallResponse); - - // A sequence of requests with each request served by the server immediately. - // As one request could lead to multiple responses, this interface - // demonstrates the idea of full duplexing. - rpc FullDuplexCall(stream StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); - - // A sequence of requests followed by a sequence of responses. - // The server buffers all the client requests and then serves them in order. A - // stream of responses are returned to the client when the server starts with - // first request. - rpc HalfDuplexCall(stream StreamingOutputCallRequest) - returns (stream StreamingOutputCallResponse); -} - - -// A simple service NOT implemented at servers so clients can test for -// that case. -service UnimplementedService { - // A call that no server should implement - rpc UnimplementedCall(grpc.testing.Empty) returns(grpc.testing.Empty); -} - -// A service used to control reconnect server. -service ReconnectService { - rpc Start(grpc.testing.Empty) returns (grpc.testing.Empty); - rpc Stop(grpc.testing.Empty) returns (grpc.testing.ReconnectInfo); -} diff --git a/src/ruby/bin/grpc_ruby_interop_client b/src/ruby/bin/grpc_ruby_interop_client deleted file mode 100755 index e79fd33aa50..00000000000 --- a/src/ruby/bin/grpc_ruby_interop_client +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env ruby - -# 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. - -# Provides a gem binary entry point for the interop client. -require 'test/client' diff --git a/src/ruby/bin/grpc_ruby_interop_server b/src/ruby/bin/grpc_ruby_interop_server deleted file mode 100755 index 656a5f7c998..00000000000 --- a/src/ruby/bin/grpc_ruby_interop_server +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env ruby - -# 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. - -# Provides a gem binary entry point for the interop server -require 'test/server' diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb deleted file mode 100755 index c6b0d00ec63..00000000000 --- a/src/ruby/bin/interop/interop_server.rb +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env ruby - -# 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. - -# ####################################################################### -# DEPRECATED: The behaviour in this file has been moved to pb/test/server.rb -# -# This file remains to support existing tools and scripts that use it. -# ###################################################################### -# -# interop_server is a Testing app that runs a gRPC interop testing server. -# -# It helps validate interoperation b/w gRPC in different environments -# -# Helps validate interoperation b/w different gRPC implementations. -# -# Usage: $ path/to/interop_server.rb --port - -this_dir = File.expand_path(File.dirname(__FILE__)) -pb_dir = File.join(File.dirname(File.dirname(this_dir)), 'pb') -$LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) - -require 'test/server' diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index f5fdbb2ffd7..48c49a21e91 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -214,6 +214,35 @@ static VALUE grpc_rb_call_get_peer(VALUE self) { return res; } +/* Called to obtain the x509 cert of an authenticated peer. */ +static VALUE grpc_rb_call_get_peer_cert(VALUE self) { + grpc_call *call = NULL; + VALUE res = Qnil; + grpc_auth_context *ctx = NULL; + TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call); + + ctx = grpc_call_auth_context(call); + + if (!ctx || !grpc_auth_context_peer_is_authenticated(ctx)) { + return Qnil; + } + + { + grpc_auth_property_iterator it = + grpc_auth_context_find_properties_by_name(ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME); + const grpc_auth_property *prop = grpc_auth_property_iterator_next(&it); + if (prop == NULL) { + return Qnil; + } + + res = rb_str_new2(prop->value); + } + + grpc_auth_context_release(ctx); + + return res; +} + /* call-seq: status = call.status @@ -861,6 +890,7 @@ void Init_grpc_call() { rb_define_method(grpc_rb_cCall, "run_batch", grpc_rb_call_run_batch, 4); rb_define_method(grpc_rb_cCall, "cancel", grpc_rb_call_cancel, 0); rb_define_method(grpc_rb_cCall, "peer", grpc_rb_call_get_peer, 0); + rb_define_method(grpc_rb_cCall, "peer_cert", grpc_rb_call_get_peer_cert, 0); rb_define_method(grpc_rb_cCall, "status", grpc_rb_call_get_status, 0); rb_define_method(grpc_rb_cCall, "status=", grpc_rb_call_set_status, 1); rb_define_method(grpc_rb_cCall, "metadata", grpc_rb_call_get_metadata, 0); diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb index 4e23cd7af2d..a56c49ff59e 100644 --- a/src/ruby/lib/grpc.rb +++ b/src/ruby/lib/grpc.rb @@ -32,13 +32,13 @@ unless ENV['GRPC_DEFAULT_SSL_ROOTS_FILE_PATH'] ENV['GRPC_DEFAULT_SSL_ROOTS_FILE_PATH'] = ssl_roots_path end -require 'grpc/errors' -require 'grpc/grpc' -require 'grpc/logconfig' -require 'grpc/notifier' -require 'grpc/version' -require 'grpc/core/time_consts' -require 'grpc/generic/active_call' -require 'grpc/generic/client_stub' -require 'grpc/generic/service' -require 'grpc/generic/rpc_server' +require_relative 'grpc/errors' +require_relative 'grpc/grpc' +require_relative 'grpc/logconfig' +require_relative 'grpc/notifier' +require_relative 'grpc/version' +require_relative 'grpc/core/time_consts' +require_relative 'grpc/generic/active_call' +require_relative 'grpc/generic/client_stub' +require_relative 'grpc/generic/service' +require_relative 'grpc/generic/rpc_server' diff --git a/src/ruby/lib/grpc/core/time_consts.rb b/src/ruby/lib/grpc/core/time_consts.rb index 3b8c2daa07e..5be7ed2cb79 100644 --- a/src/ruby/lib/grpc/core/time_consts.rb +++ b/src/ruby/lib/grpc/core/time_consts.rb @@ -27,7 +27,7 @@ # (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/grpc' +require_relative '../grpc' # GRPC contains the General RPC module. module GRPC diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb index 1d7588c18df..a1dd1e3e9d8 100644 --- a/src/ruby/lib/grpc/errors.rb +++ b/src/ruby/lib/grpc/errors.rb @@ -27,7 +27,7 @@ # (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/grpc' +require_relative './grpc' # GRPC contains the General RPC module. module GRPC diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb index e80d24edc9b..ecf3cc32935 100644 --- a/src/ruby/lib/grpc/generic/active_call.rb +++ b/src/ruby/lib/grpc/generic/active_call.rb @@ -28,7 +28,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. require 'forwardable' -require 'grpc/generic/bidi_call' +require_relative 'bidi_call' class Struct # BatchResult is the struct returned by calls to call#start_batch. @@ -59,7 +59,8 @@ module GRPC include Core::CallOps extend Forwardable attr_reader(:deadline) - def_delegators :@call, :cancel, :metadata, :write_flag, :write_flag= + def_delegators :@call, :cancel, :metadata, :write_flag, :write_flag=, + :peer, :peer_cert # client_invoke begins a client invocation. # @@ -472,7 +473,7 @@ module GRPC # SingleReqView limits access to an ActiveCall's methods for use in server # handlers that receive just one request. SingleReqView = view_class(:cancelled, :deadline, :metadata, - :output_metadata) + :output_metadata, :peer, :peer_cert) # MultiReqView limits access to an ActiveCall's methods for use in # server client_streamer handlers. diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb index 6b9b7856933..1f6d5f365d7 100644 --- a/src/ruby/lib/grpc/generic/bidi_call.rb +++ b/src/ruby/lib/grpc/generic/bidi_call.rb @@ -28,7 +28,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. require 'forwardable' -require 'grpc/grpc' +require_relative '../grpc' # GRPC contains the General RPC module. module GRPC diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb index a6bb92d72c8..68e167a69fc 100644 --- a/src/ruby/lib/grpc/generic/client_stub.rb +++ b/src/ruby/lib/grpc/generic/client_stub.rb @@ -27,8 +27,8 @@ # (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/generic/active_call' -require 'grpc/version' +require_relative 'active_call' +require_relative '../version' # GRPC contains the General RPC module. module GRPC diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb index dd90d8d91dc..cc21ffd3c5b 100644 --- a/src/ruby/lib/grpc/generic/rpc_desc.rb +++ b/src/ruby/lib/grpc/generic/rpc_desc.rb @@ -27,7 +27,7 @@ # (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/grpc' +require_relative '../grpc' # GRPC contains the General RPC module. module GRPC diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 5ba77db1737..7f3a38a9f46 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -27,9 +27,9 @@ # (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/grpc' -require 'grpc/generic/active_call' -require 'grpc/generic/service' +require_relative '../grpc' +require_relative 'active_call' +require_relative 'service' require 'thread' # A global that contains signals the gRPC servers should respond to. @@ -332,10 +332,15 @@ module GRPC # the current thread to terminate it. def run_till_terminated GRPC.trap_signals - t = Thread.new { run } + stopped = false + t = Thread.new do + run + stopped = true + end wait_till_running loop do sleep SIGNAL_CHECK_PERIOD + break if stopped break unless GRPC.handle_signals end stop @@ -434,7 +439,6 @@ module GRPC begin an_rpc = @server.request_call(@cq, loop_tag, INFINITE_FUTURE) break if (!an_rpc.nil?) && an_rpc.call.nil? - active_call = new_active_server_call(an_rpc) unless active_call.nil? @pool.schedule(active_call) do |ac| diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb index 410e1add7dc..8e940b5b13e 100644 --- a/src/ruby/lib/grpc/generic/service.rb +++ b/src/ruby/lib/grpc/generic/service.rb @@ -27,8 +27,8 @@ # (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/generic/client_stub' -require 'grpc/generic/rpc_desc' +require_relative 'client_stub' +require_relative 'rpc_desc' # GRPC contains the General RPC module. module GRPC diff --git a/src/ruby/lib/grpc/grpc.rb b/src/ruby/lib/grpc/grpc.rb index 250f6dd30d4..b60a828d666 100644 --- a/src/ruby/lib/grpc/grpc.rb +++ b/src/ruby/lib/grpc/grpc.rb @@ -28,7 +28,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. begin - require "grpc/#{RUBY_VERSION.sub(/\.\d$/, '')}/grpc_c" + require_relative "#{RUBY_VERSION.sub(/\.\d$/, '')}/grpc_c" rescue LoadError - require 'grpc/grpc_c' + require_relative 'grpc_c' end diff --git a/src/ruby/pb/grpc/testing/metrics.rb b/src/ruby/pb/grpc/testing/metrics.rb new file mode 100644 index 00000000000..3b3c8cd61bb --- /dev/null +++ b/src/ruby/pb/grpc/testing/metrics.rb @@ -0,0 +1,28 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: grpc/testing/metrics.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_message "grpc.testing.GaugeResponse" do + optional :name, :string, 1 + oneof :value do + optional :long_value, :int64, 2 + optional :double_value, :double, 3 + optional :string_value, :string, 4 + end + end + add_message "grpc.testing.GaugeRequest" do + optional :name, :string, 1 + end + add_message "grpc.testing.EmptyMessage" do + end +end + +module Grpc + module Testing + GaugeResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.GaugeResponse").msgclass + GaugeRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.GaugeRequest").msgclass + EmptyMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.EmptyMessage").msgclass + end +end diff --git a/src/ruby/pb/grpc/testing/metrics_services.rb b/src/ruby/pb/grpc/testing/metrics_services.rb new file mode 100644 index 00000000000..f5778bbbb19 --- /dev/null +++ b/src/ruby/pb/grpc/testing/metrics_services.rb @@ -0,0 +1,27 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# Source: grpc/testing/metrics.proto for package 'grpc.testing' + +require 'grpc' +require 'grpc/testing/metrics' + +module Grpc + module Testing + module MetricsService + + # TODO: add proto service documentation here + class Service + + include GRPC::GenericService + + self.marshal_class_method = :encode + self.unmarshal_class_method = :decode + self.service_name = 'grpc.testing.MetricsService' + + rpc :GetAllGauges, EmptyMessage, stream(GaugeResponse) + rpc :GetGauge, GaugeRequest, GaugeResponse + end + + Stub = Service.rpc_stub_class + end + end +end diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb index 2f83e67c524..95b059a18ef 100755 --- a/src/ruby/pb/test/client.rb +++ b/src/ruby/pb/test/client.rb @@ -38,23 +38,23 @@ # --server_port= \ # --test_case= +# These lines are required for the generated files to load grpc this_dir = File.expand_path(File.dirname(__FILE__)) lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib') -pb_dir = File.dirname(File.dirname(this_dir)) +pb_dir = File.dirname(this_dir) $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) $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_relative '../../lib/grpc' require 'googleauth' require 'google/protobuf' -require 'test/proto/empty' -require 'test/proto/messages' -require 'test/proto/test_services' +require_relative 'proto/empty' +require_relative 'proto/messages' +require_relative 'proto/test_services' AUTH_ENV = Google::Auth::CredentialsLoader::ENV_VAR @@ -208,12 +208,10 @@ class NamedTests def empty_unary resp = @stub.empty_call(Empty.new) assert('empty_unary: invalid response') { resp.is_a?(Empty) } - p 'OK: empty_unary' end def large_unary perform_large_unary - p 'OK: large_unary' end def service_account_creds @@ -230,7 +228,6 @@ class NamedTests assert("#{__callee__}: bad oauth scope") do @args.oauth_scope.include?(resp.oauth_scope) end - p "OK: #{__callee__}" end def jwt_token_creds @@ -238,7 +235,6 @@ class NamedTests wanted_email = MultiJson.load(json_key)['client_email'] resp = perform_large_unary(fill_username: true) assert("#{__callee__}: bad username") { wanted_email == resp.username } - p "OK: #{__callee__}" end def compute_engine_creds @@ -247,7 +243,6 @@ class NamedTests assert("#{__callee__}: bad username") do @args.default_service_account == resp.username end - p "OK: #{__callee__}" end def oauth2_auth_token @@ -259,7 +254,6 @@ class NamedTests assert("#{__callee__}: bad oauth scope") do @args.oauth_scope.include?(resp.oauth_scope) end - p "OK: #{__callee__}" end def per_rpc_creds @@ -279,7 +273,6 @@ class NamedTests assert("#{__callee__}: bad oauth scope") do @args.oauth_scope.include?(resp.oauth_scope) end - p "OK: #{__callee__}" end def client_streaming @@ -293,7 +286,6 @@ class NamedTests assert("#{__callee__}: aggregate payload size is incorrect") do wanted_aggregate_size == resp.aggregated_payload_size end - p "OK: #{__callee__}" end def server_streaming @@ -311,7 +303,6 @@ class NamedTests :COMPRESSABLE == r.payload.type end end - p "OK: #{__callee__}" end def ping_pong @@ -319,7 +310,6 @@ class NamedTests ppp = PingPongPlayer.new(msg_sizes) resps = @stub.full_duplex_call(ppp.each_item) resps.each { |r| ppp.queue.push(r) } - p "OK: #{__callee__}" end def timeout_on_sleeping_server @@ -332,7 +322,6 @@ class NamedTests assert("#{__callee__}: status was wrong") do e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED end - p "OK: #{__callee__}" end def empty_stream @@ -346,7 +335,6 @@ class NamedTests assert("#{__callee__}: too many responses expected 0") do count == 0 end - p "OK: #{__callee__}" end def cancel_after_begin @@ -361,7 +349,6 @@ class NamedTests fail 'Should have raised GRPC:Cancelled' rescue GRPC::Cancelled assert("#{__callee__}: call operation should be CANCELLED") { op.cancelled } - p "OK: #{__callee__}" end def cancel_after_first_response @@ -374,7 +361,6 @@ class NamedTests rescue GRPC::Cancelled assert("#{__callee__}: call operation should be CANCELLED") { op.cancelled } op.wait - p "OK: #{__callee__}" end def all @@ -442,7 +428,7 @@ def parse_args opts.on('--use_tls USE_TLS', ['false', 'true'], 'require a secure connection?') do |v| args['secure'] = v == 'true' - end +p end opts.on('--use_test_ca USE_TEST_CA', ['false', 'true'], 'if secure, use the test certificate?') do |v| args['use_test_ca'] = v == 'true' @@ -464,6 +450,9 @@ def main opts = parse_args stub = create_stub(opts) NamedTests.new(stub, opts).method(opts['test_case']).call + p "OK: #{opts['test_case']}" end -main +if __FILE__ == $0 + main +end diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb index 851e8152224..914c7cc79d7 100755 --- a/src/ruby/pb/test/server.rb +++ b/src/ruby/pb/test/server.rb @@ -39,7 +39,7 @@ this_dir = File.expand_path(File.dirname(__FILE__)) lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib') -pb_dir = File.dirname(File.dirname(this_dir)) +pb_dir = File.dirname(this_dir) $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) $LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) $LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir) diff --git a/src/ruby/bin/interop/interop_client.rb b/src/ruby/stress/metrics_server.rb old mode 100755 new mode 100644 similarity index 58% rename from src/ruby/bin/interop/interop_client.rb rename to src/ruby/stress/metrics_server.rb index 239083f37f6..13638c4d211 --- a/src/ruby/bin/interop/interop_client.rb +++ b/src/ruby/stress/metrics_server.rb @@ -1,6 +1,4 @@ -#!/usr/bin/env ruby - -# Copyright 2015, Google Inc. +# Copyright 2016, Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -29,23 +27,57 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ####################################################################### -# DEPRECATED: The behaviour in this file has been moved to pb/test/client.rb -# -# This file remains to support existing tools and scripts that use it. -# ###################################################################### -# -# interop_client is a testing tool that accesses a gRPC interop testing -# server and runs a test on it. -# -# Helps validate interoperation b/w different gRPC implementations. -# -# Usage: $ path/to/interop_client.rb --server_host= \ -# --server_port= \ -# --test_case= +require_relative '../pb/grpc/testing/metrics.rb' +require_relative '../pb/grpc/testing/metrics_services.rb' + +class Gauge + def get_name + raise NoMethodError.new + end + + def get_type + raise NoMethodError.new + end + + def get_value + raise NoMethodError.new + end +end + +class MetricsServiceImpl < Grpc::Testing::MetricsService::Service + include Grpc::Testing + @gauges + + def initialize + @gauges = {} + end + + def register_gauge(gauge) + @gauges[gauge.get_name] = gauge + end + + def make_gauge_response(gauge) + response = GaugeResponse.new(:name => gauge.get_name) + value = gauge.get_value + case gauge.get_type + when 'long' + response.long_value = value + when 'double' + response.double_value = value + when 'string' + response.string_value = value + end + response + end -this_dir = File.expand_path(File.dirname(__FILE__)) -pb_dir = File.join(File.dirname(File.dirname(this_dir)), 'pb') -$LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) + def get_all_gauges(_empty, _call) + @gauges.values.map do |gauge| + make_gauge_response gauge + end + end -require 'test/client' + def get_gauge(gauge_req, _call) + gauge = @gauges[gauge_req.name] + make_gauge_response gauge + end +end diff --git a/src/ruby/stress/stress_client.rb b/src/ruby/stress/stress_client.rb new file mode 100755 index 00000000000..698f9f1b871 --- /dev/null +++ b/src/ruby/stress/stress_client.rb @@ -0,0 +1,155 @@ +#!/usr/bin/env ruby + +# Copyright 2016, 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 'optparse' +require 'thread' +require_relative '../pb/test/client' +require_relative './metrics_server' +require_relative '../lib/grpc' + +class QpsGauge < Gauge + @query_count + @query_mutex + @start_time + + def initialize + @query_count = 0 + @query_mutex = Mutex.new + @start_time = Time.now + end + + def increment_queries + @query_mutex.synchronize { @query_count += 1} + end + + def get_name + 'qps' + end + + def get_type + 'long' + end + + def get_value + (@query_mutex.synchronize { @query_count / (Time.now - @start_time) }).to_i + end +end + +def start_metrics_server(port) + host = "0.0.0.0:#{port}" + server = GRPC::RpcServer.new + server.add_http2_port(host, :this_port_is_insecure) + service = MetricsServiceImpl.new + server.handle(service) + server_thread = Thread.new { server.run_till_terminated } + [server, service, server_thread] +end + +StressArgs = Struct.new(:server_addresses, :test_cases, :duration, + :channels_per_server, :concurrent_calls, :metrics_port) + +def start(stress_args) + running = true + threads = [] + qps_gauge = QpsGauge.new + metrics_server, metrics_service, metrics_thread = + start_metrics_server(stress_args.metrics_port) + metrics_service.register_gauge(qps_gauge) + stress_args.server_addresses.each do |address| + stress_args.channels_per_server.times do + client_args = Args.new + client_args.host, client_args.port = address.split(':') + client_args.secure = false + client_args.test_case = '' + stub = create_stub(client_args) + named_tests = NamedTests.new(stub, client_args) + stress_args.concurrent_calls.times do + threads << Thread.new do + while running + named_tests.method(stress_args.test_cases.sample).call + qps_gauge.increment_queries + end + end + end + end + end + if stress_args.duration >= 0 + sleep stress_args.duration + running = false + metrics_server.stop + p "QPS: #{qps_gauge.get_value}" + threads.each { |thd| thd.join; } + end + metrics_thread.join +end + +def parse_stress_args + stress_args = StressArgs.new + stress_args.server_addresses = ['localhost:8080'] + stress_args.test_cases = [] + stress_args.duration = -1 + stress_args.channels_per_server = 1 + stress_args.concurrent_calls = 1 + stress_args.metrics_port = '8081' + OptionParser.new do |opts| + opts.on('--server_addresses [LIST]', Array) do |addrs| + stress_args.server_addresses = addrs + end + opts.on('--test_cases cases', Array) do |cases| + stress_args.test_cases = (cases.map do |item| + split = item.split(':') + [split[0]] * split[1].to_i + end).reduce([], :+) + end + opts.on('--test_duration_secs [INT]', OptionParser::DecimalInteger) do |time| + stress_args.duration = time + end + opts.on('--num_channels_per_server [INT]', OptionParser::DecimalInteger) do |channels| + stress_args.channels_per_server = channels + end + opts.on('--num_stubs_per_channel [INT]', OptionParser::DecimalInteger) do |stubs| + stress_args.concurrent_calls = stubs + end + opts.on('--metrics_port [port]') do |port| + stress_args.metrics_port = port + end + end.parse! + stress_args +end + +def main + opts = parse_stress_args + start(opts) +end + +if __FILE__ == $0 + main +end diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template index 6f8d1fb9e6c..ce775ffb90b 100644 --- a/templates/grpc.gemspec.template +++ b/templates/grpc.gemspec.template @@ -26,10 +26,6 @@ s.files += Dir.glob('include/grpc/**/*') s.test_files = Dir.glob('src/ruby/spec/**/*') s.bindir = 'src/ruby/bin' - ${'%'}w(math noproto).each do |b| - s.executables += ["#{b}_client.rb", "#{b}_server.rb"] - end - s.executables += %w(grpc_ruby_interop_client grpc_ruby_interop_server) s.require_paths = %w( src/ruby/bin src/ruby/lib src/ruby/pb ) s.platform = Gem::Platform::RUBY diff --git a/templates/package.json.template b/templates/package.json.template index 5db270608b0..11718b1ccbd 100644 --- a/templates/package.json.template +++ b/templates/package.json.template @@ -21,7 +21,7 @@ "lib": "src/node/src" }, "scripts": { - "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js", + "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js --exclude-path=src/node/.jshintignore", "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint", "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json", "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test", @@ -37,6 +37,7 @@ "devDependencies": { "async": "^1.5.0", "google-auth-library": "^0.9.2", + "google-protobuf": "^3.0.0-alpha.5", "istanbul": "^0.3.21", "jsdoc": "^3.3.2", "jshint": "^2.5.0", @@ -74,5 +75,25 @@ "binding.gyp" ], "main": "src/node/index.js", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "jshintConfig" : { + "bitwise": true, + "curly": true, + "eqeqeq": true, + "esnext": true, + "freeze": true, + "immed": true, + "indent": 2, + "latedef": "nofunc", + "maxlen": 80, + "mocha": true, + "newcap": true, + "node": true, + "noarg": true, + "quotmark": "single", + "strict": true, + "trailing": true, + "undef": true, + "unused": "vars" + } } diff --git a/templates/package.xml.template b/templates/package.xml.template index 2f498c02f45..63132dac947 100644 --- a/templates/package.xml.template +++ b/templates/package.xml.template @@ -12,7 +12,7 @@ grpc-packages@google.com yes - 2016-03-01 + 2016-04-19 ${settings.php_version.php()} @@ -24,7 +24,7 @@ BSD - - Increase unit test code coverage #5225 + - destroy grpc_byte_buffer after startBatch #6096 @@ -157,8 +157,8 @@ - ${settings.php_version.php()} - ${settings.php_version.php()} + 0.8.1 + 0.8.1 beta @@ -170,5 +170,20 @@ - Increase unit test code coverage #5225 + + + ${settings.php_version.php()} + ${settings.php_version.php()} + + + beta + beta + + 2016-04-19 + BSD + + - destroy grpc_byte_buffer after startBatch #6096 + + diff --git a/templates/src/csharp/Grpc.Core/VersionInfo.cs.template b/templates/src/csharp/Grpc.Core/VersionInfo.cs.template index 3ca111e72b1..96cf2ee17f4 100644 --- a/templates/src/csharp/Grpc.Core/VersionInfo.cs.template +++ b/templates/src/csharp/Grpc.Core/VersionInfo.cs.template @@ -37,13 +37,20 @@ { /// /// Provides info about current version of gRPC. + /// See https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/ + /// for rationale about assembly versioning. /// public static class VersionInfo { /// - /// Current version of gRPC C# assemblies + /// Current AssemblyVersion attribute of gRPC C# assemblies /// - public const string CurrentAssemblyVersion = "${settings.version.major}.${settings.version.minor}.${settings.version.patch}.0"; + public const string CurrentAssemblyVersion = "1.0.0.0"; + + /// + /// Current AssemblyFileVersion of gRPC C# assemblies + /// + public const string CurrentAssemblyFileVersion = "${settings.version.major}.${settings.version.minor}.${settings.version.patch}.0"; /// /// Current version of gRPC C# diff --git a/tools/run_tests/run_interop_tests.py b/tools/run_tests/run_interop_tests.py index 28b91f8b623..18d4c1072b8 100755 --- a/tools/run_tests/run_interop_tests.py +++ b/tools/run_tests/run_interop_tests.py @@ -270,13 +270,13 @@ class RubyLanguage: self.safename = str(self) def client_cmd(self, args): - return ['ruby', 'src/ruby/bin/interop/interop_client.rb'] + args + return ['ruby', 'src/ruby/pb/test/client.rb'] + args def cloud_to_prod_env(self): return {} def server_cmd(self, args): - return ['ruby', 'src/ruby/bin/interop/interop_server.rb', '--use_tls=true'] + args + return ['ruby', 'src/ruby/pb/test/server.rb', '--use_tls=true'] + args def global_env(self): return {} @@ -314,7 +314,8 @@ class PythonLanguage: ] def global_env(self): - return {'LD_LIBRARY_PATH': '{}/libs/opt'.format(DOCKER_WORKDIR_ROOT)} + return {'LD_LIBRARY_PATH': '{}/libs/opt'.format(DOCKER_WORKDIR_ROOT), + 'PYTHONPATH': '{}/src/python/gens'.format(DOCKER_WORKDIR_ROOT)} def unimplemented_test_cases(self): return _SKIP_ADVANCED + _SKIP_COMPRESSION + ['jwt_token_creds', @@ -590,8 +591,8 @@ prod_servers = { False), 'cloud_gateway_v2': ('216.239.32.255', 'grpc-test2.sandbox.googleapis.com', True), - 'gateway_v4': ('grpc-test4.sandbox.googleapis.com', - 'grpc-test4.sandbox.googleapis.com', True), + 'gateway_v4': ('grpc-test4.sandbox.googleapis.com', + 'grpc-test4.sandbox.googleapis.com', True), 'cloud_gateway_v4': ('216.239.32.255', 'grpc-test4.sandbox.googleapis.com', True), } diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 9dff686bbf6..4b9898539d5 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -371,6 +371,7 @@ class PythonLanguage(object): tests_json = json.load(tests_json_file) environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS) environment['PYVER'] = '2.7' + environment['PYTHONPATH'] = os.path.abspath('src/python/gens') if self.config.build_config != 'gcov': return [self.config.job_spec( ['tools/run_tests/run_python.sh'], diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json index bb51ecd0c4f..e94387cb4d7 100644 --- a/tools/run_tests/sources_and_headers.json +++ b/tools/run_tests/sources_and_headers.json @@ -2170,6 +2170,19 @@ "third_party": false, "type": "target" }, + { + "deps": [ + "grpc_plugin_support" + ], + "headers": [], + "language": "c++", + "name": "grpc_node_plugin", + "src": [ + "src/compiler/node_plugin.cc" + ], + "third_party": false, + "type": "target" + }, { "deps": [ "grpc_plugin_support" @@ -4363,6 +4376,8 @@ "src/compiler/csharp_generator.h", "src/compiler/csharp_generator_helpers.h", "src/compiler/generator_helpers.h", + "src/compiler/node_generator.h", + "src/compiler/node_generator_helpers.h", "src/compiler/objective_c_generator.h", "src/compiler/objective_c_generator_helpers.h", "src/compiler/python_generator.h", @@ -4382,6 +4397,9 @@ "src/compiler/csharp_generator.h", "src/compiler/csharp_generator_helpers.h", "src/compiler/generator_helpers.h", + "src/compiler/node_generator.cc", + "src/compiler/node_generator.h", + "src/compiler/node_generator_helpers.h", "src/compiler/objective_c_generator.cc", "src/compiler/objective_c_generator.h", "src/compiler/objective_c_generator_helpers.h", diff --git a/vsprojects/grpc_protoc_plugins.sln b/vsprojects/grpc_protoc_plugins.sln index ef1cbb8e572..ace295daeae 100644 --- a/vsprojects/grpc_protoc_plugins.sln +++ b/vsprojects/grpc_protoc_plugins.sln @@ -24,6 +24,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_csharp_plugin", "vcxpr {B6E81D84-2ACB-41B8-8781-493A944C7817} = {B6E81D84-2ACB-41B8-8781-493A944C7817} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_node_plugin", "vcxproj\.\grpc_node_plugin\grpc_node_plugin.vcxproj", "{57ABD9A2-CE8E-CCA7-5171-35C4534F3595}" + ProjectSection(myProperties) = preProject + lib = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {B6E81D84-2ACB-41B8-8781-493A944C7817} = {B6E81D84-2ACB-41B8-8781-493A944C7817} + EndProjectSection +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "grpc_objective_c_plugin", "vcxproj\.\grpc_objective_c_plugin\grpc_objective_c_plugin.vcxproj", "{19564640-CEE6-4921-ABA5-676ED79A36F6}" ProjectSection(myProperties) = preProject lib = "False" @@ -80,6 +88,14 @@ Global {3C813052-A49A-4662-B90A-1ADBEC7EE453}.Debug|x64.Build.0 = Debug|x64 {3C813052-A49A-4662-B90A-1ADBEC7EE453}.Release|Win32.Build.0 = Release|Win32 {3C813052-A49A-4662-B90A-1ADBEC7EE453}.Release|x64.Build.0 = Release|x64 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Debug|Win32.ActiveCfg = Debug|Win32 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Debug|x64.ActiveCfg = Debug|x64 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Release|Win32.ActiveCfg = Release|Win32 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Release|x64.ActiveCfg = Release|x64 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Debug|Win32.Build.0 = Debug|Win32 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Debug|x64.Build.0 = Debug|x64 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Release|Win32.Build.0 = Release|Win32 + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595}.Release|x64.Build.0 = Release|x64 {19564640-CEE6-4921-ABA5-676ED79A36F6}.Debug|Win32.ActiveCfg = Debug|Win32 {19564640-CEE6-4921-ABA5-676ED79A36F6}.Debug|x64.ActiveCfg = Debug|x64 {19564640-CEE6-4921-ABA5-676ED79A36F6}.Release|Win32.ActiveCfg = Release|Win32 diff --git a/vsprojects/vcxproj/grpc_node_plugin/grpc_node_plugin.vcxproj b/vsprojects/vcxproj/grpc_node_plugin/grpc_node_plugin.vcxproj new file mode 100644 index 00000000000..faf93fd1365 --- /dev/null +++ b/vsprojects/vcxproj/grpc_node_plugin/grpc_node_plugin.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {57ABD9A2-CE8E-CCA7-5171-35C4534F3595} + true + $(SolutionDir)IntDir\$(MSBuildProjectName)\ + + + + v100 + + + v110 + + + v120 + + + v140 + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + grpc_node_plugin + + + grpc_node_plugin + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDebug + true + None + false + + + Console + true + false + + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDebug + true + None + false + + + Console + true + false + + + + + + NotUsing + Level3 + MaxSpeed + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + true + true + MultiThreaded + true + None + false + + + Console + true + false + true + true + + + + + + NotUsing + Level3 + MaxSpeed + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + true + true + MultiThreaded + true + None + false + + + Console + true + false + true + true + + + + + + + + + + {B6E81D84-2ACB-41B8-8781-493A944C7817} + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + diff --git a/vsprojects/vcxproj/grpc_node_plugin/grpc_node_plugin.vcxproj.filters b/vsprojects/vcxproj/grpc_node_plugin/grpc_node_plugin.vcxproj.filters new file mode 100644 index 00000000000..28b197f6f39 --- /dev/null +++ b/vsprojects/vcxproj/grpc_node_plugin/grpc_node_plugin.vcxproj.filters @@ -0,0 +1,18 @@ + + + + + src\compiler + + + + + + {089d5d6b-d438-dc98-b30f-bd608e3bbb78} + + + {1cc34440-c001-7578-c4d3-78f5d98fb602} + + + + diff --git a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj index 4c80226cbaf..a81d31768d7 100644 --- a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj +++ b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj @@ -160,6 +160,8 @@ + + @@ -173,6 +175,8 @@ + + diff --git a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters index fc125d911ef..b3d2dc2f3d5 100644 --- a/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters +++ b/vsprojects/vcxproj/grpc_plugin_support/grpc_plugin_support.vcxproj.filters @@ -7,6 +7,9 @@ src\compiler + + src\compiler + src\compiler @@ -50,6 +53,12 @@ src\compiler + + src\compiler + + + src\compiler + src\compiler