File size: 4,763 Bytes
c1b3a0c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
"use strict";
module.exports = Service;
var util = require("../util/minimal");
// Extends EventEmitter
(Service.prototype = Object.create(util.EventEmitter.prototype)).constructor = Service;
/**
* A service method callback as used by {@link rpc.ServiceMethod|ServiceMethod}.
*
* Differs from {@link RPCImplCallback} in that it is an actual callback of a service method which may not return `response = null`.
* @typedef rpc.ServiceMethodCallback
* @template TRes extends Message<TRes>
* @type {function}
* @param {Error|null} error Error, if any
* @param {TRes} [response] Response message
* @returns {undefined}
*/
/**
* A service method part of a {@link rpc.Service} as created by {@link Service.create}.
* @typedef rpc.ServiceMethod
* @template TReq extends Message<TReq>
* @template TRes extends Message<TRes>
* @type {function}
* @param {TReq|Properties<TReq>} request Request message or plain object
* @param {rpc.ServiceMethodCallback<TRes>} [callback] Node-style callback called with the error, if any, and the response message
* @returns {Promise<Message<TRes>>} Promise if `callback` has been omitted, otherwise `undefined`
*/
/**
* Constructs a new RPC service instance.
* @classdesc An RPC service as returned by {@link Service#create}.
* @exports rpc.Service
* @extends util.EventEmitter
* @constructor
* @param {RPCImpl} rpcImpl RPC implementation
* @param {boolean} [requestDelimited=false] Whether requests are length-delimited
* @param {boolean} [responseDelimited=false] Whether responses are length-delimited
*/
function Service(rpcImpl, requestDelimited, responseDelimited) {
if (typeof rpcImpl !== "function")
throw TypeError("rpcImpl must be a function");
util.EventEmitter.call(this);
/**
* RPC implementation. Becomes `null` once the service is ended.
* @type {RPCImpl|null}
*/
this.rpcImpl = rpcImpl;
/**
* Whether requests are length-delimited.
* @type {boolean}
*/
this.requestDelimited = Boolean(requestDelimited);
/**
* Whether responses are length-delimited.
* @type {boolean}
*/
this.responseDelimited = Boolean(responseDelimited);
}
/**
* Calls a service method through {@link rpc.Service#rpcImpl|rpcImpl}.
* @param {Method|rpc.ServiceMethod<TReq,TRes>} method Reflected or static method
* @param {Constructor<TReq>} requestCtor Request constructor
* @param {Constructor<TRes>} responseCtor Response constructor
* @param {TReq|Properties<TReq>} request Request message or plain object
* @param {rpc.ServiceMethodCallback<TRes>} callback Service callback
* @returns {undefined}
* @template TReq extends Message<TReq>
* @template TRes extends Message<TRes>
*/
Service.prototype.rpcCall = function rpcCall(method, requestCtor, responseCtor, request, callback) {
if (!request)
throw TypeError("request must be specified");
var self = this;
if (!callback)
return util.asPromise(rpcCall, self, method, requestCtor, responseCtor, request);
if (!self.rpcImpl) {
setTimeout(function() { callback(Error("already ended")); }, 0);
return undefined;
}
try {
return self.rpcImpl(
method,
requestCtor[self.requestDelimited ? "encodeDelimited" : "encode"](request).finish(),
function rpcCallback(err, response) {
if (err) {
self.emit("error", err, method);
return callback(err);
}
if (response === null) {
self.end(/* endedByRPC */ true);
return undefined;
}
if (!(response instanceof responseCtor)) {
try {
response = responseCtor[self.responseDelimited ? "decodeDelimited" : "decode"](response);
} catch (err) {
self.emit("error", err, method);
return callback(err);
}
}
self.emit("data", response, method);
return callback(null, response);
}
);
} catch (err) {
self.emit("error", err, method);
setTimeout(function() { callback(err); }, 0);
return undefined;
}
};
/**
* Ends this service and emits the `end` event.
* @param {boolean} [endedByRPC=false] Whether the service has been ended by the RPC implementation.
* @returns {rpc.Service} `this`
*/
Service.prototype.end = function end(endedByRPC) {
if (this.rpcImpl) {
if (!endedByRPC) // signal end to rpcImpl
this.rpcImpl(null, null, null);
this.rpcImpl = null;
this.emit("end").off();
}
return this;
};
|