Merge remote-tracking branch 'upstream/master'
diff --git a/src/node/server.js b/src/node/server.js
index 7f3e025..2704c68 100644
--- a/src/node/server.js
+++ b/src/node/server.js
@@ -73,6 +73,7 @@
    * @param {Error} err The error object
    */
   function setStatus(err) {
+    console.log('Server setting status to', err);
     var code = grpc.status.INTERNAL;
     var details = 'Unknown Error';
 
diff --git a/src/node/surface_client.js b/src/node/surface_client.js
index 9c40b0a..acd2208 100644
--- a/src/node/surface_client.js
+++ b/src/node/surface_client.js
@@ -178,7 +178,7 @@
   /**
    * Make a unary request with this method on the given channel with the given
    * argument, callback, etc.
-   * @param {client.Channel} channel The channel on which to make the request
+   * @this {SurfaceClient} Client object. Must have a channel member.
    * @param {*} argument The argument to the call. Should be serializable with
    *     serialize
    * @param {function(?Error, value=)} callback The callback to for when the
@@ -189,8 +189,8 @@
    *     Defaults to infinite future
    * @return {EventEmitter} An event emitter for stream related events
    */
-  function makeUnaryRequest(channel, argument, callback, metadata, deadline) {
-    var stream = client.makeRequest(channel, method, metadata, deadline);
+  function makeUnaryRequest(argument, callback, metadata, deadline) {
+    var stream = client.makeRequest(this.channel, method, metadata, deadline);
     var emitter = new EventEmitter();
     forwardEvent(stream, emitter, 'status');
     forwardEvent(stream, emitter, 'metadata');
@@ -220,7 +220,7 @@
   /**
    * Make a client stream request with this method on the given channel with the
    * given callback, etc.
-   * @param {client.Channel} channel The channel on which to make the request
+   * @this {SurfaceClient} Client object. Must have a channel member.
    * @param {function(?Error, value=)} callback The callback to for when the
    *     response is received
    * @param {array=} metadata Array of metadata key/value pairs to add to the
@@ -229,8 +229,8 @@
    *     Defaults to infinite future
    * @return {EventEmitter} An event emitter for stream related events
    */
-  function makeClientStreamRequest(channel, callback, metadata, deadline) {
-    var stream = client.makeRequest(channel, method, metadata, deadline);
+  function makeClientStreamRequest(callback, metadata, deadline) {
+    var stream = client.makeRequest(this.channel, method, metadata, deadline);
     var obj_stream = new ClientWritableObjectStream(stream, serialize, {});
     stream.on('data', function forwardData(chunk) {
       try {
@@ -256,7 +256,7 @@
   /**
    * Make a server stream request with this method on the given channel with the
    * given argument, etc.
-   * @param {client.Channel} channel The channel on which to make the request
+   * @this {SurfaceClient} Client object. Must have a channel member.
    * @param {*} argument The argument to the call. Should be serializable with
    *     serialize
    * @param {array=} metadata Array of metadata key/value pairs to add to the
@@ -265,8 +265,8 @@
    *     Defaults to infinite future
    * @return {EventEmitter} An event emitter for stream related events
    */
-  function makeServerStreamRequest(channel, argument, metadata, deadline) {
-    var stream = client.makeRequest(channel, method, metadata, deadline);
+  function makeServerStreamRequest(argument, metadata, deadline) {
+    var stream = client.makeRequest(this.channel, method, metadata, deadline);
     var obj_stream = new ClientReadableObjectStream(stream, deserialize, {});
     stream.write(serialize(argument));
     stream.end();
@@ -287,15 +287,15 @@
 function makeBidiStreamRequestFunction(method, serialize, deserialize) {
   /**
    * Make a bidirectional stream request with this method on the given channel.
-   * @param {client.Channel} channel The channel on which to make the request
+   * @this {SurfaceClient} Client object. Must have a channel member.
    * @param {array=} metadata Array of metadata key/value pairs to add to the
    *     call
    * @param {(number|Date)=} deadline The deadline for processing this request.
    *     Defaults to infinite future
    * @return {EventEmitter} An event emitter for stream related events
    */
-  function makeBidiStreamRequest(channel, metadata, deadline) {
-    var stream = client.makeRequest(channel, method, metadata, deadline);
+  function makeBidiStreamRequest(metadata, deadline) {
+    var stream = client.makeRequest(this.channel, method, metadata, deadline);
     var obj_stream = new ClientBidiObjectStream(stream,
                                                 serialize,
                                                 deserialize,
@@ -306,29 +306,63 @@
 }
 
 /**
- * See docs for makeUnaryRequestFunction
+ * Map with short names for each of the requester maker functions. Used in
+ * makeClientConstructor
  */
-exports.makeUnaryRequestFunction = makeUnaryRequestFunction;
+var requester_makers = {
+  unary: makeUnaryRequestFunction,
+  server_stream: makeServerStreamRequestFunction,
+  client_stream: makeClientStreamRequestFunction,
+  bidi: makeBidiStreamRequestFunction
+}
 
 /**
- * See docs for makeClientStreamRequestFunction
+ * Creates a constructor for clients with a service defined by the methods
+ * object. The methods object has string keys and values of this form:
+ * {serialize: function, deserialize: function, client_stream: bool,
+ *  server_stream: bool}
+ * @param {!Object<string, Object>} methods Method descriptor for each method
+ *     the client should expose
+ * @param {string} prefix The prefix to prepend to each method name
+ * @return {function(string, Object)} New client constructor
  */
-exports.makeClientStreamRequestFunction = makeClientStreamRequestFunction;
+function makeClientConstructor(methods, prefix) {
+  /**
+   * Create a client with the given methods
+   * @constructor
+   * @param {string} address The address of the server to connect to
+   * @param {Object} options Options to pass to the underlying channel
+   */
+  function SurfaceClient(address, options) {
+    this.channel = new client.Channel(address, options);
+  }
 
-/**
- * See docs for makeServerStreamRequestFunction
- */
-exports.makeServerStreamRequestFunction = makeServerStreamRequestFunction;
+  _.each(methods, function(method, name) {
+    var method_type;
+    if (method.client_stream) {
+      if (method.server_stream) {
+        method_type = 'bidi';
+      } else {
+        method_type = 'client_stream';
+      }
+    } else {
+      if (method.server_stream) {
+        method_type = 'server_stream';
+      } else {
+        method_type = 'unary';
+      }
+    }
+    SurfaceClient.prototype[name] = requester_makers[method_type](
+        prefix + name,
+        method.serialize,
+        method.deserialize);
+  });
 
-/**
- * See docs for makeBidiStreamRequestFunction
- */
-exports.makeBidiStreamRequestFunction = makeBidiStreamRequestFunction;
+  return SurfaceClient;
+}
 
-/**
- * See docs for client.Channel
- */
-exports.Channel = client.Channel;
+exports.makeClientConstructor = makeClientConstructor;
+
 /**
  * See docs for client.status
  */
diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js
index f3697ac..591e865 100644
--- a/src/node/test/math_client_test.js
+++ b/src/node/test/math_client_test.js
@@ -32,13 +32,14 @@
  */
 
 var assert = require('assert');
-var client = require('../surface_client.js');
 var ProtoBuf = require('protobufjs');
 var port_picker = require('../port_picker');
 
 var builder = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto');
 var math = builder.build('math');
 
+var client = require('../surface_client.js');
+var makeConstructor = client.makeClientConstructor;
 /**
  * Get a function that deserializes a specific type of protobuf.
  * @param {function()} cls The constructor of the message type to deserialize
@@ -56,78 +57,60 @@
 }
 
 /**
- * Serialize an object to a buffer
- * @param {*} arg The object to serialize
- * @return {Buffer} The serialized object
+ * Get a function that serializes objects to a buffer by protobuf class.
+ * @param {function()} Cls The constructor of the message type to serialize
+ * @return {function(Cls):Buffer} The serialization function
  */
-function serialize(arg) {
-  return new Buffer(arg.encode().toBuffer());
+function serializeCls(Cls) {
+  /**
+   * Serialize an object to a Buffer
+   * @param {Object} arg The object to serialize
+   * @return {Buffer} The serialized object
+   */
+  return function serialize(arg) {
+    return new Buffer(new Cls(arg).encode().toBuffer());
+  };
 }
 
-/**
- * Sends a Div request on the channel.
- * @param {client.Channel} channel The channel on which to make the request
- * @param {DivArg} argument The argument to the call. Should be serializable
- *     with serialize
- * @param {function(?Error, value=)} The callback to for when the response is
- *     received
- * @param {array=} Array of metadata key/value pairs to add to the call
- * @param {(number|Date)=} deadline The deadline for processing this request.
- *     Defaults to infinite future
- * @return {EventEmitter} An event emitter for stream related events
- */
-var div = client.makeUnaryRequestFunction(
-    '/Math/Div',
-    serialize,
-    deserializeCls(math.DivReply));
-
-/**
- * Sends a Fib request on the channel.
- * @param {client.Channel} channel The channel on which to make the request
- * @param {*} argument The argument to the call. Should be serializable with
- *     serialize
- * @param {array=} Array of metadata key/value pairs to add to the call
- * @param {(number|Date)=} deadline The deadline for processing this request.
- *     Defaults to infinite future
- * @return {EventEmitter} An event emitter for stream related events
- */
-var fib = client.makeServerStreamRequestFunction(
-    '/Math/Fib',
-    serialize,
-    deserializeCls(math.Num));
-
-/**
- * Sends a Sum request on the channel.
- * @param {client.Channel} channel The channel on which to make the request
- * @param {function(?Error, value=)} The callback to for when the response is
- *     received
- * @param {array=} Array of metadata key/value pairs to add to the call
- * @param {(number|Date)=} deadline The deadline for processing this request.
- *     Defaults to infinite future
- * @return {EventEmitter} An event emitter for stream related events
- */
-var sum = client.makeClientStreamRequestFunction(
-    '/Math/Sum',
-    serialize,
-    deserializeCls(math.Num));
-
-/**
- * Sends a DivMany request on the channel.
- * @param {client.Channel} channel The channel on which to make the request
- * @param {array=} Array of metadata key/value pairs to add to the call
- * @param {(number|Date)=} deadline The deadline for processing this request.
- *     Defaults to infinite future
- * @return {EventEmitter} An event emitter for stream related events
- */
-var divMany = client.makeBidiStreamRequestFunction(
-    '/Math/DivMany',
-    serialize,
-    deserializeCls(math.DivReply));
+/* This function call creates a client constructor for clients that expose the
+ * four specified methods. This specifies how to serialize messages that the
+ * client sends and deserialize messages that the server sends, and whether the
+ * client or the server will send a stream of messages, for each method. This
+ * also specifies a prefix tha twill be added to method names when sending them
+ * on the wire. This function call and all of the preceding code in this file
+ * are intended to approximate what the generated code will look like for the
+ * math client */
+var MathClient = makeConstructor({
+  Div: {
+    serialize: serializeCls(math.DivArgs),
+    deserialize: deserializeCls(math.DivReply),
+    client_stream: false,
+    server_stream: false
+  },
+  Fib: {
+    serialize: serializeCls(math.FibArgs),
+    deserialize: deserializeCls(math.Num),
+    client_stream: false,
+    server_stream: true
+  },
+  Sum: {
+    serialize: serializeCls(math.Num),
+    deserialize: deserializeCls(math.Num),
+    client_stream: true,
+    server_stream: false
+  },
+  DivMany: {
+    serialize: serializeCls(math.DivArgs),
+    deserialize: deserializeCls(math.DivReply),
+    client_stream: true,
+    server_stream: true
+  }
+}, '/Math/');
 
 /**
  * Channel to use to make requests to a running server.
  */
-var channel;
+var math_client;
 
 /**
  * Server to test against
@@ -139,7 +122,7 @@
   before(function(done) {
     port_picker.nextAvailablePort(function(port) {
       server.bind(port).listen();
-      channel = new client.Channel(port);
+      math_client = new MathClient(port);
       done();
     });
   });
@@ -147,11 +130,11 @@
     server.shutdown();
   });
   it('should handle a single request', function(done) {
-    var arg = new math.DivArgs({dividend: 7, divisor: 4});
-    var call = div(channel, arg, function handleDivResult(err, value) {
+    var arg = {dividend: 7, divisor: 4};
+    var call = math_client.Div(arg, function handleDivResult(err, value) {
       assert.ifError(err);
-      assert.equal(value.get('quotient'), 1);
-      assert.equal(value.get('remainder'), 3);
+      assert.equal(value.quotient, 1);
+      assert.equal(value.remainder, 3);
     });
     call.on('status', function checkStatus(status) {
       assert.strictEqual(status.code, client.status.OK);
@@ -159,12 +142,11 @@
     });
   });
   it('should handle a server streaming request', function(done) {
-    var arg = new math.FibArgs({limit: 7});
-    var call = fib(channel, arg);
+    var call = math_client.Fib({limit: 7});
     var expected_results = [1, 1, 2, 3, 5, 8, 13];
     var next_expected = 0;
     call.on('data', function checkResponse(value) {
-      assert.equal(value.get('num'), expected_results[next_expected]);
+      assert.equal(value.num, expected_results[next_expected]);
       next_expected += 1;
     });
     call.on('status', function checkStatus(status) {
@@ -173,12 +155,12 @@
     });
   });
   it('should handle a client streaming request', function(done) {
-    var call = sum(channel, function handleSumResult(err, value) {
+    var call = math_client.Sum(function handleSumResult(err, value) {
       assert.ifError(err);
-      assert.equal(value.get('num'), 21);
+      assert.equal(value.num, 21);
     });
     for (var i = 0; i < 7; i++) {
-      call.write(new math.Num({'num': i}));
+      call.write({'num': i});
     }
     call.end();
     call.on('status', function checkStatus(status) {
@@ -188,17 +170,17 @@
   });
   it('should handle a bidirectional streaming request', function(done) {
     function checkResponse(index, value) {
-      assert.equal(value.get('quotient'), index);
-      assert.equal(value.get('remainder'), 1);
+      assert.equal(value.quotient, index);
+      assert.equal(value.remainder, 1);
     }
-    var call = divMany(channel);
+    var call = math_client.DivMany();
     var response_index = 0;
     call.on('data', function(value) {
       checkResponse(response_index, value);
       response_index += 1;
     });
     for (var i = 0; i < 7; i++) {
-      call.write(new math.DivArgs({dividend: 2 * i + 1, divisor: 2}));
+      call.write({dividend: 2 * i + 1, divisor: 2});
     }
     call.end();
     call.on('status', function checkStatus(status) {