aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/node
diff options
context:
space:
mode:
Diffstat (limited to 'src/node')
-rw-r--r--src/node/examples/route_guide.proto120
-rw-r--r--src/node/examples/route_guide_client.js231
-rw-r--r--src/node/examples/route_guide_db.json601
-rw-r--r--src/node/examples/route_guide_server.js249
-rw-r--r--src/node/interop/empty.proto6
-rw-r--r--src/node/package.json1
6 files changed, 1202 insertions, 6 deletions
diff --git a/src/node/examples/route_guide.proto b/src/node/examples/route_guide.proto
new file mode 100644
index 0000000000..442112823e
--- /dev/null
+++ b/src/node/examples/route_guide.proto
@@ -0,0 +1,120 @@
+// 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";
+
+option java_package = "io.grpc.examples";
+
+package examples;
+
+// Interface exported by the server.
+service RouteGuide {
+ // A simple RPC.
+ //
+ // Obtains the feature at a given position.
+ rpc GetFeature(Point) returns (Feature) {}
+
+ // A server-to-client streaming RPC.
+ //
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ rpc ListFeatures(Rectangle) returns (stream Feature) {}
+
+ // A client-to-server streaming RPC.
+ //
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ rpc RecordRoute(stream Point) returns (RouteSummary) {}
+
+ // A Bidirectional streaming RPC.
+ //
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
+}
+
+// Points are represented as latitude-longitude pairs in the E7 representation
+// (degrees multiplied by 10**7 and rounded to the nearest integer).
+// Latitudes should be in the range +/- 90 degrees and longitude should be in
+// the range +/- 180 degrees (inclusive).
+message Point {
+ optional int32 latitude = 1;
+ optional int32 longitude = 2;
+}
+
+// A latitude-longitude rectangle, represented as two diagonally opposite
+// points "lo" and "hi".
+message Rectangle {
+ // One corner of the rectangle.
+ optional Point lo = 1;
+
+ // The other corner of the rectangle.
+ optional Point hi = 2;
+}
+
+// A feature names something at a given point.
+//
+// If a feature could not be named, the name is empty.
+message Feature {
+ // The name of the feature.
+ optional string name = 1;
+
+ // The point where the feature is detected.
+ optional Point location = 2;
+}
+
+// A RouteNote is a message sent while at a given point.
+message RouteNote {
+ // The location from which the message is sent.
+ optional Point location = 1;
+
+ // The message to be sent.
+ optional string message = 2;
+}
+
+// A RouteSummary is received in response to a RecordRoute rpc.
+//
+// It contains the number of individual points received, the number of
+// detected features, and the total distance covered as the cumulative sum of
+// the distance between each point.
+message RouteSummary {
+ // The number of points received.
+ optional int32 point_count = 1;
+
+ // The number of known features passed while traversing the route.
+ optional int32 feature_count = 2;
+
+ // The distance covered in metres.
+ optional int32 distance = 3;
+
+ // The duration of the traversal in seconds.
+ optional int32 elapsed_time = 4;
+}
diff --git a/src/node/examples/route_guide_client.js b/src/node/examples/route_guide_client.js
new file mode 100644
index 0000000000..d4c083a6c5
--- /dev/null
+++ b/src/node/examples/route_guide_client.js
@@ -0,0 +1,231 @@
+// 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.
+
+var async = require('async');
+var fs = require('fs');
+var parseArgs = require('minimist');
+var path = require('path');
+var _ = require('underscore');
+var grpc = require('..');
+var examples = grpc.load(__dirname + '/route_guide.proto').examples;
+var client = new examples.RouteGuide('localhost:50051');
+
+var COORD_FACTOR = 1e7;
+
+/**
+ * Run the getFeature demo. Calls getFeature with a point known to have a
+ * feature and a point known not to have a feature.
+ * @param {function} callback Called when this demo is complete
+ */
+function runGetFeature(callback) {
+ var next = _.after(2, callback);
+ function featureCallback(error, feature) {
+ if (error) {
+ callback(error);
+ }
+ if (feature.name === '') {
+ console.log('Found no feature at ' +
+ feature.location.latitude/COORD_FACTOR + ', ' +
+ feature.location.longitude/COORD_FACTOR);
+ } else {
+ console.log('Found feature called "' + feature.name + '" at ' +
+ feature.location.latitude/COORD_FACTOR + ', ' +
+ feature.location.longitude/COORD_FACTOR);
+ }
+ next();
+ }
+ var point1 = {
+ latitude: 409146138,
+ longitude: -746188906
+ };
+ var point2 = {
+ latitude: 0,
+ longitude: 0
+ };
+ client.getFeature(point1, featureCallback);
+ client.getFeature(point2, featureCallback);
+}
+
+/**
+ * Run the listFeatures demo. Calls listFeatures with a rectangle containing all
+ * of the features in the pre-generated database. Prints each response as it
+ * comes in.
+ * @param {function} callback Called when this demo is complete
+ */
+function runListFeatures(callback) {
+ var rectangle = {
+ lo: {
+ latitude: 400000000,
+ longitude: -750000000
+ },
+ hi: {
+ latitude: 420000000,
+ longitude: -730000000
+ }
+ };
+ console.log('Looking for features between 40, -75 and 42, -73');
+ var call = client.listFeatures(rectangle);
+ call.on('data', function(feature) {
+ console.log('Found feature called "' + feature.name + '" at ' +
+ feature.location.latitude/COORD_FACTOR + ', ' +
+ feature.location.longitude/COORD_FACTOR);
+ });
+ call.on('end', callback);
+}
+
+/**
+ * Run the recordRoute demo. Sends several randomly chosen points from the
+ * pre-generated feature database with a variable delay in between. Prints the
+ * statistics when they are sent from the server.
+ * @param {function} callback Called when this demo is complete
+ */
+function runRecordRoute(callback) {
+ var argv = parseArgs(process.argv, {
+ string: 'db_path'
+ });
+ fs.readFile(path.resolve(argv.db_path), function(err, data) {
+ if (err) callback(err);
+ var feature_list = JSON.parse(data);
+
+ var num_points = 10;
+ var call = client.recordRoute(function(error, stats) {
+ if (error) {
+ callback(error);
+ }
+ console.log('Finished trip with', stats.point_count, 'points');
+ console.log('Passed', stats.feature_count, 'features');
+ console.log('Travelled', stats.distance, 'meters');
+ console.log('It took', stats.elapsed_time, 'seconds');
+ callback();
+ });
+ /**
+ * Constructs a function that asynchronously sends the given point and then
+ * delays sending its callback
+ * @param {number} lat The latitude to send
+ * @param {number} lng The longitude to send
+ * @return {function(function)} The function that sends the point
+ */
+ function pointSender(lat, lng) {
+ /**
+ * Sends the point, then calls the callback after a delay
+ * @param {function} callback Called when complete
+ */
+ return function(callback) {
+ console.log('Visiting point ' + lat/COORD_FACTOR + ', ' +
+ lng/COORD_FACTOR);
+ call.write({
+ latitude: lat,
+ longitude: lng
+ });
+ _.delay(callback, _.random(500, 1500));
+ };
+ }
+ var point_senders = [];
+ for (var i = 0; i < num_points; i++) {
+ var rand_point = feature_list[_.random(0, feature_list.length - 1)];
+ point_senders[i] = pointSender(rand_point.location.latitude,
+ rand_point.location.longitude);
+ }
+ async.series(point_senders, function() {
+ call.end();
+ });
+ });
+}
+
+/**
+ * Run the routeChat demo. Send some chat messages, and print any chat messages
+ * that are sent from the server.
+ * @param {function} callback Called when the demo is complete
+ */
+function runRouteChat(callback) {
+ var call = client.routeChat();
+ call.on('data', function(note) {
+ console.log('Got message "' + note.message + '" at ' +
+ note.location.latitude + ', ' + note.location.longitude);
+ });
+
+ call.on('end', callback);
+
+ var notes = [{
+ location: {
+ latitude: 0,
+ longitude: 0
+ },
+ message: 'First message'
+ }, {
+ location: {
+ latitude: 0,
+ longitude: 1
+ },
+ message: 'Second message'
+ }, {
+ location: {
+ latitude: 1,
+ longitude: 0
+ },
+ message: 'Third message'
+ }, {
+ location: {
+ latitude: 0,
+ longitude: 0
+ },
+ message: 'Fourth message'
+ }];
+ for (var i = 0; i < notes.length; i++) {
+ var note = notes[i];
+ console.log('Sending message "' + note.message + '" at ' +
+ note.location.latitude + ', ' + note.location.longitude);
+ call.write(note);
+ }
+ call.end();
+}
+
+/**
+ * Run all of the demos in order
+ */
+function main() {
+ async.series([
+ runGetFeature,
+ runListFeatures,
+ runRecordRoute,
+ runRouteChat
+ ]);
+}
+
+if (require.main === module) {
+ main();
+}
+
+exports.runGetFeature = runGetFeature;
+
+exports.runListFeatures = runListFeatures;
+
+exports.runRecordRoute = runRecordRoute;
+
+exports.runRouteChat = runRouteChat;
diff --git a/src/node/examples/route_guide_db.json b/src/node/examples/route_guide_db.json
new file mode 100644
index 0000000000..9d6a980ab7
--- /dev/null
+++ b/src/node/examples/route_guide_db.json
@@ -0,0 +1,601 @@
+[{
+ "location": {
+ "latitude": 407838351,
+ "longitude": -746143763
+ },
+ "name": "Patriots Path, Mendham, NJ 07945, USA"
+}, {
+ "location": {
+ "latitude": 408122808,
+ "longitude": -743999179
+ },
+ "name": "101 New Jersey 10, Whippany, NJ 07981, USA"
+}, {
+ "location": {
+ "latitude": 413628156,
+ "longitude": -749015468
+ },
+ "name": "U.S. 6, Shohola, PA 18458, USA"
+}, {
+ "location": {
+ "latitude": 419999544,
+ "longitude": -740371136
+ },
+ "name": "5 Conners Road, Kingston, NY 12401, USA"
+}, {
+ "location": {
+ "latitude": 414008389,
+ "longitude": -743951297
+ },
+ "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
+}, {
+ "location": {
+ "latitude": 419611318,
+ "longitude": -746524769
+ },
+ "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
+}, {
+ "location": {
+ "latitude": 406109563,
+ "longitude": -742186778
+ },
+ "name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
+}, {
+ "location": {
+ "latitude": 416802456,
+ "longitude": -742370183
+ },
+ "name": "352 South Mountain Road, Wallkill, NY 12589, USA"
+}, {
+ "location": {
+ "latitude": 412950425,
+ "longitude": -741077389
+ },
+ "name": "Bailey Turn Road, Harriman, NY 10926, USA"
+}, {
+ "location": {
+ "latitude": 412144655,
+ "longitude": -743949739
+ },
+ "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
+}, {
+ "location": {
+ "latitude": 415736605,
+ "longitude": -742847522
+ },
+ "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
+}, {
+ "location": {
+ "latitude": 413843930,
+ "longitude": -740501726
+ },
+ "name": "162 Merrill Road, Highland Mills, NY 10930, USA"
+}, {
+ "location": {
+ "latitude": 410873075,
+ "longitude": -744459023
+ },
+ "name": "Clinton Road, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 412346009,
+ "longitude": -744026814
+ },
+ "name": "16 Old Brook Lane, Warwick, NY 10990, USA"
+}, {
+ "location": {
+ "latitude": 402948455,
+ "longitude": -747903913
+ },
+ "name": "3 Drake Lane, Pennington, NJ 08534, USA"
+}, {
+ "location": {
+ "latitude": 406337092,
+ "longitude": -740122226
+ },
+ "name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
+}, {
+ "location": {
+ "latitude": 406421967,
+ "longitude": -747727624
+ },
+ "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
+}, {
+ "location": {
+ "latitude": 416318082,
+ "longitude": -749677716
+ },
+ "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
+}, {
+ "location": {
+ "latitude": 415301720,
+ "longitude": -748416257
+ },
+ "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
+}, {
+ "location": {
+ "latitude": 402647019,
+ "longitude": -747071791
+ },
+ "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
+}, {
+ "location": {
+ "latitude": 412567807,
+ "longitude": -741058078
+ },
+ "name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
+}, {
+ "location": {
+ "latitude": 416855156,
+ "longitude": -744420597
+ },
+ "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
+}, {
+ "location": {
+ "latitude": 404663628,
+ "longitude": -744820157
+ },
+ "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
+}, {
+ "location": {
+ "latitude": 407113723,
+ "longitude": -749746483
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 402133926,
+ "longitude": -743613249
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400273442,
+ "longitude": -741220915
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411236786,
+ "longitude": -744070769
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411633782,
+ "longitude": -746784970
+ },
+ "name": "211-225 Plains Road, Augusta, NJ 07822, USA"
+}, {
+ "location": {
+ "latitude": 415830701,
+ "longitude": -742952812
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 413447164,
+ "longitude": -748712898
+ },
+ "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
+}, {
+ "location": {
+ "latitude": 405047245,
+ "longitude": -749800722
+ },
+ "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
+}, {
+ "location": {
+ "latitude": 418858923,
+ "longitude": -746156790
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 417951888,
+ "longitude": -748484944
+ },
+ "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
+}, {
+ "location": {
+ "latitude": 407033786,
+ "longitude": -743977337
+ },
+ "name": "26 East 3rd Street, New Providence, NJ 07974, USA"
+}, {
+ "location": {
+ "latitude": 417548014,
+ "longitude": -740075041
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410395868,
+ "longitude": -744972325
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404615353,
+ "longitude": -745129803
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406589790,
+ "longitude": -743560121
+ },
+ "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
+}, {
+ "location": {
+ "latitude": 414653148,
+ "longitude": -740477477
+ },
+ "name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
+}, {
+ "location": {
+ "latitude": 405957808,
+ "longitude": -743255336
+ },
+ "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
+}, {
+ "location": {
+ "latitude": 411733589,
+ "longitude": -741648093
+ },
+ "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
+}, {
+ "location": {
+ "latitude": 412676291,
+ "longitude": -742606606
+ },
+ "name": "1270 Lakes Road, Monroe, NY 10950, USA"
+}, {
+ "location": {
+ "latitude": 409224445,
+ "longitude": -748286738
+ },
+ "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
+}, {
+ "location": {
+ "latitude": 406523420,
+ "longitude": -742135517
+ },
+ "name": "652 Garden Street, Elizabeth, NJ 07202, USA"
+}, {
+ "location": {
+ "latitude": 401827388,
+ "longitude": -740294537
+ },
+ "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
+}, {
+ "location": {
+ "latitude": 410564152,
+ "longitude": -743685054
+ },
+ "name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 408472324,
+ "longitude": -740726046
+ },
+ "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
+}, {
+ "location": {
+ "latitude": 412452168,
+ "longitude": -740214052
+ },
+ "name": "5 White Oak Lane, Stony Point, NY 10980, USA"
+}, {
+ "location": {
+ "latitude": 409146138,
+ "longitude": -746188906
+ },
+ "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
+}, {
+ "location": {
+ "latitude": 404701380,
+ "longitude": -744781745
+ },
+ "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 409642566,
+ "longitude": -746017679
+ },
+ "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
+}, {
+ "location": {
+ "latitude": 408031728,
+ "longitude": -748645385
+ },
+ "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
+}, {
+ "location": {
+ "latitude": 413700272,
+ "longitude": -742135189
+ },
+ "name": "367 Prospect Road, Chester, NY 10918, USA"
+}, {
+ "location": {
+ "latitude": 404310607,
+ "longitude": -740282632
+ },
+ "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
+}, {
+ "location": {
+ "latitude": 409319800,
+ "longitude": -746201391
+ },
+ "name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
+}, {
+ "location": {
+ "latitude": 406685311,
+ "longitude": -742108603
+ },
+ "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
+}, {
+ "location": {
+ "latitude": 419018117,
+ "longitude": -749142781
+ },
+ "name": "43 Dreher Road, Roscoe, NY 12776, USA"
+}, {
+ "location": {
+ "latitude": 412856162,
+ "longitude": -745148837
+ },
+ "name": "Swan Street, Pine Island, NY 10969, USA"
+}, {
+ "location": {
+ "latitude": 416560744,
+ "longitude": -746721964
+ },
+ "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
+}, {
+ "location": {
+ "latitude": 405314270,
+ "longitude": -749836354
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414219548,
+ "longitude": -743327440
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415534177,
+ "longitude": -742900616
+ },
+ "name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
+}, {
+ "location": {
+ "latitude": 406898530,
+ "longitude": -749127080
+ },
+ "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
+}, {
+ "location": {
+ "latitude": 407586880,
+ "longitude": -741670168
+ },
+ "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
+}, {
+ "location": {
+ "latitude": 400106455,
+ "longitude": -742870190
+ },
+ "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
+}, {
+ "location": {
+ "latitude": 400066188,
+ "longitude": -746793294
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418803880,
+ "longitude": -744102673
+ },
+ "name": "40 Mountain Road, Napanoch, NY 12458, USA"
+}, {
+ "location": {
+ "latitude": 414204288,
+ "longitude": -747895140
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414777405,
+ "longitude": -740615601
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415464475,
+ "longitude": -747175374
+ },
+ "name": "48 North Road, Forestburgh, NY 12777, USA"
+}, {
+ "location": {
+ "latitude": 404062378,
+ "longitude": -746376177
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405688272,
+ "longitude": -749285130
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400342070,
+ "longitude": -748788996
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401809022,
+ "longitude": -744157964
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404226644,
+ "longitude": -740517141
+ },
+ "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
+}, {
+ "location": {
+ "latitude": 410322033,
+ "longitude": -747871659
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 407100674,
+ "longitude": -747742727
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418811433,
+ "longitude": -741718005
+ },
+ "name": "213 Bush Road, Stone Ridge, NY 12484, USA"
+}, {
+ "location": {
+ "latitude": 415034302,
+ "longitude": -743850945
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411349992,
+ "longitude": -743694161
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404839914,
+ "longitude": -744759616
+ },
+ "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 414638017,
+ "longitude": -745957854
+ },
+ "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
+}, {
+ "location": {
+ "latitude": 412127800,
+ "longitude": -740173578
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401263460,
+ "longitude": -747964303
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 412843391,
+ "longitude": -749086026
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418512773,
+ "longitude": -743067823
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404318328,
+ "longitude": -740835638
+ },
+ "name": "42-102 Main Street, Belford, NJ 07718, USA"
+}, {
+ "location": {
+ "latitude": 419020746,
+ "longitude": -741172328
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404080723,
+ "longitude": -746119569
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401012643,
+ "longitude": -744035134
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404306372,
+ "longitude": -741079661
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 403966326,
+ "longitude": -748519297
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405002031,
+ "longitude": -748407866
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 409532885,
+ "longitude": -742200683
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 416851321,
+ "longitude": -742674555
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406411633,
+ "longitude": -741722051
+ },
+ "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
+}, {
+ "location": {
+ "latitude": 413069058,
+ "longitude": -744597778
+ },
+ "name": "261 Van Sickle Road, Goshen, NY 10924, USA"
+}, {
+ "location": {
+ "latitude": 418465462,
+ "longitude": -746859398
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411733222,
+ "longitude": -744228360
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410248224,
+ "longitude": -747127767
+ },
+ "name": "3 Hasta Way, Newton, NJ 07860, USA"
+}]
diff --git a/src/node/examples/route_guide_server.js b/src/node/examples/route_guide_server.js
new file mode 100644
index 0000000000..bc9ed25101
--- /dev/null
+++ b/src/node/examples/route_guide_server.js
@@ -0,0 +1,249 @@
+// 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.
+
+var fs = require('fs');
+var parseArgs = require('minimist');
+var path = require('path');
+var _ = require('underscore');
+var grpc = require('..');
+var examples = grpc.load(__dirname + '/route_guide.proto').examples;
+
+var Server = grpc.buildServer([examples.RouteGuide.service]);
+
+var COORD_FACTOR = 1e7;
+
+/**
+ * For simplicity, a point is a record type that looks like
+ * {latitude: number, longitude: number}, and a feature is a record type that
+ * looks like {name: string, location: point}. feature objects with name===''
+ * are points with no feature.
+ */
+
+/**
+ * List of feature objects at points that have been requested so far.
+ */
+var feature_list = [];
+
+/**
+ * Get a feature object at the given point, or creates one if it does not exist.
+ * @param {point} point The point to check
+ * @return {feature} The feature object at the point. Note that an empty name
+ * indicates no feature
+ */
+function checkFeature(point) {
+ var feature;
+ // Check if there is already a feature object for the given point
+ for (var i = 0; i < feature_list.length; i++) {
+ feature = feature_list[i];
+ if (feature.location.latitude === point.latitude &&
+ feature.location.longitude === point.longitude) {
+ return feature;
+ }
+ }
+ var name = '';
+ feature = {
+ name: name,
+ location: point
+ };
+ return feature;
+}
+
+/**
+ * getFeature request handler. Gets a request with a point, and responds with a
+ * feature object indicating whether there is a feature at that point.
+ * @param {EventEmitter} call Call object for the handler to process
+ * @param {function(Error, feature)} callback Response callback
+ */
+function getFeature(call, callback) {
+ callback(null, checkFeature(call.request));
+}
+
+/**
+ * listFeatures request handler. Gets a request with two points, and responds
+ * with a stream of all features in the bounding box defined by those points.
+ * @param {Writable} call Writable stream for responses with an additional
+ * request property for the request value.
+ */
+function listFeatures(call) {
+ var lo = call.request.lo;
+ var hi = call.request.hi;
+ var left = _.min([lo.longitude, hi.longitude]);
+ var right = _.max([lo.longitude, hi.longitude]);
+ var top = _.max([lo.latitude, hi.latitude]);
+ var bottom = _.min([lo.latitude, hi.latitude]);
+ // For each feature, check if it is in the given bounding box
+ _.each(feature_list, function(feature) {
+ if (feature.name === '') {
+ return;
+ }
+ if (feature.location.longitude >= left &&
+ feature.location.longitude <= right &&
+ feature.location.latitude >= bottom &&
+ feature.location.latitude <= top) {
+ call.write(feature);
+ }
+ });
+ call.end();
+}
+
+/**
+ * Calculate the distance between two points using the "haversine" formula.
+ * This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
+ * @param start The starting point
+ * @param end The end point
+ * @return The distance between the points in meters
+ */
+function getDistance(start, end) {
+ function toRadians(num) {
+ return num * Math.PI / 180;
+ }
+ var lat1 = start.latitude / COORD_FACTOR;
+ var lat2 = end.latitude / COORD_FACTOR;
+ var lon1 = start.longitude / COORD_FACTOR;
+ var lon2 = end.longitude / COORD_FACTOR;
+ var R = 6371000; // metres
+ var φ1 = toRadians(lat1);
+ var φ2 = toRadians(lat2);
+ var Δφ = toRadians(lat2-lat1);
+ var Δλ = toRadians(lon2-lon1);
+
+ var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
+ Math.cos(φ1) * Math.cos(φ2) *
+ Math.sin(Δλ/2) * Math.sin(Δλ/2);
+ var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
+
+ return R * c;
+}
+
+/**
+ * recordRoute handler. Gets a stream of points, and responds with statistics
+ * about the "trip": number of points, number of known features visited, total
+ * distance traveled, and total time spent.
+ * @param {Readable} call The request point stream.
+ * @param {function(Error, routeSummary)} callback The callback to pass the
+ * response to
+ */
+function recordRoute(call, callback) {
+ var point_count = 0;
+ var feature_count = 0;
+ var distance = 0;
+ var previous = null;
+ // Start a timer
+ var start_time = process.hrtime();
+ call.on('data', function(point) {
+ point_count += 1;
+ if (checkFeature(point).name !== '') {
+ feature_count += 1;
+ }
+ /* For each point after the first, add the incremental distance from the
+ * previous point to the total distance value */
+ if (previous != null) {
+ distance += getDistance(previous, point);
+ }
+ previous = point;
+ });
+ call.on('end', function() {
+ callback(null, {
+ point_count: point_count,
+ feature_count: feature_count,
+ // Cast the distance to an integer
+ distance: distance|0,
+ // End the timer
+ elapsed_time: process.hrtime(start_time)[0]
+ });
+ });
+}
+
+var route_notes = {};
+
+/**
+ * Turn the point into a dictionary key.
+ * @param {point} point The point to use
+ * @return {string} The key for an object
+ */
+function pointKey(point) {
+ return point.latitude + ' ' + point.longitude;
+}
+
+/**
+ * routeChat handler. Receives a stream of message/location pairs, and responds
+ * with a stream of all previous messages at each of those locations.
+ * @param {Duplex} call The stream for incoming and outgoing messages
+ */
+function routeChat(call) {
+ call.on('data', function(note) {
+ var key = pointKey(note.location);
+ /* For each note sent, respond with all previous notes that correspond to
+ * the same point */
+ if (route_notes.hasOwnProperty(key)) {
+ _.each(route_notes[key], function(note) {
+ call.write(note);
+ });
+ } else {
+ route_notes[key] = [];
+ }
+ // Then add the new note to the list
+ route_notes[key].push(JSON.parse(JSON.stringify(note)));
+ });
+ call.on('end', function() {
+ call.end();
+ });
+}
+
+/**
+ * Get a new server with the handler functions in this file bound to the methods
+ * it serves.
+ * @return {Server} The new server object
+ */
+function getServer() {
+ return new Server({
+ 'examples.RouteGuide' : {
+ getFeature: getFeature,
+ listFeatures: listFeatures,
+ recordRoute: recordRoute,
+ routeChat: routeChat
+ }
+ });
+}
+
+if (require.main === module) {
+ // If this is run as a script, start a server on an unused port
+ var routeServer = getServer();
+ routeServer.bind('0.0.0.0:50051');
+ var argv = parseArgs(process.argv, {
+ string: 'db_path'
+ });
+ fs.readFile(path.resolve(argv.db_path), function(err, data) {
+ if (err) throw err;
+ feature_list = JSON.parse(data);
+ routeServer.listen();
+ });
+}
+
+exports.getServer = getServer;
diff --git a/src/node/interop/empty.proto b/src/node/interop/empty.proto
index f66a108c19..4295a0a960 100644
--- a/src/node/interop/empty.proto
+++ b/src/node/interop/empty.proto
@@ -40,10 +40,4 @@ package grpc.testing;
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
// };
//
-// MOE:begin_strip
-// The difference between this one and net/rpc/empty-message.proto is that
-// 1) The generated message here is in proto2 C++ API.
-// 2) The proto2.Empty has minimum dependencies
-// (no message_set or net/rpc dependencies)
-// MOE:end_strip
message Empty {}
diff --git a/src/node/package.json b/src/node/package.json
index fe51c4736e..e6ac550554 100644
--- a/src/node/package.json
+++ b/src/node/package.json
@@ -15,6 +15,7 @@
"underscore.string": "^3.0.0"
},
"devDependencies": {
+ "async": "^0.9.0",
"googleauth": "google/google-auth-library-nodejs",
"minimist": "^1.1.0",
"mocha": "~1.21.0",