Skip to content

Commit d3ae961

Browse files
committed
Initial take on runtime services, see #507
1 parent 6a06e95 commit d3ae961

File tree

9 files changed

+195
-7
lines changed

9 files changed

+195
-7
lines changed

dist/protobuf.js

Lines changed: 50 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/protobuf.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/protobuf.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/protobuf.min.js.gz

130 Bytes
Binary file not shown.

dist/protobuf.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/service.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,52 @@ ServicePrototype.remove = function remove(object) {
138138
}
139139
return NamespacePrototype.remove.call(this, object);
140140
};
141+
142+
/**
143+
* RPC implementation passed to {@link Service#create} performing a service request on network level, i.e. by utilizing http requests or websockets.
144+
* @typedef RPCImpl
145+
* @function
146+
* @param {Method} method Reflected method being called
147+
* @param {Uint8Array} requestData Request data
148+
* @param {function(?Error, Uint8Array=)} callback Node-style callback called with the error, if any, and the response data
149+
* @returns {undefined}
150+
*/
151+
152+
/**
153+
* Creates a runtime service using the specified rpc implementation.
154+
* @param {RPCImpl} rpc RPC implementation
155+
* @param {boolean} [requestDelimited=false] Whether request data is length delimited
156+
* @param {boolean} [responseDelimited=false] Whether response data is length delimited
157+
* @returns {Object} Runtime service
158+
*/
159+
ServicePrototype.create = function create(rpc, requestDelimited, responseDelimited) {
160+
var rpcService = {};
161+
this.getMethodsArray().forEach(function(method) {
162+
rpcService[method.resolve().name] = function(request, callback) {
163+
var requestData;
164+
try {
165+
requestData = (requestDelimited && method.resolvedRequestType.encodeDelimited(request) || method.resolvedRequestType.encode(request)).finish();
166+
} catch (err) {
167+
(typeof setImmediate === 'function' && setImmediate || setTimeout)(function() { callback(err); });
168+
return;
169+
}
170+
// Calls the custom RPC implementation with the reflected method and binary request data
171+
// and expects the rpc implementation to call its callback with the binary response data.
172+
rpc(method, requestData, function(err, responseData) {
173+
if (err) {
174+
callback(err);
175+
return;
176+
}
177+
var response;
178+
try {
179+
response = responseDelimited && method.resolvedResponseType.decodeDelimited(responseData) || method.resolvedResponseType.decode(responseData);
180+
} catch (err2) {
181+
callback(err2);
182+
return;
183+
}
184+
callback(null, response);
185+
});
186+
};
187+
});
188+
return rpcService;
189+
};

tests/data/rpc.proto

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
syntax = "proto3";
2+
3+
service MyService {
4+
rpc MyMethod (MyRequest) returns (MyResponse);
5+
}
6+
7+
message MyRequest {
8+
string path = 1;
9+
}
10+
11+
message MyResponse {
12+
int32 status = 2;
13+
}

tests/rpc.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
var tape = require("tape");
2+
3+
var protobuf = require("..");
4+
5+
tape.test("RPC", function(test) {
6+
7+
protobuf.load("tests/data/rpc.proto", function(err, root) {
8+
if (err)
9+
return test.fail(err.message);
10+
11+
var MyService = root.lookup("MyService"),
12+
MyMethod = MyService.get("MyMethod").resolve(),
13+
MyRequest = MyMethod.resolvedRequestType,
14+
MyResponse = MyMethod.resolvedResponseType;
15+
16+
function rpc(method, requestData, callback) {
17+
18+
test.test("should call the rpc impl with", function(test) {
19+
test.equal(method, MyMethod, "the reflected method");
20+
test.ok(requestData.length, "a buffer");
21+
test.ok(typeof callback === 'function', "a callback function");
22+
test.end();
23+
});
24+
test.test("should call with a buffer that contains", function(test) {
25+
test.equal(requestData[0], 3, "ldelim 3");
26+
test.equal(requestData[1], 10, "id 1, wireType 2");
27+
test.equal(requestData[2], 1, "length 1");
28+
test.equal(requestData[3], 0x2f, "the original string");
29+
test.end();
30+
});
31+
32+
setTimeout(function() {
33+
callback(null, MyResponse.encode({
34+
status: 200
35+
}).finish());
36+
});
37+
}
38+
39+
var MyService = root.lookup("MyService");
40+
var service = MyService.create(rpc, true, false);
41+
42+
test.deepEqual(Object.keys(service), [ "MyMethod" ], "should create a service with exactly one method");
43+
44+
service.MyMethod(MyRequest.create({
45+
path: "/"
46+
}), function(err, response) {
47+
if (err)
48+
return test.fail(err.message);
49+
test.ok(response instanceof MyResponse.ctor, "should return an instance of MyResponse");
50+
test.deepEqual(response, {
51+
status: 200
52+
}, "should return status 200");
53+
test.end();
54+
});
55+
});
56+
57+
});

types/protobuf.js.d.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
/*
55
* protobuf.js v6.0.1 TypeScript definitions
6-
* Generated Wed, 30 Nov 2016 13:45:03 UTC
6+
* Generated Wed, 30 Nov 2016 22:05:17 UTC
77
*/
88
declare module protobuf {
99

@@ -1259,8 +1259,28 @@ declare module protobuf {
12591259
*/
12601260
static fromJSON(name: string, json: Object): Service;
12611261

1262+
/**
1263+
* Creates a runtime service using the specified rpc implementation.
1264+
* @param {RPCImpl} rpc RPC implementation
1265+
* @param {boolean} [requestDelimited=false] Whether request data is length delimited
1266+
* @param {boolean} [responseDelimited=false] Whether response data is length delimited
1267+
* @returns {Object} Runtime service
1268+
*/
1269+
create(rpc: RPCImpl, requestDelimited?: boolean, responseDelimited?: boolean): Object;
1270+
12621271
}
12631272

1273+
/**
1274+
* RPC implementation passed to {@link Service#create} performing a service request on network level, i.e. by utilizing http requests or websockets.
1275+
* @typedef RPCImpl
1276+
* @function
1277+
* @param {Method} method Reflected method being called
1278+
* @param {Uint8Array} requestData Request data
1279+
* @param {function(?Error, Uint8Array=)} callback Node-style callback called with the error, if any, and the response data
1280+
* @returns {undefined}
1281+
*/
1282+
function RPCImpl(method: Method, requestData: Uint8Array, callback: (() => any)): undefined;
1283+
12641284
/**
12651285
* Handle object returned from {@link tokenize}.
12661286
* @typedef {Object} TokenizerHandle

0 commit comments

Comments
 (0)