src_client.js.html 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: src/client.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: src/client.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>/**
  20. * @license
  21. * Copyright 2015 gRPC authors.
  22. *
  23. * Licensed under the Apache License, Version 2.0 (the "License");
  24. * you may not use this file except in compliance with the License.
  25. * You may obtain a copy of the License at
  26. *
  27. * http://www.apache.org/licenses/LICENSE-2.0
  28. *
  29. * Unless required by applicable law or agreed to in writing, software
  30. * distributed under the License is distributed on an "AS IS" BASIS,
  31. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  32. * See the License for the specific language governing permissions and
  33. * limitations under the License.
  34. *
  35. */
  36. /**
  37. * Client module
  38. *
  39. * This module contains the factory method for creating Client classes, and the
  40. * method calling code for all types of methods.
  41. *
  42. * @example &lt;caption>Create a client and call a method on it&lt;/caption>
  43. *
  44. * var proto_obj = grpc.load(proto_file_path);
  45. * var Client = proto_obj.package.subpackage.ServiceName;
  46. * var client = new Client(server_address, client_credentials);
  47. * var call = client.unaryMethod(arguments, callback);
  48. */
  49. 'use strict';
  50. var _ = require('lodash');
  51. var arguejs = require('arguejs');
  52. var grpc = require('./grpc_extension');
  53. var common = require('./common');
  54. var Metadata = require('./metadata');
  55. var constants = require('./constants');
  56. var EventEmitter = require('events').EventEmitter;
  57. var stream = require('stream');
  58. var Readable = stream.Readable;
  59. var Writable = stream.Writable;
  60. var Duplex = stream.Duplex;
  61. var util = require('util');
  62. var version = require('../../../package.json').version;
  63. /**
  64. * Initial response metadata sent by the server when it starts processing the
  65. * call
  66. * @event grpc~ClientUnaryCall#metadata
  67. * @type {grpc.Metadata}
  68. */
  69. /**
  70. * Status of the call when it has completed.
  71. * @event grpc~ClientUnaryCall#status
  72. * @type grpc~StatusObject
  73. */
  74. util.inherits(ClientUnaryCall, EventEmitter);
  75. /**
  76. * An EventEmitter. Used for unary calls.
  77. * @constructor grpc~ClientUnaryCall
  78. * @extends external:EventEmitter
  79. * @param {grpc.internal~Call} call The call object associated with the request
  80. */
  81. function ClientUnaryCall(call) {
  82. EventEmitter.call(this);
  83. this.call = call;
  84. }
  85. util.inherits(ClientWritableStream, Writable);
  86. /**
  87. * A stream that the client can write to. Used for calls that are streaming from
  88. * the client side.
  89. * @constructor grpc~ClientWritableStream
  90. * @extends external:Writable
  91. * @borrows grpc~ClientUnaryCall#cancel as grpc~ClientWritableStream#cancel
  92. * @borrows grpc~ClientUnaryCall#getPeer as grpc~ClientWritableStream#getPeer
  93. * @borrows grpc~ClientUnaryCall#event:metadata as
  94. * grpc~ClientWritableStream#metadata
  95. * @borrows grpc~ClientUnaryCall#event:status as
  96. * grpc~ClientWritableStream#status
  97. * @param {grpc.internal~Call} call The call object to send data with
  98. * @param {grpc~serialize=} [serialize=identity] Serialization
  99. * function for writes.
  100. */
  101. function ClientWritableStream(call, serialize) {
  102. Writable.call(this, {objectMode: true});
  103. this.call = call;
  104. this.serialize = common.wrapIgnoreNull(serialize);
  105. this.on('finish', function() {
  106. var batch = {};
  107. batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
  108. call.startBatch(batch, function() {});
  109. });
  110. }
  111. /**
  112. * Write a message to the request stream. If serializing the argument fails,
  113. * the call will be cancelled and the stream will end with an error.
  114. * @name grpc~ClientWritableStream#write
  115. * @kind function
  116. * @override
  117. * @param {*} message The message to write. Must be a valid argument to the
  118. * serialize function of the corresponding method
  119. * @param {grpc.writeFlags} flags Flags to modify how the message is written
  120. * @param {Function} callback Callback for when this chunk of data is flushed
  121. * @return {boolean} As defined for [Writable]{@link external:Writable}
  122. */
  123. /**
  124. * Attempt to write the given chunk. Calls the callback when done. This is an
  125. * implementation of a method needed for implementing stream.Writable.
  126. * @private
  127. * @param {*} chunk The chunk to write
  128. * @param {grpc.writeFlags} encoding Used to pass write flags
  129. * @param {function(Error=)} callback Called when the write is complete
  130. */
  131. function _write(chunk, encoding, callback) {
  132. /* jshint validthis: true */
  133. var batch = {};
  134. var message;
  135. var self = this;
  136. if (this.writeFailed) {
  137. /* Once a write fails, just call the callback immediately to let the caller
  138. flush any pending writes. */
  139. setImmediate(callback);
  140. return;
  141. }
  142. try {
  143. message = this.serialize(chunk);
  144. } catch (e) {
  145. /* Sending this error to the server and emitting it immediately on the
  146. client may put the call in a slightly weird state on the client side,
  147. but passing an object that causes a serialization failure is a misuse
  148. of the API anyway, so that's OK. The primary purpose here is to give the
  149. programmer a useful error and to stop the stream properly */
  150. this.call.cancelWithStatus(constants.status.INTERNAL,
  151. 'Serialization failure');
  152. callback(e);
  153. return;
  154. }
  155. if (_.isFinite(encoding)) {
  156. /* Attach the encoding if it is a finite number. This is the closest we
  157. * can get to checking that it is valid flags */
  158. message.grpcWriteFlags = encoding;
  159. }
  160. batch[grpc.opType.SEND_MESSAGE] = message;
  161. this.call.startBatch(batch, function(err, event) {
  162. if (err) {
  163. /* Assume that the call is complete and that writing failed because a
  164. status was received. In that case, set a flag to discard all future
  165. writes */
  166. self.writeFailed = true;
  167. }
  168. callback();
  169. });
  170. }
  171. ClientWritableStream.prototype._write = _write;
  172. util.inherits(ClientReadableStream, Readable);
  173. /**
  174. * A stream that the client can read from. Used for calls that are streaming
  175. * from the server side.
  176. * @constructor grpc~ClientReadableStream
  177. * @extends external:Readable
  178. * @borrows grpc~ClientUnaryCall#cancel as grpc~ClientReadableStream#cancel
  179. * @borrows grpc~ClientUnaryCall#getPeer as grpc~ClientReadableStream#getPeer
  180. * @borrows grpc~ClientUnaryCall#event:metadata as
  181. * grpc~ClientReadableStream#metadata
  182. * @borrows grpc~ClientUnaryCall#event:status as
  183. * grpc~ClientReadableStream#status
  184. * @param {grpc.internal~Call} call The call object to read data with
  185. * @param {grpc~deserialize=} [deserialize=identity]
  186. * Deserialization function for reads
  187. */
  188. function ClientReadableStream(call, deserialize) {
  189. Readable.call(this, {objectMode: true});
  190. this.call = call;
  191. this.finished = false;
  192. this.reading = false;
  193. this.deserialize = common.wrapIgnoreNull(deserialize);
  194. /* Status generated from reading messages from the server. Overrides the
  195. * status from the server if not OK */
  196. this.read_status = null;
  197. /* Status received from the server. */
  198. this.received_status = null;
  199. }
  200. /**
  201. * Called when all messages from the server have been processed. The status
  202. * parameter indicates that the call should end with that status. status
  203. * defaults to OK if not provided.
  204. * @param {Object!} status The status that the call should end with
  205. * @private
  206. */
  207. function _readsDone(status) {
  208. /* jshint validthis: true */
  209. if (!status) {
  210. status = {code: constants.status.OK, details: 'OK'};
  211. }
  212. if (status.code !== constants.status.OK) {
  213. this.call.cancelWithStatus(status.code, status.details);
  214. }
  215. this.finished = true;
  216. this.read_status = status;
  217. this._emitStatusIfDone();
  218. }
  219. ClientReadableStream.prototype._readsDone = _readsDone;
  220. /**
  221. * Called to indicate that we have received a status from the server.
  222. * @private
  223. */
  224. function _receiveStatus(status) {
  225. /* jshint validthis: true */
  226. this.received_status = status;
  227. this._emitStatusIfDone();
  228. }
  229. ClientReadableStream.prototype._receiveStatus = _receiveStatus;
  230. /**
  231. * If we have both processed all incoming messages and received the status from
  232. * the server, emit the status. Otherwise, do nothing.
  233. * @private
  234. */
  235. function _emitStatusIfDone() {
  236. /* jshint validthis: true */
  237. var status;
  238. if (this.read_status &amp;&amp; this.received_status) {
  239. if (this.read_status.code !== constants.status.OK) {
  240. status = this.read_status;
  241. } else {
  242. status = this.received_status;
  243. }
  244. if (status.code === constants.status.OK) {
  245. this.push(null);
  246. } else {
  247. var error = new Error(status.details);
  248. error.code = status.code;
  249. error.metadata = status.metadata;
  250. this.emit('error', error);
  251. }
  252. this.emit('status', status);
  253. }
  254. }
  255. ClientReadableStream.prototype._emitStatusIfDone = _emitStatusIfDone;
  256. /**
  257. * Read the next object from the stream.
  258. * @private
  259. * @param {*} size Ignored because we use objectMode=true
  260. */
  261. function _read(size) {
  262. /* jshint validthis: true */
  263. var self = this;
  264. /**
  265. * Callback to be called when a READ event is received. Pushes the data onto
  266. * the read queue and starts reading again if applicable
  267. * @param {grpc.Event} event READ event object
  268. */
  269. function readCallback(err, event) {
  270. if (err) {
  271. // Something has gone wrong. Stop reading and wait for status
  272. self.finished = true;
  273. self._readsDone();
  274. return;
  275. }
  276. var data = event.read;
  277. var deserialized;
  278. try {
  279. deserialized = self.deserialize(data);
  280. } catch (e) {
  281. self._readsDone({code: constants.status.INTERNAL,
  282. details: 'Failed to parse server response'});
  283. return;
  284. }
  285. if (data === null) {
  286. self._readsDone();
  287. return;
  288. }
  289. if (self.push(deserialized) &amp;&amp; data !== null) {
  290. var read_batch = {};
  291. read_batch[grpc.opType.RECV_MESSAGE] = true;
  292. self.call.startBatch(read_batch, readCallback);
  293. } else {
  294. self.reading = false;
  295. }
  296. }
  297. if (self.finished) {
  298. self.push(null);
  299. } else {
  300. if (!self.reading) {
  301. self.reading = true;
  302. var read_batch = {};
  303. read_batch[grpc.opType.RECV_MESSAGE] = true;
  304. self.call.startBatch(read_batch, readCallback);
  305. }
  306. }
  307. }
  308. ClientReadableStream.prototype._read = _read;
  309. util.inherits(ClientDuplexStream, Duplex);
  310. /**
  311. * A stream that the client can read from or write to. Used for calls with
  312. * duplex streaming.
  313. * @constructor grpc~ClientDuplexStream
  314. * @extends external:Duplex
  315. * @borrows grpc~ClientUnaryCall#cancel as grpc~ClientDuplexStream#cancel
  316. * @borrows grpc~ClientUnaryCall#getPeer as grpc~ClientDuplexStream#getPeer
  317. * @borrows grpc~ClientWritableStream#write as grpc~ClientDuplexStream#write
  318. * @borrows grpc~ClientUnaryCall#event:metadata as
  319. * grpc~ClientDuplexStream#metadata
  320. * @borrows grpc~ClientUnaryCall#event:status as
  321. * grpc~ClientDuplexStream#status
  322. * @param {grpc.internal~Call} call Call object to proxy
  323. * @param {grpc~serialize=} [serialize=identity] Serialization
  324. * function for requests
  325. * @param {grpc~deserialize=} [deserialize=identity]
  326. * Deserialization function for responses
  327. */
  328. function ClientDuplexStream(call, serialize, deserialize) {
  329. Duplex.call(this, {objectMode: true});
  330. this.serialize = common.wrapIgnoreNull(serialize);
  331. this.deserialize = common.wrapIgnoreNull(deserialize);
  332. this.call = call;
  333. /* Status generated from reading messages from the server. Overrides the
  334. * status from the server if not OK */
  335. this.read_status = null;
  336. /* Status received from the server. */
  337. this.received_status = null;
  338. this.on('finish', function() {
  339. var batch = {};
  340. batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
  341. call.startBatch(batch, function() {});
  342. });
  343. }
  344. ClientDuplexStream.prototype._readsDone = _readsDone;
  345. ClientDuplexStream.prototype._receiveStatus = _receiveStatus;
  346. ClientDuplexStream.prototype._emitStatusIfDone = _emitStatusIfDone;
  347. ClientDuplexStream.prototype._read = _read;
  348. ClientDuplexStream.prototype._write = _write;
  349. /**
  350. * Cancel the ongoing call. Results in the call ending with a CANCELLED status,
  351. * unless it has already ended with some other status.
  352. * @alias grpc~ClientUnaryCall#cancel
  353. */
  354. function cancel() {
  355. /* jshint validthis: true */
  356. this.call.cancel();
  357. }
  358. ClientUnaryCall.prototype.cancel = cancel;
  359. ClientReadableStream.prototype.cancel = cancel;
  360. ClientWritableStream.prototype.cancel = cancel;
  361. ClientDuplexStream.prototype.cancel = cancel;
  362. /**
  363. * Get the endpoint this call/stream is connected to.
  364. * @return {string} The URI of the endpoint
  365. * @alias grpc~ClientUnaryCall#getPeer
  366. */
  367. function getPeer() {
  368. /* jshint validthis: true */
  369. return this.call.getPeer();
  370. }
  371. ClientUnaryCall.prototype.getPeer = getPeer;
  372. ClientReadableStream.prototype.getPeer = getPeer;
  373. ClientWritableStream.prototype.getPeer = getPeer;
  374. ClientDuplexStream.prototype.getPeer = getPeer;
  375. /**
  376. * Any client call type
  377. * @typedef {(ClientUnaryCall|ClientReadableStream|
  378. * ClientWritableStream|ClientDuplexStream)}
  379. * grpc.Client~Call
  380. */
  381. /**
  382. * Options that can be set on a call.
  383. * @typedef {Object} grpc.Client~CallOptions
  384. * @property {grpc~Deadline} deadline The deadline for the entire call to
  385. * complete.
  386. * @property {string} host Server hostname to set on the call. Only meaningful
  387. * if different from the server address used to construct the client.
  388. * @property {grpc.Client~Call} parent Parent call. Used in servers when
  389. * making a call as part of the process of handling a call. Used to
  390. * propagate some information automatically, as specified by
  391. * propagate_flags.
  392. * @property {number} propagate_flags Indicates which properties of a parent
  393. * call should propagate to this call. Bitwise combination of flags in
  394. * {@link grpc.propagate}.
  395. * @property {grpc.credentials~CallCredentials} credentials The credentials that
  396. * should be used to make this particular call.
  397. */
  398. /**
  399. * Get a call object built with the provided options.
  400. * @access private
  401. * @param {grpc.Client~CallOptions=} options Options object.
  402. */
  403. function getCall(channel, method, options) {
  404. var deadline;
  405. var host;
  406. var parent;
  407. var propagate_flags;
  408. var credentials;
  409. if (options) {
  410. deadline = options.deadline;
  411. host = options.host;
  412. parent = _.get(options, 'parent.call');
  413. propagate_flags = options.propagate_flags;
  414. credentials = options.credentials;
  415. }
  416. if (deadline === undefined) {
  417. deadline = Infinity;
  418. }
  419. var call = new grpc.Call(channel, method, deadline, host,
  420. parent, propagate_flags);
  421. if (credentials) {
  422. call.setCredentials(credentials);
  423. }
  424. return call;
  425. }
  426. /**
  427. * A generic gRPC client. Primarily useful as a base class for generated clients
  428. * @memberof grpc
  429. * @constructor
  430. * @param {string} address Server address to connect to
  431. * @param {grpc~ChannelCredentials} credentials Credentials to use to connect to
  432. * the server
  433. * @param {Object} options Options to apply to channel creation
  434. */
  435. function Client(address, credentials, options) {
  436. if (!options) {
  437. options = {};
  438. }
  439. /* Append the grpc-node user agent string after the application user agent
  440. * string, and put the combination at the beginning of the user agent string
  441. */
  442. if (options['grpc.primary_user_agent']) {
  443. options['grpc.primary_user_agent'] += ' ';
  444. } else {
  445. options['grpc.primary_user_agent'] = '';
  446. }
  447. options['grpc.primary_user_agent'] += 'grpc-node/' + version;
  448. /* Private fields use $ as a prefix instead of _ because it is an invalid
  449. * prefix of a method name */
  450. this.$channel = new grpc.Channel(address, credentials, options);
  451. }
  452. exports.Client = Client;
  453. /**
  454. * @callback grpc.Client~requestCallback
  455. * @param {?grpc~ServiceError} error The error, if the call
  456. * failed
  457. * @param {*} value The response value, if the call succeeded
  458. */
  459. /**
  460. * Make a unary request to the given method, using the given serialize
  461. * and deserialize functions, with the given argument.
  462. * @param {string} method The name of the method to request
  463. * @param {grpc~serialize} serialize The serialization function for
  464. * inputs
  465. * @param {grpc~deserialize} deserialize The deserialization
  466. * function for outputs
  467. * @param {*} argument The argument to the call. Should be serializable with
  468. * serialize
  469. * @param {grpc.Metadata=} metadata Metadata to add to the call
  470. * @param {grpc.Client~CallOptions=} options Options map
  471. * @param {grpc.Client~requestCallback} callback The callback to
  472. * for when the response is received
  473. * @return {grpc~ClientUnaryCall} An event emitter for stream related events
  474. */
  475. Client.prototype.makeUnaryRequest = function(method, serialize, deserialize,
  476. argument, metadata, options,
  477. callback) {
  478. /* While the arguments are listed in the function signature, those variables
  479. * are not used directly. Instead, ArgueJS processes the arguments
  480. * object. This allows for simple handling of optional arguments in the
  481. * middle of the argument list, and also provides type checking. */
  482. var args = arguejs({method: String, serialize: Function,
  483. deserialize: Function,
  484. argument: null, metadata: [Metadata, new Metadata()],
  485. options: [Object], callback: Function}, arguments);
  486. var call = getCall(this.$channel, method, args.options);
  487. var emitter = new ClientUnaryCall(call);
  488. metadata = args.metadata.clone();
  489. var client_batch = {};
  490. var message = serialize(args.argument);
  491. if (args.options) {
  492. message.grpcWriteFlags = args.options.flags;
  493. }
  494. client_batch[grpc.opType.SEND_INITIAL_METADATA] =
  495. metadata._getCoreRepresentation();
  496. client_batch[grpc.opType.SEND_MESSAGE] = message;
  497. client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
  498. client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
  499. client_batch[grpc.opType.RECV_MESSAGE] = true;
  500. client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
  501. call.startBatch(client_batch, function(err, response) {
  502. response.status.metadata = Metadata._fromCoreRepresentation(
  503. response.status.metadata);
  504. var status = response.status;
  505. var error;
  506. var deserialized;
  507. emitter.emit('metadata', Metadata._fromCoreRepresentation(
  508. response.metadata));
  509. if (status.code === constants.status.OK) {
  510. if (err) {
  511. // Got a batch error, but OK status. Something went wrong
  512. args.callback(err);
  513. return;
  514. } else {
  515. try {
  516. deserialized = deserialize(response.read);
  517. } catch (e) {
  518. /* Change status to indicate bad server response. This will result
  519. * in passing an error to the callback */
  520. status = {
  521. code: constants.status.INTERNAL,
  522. details: 'Failed to parse server response'
  523. };
  524. }
  525. }
  526. }
  527. if (status.code !== constants.status.OK) {
  528. error = new Error(status.details);
  529. error.code = status.code;
  530. error.metadata = status.metadata;
  531. args.callback(error);
  532. } else {
  533. args.callback(null, deserialized);
  534. }
  535. emitter.emit('status', status);
  536. });
  537. return emitter;
  538. };
  539. /**
  540. * Make a client stream request to the given method, using the given serialize
  541. * and deserialize functions, with the given argument.
  542. * @param {string} method The name of the method to request
  543. * @param {grpc~serialize} serialize The serialization function for
  544. * inputs
  545. * @param {grpc~deserialize} deserialize The deserialization
  546. * function for outputs
  547. * @param {grpc.Metadata=} metadata Array of metadata key/value pairs to add to
  548. * the call
  549. * @param {grpc.Client~CallOptions=} options Options map
  550. * @param {grpc.Client~requestCallback} callback The callback to for when the
  551. * response is received
  552. * @return {grpc~ClientWritableStream} An event emitter for stream related
  553. * events
  554. */
  555. Client.prototype.makeClientStreamRequest = function(method, serialize,
  556. deserialize, metadata,
  557. options, callback) {
  558. /* While the arguments are listed in the function signature, those variables
  559. * are not used directly. Instead, ArgueJS processes the arguments
  560. * object. This allows for simple handling of optional arguments in the
  561. * middle of the argument list, and also provides type checking. */
  562. var args = arguejs({method:String, serialize: Function,
  563. deserialize: Function,
  564. metadata: [Metadata, new Metadata()],
  565. options: [Object], callback: Function}, arguments);
  566. var call = getCall(this.$channel, method, args.options);
  567. metadata = args.metadata.clone();
  568. var stream = new ClientWritableStream(call, serialize);
  569. var metadata_batch = {};
  570. metadata_batch[grpc.opType.SEND_INITIAL_METADATA] =
  571. metadata._getCoreRepresentation();
  572. metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
  573. call.startBatch(metadata_batch, function(err, response) {
  574. if (err) {
  575. // The call has stopped for some reason. A non-OK status will arrive
  576. // in the other batch.
  577. return;
  578. }
  579. stream.emit('metadata', Metadata._fromCoreRepresentation(
  580. response.metadata));
  581. });
  582. var client_batch = {};
  583. client_batch[grpc.opType.RECV_MESSAGE] = true;
  584. client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
  585. call.startBatch(client_batch, function(err, response) {
  586. response.status.metadata = Metadata._fromCoreRepresentation(
  587. response.status.metadata);
  588. var status = response.status;
  589. var error;
  590. var deserialized;
  591. if (status.code === constants.status.OK) {
  592. if (err) {
  593. // Got a batch error, but OK status. Something went wrong
  594. args.callback(err);
  595. return;
  596. } else {
  597. try {
  598. deserialized = deserialize(response.read);
  599. } catch (e) {
  600. /* Change status to indicate bad server response. This will result
  601. * in passing an error to the callback */
  602. status = {
  603. code: constants.status.INTERNAL,
  604. details: 'Failed to parse server response'
  605. };
  606. }
  607. }
  608. }
  609. if (status.code !== constants.status.OK) {
  610. error = new Error(response.status.details);
  611. error.code = status.code;
  612. error.metadata = status.metadata;
  613. args.callback(error);
  614. } else {
  615. args.callback(null, deserialized);
  616. }
  617. stream.emit('status', status);
  618. });
  619. return stream;
  620. };
  621. /**
  622. * Make a server stream request to the given method, with the given serialize
  623. * and deserialize function, using the given argument
  624. * @param {string} method The name of the method to request
  625. * @param {grpc~serialize} serialize The serialization function for inputs
  626. * @param {grpc~deserialize} deserialize The deserialization
  627. * function for outputs
  628. * @param {*} argument The argument to the call. Should be serializable with
  629. * serialize
  630. * @param {grpc.Metadata=} metadata Array of metadata key/value pairs to add to
  631. * the call
  632. * @param {grpc.Client~CallOptions=} options Options map
  633. * @return {grpc~ClientReadableStream} An event emitter for stream related
  634. * events
  635. */
  636. Client.prototype.makeServerStreamRequest = function(method, serialize,
  637. deserialize, argument,
  638. metadata, options) {
  639. /* While the arguments are listed in the function signature, those variables
  640. * are not used directly. Instead, ArgueJS processes the arguments
  641. * object. */
  642. var args = arguejs({method:String, serialize: Function,
  643. deserialize: Function,
  644. argument: null, metadata: [Metadata, new Metadata()],
  645. options: [Object]}, arguments);
  646. var call = getCall(this.$channel, method, args.options);
  647. metadata = args.metadata.clone();
  648. var stream = new ClientReadableStream(call, deserialize);
  649. var start_batch = {};
  650. var message = serialize(args.argument);
  651. if (args.options) {
  652. message.grpcWriteFlags = args.options.flags;
  653. }
  654. start_batch[grpc.opType.SEND_INITIAL_METADATA] =
  655. metadata._getCoreRepresentation();
  656. start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
  657. start_batch[grpc.opType.SEND_MESSAGE] = message;
  658. start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
  659. call.startBatch(start_batch, function(err, response) {
  660. if (err) {
  661. // The call has stopped for some reason. A non-OK status will arrive
  662. // in the other batch.
  663. return;
  664. }
  665. stream.emit('metadata', Metadata._fromCoreRepresentation(
  666. response.metadata));
  667. });
  668. var status_batch = {};
  669. status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
  670. call.startBatch(status_batch, function(err, response) {
  671. if (err) {
  672. stream.emit('error', err);
  673. return;
  674. }
  675. response.status.metadata = Metadata._fromCoreRepresentation(
  676. response.status.metadata);
  677. stream._receiveStatus(response.status);
  678. });
  679. return stream;
  680. };
  681. /**
  682. * Make a bidirectional stream request with this method on the given channel.
  683. * @param {string} method The name of the method to request
  684. * @param {grpc~serialize} serialize The serialization function for inputs
  685. * @param {grpc~deserialize} deserialize The deserialization
  686. * function for outputs
  687. * @param {grpc.Metadata=} metadata Array of metadata key/value
  688. * pairs to add to the call
  689. * @param {grpc.Client~CallOptions=} options Options map
  690. * @return {grpc~ClientDuplexStream} An event emitter for stream related events
  691. */
  692. Client.prototype.makeBidiStreamRequest = function(method, serialize,
  693. deserialize, metadata,
  694. options) {
  695. /* While the arguments are listed in the function signature, those variables
  696. * are not used directly. Instead, ArgueJS processes the arguments
  697. * object. */
  698. var args = arguejs({method:String, serialize: Function,
  699. deserialize: Function,
  700. metadata: [Metadata, new Metadata()],
  701. options: [Object]}, arguments);
  702. var call = getCall(this.$channel, method, args.options);
  703. metadata = args.metadata.clone();
  704. var stream = new ClientDuplexStream(call, serialize, deserialize);
  705. var start_batch = {};
  706. start_batch[grpc.opType.SEND_INITIAL_METADATA] =
  707. metadata._getCoreRepresentation();
  708. start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
  709. call.startBatch(start_batch, function(err, response) {
  710. if (err) {
  711. // The call has stopped for some reason. A non-OK status will arrive
  712. // in the other batch.
  713. return;
  714. }
  715. stream.emit('metadata', Metadata._fromCoreRepresentation(
  716. response.metadata));
  717. });
  718. var status_batch = {};
  719. status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
  720. call.startBatch(status_batch, function(err, response) {
  721. if (err) {
  722. stream.emit('error', err);
  723. return;
  724. }
  725. response.status.metadata = Metadata._fromCoreRepresentation(
  726. response.status.metadata);
  727. stream._receiveStatus(response.status);
  728. });
  729. return stream;
  730. };
  731. /**
  732. * Close this client.
  733. */
  734. Client.prototype.close = function() {
  735. this.$channel.close();
  736. };
  737. /**
  738. * Return the underlying channel object for the specified client
  739. * @return {Channel} The channel
  740. */
  741. Client.prototype.getChannel = function() {
  742. return this.$channel;
  743. };
  744. /**
  745. * Wait for the client to be ready. The callback will be called when the
  746. * client has successfully connected to the server, and it will be called
  747. * with an error if the attempt to connect to the server has unrecoverablly
  748. * failed or if the deadline expires. This function will make the channel
  749. * start connecting if it has not already done so.
  750. * @param {grpc~Deadline} deadline When to stop waiting for a connection.
  751. * @param {function(Error)} callback The callback to call when done attempting
  752. * to connect.
  753. */
  754. Client.prototype.waitForReady = function(deadline, callback) {
  755. var self = this;
  756. var checkState = function(err) {
  757. if (err) {
  758. callback(new Error('Failed to connect before the deadline'));
  759. return;
  760. }
  761. var new_state = self.$channel.getConnectivityState(true);
  762. if (new_state === grpc.connectivityState.READY) {
  763. callback();
  764. } else if (new_state === grpc.connectivityState.FATAL_FAILURE) {
  765. callback(new Error('Failed to connect to server'));
  766. } else {
  767. self.$channel.watchConnectivityState(new_state, deadline, checkState);
  768. }
  769. };
  770. checkState();
  771. };
  772. /**
  773. * Map with short names for each of the requester maker functions. Used in
  774. * makeClientConstructor
  775. * @private
  776. */
  777. var requester_funcs = {
  778. unary: Client.prototype.makeUnaryRequest,
  779. server_stream: Client.prototype.makeServerStreamRequest,
  780. client_stream: Client.prototype.makeClientStreamRequest,
  781. bidi: Client.prototype.makeBidiStreamRequest
  782. };
  783. function getDefaultValues(metadata, options) {
  784. var res = {};
  785. res.metadata = metadata || new Metadata();
  786. res.options = options || {};
  787. return res;
  788. }
  789. /**
  790. * Map with wrappers for each type of requester function to make it use the old
  791. * argument order with optional arguments after the callback.
  792. * @access private
  793. */
  794. var deprecated_request_wrap = {
  795. unary: function(makeUnaryRequest) {
  796. return function makeWrappedUnaryRequest(argument, callback,
  797. metadata, options) {
  798. /* jshint validthis: true */
  799. var opt_args = getDefaultValues(metadata, metadata);
  800. return makeUnaryRequest.call(this, argument, opt_args.metadata,
  801. opt_args.options, callback);
  802. };
  803. },
  804. client_stream: function(makeServerStreamRequest) {
  805. return function makeWrappedClientStreamRequest(callback, metadata,
  806. options) {
  807. /* jshint validthis: true */
  808. var opt_args = getDefaultValues(metadata, options);
  809. return makeServerStreamRequest.call(this, opt_args.metadata,
  810. opt_args.options, callback);
  811. };
  812. },
  813. server_stream: _.identity,
  814. bidi: _.identity
  815. };
  816. /**
  817. * Creates a constructor for a client with the given methods, as specified in
  818. * the methods argument. The resulting class will have an instance method for
  819. * each method in the service, which is a partial application of one of the
  820. * [Client]{@link grpc.Client} request methods, depending on `requestSerialize`
  821. * and `responseSerialize`, with the `method`, `serialize`, and `deserialize`
  822. * arguments predefined.
  823. * @memberof grpc
  824. * @alias grpc~makeGenericClientConstructor
  825. * @param {grpc~ServiceDefinition} methods An object mapping method names to
  826. * method attributes
  827. * @param {string} serviceName The fully qualified name of the service
  828. * @param {Object} class_options An options object.
  829. * @param {boolean=} [class_options.deprecatedArgumentOrder=false] Indicates
  830. * that the old argument order should be used for methods, with optional
  831. * arguments at the end instead of the callback at the end. This option
  832. * is only a temporary stopgap measure to smooth an API breakage.
  833. * It is deprecated, and new code should not use it.
  834. * @return {function} New client constructor, which is a subclass of
  835. * {@link grpc.Client}, and has the same arguments as that constructor.
  836. */
  837. exports.makeClientConstructor = function(methods, serviceName,
  838. class_options) {
  839. if (!class_options) {
  840. class_options = {};
  841. }
  842. function ServiceClient(address, credentials, options) {
  843. Client.call(this, address, credentials, options);
  844. }
  845. util.inherits(ServiceClient, Client);
  846. _.each(methods, function(attrs, name) {
  847. var method_type;
  848. if (_.startsWith(name, '$')) {
  849. throw new Error('Method names cannot start with $');
  850. }
  851. if (attrs.requestStream) {
  852. if (attrs.responseStream) {
  853. method_type = 'bidi';
  854. } else {
  855. method_type = 'client_stream';
  856. }
  857. } else {
  858. if (attrs.responseStream) {
  859. method_type = 'server_stream';
  860. } else {
  861. method_type = 'unary';
  862. }
  863. }
  864. var serialize = attrs.requestSerialize;
  865. var deserialize = attrs.responseDeserialize;
  866. var method_func = _.partial(requester_funcs[method_type], attrs.path,
  867. serialize, deserialize);
  868. if (class_options.deprecatedArgumentOrder) {
  869. ServiceClient.prototype[name] = deprecated_request_wrap(method_func);
  870. } else {
  871. ServiceClient.prototype[name] = method_func;
  872. }
  873. // Associate all provided attributes with the method
  874. _.assign(ServiceClient.prototype[name], attrs);
  875. });
  876. ServiceClient.service = methods;
  877. return ServiceClient;
  878. };
  879. /**
  880. * Return the underlying channel object for the specified client
  881. * @memberof grpc
  882. * @alias grpc~getClientChannel
  883. * @param {Client} client
  884. * @return {Channel} The channel
  885. * @see grpc.Client#getChannel
  886. */
  887. exports.getClientChannel = function(client) {
  888. return Client.prototype.getChannel.call(client);
  889. };
  890. /**
  891. * Wait for the client to be ready. The callback will be called when the
  892. * client has successfully connected to the server, and it will be called
  893. * with an error if the attempt to connect to the server has unrecoverablly
  894. * failed or if the deadline expires. This function will make the channel
  895. * start connecting if it has not already done so.
  896. * @memberof grpc
  897. * @alias grpc~waitForClientReady
  898. * @param {Client} client The client to wait on
  899. * @param {grpc~Deadline} deadline When to stop waiting for a connection. Pass
  900. * Infinity to wait forever.
  901. * @param {function(Error)} callback The callback to call when done attempting
  902. * to connect.
  903. * @see grpc.Client#waitForReady
  904. */
  905. exports.waitForClientReady = function(client, deadline, callback) {
  906. Client.prototype.waitForReady.call(client, deadline, callback);
  907. };
  908. </code></pre>
  909. </article>
  910. </section>
  911. </div>
  912. <nav>
  913. <h2><a href="index.html">Home</a></h2><h3>Externals</h3><ul><li><a href="external-Duplex.html">Duplex</a></li><li><a href="external-EventEmitter.html">EventEmitter</a></li><li><a href="external-GoogleCredential.html">GoogleCredential</a></li><li><a href="external-Readable.html">Readable</a></li><li><a href="external-Writable.html">Writable</a></li></ul><h3>Classes</h3><ul><li><a href="grpc.Client.html">Client</a></li><li><a href="grpc.credentials-CallCredentials.html">CallCredentials</a></li><li><a href="grpc.credentials-ChannelCredentials.html">ChannelCredentials</a></li><li><a href="grpc.Metadata.html">Metadata</a></li><li><a href="grpc.Server.html">Server</a></li><li><a href="grpc.ServerCredentials.html">ServerCredentials</a></li><li><a href="grpc-ClientDuplexStream.html">ClientDuplexStream</a></li><li><a href="grpc-ClientReadableStream.html">ClientReadableStream</a></li><li><a href="grpc-ClientUnaryCall.html">ClientUnaryCall</a></li><li><a href="grpc-ClientWritableStream.html">ClientWritableStream</a></li><li><a href="grpc-ServerDuplexStream.html">ServerDuplexStream</a></li><li><a href="grpc-ServerReadableStream.html">ServerReadableStream</a></li><li><a href="grpc-ServerUnaryCall.html">ServerUnaryCall</a></li><li><a href="grpc-ServerWritableStream.html">ServerWritableStream</a></li></ul><h3>Events</h3><ul><li><a href="grpc-ClientDuplexStream.html#event:metadata">metadata</a></li><li><a href="grpc-ClientDuplexStream.html#event:status">status</a></li><li><a href="grpc-ClientReadableStream.html#event:metadata">metadata</a></li><li><a href="grpc-ClientReadableStream.html#event:status">status</a></li><li><a href="grpc-ClientUnaryCall.html#event:metadata">metadata</a></li><li><a href="grpc-ClientUnaryCall.html#event:status">status</a></li><li><a href="grpc-ClientWritableStream.html#event:metadata">metadata</a></li><li><a href="grpc-ClientWritableStream.html#event:status">status</a></li><li><a href="grpc-ServerDuplexStream.html#~event:cancelled">cancelled</a></li><li><a href="grpc-ServerReadableStream.html#~event:cancelled">cancelled</a></li><li><a href="grpc-ServerUnaryCall.html#~event:cancelled">cancelled</a></li><li><a href="grpc-ServerWritableStream.html#~event:cancelled">cancelled</a></li></ul><h3>Namespaces</h3><ul><li><a href="grpc.html">grpc</a></li><li><a href="grpc.credentials.html">credentials</a></li></ul>
  914. </nav>
  915. <br class="clear">
  916. <footer>
  917. Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.3</a> on Fri Sep 08 2017 11:10:31 GMT-0700 (PDT)
  918. </footer>
  919. <script> prettyPrint(); </script>
  920. <script src="scripts/linenumber.js"> </script>
  921. </body>
  922. </html>