diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..018eaf3 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,26 @@ +{ + "node":true, + "sub":true, + "laxbreak":true, + "laxcomma":true, + "regexp":true, + "asi": true, + "browser": true, + "loopfunc":true, + "expr":true, + "es3": true, + "esnext": true, + "curly": true, + "immed": true, + "latedef": false, + "expr": true, + "eqeqeq": false, + "eqnull": false, + "newcap": true, + "noarg": true, + "undef": true, + "unused": true, + "proto": true, + "strict": false, + "smarttabs": true +} diff --git a/LICENSE.md b/LICENSE.md index 5a1390e..70a0e7b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2008-2015 http://hprose.com +Copyright (c) 2008-2018 http://hprose.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index cd2e19a..de20967 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ +

Hprose

+ - Promises/A+ logo - - - + Promises/A+ logo + # Hprose for Node.js [![Join the chat at https://gitter.im/hprose/hprose-nodejs](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/hprose/hprose-nodejs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -57,6 +56,8 @@ Through *Hprose*, You can conveniently and efficiently intercommunicate between This project is the implementation of Hprose for Node.js. +Hprose 2.0 for Node.js Documents(中文版): https://github.com/hprose/hprose-nodejs/wiki + ## Usage ### Http Server diff --git a/README_zh_CN.md b/README_zh_CN.md index 5ba9662..ba57de0 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -1,10 +1,9 @@ +

Hprose

+ - Promises/A+ logo - - - + Promises/A+ logo + # Hprose for Node.js [![Join the chat at https://gitter.im/hprose/hprose-nodejs](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/hprose/hprose-nodejs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -57,6 +56,8 @@ 本项目是 Hprose 的 Node.js 语言版本实现。 +更多 Hprose 2.0 for Node.js 文档: https://github.com/hprose/hprose-nodejs/wiki + ## 使用 ### Http 服务器 diff --git a/example/client.js b/example/client.js index 345bcea..c5cafe8 100644 --- a/example/client.js +++ b/example/client.js @@ -27,6 +27,11 @@ for (var i = 0; i < max; i++) { } var end = new Date().getTime(); console.log(end - start); + +(async function() { + console.log(await proxy.hello2("async world")); +})(); + client.batch.begin(); proxy.getMaps('name', 'age', 'age', function(result) { console.log(result); diff --git a/example/co/awaitexam2.js b/example/co/awaitexam2.js new file mode 100644 index 0000000..9a4bec8 --- /dev/null +++ b/example/co/awaitexam2.js @@ -0,0 +1,14 @@ +(async function() { + try { + console.log(await Promise.resolve("promise")); + console.log(await function *() { return "generator" }); + console.log(await new Date()); + console.log(await 123); + console.log(await 3.14); + console.log(await "hello"); + console.log(await true); + } + catch (e) { + console.error(e); + } +})(); diff --git a/example/co/awaitexam3.js b/example/co/awaitexam3.js new file mode 100644 index 0000000..2890d80 --- /dev/null +++ b/example/co/awaitexam3.js @@ -0,0 +1,37 @@ +(async function() { + try { + var a = []; + for (i = 0; i < 1000000; i++) { + a[i] = i; + } + var start = Date.now(); + await a; + var end = Date.now(); + console.log(end - start); + } + catch (e) { + console.error(e); + } +})(); + +(async function() { + try { + var a = []; + a[0] = a; + console.log(await a); + } + catch (e) { + console.error(e); + } +})(); + +(async function() { + try { + var o = {}; + o.self = o; + console.log(await o); + } + catch (e) { + console.error(e); + } +})(); diff --git a/example/co/exam1.js b/example/co/exam1.js new file mode 100644 index 0000000..75dc411 --- /dev/null +++ b/example/co/exam1.js @@ -0,0 +1,7 @@ +var hprose = require('hprose'); + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + yield client.useService(); + console.log(yield client.hello("World")); +}); diff --git a/example/co/exam10.js b/example/co/exam10.js new file mode 100644 index 0000000..b0b19fd --- /dev/null +++ b/example/co/exam10.js @@ -0,0 +1,19 @@ +var hprose = require('hprose'); + + +var coroutine = hprose.wrap(function*(client) { + console.log(1); + console.log((yield client.hello("hprose"))); + var a = client.sum(1, 2, 3); + var b = client.sum(4, 5, 6); + var c = client.sum(7, 8, 9); + console.log((yield client.sum(a, b, c))); + console.log((yield client.hello("world"))); +}); + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + yield client.useService(); + coroutine(client); + coroutine(Promise.resolve(client)); +}); diff --git a/example/co/exam11.js b/example/co/exam11.js new file mode 100644 index 0000000..13021d5 --- /dev/null +++ b/example/co/exam11.js @@ -0,0 +1,11 @@ +var hprose = require('hprose'); + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + try { + console.log(yield client.invoke('ooxx')); + } + catch (e) { + console.log(e.message); + } +}); \ No newline at end of file diff --git a/example/co/exam12.js b/example/co/exam12.js new file mode 100644 index 0000000..5053384 --- /dev/null +++ b/example/co/exam12.js @@ -0,0 +1,9 @@ +var hprose = require('hprose'); + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + console.log(yield client.invoke('oo')); + console.log(yield client.invoke('xx')); +}).catch(function(e) { + console.log(e.message); +}); \ No newline at end of file diff --git a/example/co/exam13.js b/example/co/exam13.js new file mode 100644 index 0000000..265385c --- /dev/null +++ b/example/co/exam13.js @@ -0,0 +1,7 @@ +var hprose = require('hprose'); + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + console.log(yield client.invoke('oo').complete()); + console.log(yield client.invoke('xx').complete()); +}); \ No newline at end of file diff --git a/example/co/exam14.js b/example/co/exam14.js new file mode 100644 index 0000000..63ad35c --- /dev/null +++ b/example/co/exam14.js @@ -0,0 +1,26 @@ +var hprose = require('hprose'); + +function sum(a, b, callback) { + callback(a + b); +} + +var sum1 = hprose.promisify(sum); +var sum2 = hprose.thunkify(sum); + +sum1(1, 2).then(function(result) { + console.log(result); +}); + +sum2(2, 3)(function(result) { + console.log(result); +}); + +hprose.co(function*() { + console.log(yield sum1(3, 4)); + console.log(yield sum2(4, 5)); +}); + +(async function() { + console.log(await sum1(5, 6)); + console.log(await sum2(6, 7)); +})(); \ No newline at end of file diff --git a/example/co/exam15.js b/example/co/exam15.js new file mode 100644 index 0000000..02bd3dc --- /dev/null +++ b/example/co/exam15.js @@ -0,0 +1,27 @@ +var hprose = require('hprose'); + +function normal(p) { + console.log(p); + return normal; +} + +function* coroutine(p) { + console.log(yield p); + return coroutine; +} + +// hprose.co(function*() { +// var p = Promise.resolve(123); +// console.log(normal(p)); +// console.log(yield coroutine(p)); +// }) + +function* run(fn) { + var p = Promise.resolve(123); + console.log(yield hprose.Future.toPromise(fn(p))); +} + +hprose.co(function*() { + yield run(normal); + yield run(coroutine); +}); \ No newline at end of file diff --git a/example/co/exam2.js b/example/co/exam2.js new file mode 100644 index 0000000..d3e55ee --- /dev/null +++ b/example/co/exam2.js @@ -0,0 +1,16 @@ +var co = require('hprose').co; + +co(function*() { + try { + console.log(yield Promise.resolve("promise")); + console.log(yield function *() { return "generator" }); + console.log(yield new Date()); + console.log(yield 123); + console.log(yield 3.14); + console.log(yield "hello"); + console.log(yield true); + } + catch (e) { + console.error(e); + } +}); diff --git a/example/co/exam3.js b/example/co/exam3.js new file mode 100644 index 0000000..2fe398d --- /dev/null +++ b/example/co/exam3.js @@ -0,0 +1,39 @@ +var co = require('hprose').co; + +co(function*() { + try { + var a = []; + for (i = 0; i < 1000000; i++) { + a[i] = i; + } + var start = Date.now(); + yield a; + var end = Date.now(); + console.log(end - start); + } + catch (e) { + console.error(e); + } +}); + +co(function*() { + try { + var a = []; + a[0] = a; + console.log(yield a); + } + catch (e) { + console.error(e); + } +}); + +co(function*() { + try { + var o = {}; + o.self = o; + console.log(yield o); + } + catch (e) { + console.error(e); + } +}); diff --git a/example/co/exam4.js b/example/co/exam4.js new file mode 100644 index 0000000..4e5d434 --- /dev/null +++ b/example/co/exam4.js @@ -0,0 +1,16 @@ +var hprose = require("hprose"); +var co = hprose.co; +var thunkify = hprose.thunkify; + +var sum = thunkify(function(a, b, callback) { + console.log("call sum(" + Array.prototype.join.call(arguments) + ")"); + callback(null, a + b); + callback(null, a + b + a); +}); + +co(function*() { + var result = sum(1, 2); + console.log(yield result); + console.log(yield sum(2, 3)); + console.log(yield result); +}); diff --git a/example/co/exam5.js b/example/co/exam5.js new file mode 100644 index 0000000..349bf4e --- /dev/null +++ b/example/co/exam5.js @@ -0,0 +1,14 @@ +var hprose = require("hprose"); +var co = hprose.co; +var thunkify = hprose.thunkify; + +var sum = thunkify(function(a, b, callback) { + callback(a + b); +}); + +co(function*() { + var result = sum(1, 2); + console.log(yield result); + console.log(yield sum(2, 3)); + console.log(yield result); +}); diff --git a/example/co/exam6.js b/example/co/exam6.js new file mode 100644 index 0000000..799b3fe --- /dev/null +++ b/example/co/exam6.js @@ -0,0 +1,12 @@ +var hprose = require('hprose'); + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + yield client.useService(); + console.log(yield client.hello("Hprose")); + var a = client.sum(1, 2, 3); + var b = client.sum(4, 5, 6); + var c = client.sum(7, 8, 9); + console.log(yield client.sum(a, b, c)); + console.log(yield client.hello("World")); +}); diff --git a/example/co/exam7.js b/example/co/exam7.js new file mode 100644 index 0000000..440e136 --- /dev/null +++ b/example/co/exam7.js @@ -0,0 +1,18 @@ +var hprose = require('hprose'); + +var client = hprose.Client.create('http://hprose.com/example/'); +var proxy = client.useService(); + +hprose.co(function*() { + var client = yield proxy; + for (var i = 0; i < 5; i++) { + console.log((yield client.hello("1-" + i))); + } +}); + +hprose.co(function*() { + var client = yield proxy; + for (var i = 0; i < 5; i++) { + console.log((yield client.hello("2-" + i))); + } +}); diff --git a/example/co/exam8.js b/example/co/exam8.js new file mode 100644 index 0000000..90fb7cc --- /dev/null +++ b/example/co/exam8.js @@ -0,0 +1,22 @@ +var hprose = require('hprose'); + +function *hello(n, client) { + var result = []; + for (var i = 0; i < 5; i++) { + result[i] = client.hello(n + "-" + i); + } + return Promise.all(result); +} + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + yield client.useService(); + var result = yield hprose.co(function *(client) { + var result = []; + for (var i = 0; i < 3; i++) { + result[i] = hprose.co(hello, i, client); + } + return Promise.all(result); + }, client); + console.log(result); +}); diff --git a/example/co/exam9.js b/example/co/exam9.js new file mode 100644 index 0000000..dfa447e --- /dev/null +++ b/example/co/exam9.js @@ -0,0 +1,16 @@ +var hprose = require('hprose'); + +hprose.co(function*() { + var client = hprose.Client.create('http://hprose.com/example/'); + yield client.useService(); + for (var i = 0; i < 5; i++) { + console.log(yield client.hello("1-" + i)); + } + var console_log = hprose.wrap(console.log, console); + for (var i = 0; i < 5; i++) { + console_log(client.hello("2-" + i)); + } + for (var i = 0; i < 5; i++) { + console.log(yield client.hello("3-" + i)); + } +}); diff --git a/example/httpclient.js b/example/httpclient.js new file mode 100644 index 0000000..a0e0628 --- /dev/null +++ b/example/httpclient.js @@ -0,0 +1,19 @@ +/*jshint node:true, eqeqeq:true */ +'use strict'; + +var hprose = require('../lib/hprose.js'); +var client = hprose.Client.create('http://127.0.0.1:8080/', []); +client.simple = true; +client.on('error', function(func, e) { + console.log(func, e); +}); +hprose.co(function*() { + var proxy = client.useService(['hello']); + var start = new Date().getTime(); + for (var i = 0; i < 100; i++) { + var result = yield proxy.hello(i); + console.log(result); + } + var end = new Date().getTime(); + console.log("time: " + (end - start)); +}); diff --git a/example/httpserver.js b/example/httpserver.js new file mode 100644 index 0000000..6e11757 --- /dev/null +++ b/example/httpserver.js @@ -0,0 +1,21 @@ +/*jshint node:true, eqeqeq:true */ +'use strict'; + +var hprose = require('../lib/hprose.js'); + +function* hello(name) { + return 'Hello ' + name + '!'; +} + +var server = hprose.Server.create("http://0.0.0.0:8080/"); +server.crossDomain = true; +server.crossDomainXmlFile = './crossdomain.xml'; +server.debug = true; +server.addFunction(hello); +server.on('sendError', function(message) { + console.log(message.stack); +}); +process.on('SIGINT', function() { + server.stop(); +}); +server.start(); diff --git a/example/middleware/batchlog/batchloghandler.js b/example/middleware/batchlog/batchloghandler.js index d418f06..c2e5764 100644 --- a/example/middleware/batchlog/batchloghandler.js +++ b/example/middleware/batchlog/batchloghandler.js @@ -1,6 +1,6 @@ -module.exports = function(batches, context, next) { +module.exports = function*(batches, context, next) { console.log("before invoke:", batches); - var result = next(batches, context); + var result = yield next(batches, context); console.log("after invoke:", batches, result); return result; }; diff --git a/example/middleware/batchlog/loghandler.js b/example/middleware/batchlog/loghandler.js index 24b6cab..02f301f 100644 --- a/example/middleware/batchlog/loghandler.js +++ b/example/middleware/batchlog/loghandler.js @@ -1,6 +1,6 @@ -module.exports = function(name, args, context, next) { +module.exports = function*(name, args, context, next) { console.log("before invoke:", name, args); - var result = next(name, args, context); + var result = yield next(name, args, context); console.log("after invoke:", name, args, result); return result; }; diff --git a/example/middleware/cache/loghandler.js b/example/middleware/cache/loghandler.js index 24b6cab..02f301f 100644 --- a/example/middleware/cache/loghandler.js +++ b/example/middleware/cache/loghandler.js @@ -1,6 +1,6 @@ -module.exports = function(name, args, context, next) { +module.exports = function*(name, args, context, next) { console.log("before invoke:", name, args); - var result = next(name, args, context); + var result = yield next(name, args, context); console.log("after invoke:", name, args, result); return result; }; diff --git a/example/middleware/compressCache/server.js b/example/middleware/compressCache/server.js index 5ce4454..a58028e 100644 --- a/example/middleware/compressCache/server.js +++ b/example/middleware/compressCache/server.js @@ -7,9 +7,9 @@ function echo(value) { } var server = hprose.Server.create("http://0.0.0.0:8080/"); server.beforeFilter.use(stathandler('BeforeFilter')) - .use(sizehandler('Non compressed')); + .use(sizehandler('compressed')); server.addFilter(new CompressFilter('Lzp3')); server.afterFilter.use(stathandler('AfterFilter')) - .use(sizehandler('compressed')); + .use(sizehandler('Non compressed')); server.add(echo); server.start(); diff --git a/example/middleware/compressCache/sizehandler.js b/example/middleware/compressCache/sizehandler.js index d9e4da6..4c6e419 100644 --- a/example/middleware/compressCache/sizehandler.js +++ b/example/middleware/compressCache/sizehandler.js @@ -1,16 +1,9 @@ var hprose = require('hprose'); module.exports = function(message) { - return function(request, context, next) { + return function*(request, context, next) { console.log(message + ' request size: ' + request.length); - var response = next(request, context); - if (hprose.Future.isPromise(response)) { - response.then(function(data) { - console.log(message + ' response size: ' + data.length); - }); - } - else { - console.log(message + ' response size: ' + response.length); - } + var response = yield next(request, context); + console.log(message + ' response size: ' + response.length); return response; }; }; diff --git a/example/middleware/compressCache/stathandler.js b/example/middleware/compressCache/stathandler.js index d8c8d65..ac84f64 100644 --- a/example/middleware/compressCache/stathandler.js +++ b/example/middleware/compressCache/stathandler.js @@ -1,17 +1,9 @@ module.exports = function(message) { - return function(request, context, next) { + return function*(request, context, next) { var start = Date.now(); - function showstat() { - var end = Date.now(); - console.log(message + ': It takes ' + (end - start) + ' ms.'); - } - var response = next(request, context); - if (hprose.Future.isPromise(response)) { - response.then(showstat); - } - else { - showstat(); - } + var response = yield next(request, context); + var end = Date.now(); + console.log(message + ': It takes ' + (end - start) + ' ms.'); return response; }; }; diff --git a/example/middleware/log/loghandler.js b/example/middleware/log/loghandler.js index 24b6cab..d637f41 100644 --- a/example/middleware/log/loghandler.js +++ b/example/middleware/log/loghandler.js @@ -1,6 +1,16 @@ +/* module.exports = function(name, args, context, next) { console.log("before invoke:", name, args); var result = next(name, args, context); + result.then(function(result) { + console.log("after invoke:", name, args, result); + }); + return result; +}; +*/ +module.exports = function*(name, args, context, next) { + console.log("before invoke:", name, args); + var result = yield next(name, args, context); console.log("after invoke:", name, args, result); return result; }; diff --git a/example/middleware/log2/loghandler.js b/example/middleware/log2/loghandler.js index da05a2e..d182824 100644 --- a/example/middleware/log2/loghandler.js +++ b/example/middleware/log2/loghandler.js @@ -1,14 +1,7 @@ var hprose = require('hprose'); -module.exports = function(request, context, next) { +module.exports = function*(request, context, next) { console.log(hprose.BytesIO.toString(request)); - var response = next(request, context); - if (hprose.Future.isPromise(response)) { - response.then(function(data) { - console.log(hprose.BytesIO.toString(data)); - }); - } - else { - console.log(hprose.BytesIO.toString(response)); - } + var response = yield next(request, context); + console.log(hprose.BytesIO.toString(response)); return response; }; diff --git a/example/proxyclient.js b/example/proxyclient.js index 5140f69..66770c7 100644 --- a/example/proxyclient.js +++ b/example/proxyclient.js @@ -2,11 +2,33 @@ 'use strict'; var hprose = require('hprose'); -var client = hprose.Client.create('tcp://127.0.0.1:1234/', ['hello']); +var client = hprose.Client.create('tcp://127.0.0.1:1234/', []); client.fullDuplex = true; client.maxPoolSize = 1; -client.hello("World", function(result) { +var proxy = client.useService(); + +proxy.hello("World", function(result) { console.log(result); }, function(name, error) { console.error(error); }); + +var weeks = { + 'Monday': 'Mon', + 'Tuesday': 'Tue', + 'Wednesday': 'Wed', + 'Thursday': 'Thu', + 'Friday': 'Fri', + 'Saturday': 'Sat', + 'Sunday': 'Sun', +}; + +proxy.swapKeyAndValue.onsuccess = function(result, args) { + console.log(weeks.constructor, weeks); + console.log(result.constructor, result); + console.log(args.constructor, args); +}; + +proxy.swapKeyAndValue.byref = true; + +proxy.swapKeyAndValue(weeks); \ No newline at end of file diff --git a/example/serialize.js b/example/serialize.js index accce65..e9d6b3f 100644 --- a/example/serialize.js +++ b/example/serialize.js @@ -12,9 +12,9 @@ console.log(hprose.unserialize(hprose.serialize(12345678909876))); console.log(hprose.unserialize(hprose.serialize(Math.PI))); console.log(hprose.unserialize(hprose.serialize(new Date()))); console.log(hprose.serialize("Hello World!").toString()); -console.log(hprose.serialize("你好中国!").toString()); +console.log(hprose.serialize("你好中国!🇨🇳").toString()); console.log(hprose.unserialize(hprose.serialize("Hello World!"))); -console.log(hprose.unserialize(hprose.serialize("你好中国!"))); +console.log(hprose.unserialize(hprose.serialize("你好中国!🇨🇳"))); console.log(hprose.unserialize(hprose.serialize(new Buffer("你好"))).toString()); console.log(hprose.unserialize(new Buffer("l1234567890987654321234567890;"))); console.log(hprose.unserialize(hprose.serialize(NaN))); diff --git a/example/server.js b/example/server.js index f34ff0d..177badb 100644 --- a/example/server.js +++ b/example/server.js @@ -9,8 +9,8 @@ function hello(name, context) { return 'Hello ' + name + '! -- ' + context.socket.remoteAddress; } -function hello2(name) { - return 'Hello ' + name + '!'; +async function hello2(name) { + return await 'Hello ' + name + '!'; } function asyncHello(name, callback) { diff --git a/example/tcpclient4.js b/example/tcpclient4.js new file mode 100644 index 0000000..fa61533 --- /dev/null +++ b/example/tcpclient4.js @@ -0,0 +1,41 @@ +/*jshint node:true, eqeqeq:true */ +'use strict'; + +var hprose = require('../lib/hprose.js'); +var client = hprose.Client.create('tcp://127.0.0.1:4321/', ['hello']); +client.fullDuplex = true; +client.maxPoolSize = 1; +var log = hprose.Future.wrap(console.log, console); +log(client.hello("async world1")); +log(client.hello("async world2")); +log(client.hello("async world3")); +log(client.hello("async world4")); +log(client.hello("async world5")); +log(client.hello("async world6")); + +// 串行异步 +client.hello("world1") +.then(function(result) { + console.log(result); + return client.hello("world2"); +}) +.then(function(result) { + console.log(result); + return client.hello("world3"); +}) +.then(function(result) { + console.log(result); + return client.hello("world4"); +}) +.then(function(result) { + console.log(result); + return client.hello("world5"); +}) +.then(function(result) { + console.log(result); + return client.hello("world6"); +}) +.then(function(result) { + console.log(result); + client.close(); +}); diff --git a/example/tcpserver.js b/example/tcpserver.js index aeeff31..f029378 100644 --- a/example/tcpserver.js +++ b/example/tcpserver.js @@ -10,7 +10,7 @@ function hello(name, context) { } function hello2(name) { - return 'Hello ' + name + '!'; + return name; } function asyncHello(name, callback) { @@ -29,11 +29,21 @@ function getMaps() { function LogFilter() { this.inputFilter = function(value) { - console.log(hprose.BytesIO.toString(value)); + try { + console.log(hprose.BytesIO.toString(value)); + } + catch(e) { + console.log(hprose.toBinaryString(value)); + } return value; }; this.outputFilter = function(value) { - console.log(hprose.BytesIO.toString(value)); + try { + console.log(hprose.BytesIO.toString(value)); + } + catch(e) { + console.log(hprose.toBinaryString(value)); + } return value; }; } diff --git a/example/testco.js b/example/testco.js new file mode 100644 index 0000000..e0623cb --- /dev/null +++ b/example/testco.js @@ -0,0 +1,24 @@ +var co = require('../lib/hprose.js').co; + +function* sleep() { + return new Promise(function(resolve) { + setTimeout(resolve, 1); + }); +}; + +co(function*() { + + for(var i = 0; true; ++i) { + yield sleep(); + + if (i % 10000 === 0) { + console.log(process.memoryUsage()); + } + + } + +}).then(function() { + console.log('finished') +}, function(err) { + console.log('caught error: ', err.stack); +}); \ No newline at end of file diff --git a/example/testthunkify.js b/example/testthunkify.js new file mode 100644 index 0000000..43d63ee --- /dev/null +++ b/example/testthunkify.js @@ -0,0 +1,14 @@ +var thunkify = require('../lib/hprose.js').thunkify; + +var delay = thunkify(function (time, callback) { + setTimeout(callback, time); +}); + +var result = delay(100); +setTimeout(function () { + console.log('a'); + result(function () { + console.log('c'); + }); + console.log('b'); +}, 500); \ No newline at end of file diff --git a/example/timeclient.js b/example/timeclient.js index 85dd394..75f98ef 100644 --- a/example/timeclient.js +++ b/example/timeclient.js @@ -1,8 +1,8 @@ /*jshint node:true, eqeqeq:true */ 'use strict'; -var hprose = require('hprose'); -var client = hprose.Client.create("http://0.0.0.0:8080/"); +var hprose = require('../lib/hprose'); +var client = hprose.Client.create("http://127.0.0.1:8080/"); var count = 0; client.subscribe('time', function(date) { if (++count > 10) { diff --git a/example/timeoutclient.js b/example/timeoutclient.js new file mode 100644 index 0000000..4e905da --- /dev/null +++ b/example/timeoutclient.js @@ -0,0 +1,17 @@ +/*jshint node:true, eqeqeq:true */ +'use strict'; + +var hprose = require('../lib/hprose.js'); +var client = hprose.Client.create('tcp://127.0.0.1:4321/', ['sum']); +// client.fullDuplex = false; +client.keepAlive = false; +client.timeout = 600; + +client.sum(1, 2); +client.sum(1, 2).then(function(result) { + console.log("1 + 2 = " + result); +}).catch(function() { + client.sum(2, 3, function(result) { + console.log("2 + 3 = " + result); + }, { timeout: 20000 }); +}) \ No newline at end of file diff --git a/example/timeoutserver.js b/example/timeoutserver.js new file mode 100644 index 0000000..a32ba96 --- /dev/null +++ b/example/timeoutserver.js @@ -0,0 +1,19 @@ +/*jshint node:true, eqeqeq:true */ +'use strict'; + +var hprose = require('../lib/hprose.js'); + +function sum(a, b) { + var promise = new hprose.Future(); + setTimeout(function() { + promise.resolve(a + b); + }, 1000); + return promise; +} + +var server = hprose.Server.create("tcp://0.0.0.0:4321"); +server.addFunction(sum); +process.on('SIGINT', function() { + server.stop(); +}); +server.start(); diff --git a/example/timeserver.js b/example/timeserver.js index c8d49f0..b24264b 100644 --- a/example/timeserver.js +++ b/example/timeserver.js @@ -1,13 +1,30 @@ /*jshint node:true, eqeqeq:true */ 'use strict'; -var hprose = require('hprose'); +var hprose = require('../lib/hprose'); var server = hprose.Server.create("http://0.0.0.0:8080/"); server.publish('time'); + +function ClientListFilter() { + this.inputFilter = function(value) { + return value; + }; + this.outputFilter = function(value, context) { + console.log(context.clients.idlist('time')); + return value; + }; +} + +server.filter = new ClientListFilter(); + setInterval(function() { server.push('time', new Date()); }, 1000); + process.on('SIGINT', function() { - server.stop(); + console.log("server is stoping!"); + server.stop(); + process.exit(); }); + server.start(); diff --git a/example/ws_client.js b/example/ws_client.js new file mode 100644 index 0000000..d6950e0 --- /dev/null +++ b/example/ws_client.js @@ -0,0 +1,49 @@ +/*jshint node:true, eqeqeq:true */ +'use strict'; + +var hprose = require('../lib/hprose.js'); +var client = hprose.Client.create('ws://127.0.0.1:8080', []); +client.keepAlive = false; +client.simple = true; +var var_dump = hprose.Future.wrap(console.log, console); +var proxy = client.useService(['hello']); + +var_dump(proxy.hello('async world1')); +var_dump(proxy.hello('async world2')); +var_dump(proxy.hello('async world3')); +var_dump(proxy.hello('async world4')); +var_dump(proxy.hello('async world5')); +var_dump(proxy.hello('async world6')); + +proxy.hello("world1") +.then(function(result) { + console.log(result); + return proxy.hello("world2"); +}) +.then(function(result) { + console.log(result); + return proxy.hello("world3"); +}) +.then(function(result) { + console.log(result); + return proxy.hello("world4"); +}) +.then(function(result) { + console.log(result); + return proxy.hello("world5"); +}) +.then(function(result) { + console.log(result); + return proxy.hello("world6"); +}) +.then(function(result) { + console.log(result); +}); + +var_dump(proxy.hello('async world1')); +var_dump(proxy.hello('async world2')); +var_dump(proxy.hello('async world3')); +var_dump(proxy.hello('async world4')); +var_dump(proxy.hello('async world5')); +var_dump(proxy.hello('async world6')); + diff --git a/lib/client/Client.js b/lib/client/Client.js index 4902f7d..0a89255 100644 --- a/lib/client/Client.js +++ b/lib/client/Client.js @@ -6,27 +6,24 @@ | http://www.hprose.org/ | | | \**********************************************************/ - /**********************************************************\ * * * hprose/client/Client.js * * * * HproseClient for Node.js. * * * - * LastModified: Aug 18, 2015 * + * LastModified: Feb 6, 2018 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ -/*global Proxy */ +/* global Proxy */ 'use strict'; var util = require('util'); var parse = require('url').parse; var EventEmitter = require('events').EventEmitter; -var setImmediate = global.setImmediate; var Tags = global.hprose.Tags; var ResultMode = global.hprose.ResultMode; var BytesIO = global.hprose.BytesIO; @@ -34,6 +31,7 @@ var Writer = global.hprose.Writer; var Reader = global.hprose.Reader; var Future = global.hprose.Future; var slice = Function.prototype.call.bind(Array.prototype.slice); +var isObjectEmpty = global.hprose.isObjectEmpty; var GETFUNCTIONS = new Uint8Array(1); GETFUNCTIONS[0] = Tags.TagEnd; @@ -45,24 +43,56 @@ var s_string = 'string'; var s_number = 'number'; var s_function = 'function'; var s_object = 'object'; -var s_undefined = 'undefined'; -function HproseProxy(setFunction, ns) { +function HproseOldProxy(setFunction, ns) { + var settings = {}; + var target = {}; this.get = function(proxy, name) { - if (ns) name = ns + '_' + name; - return Proxy.createFunction( - new HproseProxy(setFunction, name), - setFunction(this, name) - ); + if (ns) { name = ns + '_' + name; } + if (name === 'then') { return undefined; } + if (!proxy.hasOwnProperty(name)) { + settings[name] = {}; + var handler = new HproseOldProxy(setFunction, name); + handler.set = function(proxy, prop, value) { + settings[name][prop] = value; + return true; + }; + target[name] = Proxy.createFunction(handler, setFunction(settings, name)); + } + return target[name]; + }; +} + +function HproseProxy(setFunction, ns) { + var settings = {}; + this.get = function(target, prop, receiver) { + var name = prop.toString(); + if (ns) { name = ns + '_' + name; } + if (name === 'then') { return undefined; } + if (!target.hasOwnProperty(name)) { + settings[name] = {}; + var handler = new HproseProxy(setFunction, name); + var func = setFunction(settings, name); + handler.apply = function(target, thisArg, argumentsList) { + return func.apply(null, argumentsList); + } + handler.set = function(target, prop, value, receiver) { + settings[name][prop] = value; + return true; + }; + target[name] = new Proxy(function() {}, handler); + } + return target[name]; }; } function Client(uri, functions, settings) { EventEmitter.call(this); + this.on('error', noop); // private members var _uri, - _uris = [], + _uriList = [], _index = -1, _byref = false, _simple = false, @@ -70,10 +100,12 @@ function Client(uri, functions, settings) { _retry = 10, _idempotent = false, _failswitch = false, + _failround = 0, _lock = false, _tasks = [], _useHarmonyMap = false, _onerror = noop, + _onfailswitch = noop, _filters = [], _batch = false, _batches = [], @@ -111,46 +143,92 @@ function Client(uri, functions, settings) { request = outputFilter(request, context); return _afterFilterHandler(request, context) .then(function(response) { - if (context.oneway) return; + if (context.oneway) { return; } return inputFilter(response, context); }); } function afterFilterHandler(request, context) { - return self.sendAndReceive(request, context); + return self.sendAndReceive(request, context).catchError(function(e) { + var response = retry(request, context); + if (response !== null) { + return response; + } + throw e; + }); } function sendAndReceive(request, context, onsuccess, onerror) { - _beforeFilterHandler(request, context) - .then(onsuccess, function(e) { - if (retry(request, context, onsuccess, onerror)) return; - onerror(e); - }); + _beforeFilterHandler(request, context).then(onsuccess, onerror); + } + + function failswitch() { + var n = _uriList.length; + if (n > 1) { + var i = _index + 1; + if (i >= n) { + i = 0; + _failround++; + } + _index = i; + _uri = _uriList[_index]; + } + else { + _failround++; + } + _onfailswitch(self); + self.emit('failswitch', self); } - function retry(data, context, onsuccess, onerror) { + function retry(data, context) { if (context.failswitch) { - if (++_index >= _uris.length) { - _index = 0; - _uri = _uris[_index]; + failswitch(); + } + if (context.idempotent && (context.retried < context.retry)) { + var interval = ++context.retried * 500; + if (context.failswitch) { + interval -= (_uriList.length - 1) * 500; + } + if (interval > 5000) { + interval = 5000; + } + if (interval > 0) { + return Future.delayed(interval, function() { + return afterFilterHandler(data, context); + }); + } + else { + return afterFilterHandler(data, context); } } - if (context.idempotent) { - if (--context.retry >= 0) { - var interval = (10 - context.retry) * 500; - if (context.retry > 10) interval = 500; - global.setTimeout(function() { - sendAndReceive(data, context, onsuccess, onerror); - }, interval); - return true; + return null; + } + + function normalizeFunctions(functions) { + var root = [Object.create(null)]; + for (var i in functions) { + var func = functions[i].split('_'); + var n = func.length - 1; + if (n > 0) { + var node = root; + for (var j = 0; j < n; j++) { + var f = func[j]; + if (node[0][f] === undefined) { + node[0][f] = [Object.create(null)]; + } + node = node[0][f]; + } + node.push(func[n]); } + root.push(functions[i]); } - return false; + return root; } function initService(stub) { var context = { retry: _retry, + retried: 0, idempotent: true, failswitch: true, timeout: _timeout, @@ -168,7 +246,7 @@ function Client(uri, functions, settings) { error = new Error(reader.readString()); break; case Tags.TagFunctions: - var functions = reader.readList(); + var functions = normalizeFunctions(reader.readList()); reader.checkTag(Tags.TagEnd); setFunctions(stub, functions); break; @@ -204,12 +282,12 @@ function Client(uri, functions, settings) { } function setMethods(stub, obj, namespace, name, methods) { - if (obj[name] !== undefined) return; + if (obj[name] !== undefined) { return; } obj[name] = {}; if (typeof(methods) === s_string || methods.constructor === Object) { methods = [methods]; } - if (util.isArray(methods)) { + if (Array.isArray(methods)) { for (var i = 0; i < methods.length; i++) { var m = methods[i]; if (typeof(m) === s_string) { @@ -217,7 +295,7 @@ function Client(uri, functions, settings) { } else { for (var n in m) { - setMethods(stub, obj[name], name + '_', n, m[n]); + setMethods(stub, obj[name], namespace + name + '_', n, m[n]); } } } @@ -242,7 +320,7 @@ function Client(uri, functions, settings) { function copyargs(src, dest) { var n = Math.min(src.length, dest.length); - for (var i = 0; i < n; ++i) dest[i] = src[i]; + for (var i = 0; i < n; ++i) { dest[i] = src[i]; } } function initContext(batch) { @@ -264,6 +342,7 @@ function Client(uri, functions, settings) { simple: _simple, timeout: _timeout, retry: _retry, + retried: 0, idempotent: _idempotent, failswitch: _failswitch, oneway: false, @@ -288,9 +367,9 @@ function Client(uri, functions, settings) { } var i = 0, n = args.length; for (; i < n; ++i) { - if (typeof args[i] === s_function) break; + if (typeof args[i] === s_function) { break; } } - if (i === n) return context; + if (i === n) { return context; } var extra = args.splice(i, n - i); context.onsuccess = extra[0]; n = extra.length; @@ -432,7 +511,7 @@ function Client(uri, functions, settings) { return function() { if (sync) { _lock = false; - setImmediate(function(tasks) { + process.nextTick(function(tasks) { tasks.forEach(function(task) { if ('settings' in task) { endBatch(task.settings) @@ -449,7 +528,7 @@ function Client(uri, functions, settings) { } function call(name, args, context) { - if (context.sync) _lock = true; + if (context.sync) { _lock = true; } var promise = Future.promise(function(resolve, reject) { _invokeHandler(name, args, context).then(function(result) { try { @@ -493,6 +572,7 @@ function Client(uri, functions, settings) { var context = { timeout: _timeout, retry: _retry, + retried: 0, idempotent: _idempotent, failswitch: _failswitch, oneway: false, @@ -615,9 +695,9 @@ function Client(uri, functions, settings) { }); } var batchSize = _batches.length; - if (batchSize === 0) return; + if (batchSize === 0) { return Future.value([]); } var context = getBatchContext(settings); - if (context.sync) _lock = true; + if (context.sync) { _lock = true; } var batches = _batches; _batches = []; var promise = Future.promise(function(resolve, reject) { @@ -672,15 +752,43 @@ function Client(uri, functions, settings) { _onerror = value; } } + function getOnFailswitch() { + return _onfailswitch; + } + function setOnFailswitch(value) { + if (typeof(value) === s_function) { + _onfailswitch = value; + } + } function getUri() { return _uri; } + function getUriList() { + return _uriList; + } + function setUriList(uriList) { + if (typeof(uriList) === s_string) { + _uriList = [uriList]; + } + else if (Array.isArray(uriList)) { + _uriList = uriList.slice(0); + _uriList.sort(function() { return Math.random() - 0.5; }); + } + else { + return; + } + _index = 0; + _uri = _uriList[_index]; + } function getFailswitch() { return _failswitch; } function setFailswitch(value) { _failswitch = !!value; } + function getFailround() { + return _failround; + } function getTimeout() { return _timeout; } @@ -779,6 +887,9 @@ function Client(uri, functions, settings) { _filters.splice(i, 1); return true; } + function filters() { + return _filters; + } function useService(uri, functions, create) { if (create === undefined) { if (typeof(functions) === s_boolean) { @@ -791,7 +902,7 @@ function Client(uri, functions, settings) { uri = false; } else if (uri && uri.constructor === Object || - util.isArray(uri)) { + Array.isArray(uri)) { functions = uri; uri = false; } @@ -811,15 +922,20 @@ function Client(uri, functions, settings) { (functions && functions.constructor === Object)) { functions = [functions]; } - if (util.isArray(functions)) { + if (Array.isArray(functions)) { setFunctions(stub, functions); } else { if (typeof(Proxy) === 'undefined') { - setImmediate(initService, stub); + process.nextTick(initService, stub); return _ready; } - stub = Proxy.create(new HproseProxy(setFunction)); + if ('create' in Proxy) { + stub = Proxy.create(new HproseOldProxy(setFunction)); + } + else { + stub = new Proxy({}, new HproseProxy(setFunction)); + } } _ready.resolve(stub); return stub; @@ -829,9 +945,9 @@ function Client(uri, functions, settings) { if ((argc < 1) || (typeof name !== s_string)) { throw new Error('name must be a string'); } - if (argc === 1) args = []; + if (argc === 1) { args = []; } if (argc === 2) { - if (!util.isArray(args)) { + if (!Array.isArray(args)) { var _args = []; if (typeof args !== s_function) { _args.push(noop); @@ -853,22 +969,18 @@ function Client(uri, functions, settings) { function ready(onComplete, onError) { return _ready.then(onComplete, onError); } - function getTopic(name, id, create) { + function getTopic(name, id) { if (_topics[name]) { var topics = _topics[name]; if (topics[id]) { return topics[id]; } - return null; - } - if (create) { - _topics[name] = Object.create(null); } return null; } - // subscribe(name, callback, timeout) - // subscribe(name, id, callback, timeout) - function subscribe(name, id, callback, timeout) { + // subscribe(name, callback, timeout, failswitch) + // subscribe(name, id, callback, timeout, failswitch) + function subscribe(name, id, callback, timeout, failswitch) { if (typeof name !== s_string) { throw new TypeError('topic name must be a string.'); } @@ -880,14 +992,14 @@ function Client(uri, functions, settings) { throw new TypeError('callback must be a function.'); } } + if (!_topics[name]) { + _topics[name] = Object.create(null); + } if (typeof id === s_function) { timeout = callback; callback = id; - if (_id === null) { - _id = autoId(); - } - _id.then(function(id) { - subscribe(name, id, callback, timeout); + autoId().then(function(id) { + subscribe(name, id, callback, timeout, failswitch); }); return; } @@ -896,23 +1008,24 @@ function Client(uri, functions, settings) { } if (Future.isPromise(id)) { id.then(function(id) { - subscribe(name, id, callback, timeout); + subscribe(name, id, callback, timeout, failswitch); }); return; } - if (timeout === undefined) timeout = _timeout; - var topic = getTopic(name, id, true); + // Default subscribe timeout is 5 minutes. + if (timeout === undefined) { timeout = 300000; } + var topic = getTopic(name, id); if (topic === null) { var cb = function() { _invoke(self, name, [id, topic.handler, cb, { idempotent: true, - failswitch: false, + failswitch: failswitch, timeout: timeout }], false); }; topic = { handler: function(result) { - var topic = getTopic(name, id, false); + var topic = getTopic(name, id); if (topic) { if (result !== null) { var callbacks = topic.callbacks; @@ -923,7 +1036,7 @@ function Client(uri, functions, settings) { catch (e) {} } } - if (getTopic(name, id, false) !== null) cb(); + if (getTopic(name, id) !== null) { cb(); } } }, callbacks: [callback] @@ -1000,12 +1113,28 @@ function Client(uri, functions, settings) { else { delTopic(_topics[name], id, callback); } + if (isObjectEmpty(_topics[name])) { + delete _topics[name]; + } + } + function isSubscribed(name) { + return !!_topics[name]; + } + function subscribedList() { + var list = []; + for (var name in _topics) { + list.push(name); + } + return list; } function getId() { return _id; } function autoId() { - return _invoke(self, '#', [], false); + if (_id === null) { + _id = _invoke(self, '#', [], false); + } + return _id; } autoId.sync = true; autoId.idempotent = true; @@ -1015,14 +1144,7 @@ function Client(uri, functions, settings) { _invokeHandler = _invokeHandlers.reduceRight( function(next, handler) { return function(name, args, context) { - try { - var result = handler(name, args, context, next); - if (Future.isFuture(result)) return result; - return Future.value(result); - } - catch (e) { - return Future.error(e); - } + return Future.toPromise(handler(name, args, context, next)); }; }, invokeHandler); } @@ -1031,14 +1153,7 @@ function Client(uri, functions, settings) { _batchInvokeHandler = _batchInvokeHandlers.reduceRight( function(next, handler) { return function(batches, context) { - try { - var result = handler(batches, context, next); - if (Future.isFuture(result)) return result; - return Future.value(result); - } - catch (e) { - return Future.error(e); - } + return Future.toPromise(handler(batches, context, next)); }; }, batchInvokeHandler); } @@ -1047,14 +1162,7 @@ function Client(uri, functions, settings) { _beforeFilterHandler = _beforeFilterHandlers.reduceRight( function(next, handler) { return function(request, context) { - try { - var response = handler(request, context, next); - if (Future.isFuture(response)) return response; - return Future.value(response); - } - catch (e) { - return Future.error(e); - } + return Future.toPromise(handler(request, context, next)); }; }, beforeFilterHandler); } @@ -1063,14 +1171,7 @@ function Client(uri, functions, settings) { _afterFilterHandler = _afterFilterHandlers.reduceRight( function(next, handler) { return function(request, context) { - try { - var response = handler(request, context, next); - if (Future.isFuture(response)) return response; - return Future.value(response); - } - catch (e) { - return Future.error(e); - } + return Future.toPromise(handler(request, context, next)); }; }, afterFilterHandler); } @@ -1100,11 +1201,13 @@ function Client(uri, functions, settings) { }); Object.defineProperties(this, { '#': { value: autoId }, - onError: { get: getOnError, set: setOnError }, onerror: { get: getOnError, set: setOnError }, + onfailswitch: { get: getOnFailswitch, set: setOnFailswitch }, uri: { get: getUri }, + uriList: { get: getUriList, set: setUriList }, id: { get: getId }, failswitch: { get: getFailswitch, set: setFailswitch }, + failround: { get: getFailround }, timeout: { get: getTimeout, set: setTimeout }, retry: { get: getRetry, set: setRetry }, idempotent: { get: getIdempotent, set: setIdempotent }, @@ -1117,11 +1220,14 @@ function Client(uri, functions, settings) { filter: { get: getFilter, set: setFilter }, addFilter: { value: addFilter }, removeFilter: { value: removeFilter }, + filters: { get: filters }, useService: { value: useService }, invoke: { value: invoke }, ready: { value: ready }, subscribe: {value: subscribe }, unsubscribe: {value: unsubscribe }, + isSubscribed: { value : isSubscribed }, + subscribedList: { value : subscribedList }, use: { value: use }, batch: { value: batch }, beforeFilter: { value: beforeFilter }, @@ -1137,15 +1243,9 @@ function Client(uri, functions, settings) { } }); } - if (typeof(uri) === s_string) { - _uris = [uri]; - _index = 0; - useService(uri, functions); - } - else if (util.isArray(uri)) { - _uris = uri; - _index = Math.floor(Math.random() * _uris.length); - useService(_uris[_index], functions); + if (uri) { + setUriList(uri); + useService(functions); } } } @@ -1185,7 +1285,7 @@ function create(uri, functions, settings) { if (typeof uri === 'string') { checkuri(uri); } - else if (util.isArray(uri)) { + else if (Array.isArray(uri)) { uri.forEach(function(uri) { checkuri(uri); }); throw new Error('Not support multiple protocol.'); } diff --git a/lib/client/HttpClient.js b/lib/client/HttpClient.js index 3a51591..84c6f68 100644 --- a/lib/client/HttpClient.js +++ b/lib/client/HttpClient.js @@ -13,12 +13,11 @@ * * * Hprose Http Client for Node.js. * * * - * LastModified: Aug 5, 2015 * + * LastModified: Dec 4, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); @@ -27,7 +26,6 @@ var https = require('https'); var parse = require('url').parse; var TimeoutError = require('../common/TimeoutError'); -var setImmediate = global.setImmediate; var Client = global.hprose.Client; var BytesIO = global.hprose.BytesIO; var Future = global.hprose.Future; @@ -43,12 +41,12 @@ function setCookie(headers, host) { cookies = value.replace(/(^\s*)|(\s*$)/g, '').split(';'); cookie = {}; value = cookies[0].replace(/(^\s*)|(\s*$)/g, '').split('=', 2); - if (value[1] === undefined) value[1] = null; + if (value[1] === undefined) { value[1] = null; } cookie.name = value[0]; cookie.value = value[1]; for (i = 1; i < cookies.length; i++) { value = cookies[i].replace(/(^\s*)|(\s*$)/g, '').split('=', 2); - if (value[1] === undefined) value[1] = null; + if (value[1] === undefined) { value[1] = null; } cookie[value[0].toUpperCase()] = value[1]; } // Tomcat can return SetCookie2 with path wrapped in " @@ -127,7 +125,27 @@ function HttpClient(uri, functions, settings) { var self = this; - function send(request, future) { + function getRequestHeader(headers) { + var header = Object.create(null); + var name, value; + for (name in _header) { + header[name] = _header[name]; + } + if (headers) { + for (name in headers) { + value = headers[name]; + if (Array.isArray(value)) { + header[name] = value.join(', '); + } + else { + header[name] = value; + } + } + } + return header; + } + + function send(request, future, context) { request = BytesIO.toBuffer(request); var options = parse(self.uri); var protocol = options.protocol; @@ -149,16 +167,14 @@ function HttpClient(uri, functions, settings) { options[key] = self.options[key]; } options.method = 'POST'; - options.headers = Object.create(null); - for (var name in _header) { - options.headers[name] = _header[name]; - } + options.headers = getRequestHeader(context.httpHeader); options.headers['Content-Length'] = request.length; var cookie = getCookie(options.host, options.path, secure); if (cookie !== '') { options.headers.Cookie = cookie; } var req = client.request(options, function(resp) { + context.httpHeader = resp.headers; var bytes = new BytesIO(); resp.on('data', function(data) { bytes.write(data); }); resp.on('end', function() { @@ -179,11 +195,11 @@ function HttpClient(uri, functions, settings) { return req; } - function sendAndReceive(request, env) { + function sendAndReceive(request, context) { var future = new Future(); - var req = send(request, future); - if (env.timeout > 0) { - future = future.timeout(env.timeout).catchError(function(e) { + var req = send(request, future, context); + if (context.timeout > 0) { + future = future.timeout(context.timeout).catchError(function(e) { req.removeAllListeners('error'); req.on('error', noop); req.abort(); @@ -193,7 +209,7 @@ function HttpClient(uri, functions, settings) { return e instanceof TimeoutError; }); } - if (env.oneway) future.resolve(); + if (context.oneway) { future.resolve(); } return future; } @@ -229,11 +245,11 @@ function create(uri, functions, settings) { if (typeof uri === 'string') { checkuri(uri); } - else if (util.isArray(uri)) { + else if (Array.isArray(uri)) { uri.forEach(function(uri) { checkuri(uri); }); } else { - return new Error('You should set server uri first!'); + throw new Error('You should set server uri first!'); } return new HttpClient(uri, functions, settings); } diff --git a/lib/client/SocketClient.js b/lib/client/SocketClient.js index 0749418..5a9a9d8 100644 --- a/lib/client/SocketClient.js +++ b/lib/client/SocketClient.js @@ -13,19 +13,17 @@ * * * Hprose Socket Client for Node.js. * * * - * LastModified: Aug 14, 2015 * + * LastModified: Dec 2, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); var net = require('net'); var tls = require('tls'); var parse = require('url').parse; -var EventEmitter = require('events').EventEmitter; var TimeoutError = require('../common/TimeoutError'); var Client = global.hprose.Client; @@ -89,7 +87,7 @@ Object.defineProperties(SocketTransporter.prototype, { protocol === 'tcp6:') { socket = net; options.host = parser.hostname; - options.port = parseInt(parser.port); + options.port = parseInt(parser.port, 10); if (protocol === 'tcp4:') { options.family = 4; } @@ -103,7 +101,7 @@ Object.defineProperties(SocketTransporter.prototype, { protocol === 'tls:') { socket = tls; options.host = parser.hostname; - options.port = parseInt(parser.port); + options.port = parseInt(parser.port, 10); if (protocol === 'tcp4s:') { options.family = 4; } @@ -139,7 +137,7 @@ FullDuplexSocketTransporter.prototype = Object.create( fetch: { value: function() { var pool = this.pool; while (pool.length > 0) { - var conn = pool.shift(); + var conn = pool.pop(); if (conn.connected) { if (conn.count === 0) { conn.removeAllListeners('timeout'); @@ -194,19 +192,21 @@ FullDuplexSocketTransporter.prototype = Object.create( sendNext: { value: function(conn) { if (conn.count < 10) { if (this.requests.length > 0) { - var request = this.requests.shift(); + var request = this.requests.pop(); request.push(conn); this.send.apply(this, request); } else { - this.pool.push(conn); + if (this.pool.lastIndexOf(conn) < 0) { + this.pool.push(conn); + } } } } }, - send: { value: function(request, future, id, env, conn) { + send: { value: function(request, future, id, context, conn) { var self = this; - var timeout = env.timeout; + var timeout = context.timeout; if (timeout > 0) { conn.timeoutIds[id] = global.setTimeout(function() { self.clean(conn, id); @@ -233,11 +233,11 @@ FullDuplexSocketTransporter.prototype = Object.create( getNextId: { value: function() { return (this.nextid < 0x7fffffff) ? ++this.nextid : this.nextid = 0; } }, - sendAndReceive: { value: function(request, future, env) { + sendAndReceive: { value: function(request, future, context) { var conn = this.fetch(); var id = this.getNextId(); if (conn) { - this.send(request, future, id, env, conn); + this.send(request, future, id, context, conn); } else if (this.size < this.client.maxPoolSize) { conn = this.create(); @@ -250,11 +250,11 @@ FullDuplexSocketTransporter.prototype = Object.create( conn.removeAllListeners('error'); conn.connected = true; self.init(conn); - self.send(request, future, id, env, conn); + self.send(request, future, id, context, conn); }); } else { - this.requests.push([request, future, id, env]); + this.requests.push([request, future, id, context]); } } } }); @@ -270,7 +270,7 @@ HalfDuplexSocketTransporter.prototype = Object.create( fetch: { value: function() { var pool = this.pool; while (pool.length > 0) { - var conn = pool.shift(); + var conn = pool.pop(); if (conn.connected) { conn.removeAllListeners('timeout'); conn.ref(); @@ -280,12 +280,14 @@ HalfDuplexSocketTransporter.prototype = Object.create( return null; } }, recycle: { value: function(conn) { - conn.unref(); - conn.setTimeout(this.client.poolTimeout, function() { - conn.connected = false; - conn.end(); - }); - this.pool.push(conn); + if (this.pool.lastIndexOf(conn) < 0) { + conn.unref(); + conn.setTimeout(this.client.poolTimeout, function() { + conn.connected = false; + conn.end(); + }); + this.pool.push(conn); + } } }, clean: { value: function(conn) { conn.removeAllListeners('receive'); @@ -297,7 +299,7 @@ HalfDuplexSocketTransporter.prototype = Object.create( } }, sendNext: { value: function(conn) { if (this.requests.length > 0) { - var request = this.requests.shift(); + var request = this.requests.pop(); request.push(conn); this.send.apply(this, request); } @@ -305,13 +307,14 @@ HalfDuplexSocketTransporter.prototype = Object.create( this.recycle(conn); } } }, - send: { value: function(request, future, env, conn) { + send: { value: function(request, future, context, conn) { var self = this; - var timeout = env.timeout; + var timeout = context.timeout; if (timeout > 0) { conn.timeoutId = global.setTimeout(function() { self.clean(conn); - self.recycle(conn); + conn.connected = false; + conn.end(); future.reject(new TimeoutError('timeout')); }, timeout); } @@ -328,16 +331,16 @@ HalfDuplexSocketTransporter.prototype = Object.create( var len = request.length; var buf = new Buffer(4 + len); - buf.writeUInt32BE(len, 0); + buf.writeInt32BE(len, 0); for (var i = 0; i < len; i++) { buf[i + 4] = request[i]; } conn.write(buf); } }, - sendAndReceive: { value: function(request, future, env) { + sendAndReceive: { value: function(request, future, context) { var conn = this.fetch(); if (conn) { - this.send(request, future, env, conn); + this.send(request, future, context, conn); } else if (this.size < this.client.maxPoolSize) { conn = this.create(); @@ -349,11 +352,11 @@ HalfDuplexSocketTransporter.prototype = Object.create( conn.once('connect', function() { conn.removeAllListeners('error'); conn.connected = true; - self.send(request, future, env, conn); + self.send(request, future, context, conn); }); } else { - this.requests.push([request, future, env]); + this.requests.push([request, future, context]); } } } }); @@ -419,21 +422,21 @@ function SocketClient(uri, functions, settings) { } } - function sendAndReceive(request, env) { + function sendAndReceive(request, context) { var future = new Future(); if (_fullDuplex) { if ((fdtrans === null) || (fdtrans.uri !== self.uri)) { fdtrans = new FullDuplexSocketTransporter(self); } - fdtrans.sendAndReceive(request, future, env); + fdtrans.sendAndReceive(request, future, context); } else { if ((hdtrans === null) || (hdtrans.uri !== self.uri)) { hdtrans = new HalfDuplexSocketTransporter(self); } - hdtrans.sendAndReceive(request, future, env); + hdtrans.sendAndReceive(request, future, context); } - if (env.oneway) future.resolve(); + if (context.oneway) { future.resolve(); } return future; } @@ -465,11 +468,11 @@ function create(uri, functions, settings) { if (typeof uri === 'string') { checkuri(uri); } - else if (util.isArray(uri)) { + else if (Array.isArray(uri)) { uri.forEach(function(uri) { checkuri(uri); }); } else { - return new Error('You should set server uri first!'); + throw new Error('You should set server uri first!'); } return new SocketClient(uri, functions, settings); } diff --git a/lib/client/WebSocketClient.js b/lib/client/WebSocketClient.js index b10a68d..0d8b60d 100644 --- a/lib/client/WebSocketClient.js +++ b/lib/client/WebSocketClient.js @@ -12,16 +12,17 @@ * * * Hprose WebSocket Client for HTML5. * * * - * LastModified: Aug 15, 2015 * + * LastModified: Aug 20, 2017 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); var parse = require('url').parse; + +/*jshint -W079*/ var WebSocket = require('ws'); var Client = global.hprose.Client; @@ -29,8 +30,6 @@ var BytesIO = global.hprose.BytesIO; var Future = global.hprose.Future; var TimeoutError = require('../common/TimeoutError'); -function noop(){} - function WebSocketClient(uri, functions, settings) { if (this.constructor !== WebSocketClient) { return new WebSocketClient(uri, functions, settings); @@ -39,7 +38,6 @@ function WebSocketClient(uri, functions, settings) { var _id = 0; var _count = 0; - var _reqcount = 0; var _futures = []; var _requests = []; var _ready = null; @@ -71,7 +69,7 @@ function WebSocketClient(uri, functions, settings) { function onopen() { _ready.resolve(); } - function onmessage(data, flags) { + function onmessage(data) { var bytes = new BytesIO(data); var id = bytes.readInt32BE(); var future = _futures[id]; @@ -82,11 +80,11 @@ function WebSocketClient(uri, functions, settings) { } if ((_count < 100) && (_requests.length > 0)) { ++_count; - var request = _requests.shift(); + var request = _requests.pop(); _ready.then(function() { send(request[0], request[1]); }); } if (_count === 0) { - if (!self.keepAlive) close(); + if (!self.keepAlive) { close(); } } } function onclose(code, message) { @@ -109,25 +107,26 @@ function WebSocketClient(uri, functions, settings) { ws.on('error', onerror); ws.on('close', onclose); } - function sendAndReceive(request, env) { - if (ws === null || - ws.readyState === WebSocket.CLOSING || - ws.readyState === WebSocket.CLOSED) { - connect(); - } + function sendAndReceive(request, context) { var id = getNextId(); var future = new Future(); _futures[id] = future; - if (env.timeout > 0) { - future = future.timeout(env.timeout).catchError(function(e) { + if (context.timeout > 0) { + future = future.timeout(context.timeout).catchError(function(e) { delete _futures[id]; --_count; + close(); throw e; }, function(e) { return e instanceof TimeoutError; }); } + if (ws === null || + ws.readyState === WebSocket.CLOSING || + ws.readyState === WebSocket.CLOSED) { + connect(); + } if (_count < 100) { ++_count; _ready.then(function() { send(id, request); }); @@ -135,7 +134,7 @@ function WebSocketClient(uri, functions, settings) { else { _requests.push([id, request]); } - if (env.oneway) future.resolve(); + if (context.oneway) { future.resolve(); } return future; } function close() { @@ -149,7 +148,7 @@ function WebSocketClient(uri, functions, settings) { } Object.defineProperties(this, { sendAndReceive: { value: sendAndReceive }, - close: { value: close }, + close: { value: close } }); } @@ -166,11 +165,11 @@ function create(uri, functions, settings) { if (typeof uri === 'string') { checkuri(uri); } - else if (util.isArray(uri)) { + else if (Array.isArray(uri)) { uri.forEach(function(uri) { checkuri(uri); }); } else { - return new Error('You should set server uri first!'); + throw new Error('You should set server uri first!'); } return new WebSocketClient(uri, functions, settings); } diff --git a/lib/common/Future.js b/lib/common/Future.js index c3fb4d2..7176bde 100644 --- a/lib/common/Future.js +++ b/lib/common/Future.js @@ -13,440 +13,612 @@ * * * Hprose Future for Node.js. * * * - * LastModified: Aug 2, 2015 * + * LastModified: Dec 5, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ -'use strict'; +(function() { + 'use strict'; -var util = require('util'); -var TimeoutError = require('./TimeoutError'); + var TimeoutError = require('./TimeoutError'); -var PENDING = 0; -var FULFILLED = 1; -var REJECTED = 2; + var PENDING = 0; + var FULFILLED = 1; + var REJECTED = 2; -var hasPromise = 'Promise' in global; -var setImmediate = global.setImmediate; -var setTimeout = global.setTimeout; -var clearTimeout = global.clearTimeout; -var foreach = Function.prototype.call.bind(Array.prototype.forEach); -var slice = Function.prototype.call.bind(Array.prototype.slice); + var hasPromise = 'Promise' in global; + var setTimeout = global.setTimeout; + var clearTimeout = global.clearTimeout; + var foreach = Array.prototype.forEach; + var slice = Array.prototype.slice; -function Future(computation) { - Object.defineProperties(this, { - _subscribers: { value: [] }, - resolve: { value: this.resolve.bind(this) }, - reject: { value: this.reject.bind(this) }, - }); - var self = this; - if (typeof computation === 'function') { - setImmediate(function() { + function Future(computation) { + var self = this; + Object.defineProperties(this, { + _subscribers: { value: [] }, + resolve: { value: this.resolve.bind(this) }, + reject: { value: this.reject.bind(this) } + }); + if (typeof computation === 'function') { + process.nextTick(function() { + try { + self.resolve(computation()); + } + catch(e) { + self.reject(e); + } + }); + } + } + + function isFuture(obj) { + return obj instanceof Future; + } + + function toFuture(obj) { + return isFuture(obj) ? obj : value(obj); + } + + function isPromise(obj) { + return 'function' === typeof obj.then; + } + + function delayed(duration, value) { + var computation = (typeof value === 'function') ? + value : + function() { return value; }; + var future = new Future(); + setTimeout(function() { try { - self.resolve(computation()); + future.resolve(computation()); } catch(e) { - self.reject(e); + future.reject(e); } - }); + }, duration); + return future; } -} -function isFuture(obj) { - return obj instanceof Future; -} + function error(e) { + var future = new Future(); + future.reject(e); + return future; + } -function isPromise(obj) { - return isFuture(obj) || (hasPromise && (obj instanceof global.Promise) && (typeof (obj.then === 'function'))); -} + function value(v) { + var future = new Future(); + future.resolve(v); + return future; + } -function delayed(duration, value) { - var computation = (typeof value === 'function') ? - value : - function() { return value; }; - var future = new Future(); - setTimeout(function() { + function sync(computation) { try { - future.resolve(computation()); + var result = computation(); + return value(result); } catch(e) { - future.reject(e); + return error(e); } - }, duration); - return future; -} - -function error(e) { - var future = new Future(); - future.reject(e); - return future; -} - -function value(v) { - var future = new Future(); - future.resolve(v); - return future; -} - -function sync(computation) { - try { - var result = computation(); - return value(result); - } - catch(e) { - return error(e); - } -} - -function promise(executor) { - var future = new Future(); - executor(future.resolve, future.reject); - return future; -} - -function arraysize(array) { - var size = 0; - foreach(array, function() { ++size; }); - return size; -} - -function all(array) { - array = isPromise(array) ? array : value(array); - return array.then(function(array) { - var n = array.length; - var count = arraysize(array); - var result = new Array(n); - if (count === 0) return value(result); + } + + function promise(executor) { var future = new Future(); - foreach(array, function(element, index) { - var f = (isPromise(element) ? element : value(element)); - f.then(function(value) { - result[index] = value; - if (--count === 0) { - future.resolve(result); - } - }, - future.reject); - }); + executor(future.resolve, future.reject); return future; - }); -} + } -function join() { - return all(arguments); -} + function arraysize(array) { + var size = 0; + foreach.call(array, function() { ++size; }); + return size; + } -function race(array) { - array = isPromise(array) ? array : value(array); - return array.then(function(array) { - var future = new Future(); - foreach(array, function(element) { - var f = (isPromise(element) ? element : value(element)); - f.then(future.resolve, future.reject); + function all(array) { + return toFuture(array).then(function(array) { + var n = array.length; + var count = arraysize(array); + var result = new Array(n); + if (count === 0) { return result; } + var future = new Future(); + foreach.call(array, function(element, index) { + toFuture(element).then(function(value) { + result[index] = value; + if (--count === 0) { + future.resolve(result); + } + }, + future.reject); + }); + return future; }); - return future; - }); -} - -function any(array) { - array = isPromise(array) ? array : value(array); - return array.then(function(array) { - var n = array.length; - var count = arraysize(array); - if (count === 0) { - throw new RangeError('any(): array must not be empty'); - } - var reasons = new Array(n); - var future = new Future(); - foreach(array, function(element, index) { - var f = (isPromise(element) ? element : value(element)); - f.then(future.resolve, function(e) { - reasons[index] = e; - if (--count === 0) { - future.reject(reasons); - } + } + + function join() { + return all(arguments); + } + + function race(array) { + return toFuture(array).then(function(array) { + var future = new Future(); + foreach.call(array, function(element) { + toFuture(element).fill(future); }); + return future; }); - return future; - }); -} - -function settle(array) { - array = isPromise(array) ? array : value(array); - return array.then(function(array) { - var n = array.length; - var count = arraysize(array); - var result = new Array(n); - if (count === 0) return value(result); - var future = new Future(); - foreach(array, function(element, index) { - var f = (isPromise(element) ? element : value(element)); - f.whenComplete(function() { - result[index] = f.inspect(); - if (--count === 0) { - future.resolve(result); - } + } + + function any(array) { + return toFuture(array).then(function(array) { + var n = array.length; + var count = arraysize(array); + if (count === 0) { + throw new RangeError('any(): array must not be empty'); + } + var reasons = new Array(n); + var future = new Future(); + foreach.call(array, function(element, index) { + toFuture(element).then(future.resolve, function(e) { + reasons[index] = e; + if (--count === 0) { + future.reject(reasons); + } + }); }); + return future; }); - return future; - }); -} + } -function attempt(handler/*, arg1, arg2, ... */) { - var args = slice(arguments, 1); - return all(args).then(function(args) { - return handler.apply(undefined, args); - }); -} + function settle(array) { + return toFuture(array).then(function(array) { + var n = array.length; + var count = arraysize(array); + var result = new Array(n); + if (count === 0) { return result; } + var future = new Future(); + foreach.call(array, function(element, index) { + var f = toFuture(element); + f.complete(function() { + result[index] = f.inspect(); + if (--count === 0) { + future.resolve(result); + } + }); + }); + return future; + }); + } -function run(handler, thisArg/*, arg1, arg2, ... */) { - var args = slice(arguments, 2); - return all(args).then(function(args) { - return handler.apply(thisArg, args); - }); -} + function attempt(handler/*, arg1, arg2, ... */) { + var thisArg = (function() { return this; })(); + var args = slice.call(arguments, 1); + return all(args).then(function(args) { + return handler.apply(thisArg, args); + }); + } -function wrap(handler, thisArg) { - return function() { - return all(arguments).then(function(args) { + function run(handler, thisArg/*, arg1, arg2, ... */) { + var args = slice.call(arguments, 2); + return all(args).then(function(args) { return handler.apply(thisArg, args); }); - }; -} + } -function forEach(array, callback, thisArg) { - return all(array).then(function(array) { - return array.forEach(callback, thisArg); - }); -} + function isGenerator(obj) { + if (!obj) { + return false; + } + return 'function' == typeof obj.next && 'function' == typeof obj['throw']; + } -function every(array, callback, thisArg) { - return all(array).then(function(array) { - return array.every(callback, thisArg); - }); -} + function isGeneratorFunction(obj) { + if (!obj) { + return false; + } + var constructor = obj.constructor; + if (!constructor) { + return false; + } + if ('GeneratorFunction' === constructor.name || + 'GeneratorFunction' === constructor.displayName) { + return true; + } + return isGenerator(constructor.prototype); + } -function some(array, callback, thisArg) { - return all(array).then(function(array) { - return array.some(callback, thisArg); - }); -} + function getThunkCallback(future) { + return function(err, res) { + if (err instanceof Error) { + return future.reject(err); + } + if (arguments.length < 2) { + return future.resolve(err); + } + if (err === null || err === undefined) { + res = slice.call(arguments, 1); + } + else { + res = slice.call(arguments, 0); + } + if (res.length == 1) { + future.resolve(res[0]); + } + else { + future.resolve(res); + } + }; + } -function filter(array, callback, thisArg) { - return all(array).then(function(array) { - return array.filter(callback, thisArg); - }); -} + function thunkToPromise(fn) { + if (isGeneratorFunction(fn) || isGenerator(fn)) { + return co(fn); + } + var thisArg = (function() { return this; })(); + var future = new Future(); + fn.call(thisArg, getThunkCallback(future)); + return future; + } -function map(array, callback, thisArg) { - return all(array).then(function(array) { - return array.map(callback, thisArg); - }); -} + function thunkify(fn) { + return function() { + var args = slice.call(arguments, 0); + var thisArg = this; + var results = new Future(); + args.push(function() { + thisArg = this; + results.resolve(arguments); + }); + try { + fn.apply(this, args); + } + catch (err) { + results.resolve([err]); + } + return function(done) { + results.then(function(results) { + done.apply(thisArg, results); + }); + }; + }; + } -function reduce(array, callback, initialValue) { - if (arguments.length > 2) { - return all(array).then(function(array) { - if (!isPromise(initialValue)) { - initialValue = value(initialValue); + function promisify(fn) { + return function() { + var args = slice.call(arguments, 0); + var future = new Future(); + args.push(getThunkCallback(future)); + try { + fn.apply(this, args); + } + catch (err) { + future.reject(err); + } + return future; + }; + } + + function toPromise(obj) { + if (isGeneratorFunction(obj) || isGenerator(obj)) { + return co(obj); + } + return toFuture(obj); + } + + function co(gen) { + var thisArg = (function() { return this; })(); + if (typeof gen === 'function') { + var args = slice.call(arguments, 1); + gen = gen.apply(thisArg, args); + } + + if (!gen || typeof gen.next !== 'function') { + return toFuture(gen); + } + + var future = new Future(); + + function onFulfilled(res) { + try { + next(gen.next(res)); + } + catch (e) { + future.reject(e); + } + } + + function onRejected(err) { + try { + next(gen['throw'](err)); } - return initialValue.then(function(value) { - return array.reduce(callback, value); + catch (e) { + future.reject(e); + } + } + + function next(ret) { + if (ret.done) { + future.resolve(ret.value); + } + else { + (('function' == typeof ret.value) ? + thunkToPromise(ret.value) : + toPromise(ret.value)).then(onFulfilled, onRejected); + } + } + + onFulfilled(); + + return future; + } + + function wrap(handler, thisArg) { + return function() { + thisArg = thisArg || this; + return all(arguments).then(function(args) { + var result = handler.apply(thisArg, args); + if (isGeneratorFunction(result) || isGenerator(result)) { + return co.call(thisArg, result); + } + return result; }); + }; + } + + co.wrap = wrap; + + function forEach(array, callback, thisArg) { + thisArg = thisArg || (function() { return this; })(); + return all(array).then(function(array) { + return array.forEach(callback, thisArg); }); } - return all(array).then(function(array) { - return array.reduce(callback); - }); -} -function reduceRight(array, callback, initialValue) { - if (arguments.length > 2) { + function every(array, callback, thisArg) { + thisArg = thisArg || (function() { return this; })(); return all(array).then(function(array) { - if (!isPromise(initialValue)) { - initialValue = value(initialValue); - } - return initialValue.then(function(value) { - return array.reduceRight(callback, value); - }); + return array.every(callback, thisArg); }); } - return all(array).then(function(array) { - return array.reduceRight(callback); - }); -} - -Object.defineProperties(Future, { - // port from Dart - delayed: { value: delayed }, - error: { value: error }, - sync: { value: sync }, - value: { value: value }, - // Promise compatible - all: { value: all }, - race: { value: race }, - resolve: { value: value }, - reject: { value: error }, - // extended methods - promise: { value: promise }, - isFuture: { value: isFuture }, - isPromise: { value: isPromise }, - join: { value: join }, - any: { value: any }, - settle: { value: settle }, - attempt: { value: attempt }, - run: { value: run }, - wrap: { value: wrap }, - // for array - forEach: { value: forEach }, - every: { value: every }, - some: { value: some }, - filter: { value: filter }, - map: { value: map }, - reduce: { value: reduce }, - reduceRight: { value: reduceRight } -}); - -function _call(callback, next, x) { - setImmediate(function() { - try { - var r = callback(x); - next.resolve(r); + + function some(array, callback, thisArg) { + thisArg = thisArg || (function() { return this; })(); + return all(array).then(function(array) { + return array.some(callback, thisArg); + }); + } + + function filter(array, callback, thisArg) { + thisArg = thisArg || (function() { return this; })(); + return all(array).then(function(array) { + return array.filter(callback, thisArg); + }); + } + + function map(array, callback, thisArg) { + thisArg = thisArg || (function() { return this; })(); + return all(array).then(function(array) { + return array.map(callback, thisArg); + }); + } + + function reduce(array, callback, initialValue) { + if (arguments.length > 2) { + return all(array).then(function(array) { + return toFuture(initialValue).then(function(value) { + return array.reduce(callback, value); + }); + }); } - catch(e) { - next.reject(e); + return all(array).then(function(array) { + return array.reduce(callback); + }); + } + + function reduceRight(array, callback, initialValue) { + if (arguments.length > 2) { + return all(array).then(function(array) { + return toFuture(initialValue).then(function(value) { + return array.reduceRight(callback, value); + }); + }); } - }); -} + return all(array).then(function(array) { + return array.reduceRight(callback); + }); + } + + function indexOf(array, searchElement, fromIndex) { + return all(array).then(function(array) { + return toFuture(searchElement).then(function(searchElement) { + return array.indexOf(searchElement, fromIndex); + }); + }); + } -function _reject(onreject, next, e) { - if (onreject) { - _call(onreject, next, e); + function lastIndexOf(array, searchElement, fromIndex) { + return all(array).then(function(array) { + return toFuture(searchElement).then(function(searchElement) { + if (fromIndex === undefined) { + fromIndex = array.length - 1; + } + return array.lastIndexOf(searchElement, fromIndex); + }); + }); } - else { - next.reject(e); + + function includes(array, searchElement, fromIndex) { + return all(array).then(function(array) { + return toFuture(searchElement).then(function(searchElement) { + return array.includes(searchElement, fromIndex); + }); + }); } -} + function find(array, predicate, thisArg) { + thisArg = thisArg || (function() { return this; })(); + return all(array).then(function(array) { + return array.find(predicate, thisArg); + }); + } -function _resolve(onfulfill, onreject, self, next, x) { - function resolvePromise(y) { - _resolve(onfulfill, onreject, self, next, y); + function findIndex(array, predicate, thisArg) { + thisArg = thisArg || (function() { return this; })(); + return all(array).then(function(array) { + return array.findIndex(predicate, thisArg); + }); } - function rejectPromise(r) { - _reject(onreject, next, r); + + Object.defineProperties(Future, { + // port from Dart + delayed: { value: delayed }, + error: { value: error }, + sync: { value: sync }, + value: { value: value }, + // Promise compatible + all: { value: all }, + race: { value: race }, + resolve: { value: value }, + reject: { value: error }, + // extended methods + promise: { value: promise }, + isFuture: { value: isFuture }, + toFuture: { value: toFuture }, + isPromise: { value: isPromise }, + toPromise: { value: toPromise }, + join: { value: join }, + any: { value: any }, + settle: { value: settle }, + attempt: { value: attempt }, + run: { value: run }, + thunkify: { value: thunkify }, + promisify: { value: promisify }, + co: { value: co }, + wrap: { value: wrap }, + // for array + forEach: { value: forEach }, + every: { value: every }, + some: { value: some }, + filter: { value: filter }, + map: { value: map }, + reduce: { value: reduce }, + reduceRight: { value: reduceRight }, + indexOf: { value: indexOf }, + lastIndexOf: { value: lastIndexOf }, + includes: { value: includes }, + find: { value: find }, + findIndex: { value: findIndex } + }); + + function _call(callback, next, x) { + process.nextTick(function() { + try { + var r = callback(x); + next.resolve(r); + } + catch(e) { + next.reject(e); + } + }); } - if (isPromise(x)) { - if (x === self) { - rejectPromise(new TypeError('Self resolution')); - return; + + function _resolve(onfulfill, next, x) { + if (onfulfill) { + _call(onfulfill, next, x); + } + else { + next.resolve(x); } - x.then(resolvePromise, rejectPromise); - return; } - if ((x !== null) && - (typeof x === 'object') || - (typeof x === 'function')) { - var then; - try { - then = x.then; + + function _reject(onreject, next, e) { + if (onreject) { + _call(onreject, next, e); } - catch (e) { - rejectPromise(e); - return; + else { + next.reject(e); } - if (typeof then === 'function') { - var notrun = true; - try { - then.call(x, function(y) { - if (notrun) { - notrun = false; - resolvePromise(y); - } - }, function(r) { - if (notrun) { - notrun = false; - rejectPromise(r); - } - }); + } + + Object.defineProperties(Future.prototype, { + _value: { writable: true }, + _reason: { writable: true }, + _state: { value: PENDING, writable: true }, + resolve: { value: function(value) { + if (value === this) { + this.reject(new TypeError('Self resolution')); return; } - catch (e) { - if (notrun) { - notrun = false; - rejectPromise(e); - } + if (isFuture(value)) { + value.fill(this); + return; } - return; - } - } - if (onfulfill) { - _call(onfulfill, next, x); - } - else { - next.resolve(x); - } -} - -Object.defineProperties(Future.prototype, { - _value: { writable: true }, - _reason: { writable: true }, - _state: { value: PENDING, writable: true }, - resolve: { value: function(value) { - if (this._state === PENDING) { - this._state = FULFILLED; - this._value = value; - var subscribers = this._subscribers; - while (subscribers.length > 0) { - var subscriber = subscribers.shift(); - _resolve(subscriber.onfulfill, - subscriber.onreject, - this, - subscriber.next, - value); + if ((value !== null) && + (typeof value === 'object') || + (typeof value === 'function')) { + var then; + try { + then = value.then; + } + catch (e) { + this.reject(e); + return; + } + if (typeof then === 'function') { + var notrun = true; + try { + var self = this; + then.call(value, function(y) { + if (notrun) { + notrun = false; + self.resolve(y); + } + }, function(r) { + if (notrun) { + notrun = false; + self.reject(r); + } + }); + return; + } + catch (e) { + if (notrun) { + notrun = false; + this.reject(e); + } + } + return; + } } - } - } }, - reject: { value: function(reason) { - if (this._state === PENDING) { - this._state = REJECTED; - this._reason = reason; - var subscribers = this._subscribers; - while (subscribers.length > 0) { - var subscriber = subscribers.shift(); - if (subscriber.onreject) { - _call(subscriber.onreject, - subscriber.next, - reason); + if (this._state === PENDING) { + this._state = FULFILLED; + this._value = value; + var subscribers = this._subscribers; + while (subscribers.length > 0) { + var subscriber = subscribers.shift(); + _resolve(subscriber.onfulfill, subscriber.next, value); } - else { - subscriber.next.reject(reason); + } + } }, + reject: { value: function(reason) { + if (this._state === PENDING) { + this._state = REJECTED; + this._reason = reason; + var subscribers = this._subscribers; + while (subscribers.length > 0) { + var subscriber = subscribers.shift(); + _reject(subscriber.onreject, subscriber.next, reason); } } - } - } }, - then: { value: function(onfulfill, onreject) { - if (typeof onfulfill !== 'function') onfulfill = null; - if (typeof onreject !== 'function') onreject = null; - if (onfulfill || onreject) { + } }, + then: { value: function(onfulfill, onreject) { + if (typeof onfulfill !== 'function') { onfulfill = null; } + if (typeof onreject !== 'function') { onreject = null; } var next = new Future(); if (this._state === FULFILLED) { - if (onfulfill) { - _resolve(onfulfill, onreject, this, next, this._value); - } - else { - next.resolve(this._value); - } + _resolve(onfulfill, next, this._value); } else if (this._state === REJECTED) { - if (onreject) { - _call(onreject, next, this._reason); - } - else { - next.reject(this._reason); - } + _reject(onreject, next, this._reason); } else { this._subscribers.push({ @@ -456,198 +628,225 @@ Object.defineProperties(Future.prototype, { }); } return next; - } - return this; - } }, - inspect: { value: function() { - switch (this._state) { - case PENDING: return { state: 'pending' }; - case FULFILLED: return { state: 'fulfilled', value: this._value }; - case REJECTED: return { state: 'rejected', reason: this._reason }; - } - } }, - catchError: { value: function(onreject, test) { - if (typeof test === 'function') { - var self = this; - return this['catch'](function(e) { - if (test(e)) { - return self['catch'](onreject); - } - else { - throw e; - } + } }, + done: { value: function(onfulfill, onreject) { + this.then(onfulfill, onreject).then(null, function(error) { + process.nextTick(function() { throw error; }); }); - } - return this['catch'](onreject); - } }, - 'catch': { value: function(onreject) { - return this.then(null, onreject); - } }, - whenComplete: { value: function(action) { - return this.then( - function(v) { - var f = action(); - if (f === undefined) return v; - f = isPromise(f) ? f : value(f); - return f.then(function() { return v; }); - }, - function(e) { - var f = action(); - if (f === undefined) throw e; - f = isPromise(f) ? f : value(f); - return f.then(function() { throw e; }); - } - ); - } }, - timeout: { value: function(duration, reason) { - var future = new Future(); - var timeoutId = setTimeout(function() { - future.reject(reason || new TimeoutError('timeout')); - }, duration); - this.whenComplete(function() { clearTimeout(timeoutId); }) - .then(future.resolve, future.reject); - return future; - } }, - delay: { value: function(duration) { - var future = new Future(); - this.then(function(result) { - setTimeout(function() { - future.resolve(result); + } }, + inspect: { value: function() { + switch (this._state) { + case PENDING: return { state: 'pending' }; + case FULFILLED: return { state: 'fulfilled', value: this._value }; + case REJECTED: return { state: 'rejected', reason: this._reason }; + } + } }, + catchError: { value: function(onreject, test) { + if (typeof test === 'function') { + var self = this; + return this['catch'](function(e) { + if (test(e)) { + return self['catch'](onreject); + } + else { + throw e; + } + }); + } + return this['catch'](onreject); + } }, + 'catch': { value: function(onreject) { + return this.then(null, onreject); + } }, + fail: { value: function(onreject) { + this.done(null, onreject); + } }, + whenComplete: { value: function(action) { + return this.then( + function(v) { action(); return v; }, + function(e) { action(); throw e; } + ); + } }, + complete: { value: function(oncomplete) { + oncomplete = oncomplete || function(v) { return v; }; + return this.then(oncomplete, oncomplete); + } }, + always: { value: function(oncomplete) { + this.done(oncomplete, oncomplete); + } }, + fill: { value: function(future) { + this.then(future.resolve, future.reject); + } }, + timeout: { value: function(duration, reason) { + var future = new Future(); + var timeoutId = setTimeout(function() { + future.reject(reason || new TimeoutError('timeout')); }, duration); - }, - future.reject); - return future; - } }, - tap: { value: function(onfulfilledSideEffect, thisArg) { - return this.then(function(result) { - onfulfilledSideEffect.call(thisArg, result); - return result; - }); - } }, - spread: { value: function(onfulfilledArray, thisArg) { - return this.then(function(array) { - return onfulfilledArray.apply(thisArg, array); - }); - } }, - get: { value: function(key) { - return this.then(function(result) { - return result[key]; - }); - } }, - set: { value: function(key, value) { - return this.then(function(result) { - result[key] = value; - return result; - }); - } }, - apply: { value: function(method, args) { - args = args || []; - return this.then(function(result) { - return all(args).then(function(args) { - return result[method].apply(result, args); + this.whenComplete(function() { clearTimeout(timeoutId); }) + .fill(future); + return future; + } }, + delay: { value: function(duration) { + var future = new Future(); + this.then(function(result) { + setTimeout(function() { + future.resolve(result); + }, duration); + }, + future.reject); + return future; + } }, + tap: { value: function(onfulfilledSideEffect, thisArg) { + return this.then(function(result) { + onfulfilledSideEffect.call(thisArg, result); + return result; }); - }); - } }, - call: { value: function(method) { - var args = slice(arguments, 1); - return this.then(function(result) { - return all(args).then(function(args) { - return result[method].apply(result, args); + } }, + spread: { value: function(onfulfilledArray, thisArg) { + return this.then(function(array) { + return onfulfilledArray.apply(thisArg, array); }); - }); - } }, - bind: { value: function(method) { - var bindargs = slice(arguments); - if (util.isArray(method)) { - for (var i = 0, n = method.length; i < n; ++i) { - bindargs[0] = method[i]; - this.bind.apply(this, bindargs); - } - return; - } - bindargs.shift(); - var self = this; - Object.defineProperty(this, method, { value: function() { - var args = slice(arguments); - return self.then(function(result) { - return all(bindargs.concat(args)).then(function(args) { + } }, + get: { value: function(key) { + return this.then(function(result) { + return result[key]; + }); + } }, + set: { value: function(key, value) { + return this.then(function(result) { + result[key] = value; + return result; + }); + } }, + apply: { value: function(method, args) { + args = args || []; + return this.then(function(result) { + return all(args).then(function(args) { return result[method].apply(result, args); }); }); - } }); - return this; - } }, - forEach: { value: function(callback, thisArg) { - return forEach(this, callback, thisArg); - } }, - every: { value: function(callback, thisArg) { - return every(this, callback, thisArg); - } }, - some: { value: function(callback, thisArg) { - return some(this, callback, thisArg); - } }, - filter: { value: function(callback, thisArg) { - return filter(this, callback, thisArg); - } }, - map: { value: function(callback, thisArg) { - return map(this, callback, thisArg); - } }, - reduce: { value: function(callback, initialValue) { - if (arguments.length > 1) { - return reduce(this, callback, initialValue); - } - return reduce(this, callback); - } }, - reduceRight: { value: function(callback, initialValue) { - if (arguments.length > 1) { - return reduceRight(this, callback, initialValue); - } - return reduceRight(this, callback); - } } -}); - -global.hprose.Future = Future; - -function Completer() { - var future = new Future(); - Object.defineProperties(this, { - future: { value: future }, - complete: { value: future.resolve }, - completeError: { value: future.reject }, - isCompleted: { get: function() { - return ( future._state !== PENDING ); + } }, + call: { value: function(method) { + var args = slice.call(arguments, 1); + return this.then(function(result) { + return all(args).then(function(args) { + return result[method].apply(result, args); + }); + }); + } }, + bind: { value: function(method) { + var bindargs = slice.call(arguments); + if (Array.isArray(method)) { + for (var i = 0, n = method.length; i < n; ++i) { + bindargs[0] = method[i]; + this.bind.apply(this, bindargs); + } + return; + } + bindargs.shift(); + var self = this; + Object.defineProperty(this, method, { value: function() { + var args = slice.call(arguments); + return self.then(function(result) { + return all(bindargs.concat(args)).then(function(args) { + return result[method].apply(result, args); + }); + }); + } }); + return this; + } }, + forEach: { value: function(callback, thisArg) { + return forEach(this, callback, thisArg); + } }, + every: { value: function(callback, thisArg) { + return every(this, callback, thisArg); + } }, + some: { value: function(callback, thisArg) { + return some(this, callback, thisArg); + } }, + filter: { value: function(callback, thisArg) { + return filter(this, callback, thisArg); + } }, + map: { value: function(callback, thisArg) { + return map(this, callback, thisArg); + } }, + reduce: { value: function(callback, initialValue) { + if (arguments.length > 1) { + return reduce(this, callback, initialValue); + } + return reduce(this, callback); + } }, + reduceRight: { value: function(callback, initialValue) { + if (arguments.length > 1) { + return reduceRight(this, callback, initialValue); + } + return reduceRight(this, callback); + } }, + indexOf: { value: function(searchElement, fromIndex) { + return indexOf(this, searchElement, fromIndex); + } }, + lastIndexOf: { value: function(searchElement, fromIndex) { + return lastIndexOf(this, searchElement, fromIndex); + } }, + includes: { value: function(searchElement, fromIndex) { + return includes(this, searchElement, fromIndex); + } }, + find: { value: function(predicate, thisArg) { + return find(this, predicate, thisArg); + } }, + findIndex: { value: function(predicate, thisArg) { + return findIndex(this, predicate, thisArg); } } }); -} -global.hprose.Completer = Completer; + global.hprose.Future = Future; -global.hprose.resolved = value; + global.hprose.thunkify = thunkify; + global.hprose.promisify = promisify; + global.hprose.co = co; + global.hprose.co.wrap = global.hprose.wrap = wrap; -global.hprose.rejected = error; + function Completer() { + var future = new Future(); + Object.defineProperties(this, { + future: { value: future }, + complete: { value: future.resolve }, + completeError: { value: future.reject }, + isCompleted: { get: function() { + return ( future._state !== PENDING ); + } } + }); + } -global.hprose.deferred = function() { - var self = new Future(); - return Object.create(null, { - promise: { value: self }, - resolve: { value: self.resolve }, - reject: { value: self.reject }, - }); -}; + global.hprose.Completer = Completer; + + global.hprose.resolved = value; + + global.hprose.rejected = error; + + global.hprose.deferred = function() { + var self = new Future(); + return Object.create(null, { + promise: { value: self }, + resolve: { value: self.resolve }, + reject: { value: self.reject } + }); + }; -if (hasPromise) return; + if (hasPromise) { return; } -global.Promise = function(executor) { - Future.call(this); - executor(this.resolve, this.reject); -}; + global.Promise = function(executor) { + Future.call(this); + executor(this.resolve, this.reject); + }; -global.Promise.prototype = Object.create(Future.prototype); -global.Promise.prototype.constructor = Future; + global.Promise.prototype = Object.create(Future.prototype); + global.Promise.prototype.constructor = Future; -Object.defineProperties(global.Promise, { - all: { value: all }, - race: { value: race }, - resolve: { value: value }, - reject: { value: error } -}); + Object.defineProperties(global.Promise, { + all: { value: all }, + race: { value: race }, + resolve: { value: value }, + reject: { value: error } + }); +})(); diff --git a/lib/common/HarmonyMaps.js b/lib/common/HarmonyMaps.js index 64c6ec6..e3a2771 100644 --- a/lib/common/HarmonyMaps.js +++ b/lib/common/HarmonyMaps.js @@ -13,283 +13,288 @@ * * * Harmony Maps for Node.js. * * * - * LastModified: Aug 2, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, unused:false, eqeqeq:true */ -'use strict'; +(function() { + 'use strict'; -var hasWeakMap = 'WeakMap' in global; -var hasMap = 'Map' in global; -var hasForEach = true; + var hasWeakMap = 'WeakMap' in global; + var hasMap = 'Map' in global; + var hasForEach = true; -if (hasMap) { - hasForEach = 'forEach' in new global.Map(); -} + if (hasMap) { + hasForEach = 'forEach' in new global.Map(); + } -if (hasWeakMap && hasMap && hasForEach) return; + if (hasWeakMap && hasMap && hasForEach) { return; } -var util = require('util'); - -var namespaces = Object.create(null); -var count = 0; -var reDefineValueOf = function (obj) { - var privates = Object.create(null); - var baseValueOf = obj.valueOf; - Object.defineProperty(obj, 'valueOf', { - value: function (namespace, n) { - if ((this === obj) && - (n in namespaces) && - (namespaces[n] === namespace)) { - if (!(n in privates)) privates[n] = Object.create(null); - return privates[n]; - } - else { - return baseValueOf.apply(this, arguments); - } - }, - writable: true, - configurable: true, - enumerable: false - }); -}; + var namespaces = Object.create(null); + var count = 0; + var reDefineValueOf = function (obj) { + var privates = Object.create(null); + var baseValueOf = obj.valueOf; + Object.defineProperty(obj, 'valueOf', { + value: function (namespace, n) { + if ((this === obj) && + (n in namespaces) && + (namespaces[n] === namespace)) { + if (!(n in privates)) { + privates[n] = Object.create(null); + } + return privates[n]; + } + else { + return baseValueOf.apply(this, arguments); + } + }, + writable: true, + configurable: true, + enumerable: false + }); + }; -if (!hasWeakMap) { - global.WeakMap = function WeakMap() { - var namespace = Object.create(null); - var n = count++; - namespaces[n] = namespace; - var map = function (key) { - if (key !== Object(key)) throw new Error('value is not a non-null object'); - var privates = key.valueOf(namespace, n); - if (privates !== key.valueOf()) return privates; - reDefineValueOf(key); - return key.valueOf(namespace, n); - }; - var m = Object.create(WeakMap.prototype, { - get: { - value: function (key) { - return map(key).value; + if (!hasWeakMap) { + global.WeakMap = function WeakMap() { + var namespace = Object.create(null); + var n = count++; + namespaces[n] = namespace; + var map = function (key) { + if (key !== Object(key)) { + throw new Error('value is not a non-null object'); } - }, - set: { - value: function (key, value) { - map(key).value = value; + var privates = key.valueOf(namespace, n); + if (privates !== key.valueOf()) { + return privates; } - }, - has: { - value: function (key) { - return 'value' in map(key); + reDefineValueOf(key); + return key.valueOf(namespace, n); + }; + var m = Object.create(WeakMap.prototype, { + get: { + value: function (key) { + return map(key).value; + } + }, + set: { + value: function (key, value) { + map(key).value = value; + } + }, + has: { + value: function (key) { + return 'value' in map(key); + } + }, + 'delete': { + value: function (key) { + return delete map(key).value; + } + }, + clear: { + value: function () { + delete namespaces[n]; + n = count++; + namespaces[n] = namespace; + } } - }, - 'delete': { - value: function (key) { - return delete map(key).value; + }); + if (arguments.length > 0 && Array.isArray(arguments[0])) { + var iterable = arguments[0]; + for (var i = 0, len = iterable.length; i < len; i++) { + m.set(iterable[i][0], iterable[i][1]); } - }, - clear: { - value: function () { + } + return m; + }; + } + + if (!hasMap) { + var objectMap = function () { + var namespace = Object.create(null); + var n = count++; + var nullMap = Object.create(null); + namespaces[n] = namespace; + var map = function (key) { + if (key === null) { return nullMap; } + var privates = key.valueOf(namespace, n); + if (privates !== key.valueOf()) { return privates; } + reDefineValueOf(key); + return key.valueOf(namespace, n); + }; + return { + get: function (key) { return map(key).value; }, + set: function (key, value) { map(key).value = value; }, + has: function (key) { return 'value' in map(key); }, + 'delete': function (key) { return delete map(key).value; }, + clear: function () { delete namespaces[n]; n = count++; namespaces[n] = namespace; } - } - }); - if (arguments.length > 0 && util.isArray(arguments[0])) { - var iterable = arguments[0]; - for (var i = 0, len = iterable.length; i < len; i++) { - m.set(iterable[i][0], iterable[i][1]); - } - } - return m; - }; -} - -if (!hasMap) { - var objectMap = function () { - var namespace = Object.create(null); - var n = count++; - var nullMap = Object.create(null); - namespaces[n] = namespace; - var map = function (key) { - if (key === null) return nullMap; - var privates = key.valueOf(namespace, n); - if (privates !== key.valueOf()) return privates; - reDefineValueOf(key); - return key.valueOf(namespace, n); + }; }; - return { - get: function (key) { return map(key).value; }, - set: function (key, value) { map(key).value = value; }, - has: function (key) { return 'value' in map(key); }, - 'delete': function (key) { return delete map(key).value; }, - clear: function () { - delete namespaces[n]; - n = count++; - namespaces[n] = namespace; - } + var noKeyMap = function () { + var map = Object.create(null); + return { + get: function () { return map.value; }, + set: function (_, value) { map.value = value; }, + has: function () { return 'value' in map; }, + 'delete': function () { return delete map.value; }, + clear: function () { map = Object.create(null); } + }; }; - }; - var noKeyMap = function () { - var map = Object.create(null); - return { - get: function () { return map.value; }, - set: function (_, value) { map.value = value; }, - has: function () { return 'value' in map; }, - 'delete': function () { return delete map.value; }, - clear: function () { map = Object.create(null); } - }; - }; - var scalarMap = function () { - var map = Object.create(null); - return { - get: function (key) { return map[key]; }, - set: function (key, value) { map[key] = value; }, - has: function (key) { return key in map; }, - 'delete': function (key) { return delete map[key]; }, - clear: function () { map = Object.create(null); } + var scalarMap = function () { + var map = Object.create(null); + return { + get: function (key) { return map[key]; }, + set: function (key, value) { map[key] = value; }, + has: function (key) { return key in map; }, + 'delete': function (key) { return delete map[key]; }, + clear: function () { map = Object.create(null); } + }; }; - }; - global.Map = function Map() { - var map = { - 'number': scalarMap(), - 'string': scalarMap(), - 'boolean': scalarMap(), - 'object': objectMap(), - 'function': objectMap(), - 'unknown': objectMap(), - 'undefined': noKeyMap(), - 'null': noKeyMap() - }; - var size = 0; - var keys = []; - var m = Object.create(Map.prototype, { - size: { - get : function () { return size; } - }, - get: { - value: function (key) { - return map[typeof(key)].get(key); - } - }, - set: { - value: function (key, value) { - if (!this.has(key)) { - keys.push(key); - size++; + global.Map = function Map() { + var map = { + 'number': scalarMap(), + 'string': scalarMap(), + 'boolean': scalarMap(), + 'object': objectMap(), + 'function': objectMap(), + 'unknown': objectMap(), + 'undefined': noKeyMap(), + 'null': noKeyMap() + }; + var size = 0; + var keys = []; + var m = Object.create(Map.prototype, { + size: { + get : function () { return size; } + }, + get: { + value: function (key) { + return map[typeof(key)].get(key); } - map[typeof(key)].set(key, value); - } - }, - has: { - value: function (key) { - return map[typeof(key)].has(key); - } - }, - 'delete': { - value: function (key) { - if (this.has(key)) { - size--; - keys.splice(keys.indexOf(key), 1); - return map[typeof(key)]['delete'](key); + }, + set: { + value: function (key, value) { + if (!this.has(key)) { + keys.push(key); + size++; + } + map[typeof(key)].set(key, value); } - return false; - } - }, - clear: { - value: function () { - keys.length = 0; - for (var key in map) map[key].clear(); - size = 0; - } - }, - forEach: { - value: function (callback, thisArg) { - for (var i = 0, n = keys.length; i < n; i++) { - callback.call(thisArg, this.get(keys[i]), keys[i], this); + }, + has: { + value: function (key) { + return map[typeof(key)].has(key); + } + }, + 'delete': { + value: function (key) { + if (this.has(key)) { + size--; + keys.splice(keys.indexOf(key), 1); + return map[typeof(key)]['delete'](key); + } + return false; + } + }, + clear: { + value: function () { + keys.length = 0; + for (var key in map) { map[key].clear(); } + size = 0; + } + }, + forEach: { + value: function (callback, thisArg) { + for (var i = 0, n = keys.length; i < n; i++) { + callback.call(thisArg, this.get(keys[i]), keys[i], this); + } } } + }); + if (arguments.length > 0 && Array.isArray(arguments[0])) { + var iterable = arguments[0]; + for (var i = 0, len = iterable.length; i < len; i++) { + m.set(iterable[i][0], iterable[i][1]); + } } - }); - if (arguments.length > 0 && util.isArray(arguments[0])) { - var iterable = arguments[0]; - for (var i = 0, len = iterable.length; i < len; i++) { - m.set(iterable[i][0], iterable[i][1]); - } - } - return m; - }; -} + return m; + }; + } -if (!hasForEach) { - var OldMap = global.Map; - global.Map = function Map() { - var map = new OldMap(); - var size = 0; - var keys = []; - var m = Object.create(Map.prototype, { - size: { - get : function () { return size; } - }, - get: { - value: function (key) { - return map.get(key); - } - }, - set: { - value: function (key, value) { - if (!map.has(key)) { - keys.push(key); - size++; + if (!hasForEach) { + var OldMap = global.Map; + global.Map = function Map() { + var map = new OldMap(); + var size = 0; + var keys = []; + var m = Object.create(Map.prototype, { + size: { + get : function () { return size; } + }, + get: { + value: function (key) { + return map.get(key); } - map.set(key, value); - } - }, - has: { - value: function (key) { - return map.has(key); - } - }, - 'delete': { - value: function (key) { - if (map.has(key)) { - size--; - keys.splice(keys.indexOf(key), 1); - return map['delete'](key); + }, + set: { + value: function (key, value) { + if (!map.has(key)) { + keys.push(key); + size++; + } + map.set(key, value); } - return false; - } - }, - clear: { - value: function () { - if ('clear' in map) { - map.clear(); + }, + has: { + value: function (key) { + return map.has(key); } - else { + }, + 'delete': { + value: function (key) { + if (map.has(key)) { + size--; + keys.splice(keys.indexOf(key), 1); + return map['delete'](key); + } + return false; + } + }, + clear: { + value: function () { + if ('clear' in map) { + map.clear(); + } + else { + for (var i = 0, n = keys.length; i < n; i++) { + map['delete'](keys[i]); + } + } + keys.length = 0; + size = 0; + } + }, + forEach: { + value: function (callback, thisArg) { for (var i = 0, n = keys.length; i < n; i++) { - map['delete'](keys[i]); + callback.call(thisArg, this.get(keys[i]), keys[i], this); } } - keys.length = 0; - size = 0; } - }, - forEach: { - value: function (callback, thisArg) { - for (var i = 0, n = keys.length; i < n; i++) { - callback.call(thisArg, this.get(keys[i]), keys[i], this); - } + }); + if (arguments.length > 0 && Array.isArray(arguments[0])) { + var iterable = arguments[0]; + for (var i = 0, len = iterable.length; i < len; i++) { + m.set(iterable[i][0], iterable[i][1]); } } - }); - if (arguments.length > 0 && util.isArray(arguments[0])) { - var iterable = arguments[0]; - for (var i = 0, len = iterable.length; i < len; i++) { - m.set(iterable[i][0], iterable[i][1]); - } - } - return m; - }; -} + return m; + }; + } +})(); diff --git a/lib/common/Helper.js b/lib/common/Helper.js new file mode 100644 index 0000000..6a6802d --- /dev/null +++ b/lib/common/Helper.js @@ -0,0 +1,98 @@ +/**********************************************************\ +| | +| hprose | +| | +| Official WebSite: http://www.hprose.com/ | +| http://www.hprose.org/ | +| | +\**********************************************************/ + +/**********************************************************\ + * * + * hprose/common/Helper.js * + * * + * Hprose Helper for Node.js. * + * * + * LastModified: Oct 12, 2016 * + * Author: Ma Bingyao * + * * +\**********************************************************/ + +(function() { + 'use strict'; + + function generic(method) { + if (typeof method !== "function") { + throw new TypeError(method + " is not a function"); + } + return function(context) { + return method.apply(context, Array.prototype.slice.call(arguments, 1)); + }; + } + + var arrayLikeObjectArgumentsEnabled = true; + + try { + String.fromCharCode.apply(String, new Uint8Array([1])); + } + catch (e) { + arrayLikeObjectArgumentsEnabled = false; + } + + function toArray(arrayLikeObject) { + var n = arrayLikeObject.length; + var a = new Array(n); + for (var i = 0; i < n; ++i) { + a[i] = arrayLikeObject[i]; + } + return a; + } + + var getCharCodes = arrayLikeObjectArgumentsEnabled ? function(bytes) { return bytes; } : toArray; + + function toBinaryString(bytes) { + if (bytes instanceof ArrayBuffer) { + bytes = new Uint8Array(bytes); + } + var n = bytes.length; + if (n < 0xFFFF) { + return String.fromCharCode.apply(String, getCharCodes(bytes)); + } + var remain = n & 0x7FFF; + var count = n >> 15; + var a = new Array(remain ? count + 1 : count); + for (var i = 0; i < count; ++i) { + a[i] = String.fromCharCode.apply(String, getCharCodes(bytes.subarray(i << 15, (i + 1) << 15))); + } + if (remain) { + a[count] = String.fromCharCode.apply(String, getCharCodes(bytes.subarray(count << 15, n))); + } + return a.join(''); + } + + function toUint8Array(bs) { + var n = bs.length; + var data = new Uint8Array(n); + for (var i = 0; i < n; i++) { + data[i] = bs.charCodeAt(i) & 0xFF; + } + return data; + } + + var isObjectEmpty = function (obj) { + if (obj) { + var prop; + for (prop in obj) { + return false; + } + } + return true; + } + + global.hprose.generic = generic; + global.hprose.toBinaryString = toBinaryString; + global.hprose.toUint8Array = toUint8Array; + global.hprose.toArray = toArray; + global.hprose.isObjectEmpty = isObjectEmpty; + +})(); diff --git a/lib/common/Polyfill.js b/lib/common/Polyfill.js new file mode 100644 index 0000000..a636bbf --- /dev/null +++ b/lib/common/Polyfill.js @@ -0,0 +1,413 @@ +/**********************************************************\ +| | +| hprose | +| | +| Official WebSite: http://www.hprose.com/ | +| http://www.hprose.org/ | +| | +\**********************************************************/ + +/**********************************************************\ + * * + * Polyfill.js * + * * + * Polyfill for Node.js. * + * * + * LastModified: Mar 3, 2016 * + * Author: Ma Bingyao * + * * +\**********************************************************/ + +'use strict'; +/* Function */ +if (!Function.prototype.bind) { + Object.defineProperty(Function.prototype, 'bind', { value: function(oThis) { + if (typeof this !== 'function') { + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + var aArgs = Array.prototype.slice.call(arguments, 1), + toBind = this, + NOP = function() {}, + bound = function() { + return toBind.apply(this instanceof NOP ? this : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + if (this.prototype) { + NOP.prototype = this.prototype; + } + bound.prototype = new NOP(); + return bound; + } }); +} +/* Array */ +if (!Array.prototype.includes) { + Object.defineProperty(Array.prototype, 'includes', { value: function(searchElement /*, fromIndex*/ ) { + var O = Object(this); + var len = parseInt(O.length, 10) || 0; + if (len === 0) { + return false; + } + var n = parseInt(arguments[1], 10) || 0; + var k; + if (n >= 0) { + k = n; + } + else { + k = len + n; + if (k < 0) { k = 0; } + } + var currentElement; + while (k < len) { + currentElement = O[k]; + if (searchElement === currentElement || + (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN + return true; + } + k++; + } + return false; + } }); +} +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { value: function(predicate) { + if (this === null || this === undefined) { + throw new TypeError('Array.prototype.find called on null or undefined'); + } + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + var list = Object(this); + var length = list.length >>> 0; + var thisArg = arguments[1]; + var value; + for (var i = 0; i < length; i++) { + value = list[i]; + if (predicate.call(thisArg, value, i, list)) { + return value; + } + } + return undefined; + } }); +} +if (!Array.prototype.findIndex) { + Object.defineProperty(Array.prototype, 'findIndex', { value: function(predicate) { + if (this === null || this === undefined) { + throw new TypeError('Array.prototype.findIndex called on null or undefined'); + } + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + var list = Object(this); + var length = list.length >>> 0; + var thisArg = arguments[1]; + var value; + + for (var i = 0; i < length; i++) { + value = list[i]; + if (predicate.call(thisArg, value, i, list)) { + return i; + } + } + return -1; + } }); +} +if (!Array.prototype.fill) { + Object.defineProperty(Array.prototype, 'fill', { value: function(value) { + if (this === null || this === undefined) { + throw new TypeError('this is null or not defined'); + } + var O = Object(this); + var len = O.length >>> 0; + var start = arguments[1]; + var relativeStart = start >> 0; + var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); + var end = arguments[2]; + var relativeEnd = end === undefined ? len : end >> 0; + var f = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); + + while (k < f) { + O[k] = value; + k++; + } + return O; + } }); +} +if (!Array.prototype.copyWithin) { + Object.defineProperty(Array.prototype, 'copyWithin', { value: function(target, start/*, end*/) { + if (this === null || this === undefined) { + throw new TypeError('this is null or not defined'); + } + var O = Object(this); + var len = O.length >>> 0; + var relativeTarget = target >> 0; + var to = relativeTarget < 0 ? Math.max(len + relativeTarget, 0) : Math.min(relativeTarget, len); + var relativeStart = start >> 0; + var from = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); + var end = arguments[2]; + var relativeEnd = end === undefined ? len : end >> 0; + var f = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); + var count = Math.min(f - from, len - to); + var direction = 1; + if (from < to && to < (from + count)) { + direction = -1; + from += count - 1; + to += count - 1; + } + while (count > 0) { + if (from in O) { + O[to] = O[from]; + } + else { + delete O[to]; + } + from += direction; + to += direction; + count--; + } + return O; + } }); +} +if (!Array.from) { + Object.defineProperty(Array, 'from', { value: (function() { + var toStr = Object.prototype.toString; + var isCallable = function(fn) { + return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; + }; + var toInteger = function(value) { + var number = Number(value); + if (isNaN(number)) { return 0; } + if (number === 0 || !isFinite(number)) { return number; } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + var maxSafeInteger = Math.pow(2, 53) - 1; + var toLength = function(value) { + var len = toInteger(value); + return Math.min(Math.max(len, 0), maxSafeInteger); + }; + + return function(arrayLike/*, mapFn, thisArg */) { + var C = this; + var items = Object(arrayLike); + if (arrayLike === null || arrayLike === undefined) { + throw new TypeError("Array.from requires an array-like object - not null or undefined"); + } + var mapFn = arguments.length > 1 ? arguments[1] : void undefined; + var T; + if (typeof mapFn !== 'undefined') { + if (!isCallable(mapFn)) { + throw new TypeError('Array.from: when provided, the second argument must be a function'); + } + if (arguments.length > 2) { + T = arguments[2]; + } + } + var len = toLength(items.length); + var A = isCallable(C) ? Object(new C(len)) : new Array(len); + var k = 0; + var kValue; + while (k < len) { + kValue = items[k]; + if (mapFn) { + A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); + } + else { + A[k] = kValue; + } + k += 1; + } + A.length = len; + return A; + }; + }()) }); +} +if (!Array.of) { + Object.defineProperty(Array, 'of', { value: function() { + return Array.prototype.slice.call(arguments); + } }); +} +/* String */ +if (!String.prototype.startsWith) { + Object.defineProperty(String.prototype, 'startsWith', { value: function(searchString, position){ + position = position || 0; + return this.substr(position, searchString.length) === searchString; + } }); +} +if (!String.prototype.endsWith) { + Object.defineProperty(String.prototype, 'endsWith', { value: function(searchString, position) { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + } }); +} +if (!String.prototype.includes) { + Object.defineProperty(String.prototype, 'includes', { value: function() { + if (typeof arguments[1] === "number") { + if (this.length < arguments[0].length + arguments[1].length) { + return false; + } + else { + return this.substr(arguments[1], arguments[0].length) === arguments[0]; + } + } + else { + return String.prototype.indexOf.apply(this, arguments) !== -1; + } + } }); +} +if (!String.prototype.repeat) { + Object.defineProperty(String.prototype, 'repeat', { value: function(count) { + var str = this.toString(); + count = +count; + if (count !== count) { + count = 0; + } + if (count < 0) { + throw new RangeError('repeat count must be non-negative'); + } + if (count === Infinity) { + throw new RangeError('repeat count must be less than infinity'); + } + count = Math.floor(count); + if (str.length === 0 || count === 0) { + return ''; + } + // Ensuring count is a 31-bit integer allows us to heavily optimize the + // main part. But anyway, most current (August 2014) browsers can't handle + // strings 1 << 28 chars or longer, so: + if (str.length * count >= 1 << 28) { + throw new RangeError('repeat count must not overflow maximum string size'); + } + var rpt = ''; + for (;;) { + if ((count & 1) === 1) { + rpt += str; + } + count >>>= 1; + if (count === 0) { + break; + } + str += str; + } + // Could we try: + // return Array(count + 1).join(this); + return rpt; + } }); +} +if (!String.prototype.trim) { + Object.defineProperty(String.prototype, 'trim', { value: function() { + return this.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); + } }); +} +if (!String.prototype.trimLeft) { + Object.defineProperty(String.prototype, 'trimLeft', { value: function() { + return this.toString().replace(/^[\s\xa0]+/, ''); + } }); +} +if (!String.prototype.trimRight) { + Object.defineProperty(String.prototype, 'trimRight', { value: function() { + return this.toString().replace(/[\s\xa0]+$/, ''); + } }); +} +/* Object */ +if (!Object.keys) { + Object.defineProperty(Object, 'keys', { value: (function () { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + return function (obj) { + if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { + throw new TypeError('Object.keys called on non-object'); + } + var result = []; + for (var prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + if (hasDontEnumBug) { + for (var i=0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + })() }); +} +/* Generic methods */ +var generic = global.hprose.generic; + +function genericMethods(obj, properties) { + var proto = obj.prototype; + for (var i = 0, len = properties.length; i < len; i++) { + var property = properties[i]; + var method = proto[property]; + if (typeof method === 'function' && typeof obj[property] === 'undefined') { + Object.defineProperty(obj, property, { value: generic(method) }); + } + } +} +genericMethods(Array, [ + "pop", + "push", + "reverse", + "shift", + "sort", + "splice", + "unshift", + "concat", + "join", + "slice", + "indexOf", + "lastIndexOf", + "filter", + "forEach", + "every", + "map", + "some", + "reduce", + "reduceRight", + "includes", + "find", + "findIndex" +]); +genericMethods(String, [ + 'quote', + 'substring', + 'toLowerCase', + 'toUpperCase', + 'charAt', + 'charCodeAt', + 'indexOf', + 'lastIndexOf', + 'include', + 'startsWith', + 'endsWith', + 'repeat', + 'trim', + 'trimLeft', + 'trimRight', + 'toLocaleLowerCase', + 'toLocaleUpperCase', + 'match', + 'search', + 'replace', + 'split', + 'substr', + 'concat', + 'slice' +]); diff --git a/lib/common/ResultMode.js b/lib/common/ResultMode.js index 353c83c..1e49aa9 100644 --- a/lib/common/ResultMode.js +++ b/lib/common/ResultMode.js @@ -13,12 +13,11 @@ * * * Hprose ResultMode for Node.js. * * * - * LastModified: May 15, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true */ 'use strict'; global.hprose.ResultMode = { diff --git a/lib/common/isError.js b/lib/common/isError.js new file mode 100644 index 0000000..caacea7 --- /dev/null +++ b/lib/common/isError.js @@ -0,0 +1,41 @@ +/**********************************************************\ +| | +| hprose | +| | +| Official WebSite: http://www.hprose.com/ | +| http://www.hprose.org/ | +| | +\**********************************************************/ + +/**********************************************************\ + * * + * hprose/common/isError.js * + * * + * isError for Node.js. * + * * + * LastModified: Sep 11, 2015 * + * Author: Ma Bingyao * + * * +\**********************************************************/ + +var objectToString = Object.prototype.toString; +var getPrototypeOf = Object.getPrototypeOf; +var ERROR_TYPE = '[object Error]'; + +function isError(err) { + if (err instanceof Error) { + return true; + } + if (typeof err !== 'object') { + return false; + } + while (err) { + if (objectToString.call(err) === ERROR_TYPE) { + return true; + } + err = getPrototypeOf(err); + } + return false; +} + +module.exports = isError; diff --git a/lib/common/setImmediate.js b/lib/common/setImmediate.js deleted file mode 100644 index 6fe032d..0000000 --- a/lib/common/setImmediate.js +++ /dev/null @@ -1,72 +0,0 @@ -/**********************************************************\ -| | -| hprose | -| | -| Official WebSite: http://www.hprose.com/ | -| http://www.hprose.org/ | -| | -\**********************************************************/ - -/**********************************************************\ - * * - * hprose/common/setImmediate.js * - * * - * setImmediate for Node.js. * - * * - * LastModified: Jul 19, 2015 * - * Author: Ma Bingyao * - * * -\**********************************************************/ - -/*jshint node:true, eqeqeq:true */ -'use strict'; - -if (global.setImmediate) return; - -var slice = Function.prototype.call.bind(Array.prototype.slice); -var nextId = 1; -var tasks = {}; -var lock = false; - -function wrap(handler) { - var args = slice(arguments, 1); - return function() { - handler.apply(undefined, args); - }; -} - -function run(handleId) { - if (lock) { - global.setTimeout(wrap(run, handleId), 0); - } - else { - var task = tasks[handleId]; - if (task) { - lock = true; - try { - task(); - } - finally { - clear(handleId); - lock = false; - } - } - } -} - -function create(args) { - tasks[nextId] = wrap.apply(undefined, args); - return nextId++; -} - -function clear(handleId) { - delete tasks[handleId]; -} - -global.setImmediate = function() { - var handleId = create(arguments); - global.process.nextTick( wrap( run, handleId ) ); - return handleId; -}; - -global.clearImmediate = clear; diff --git a/lib/filter/JSONRPCClientFilter.js b/lib/filter/JSONRPCClientFilter.js index fccde74..79d9ca4 100644 --- a/lib/filter/JSONRPCClientFilter.js +++ b/lib/filter/JSONRPCClientFilter.js @@ -13,18 +13,18 @@ * * * jsonrpc client filter for Node.js. * * * - * LastModified: Jul 17, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var Tags = global.hprose.Tags; var BytesIO = global.hprose.BytesIO; var Writer = global.hprose.Writer; var Reader = global.hprose.Reader; +var JSON = global.JSON; var s_id = 1; @@ -32,7 +32,7 @@ function JSONRPCClientFilter(version) { this.version = version || '2.0'; } -JSONRPCClientFilter.prototype.inputFilter = function inputFilter(data, context) { +JSONRPCClientFilter.prototype.inputFilter = function inputFilter(data/*, context*/) { var json = BytesIO.toString(data); if (json.charAt(0) === '{') { json = '[' + json + ']'; @@ -55,7 +55,7 @@ JSONRPCClientFilter.prototype.inputFilter = function inputFilter(data, context) return stream.bytes; }; -JSONRPCClientFilter.prototype.outputFilter = function outputFilter(data, context) { +JSONRPCClientFilter.prototype.outputFilter = function outputFilter(data/*, context*/) { var requests = []; var stream = new BytesIO(data); var reader = new Reader(stream, false, false); diff --git a/lib/filter/JSONRPCServiceFilter.js b/lib/filter/JSONRPCServiceFilter.js index d966687..02e6148 100644 --- a/lib/filter/JSONRPCServiceFilter.js +++ b/lib/filter/JSONRPCServiceFilter.js @@ -13,18 +13,18 @@ * * * jsonrpc service filter for Node.js. * * * - * LastModified: May 21, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var Tags = global.hprose.Tags; var BytesIO = global.hprose.BytesIO; var Writer = global.hprose.Writer; var Reader = global.hprose.Reader; +var JSON = global.JSON; var leftbrace = 0x7B; // '{' var leftbracket = 0x5B; // '[' diff --git a/lib/hprose.js b/lib/hprose.js index 0e1d19d..d7bdfb7 100644 --- a/lib/hprose.js +++ b/lib/hprose.js @@ -13,18 +13,18 @@ * * * hprose for Node.js. * * * - * LastModified: Jun 22, 2015 * + * LastModified: Sep 30, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; global.hprose = global.hprose || Object.create(null); +require('./common/Helper.js'); +require('./common/Polyfill.js'); require('./common/HarmonyMaps.js'); -require('./common/setImmediate.js'); require('./common/Future.js'); require('./common/ResultMode.js'); @@ -52,6 +52,8 @@ require('./server/Server.js'); require('./filter/JSONRPCClientFilter.js'); require('./filter/JSONRPCServiceFilter.js'); +require('./utils/regenerator-runtime.js'); + global.HproseCompleter = global.hprose.Completer; global.HproseFuture = global.hprose.Future; global.HproseResultMode = global.hprose.ResultMode; diff --git a/lib/io/BytesIO.js b/lib/io/BytesIO.js index 53e7b7b..381ff99 100644 --- a/lib/io/BytesIO.js +++ b/lib/io/BytesIO.js @@ -13,31 +13,31 @@ * * * Hprose BytesIO for Node.js. * * * - * LastModified: Aug 21, 2015 * + * LastModified: Oct 23, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; +var toBinaryString = global.hprose.toBinaryString; + var _EMPTY_BYTES = new Uint8Array(0); var _INIT_SIZE = 1024; -var indexof = Function.prototype.call.bind(Array.prototype.indexOf); function writeInt32BE(bytes, p, i) { - bytes[p++] = i >>> 24 & 0xff; - bytes[p++] = i >>> 16 & 0xff; - bytes[p++] = i >>> 8 & 0xff; - bytes[p++] = i & 0xff; + bytes[p++] = i >>> 24 & 0xFF; + bytes[p++] = i >>> 16 & 0xFF; + bytes[p++] = i >>> 8 & 0xFF; + bytes[p++] = i & 0xFF; return p; } function writeInt32LE(bytes, p, i) { - bytes[p++] = i & 0xff; - bytes[p++] = i >>> 8 & 0xff; - bytes[p++] = i >>> 16 & 0xff; - bytes[p++] = i >>> 24 & 0xff; + bytes[p++] = i & 0xFF; + bytes[p++] = i >>> 8 & 0xFF; + bytes[p++] = i >>> 16 & 0xFF; + bytes[p++] = i >>> 24 & 0xFF; return p; } @@ -52,7 +52,7 @@ function writeString(bytes, p, str) { bytes[p++] = 0xC0 | (codeUnit >> 6); bytes[p++] = 0x80 | (codeUnit & 0x3F); } - else if (codeUnit < 0xD800 || codeUnit > 0xDfff) { + else if (codeUnit < 0xD800 || codeUnit > 0xDFFF) { bytes[p++] = 0xE0 | (codeUnit >> 12); bytes[p++] = 0x80 | ((codeUnit >> 6) & 0x3F); bytes[p++] = 0x80 | (codeUnit & 0x3F); @@ -97,21 +97,17 @@ function readShortString(bytes, n) { if (off < len) { charCodes[i] = ((unit & 0x1F) << 6) | (bytes[off++] & 0x3F); + break; } - else { - throw new Error('Unfinished UTF-8 octet sequence'); - } - break; + throw new Error('Unfinished UTF-8 octet sequence'); case 14: if (off + 1 < len) { charCodes[i] = ((unit & 0x0F) << 12) | ((bytes[off++] & 0x3F) << 6) | (bytes[off++] & 0x3F); + break; } - else { - throw new Error('Unfinished UTF-8 octet sequence'); - } - break; + throw new Error('Unfinished UTF-8 octet sequence'); case 15: if (off + 2 < len) { var rune = (((unit & 0x07) << 18) | @@ -121,15 +117,11 @@ function readShortString(bytes, n) { if (0 <= rune && rune <= 0xFFFFF) { charCodes[i++] = (((rune >> 10) & 0x03FF) | 0xD800); charCodes[i] = ((rune & 0x03FF) | 0xDC00); + break; } - else { - throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16)); - } - } - else { - throw new Error('Unfinished UTF-8 octet sequence'); + throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16)); } - break; + throw new Error('Unfinished UTF-8 octet sequence'); default: throw new Error('Bad UTF-8 encoding 0x' + unit.toString(16)); } @@ -142,7 +134,7 @@ function readShortString(bytes, n) { function readLongString(bytes, n) { var buf = []; - var charCodes = new Uint16Array(0xffff); + var charCodes = new Uint16Array(0x8000); var i = 0, off = 0; for (var len = bytes.length; i < n && off < len; i++) { var unit = bytes[off++]; @@ -162,21 +154,17 @@ function readLongString(bytes, n) { if (off < len) { charCodes[i] = ((unit & 0x1F) << 6) | (bytes[off++] & 0x3F); + break; } - else { - throw new Error('Unfinished UTF-8 octet sequence'); - } - break; + throw new Error('Unfinished UTF-8 octet sequence'); case 14: if (off + 1 < len) { charCodes[i] = ((unit & 0x0F) << 12) | ((bytes[off++] & 0x3F) << 6) | (bytes[off++] & 0x3F); + break; } - else { - throw new Error('Unfinished UTF-8 octet sequence'); - } - break; + throw new Error('Unfinished UTF-8 octet sequence'); case 15: if (off + 2 < len) { var rune = (((unit & 0x07) << 18) | @@ -186,19 +174,15 @@ function readLongString(bytes, n) { if (0 <= rune && rune <= 0xFFFFF) { charCodes[i++] = (((rune >> 10) & 0x03FF) | 0xD800); charCodes[i] = ((rune & 0x03FF) | 0xDC00); + break; } - else { - throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16)); - } - } - else { - throw new Error('Unfinished UTF-8 octet sequence'); + throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16)); } - break; + throw new Error('Unfinished UTF-8 octet sequence'); default: throw new Error('Bad UTF-8 encoding 0x' + unit.toString(16)); } - if (i >= 65534) { + if (i >= 0x7FFF - 1) { var size = i + 1; buf.push(String.fromCharCode.apply(String, charCodes.subarray(0, size))); n -= size; @@ -212,16 +196,16 @@ function readLongString(bytes, n) { } function readString(bytes, n) { - if (n === undefined || n === null || (n < 0)) n = bytes.length; - if (n === 0) return ['', 0]; - return ((n < 100000) ? + if (n === undefined || n === null || (n < 0)) { n = bytes.length; } + if (n === 0) { return ['', 0]; } + return ((n < 0xFFFF) ? readShortString(bytes, n) : readLongString(bytes, n)); } function readStringAsBytes(bytes, n) { - if (n === undefined) n = bytes.length; - if (n === 0) return _EMPTY_BYTES; + if (n === undefined) { n = bytes.length; } + if (n === 0) { return [_EMPTY_BYTES, 0]; } var i = 0, off = 0; for (var len = bytes.length; i < n && off < len; i++) { var unit = bytes[off++]; @@ -239,19 +223,15 @@ function readStringAsBytes(bytes, n) { case 13: if (off < len) { off++; + break; } - else { - throw new Error('Unfinished UTF-8 octet sequence'); - } - break; + throw new Error('Unfinished UTF-8 octet sequence'); case 14: if (off + 1 < len) { off += 2; + break; } - else { - throw new Error('Unfinished UTF-8 octet sequence'); - } - break; + throw new Error('Unfinished UTF-8 octet sequence'); case 15: if (off + 2 < len) { var rune = (((unit & 0x07) << 18) | @@ -260,15 +240,11 @@ function readStringAsBytes(bytes, n) { (bytes[off++] & 0x3F)) - 0x10000; if (0 <= rune && rune <= 0xFFFFF) { i++; + break; } - else { - throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16)); - } - } - else { - throw new Error('Unfinished UTF-8 octet sequence'); + throw new Error('Character outside valid Unicode range: 0x' + rune.toString(16)); } - break; + throw new Error('Unfinished UTF-8 octet sequence'); default: throw new Error('Bad UTF-8 encoding 0x' + unit.toString(16)); } @@ -385,9 +361,9 @@ Object.defineProperties(BytesIO.prototype, { throw new TypeError('value is out of bounds'); } }, writeUInt32BE: { value: function(i) { - if ((i === (i | 0)) && (i >= 0)) { + if (((i & 0x7FFFFFFF) + 0x80000000 === i) && (i >= 0)) { this._grow(4); - this._length = writeInt32BE(this._bytes, this._length, i); + this._length = writeInt32BE(this._bytes, this._length, i | 0); return; } throw new TypeError('value is out of bounds'); @@ -401,16 +377,16 @@ Object.defineProperties(BytesIO.prototype, { throw new TypeError('value is out of bounds'); } }, writeUInt32LE: { value: function(i) { - if ((i === (i | 0)) && (i >= 0)) { + if (((i & 0x7FFFFFFF) + 0x80000000 === i) && (i >= 0)) { this._grow(4); - this._length = writeInt32LE(this._bytes, this._length, i); + this._length = writeInt32LE(this._bytes, this._length, i | 0); return; } throw new TypeError('value is out of bounds'); } }, write: { value: function(data) { var n = data.byteLength || data.length; - if (n === 0) return; + if (n === 0) { return; } this._grow(n); var bytes = this._bytes; var length = this._length; @@ -434,7 +410,7 @@ Object.defineProperties(BytesIO.prototype, { } }, writeAsciiString: { value: function(str) { var n = str.length; - if (n === 0) return; + if (n === 0) { return; } this._grow(n); var bytes = this._bytes; var l = this._length; @@ -445,7 +421,7 @@ Object.defineProperties(BytesIO.prototype, { } }, writeString: { value: function(str) { var n = str.length; - if (n === 0) return; + if (n === 0) { return; } // A single code unit uses at most 3 bytes. // Two code units at most 4. this._grow(n * 3); @@ -473,7 +449,7 @@ Object.defineProperties(BytesIO.prototype, { readUInt32BE: { value: function() { var value = this.readInt32BE(); if (value < 0) { - return (value & 0x7fffffff) + 0x80000000; + return (value & 0x7FFFFFFF) + 0x80000000; } return value; } }, @@ -493,7 +469,7 @@ Object.defineProperties(BytesIO.prototype, { readUInt32LE: { value: function() { var value = this.readInt32LE(); if (value < 0) { - return (value & 0x7fffffff) + 0x80000000; + return (value & 0x7FFFFFFF) + 0x80000000; } return value; } }, @@ -501,7 +477,7 @@ Object.defineProperties(BytesIO.prototype, { if (this._off + n > this._length) { n = this._length - this._off; } - if (n === 0) return _EMPTY_BYTES; + if (n === 0) { return _EMPTY_BYTES; } return this._bytes.subarray(this._off, this._off += n); } }, skip: { value: function(n) { @@ -516,7 +492,7 @@ Object.defineProperties(BytesIO.prototype, { } }, // the result is an Uint8Array, and includes tag. readBytes: { value: function(tag) { - var pos = indexof(this._bytes, tag, this._off); + var pos = Array.indexOf(this._bytes, tag, this._off); var buf; if (pos === -1) { buf = this._bytes.subarray(this._off, this._length); @@ -531,7 +507,7 @@ Object.defineProperties(BytesIO.prototype, { // the result is a String, and doesn't include tag. // but the position is the same as readBytes readUntil: { value: function(tag) { - var pos = indexof(this._bytes, tag, this._off); + var pos = Array.indexOf(this._bytes, tag, this._off); var str = ''; if (pos === this._off) { this._off++; @@ -550,21 +526,8 @@ Object.defineProperties(BytesIO.prototype, { if (this._off + n > this._length) { n = this._length - this._off; } - if (n === 0) return ''; - var bytes = this._bytes.subarray(this._off, this._off += n); - if (n < 100000) { - return String.fromCharCode.apply(String, bytes); - } - var remain = n & 0xffff; - var count = n >> 16; - var a = new Array(remain ? count + 1 : count); - for (var i = 0; i < count; ++i) { - a[i] = String.fromCharCode.apply(String, bytes.subarray(i << 16, (i + 1) << 16)); - } - if (remain) { - a[count] = String.fromCharCode.apply(String, bytes.subarray(count << 16, n)); - } - return a.join(''); + if (n === 0) { return ''; } + return toBinaryString(this._bytes.subarray(this._off, this._off += n)); } }, // n is the UTF16 length readStringAsBytes: { value: function(n) { @@ -615,7 +578,7 @@ Object.defineProperties(BytesIO.prototype, { function toString(data) { /* jshint -W086 */ - if (data.length === 0) return ''; + if (data.length === 0) { return ''; } switch(data.constructor) { case String: return data; case Buffer: return data.toString(); diff --git a/lib/io/ClassManager.js b/lib/io/ClassManager.js index 042d538..a237588 100644 --- a/lib/io/ClassManager.js +++ b/lib/io/ClassManager.js @@ -13,12 +13,11 @@ * * * hprose ClassManager for Node.js. * * * - * LastModified: May 15, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var WeakMap = global.WeakMap; diff --git a/lib/io/Formatter.js b/lib/io/Formatter.js index 271ddb2..00fb11a 100644 --- a/lib/io/Formatter.js +++ b/lib/io/Formatter.js @@ -13,12 +13,11 @@ * * * Hprose Formatter for Node.js. * * * - * LastModified: May 15, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var BytesIO = global.hprose.BytesIO; diff --git a/lib/io/Reader.js b/lib/io/Reader.js index 5422ab6..9755aff 100644 --- a/lib/io/Reader.js +++ b/lib/io/Reader.js @@ -13,13 +13,11 @@ * * * Hprose Reader for Node.js. * * * - * LastModified: Aug 3, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ -/*jshint unused:false */ 'use strict'; var Map = global.Map; @@ -656,12 +654,12 @@ Reader.prototype.constructor = Reader; Object.defineProperties(Reader.prototype, { useHarmonyMap: { value: false, writable: true }, checkTag: { value: function(expectTag, tag) { - if (tag === undefined) tag = this.stream.readByte(); - if (tag !== expectTag) unexpectedTag(tag, expectTag); + if (tag === undefined) { tag = this.stream.readByte(); } + if (tag !== expectTag) { unexpectedTag(tag, expectTag); } } }, checkTags: { value: function(expectTags, tag) { - if (tag === undefined) tag = this.stream.readByte(); - if (expectTags.indexOf(tag) >= 0) return tag; + if (tag === undefined) { tag = this.stream.readByte(); } + if (expectTags.indexOf(tag) >= 0) { return tag; } unexpectedTag(tag, expectTags); } }, unserialize: { value: function() { diff --git a/lib/io/Tags.js b/lib/io/Tags.js index a00bb62..73ff467 100644 --- a/lib/io/Tags.js +++ b/lib/io/Tags.js @@ -13,12 +13,11 @@ * * * Hprose Tags for Node.js. * * * - * LastModified: May 15, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true */ 'use strict'; global.hprose.Tags = { diff --git a/lib/io/Writer.js b/lib/io/Writer.js index 284e0e7..b881e7c 100644 --- a/lib/io/Writer.js +++ b/lib/io/Writer.js @@ -13,15 +13,13 @@ * * * Hprose Writer for Node.js. * * * - * LastModified: Aug 2, 2015 * + * LastModified: Feb 13, 2017 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true, unused:false */ 'use strict'; -var util = require('util'); var Map = global.Map; var BytesIO = global.hprose.BytesIO; var Tags = global.hprose.Tags; @@ -29,8 +27,11 @@ var ClassManager = global.hprose.ClassManager; function getClassName(obj) { var cls = obj.constructor; + if (!cls) { + return 'Object'; + } var classname = ClassManager.getClassAlias(cls); - if (classname) return classname; + if (classname) { return classname; } if (cls.name) { classname = cls.name; } @@ -125,12 +126,16 @@ function serialize(writer, value) { case Date: writer.writeDateWithRef(value); return; + case Array: + writer.writeListWithRef(value); + return; case Map: writer.writeMapWithRef(value); return; case ArrayBuffer: case Uint8Array: case BytesIO: + case Buffer: writer.writeBytesWithRef(value); return; case Int8Array: @@ -145,12 +150,9 @@ function serialize(writer, value) { writeDoubleListWithRef(writer, value); return; default: - if (util.isArray(value)) { + if (Array.isArray(value)) { writer.writeListWithRef(value); } - else if (util.isDate(value)) { - writer.writeDateWithRef(value); - } else if (Buffer.isBuffer(value)) { writer.writeBytesWithRef(value); } @@ -309,9 +311,14 @@ function writeBytes(writer, bytes) { var stream = writer.stream; stream.writeByte(Tags.TagBytes); var n = bytes.byteLength || bytes.length; - if (n > 0) stream.writeAsciiString('' + n); - stream.writeByte(Tags.TagQuote); - if (n > 0) stream.write(bytes); + if (n > 0) { + stream.writeAsciiString('' + n); + stream.writeByte(Tags.TagQuote); + stream.write(bytes); + } + else { + stream.writeByte(Tags.TagQuote); + } stream.writeByte(Tags.TagQuote); } @@ -320,9 +327,14 @@ function writeString(writer, str) { var stream = writer.stream; var n = str.length; stream.writeByte(Tags.TagString); - if (n > 0) stream.writeAsciiString('' + n); - stream.writeByte(Tags.TagQuote); - if (n > 0) stream.writeString(str); + if (n > 0) { + stream.writeAsciiString('' + n); + stream.writeByte(Tags.TagQuote); + stream.writeString(str); + } + else { + stream.writeByte(Tags.TagQuote); + } stream.writeByte(Tags.TagQuote); } @@ -331,10 +343,15 @@ function writeArray(writer, array, writeElem) { var stream = writer.stream; var n = array.length; stream.writeByte(Tags.TagList); - if (n > 0) stream.writeAsciiString('' + n); - stream.writeByte(Tags.TagOpenbrace); - for (var i = 0; i < n; i++) { - writeElem(writer, array[i]); + if (n > 0) { + stream.writeAsciiString('' + n); + stream.writeByte(Tags.TagOpenbrace); + for (var i = 0; i < n; i++) { + writeElem(writer, array[i]); + } + } + else { + stream.writeByte(Tags.TagOpenbrace); } stream.writeByte(Tags.TagClosebrace); } @@ -363,11 +380,16 @@ function writeMap(writer, map) { } var n = fields.length; stream.writeByte(Tags.TagMap); - if (n > 0) stream.writeAsciiString('' + n); - stream.writeByte(Tags.TagOpenbrace); - for (var i = 0; i < n; i++) { - serialize(writer, fields[i]); - serialize(writer, map[fields[i]]); + if (n > 0) { + stream.writeAsciiString('' + n); + stream.writeByte(Tags.TagOpenbrace); + for (var i = 0; i < n; i++) { + serialize(writer, fields[i]); + serialize(writer, map[fields[i]]); + } + } + else { + stream.writeByte(Tags.TagOpenbrace); } stream.writeByte(Tags.TagClosebrace); } @@ -377,12 +399,17 @@ function writeHarmonyMap(writer, map) { var stream = writer.stream; var n = map.size; stream.writeByte(Tags.TagMap); - if (n > 0) stream.writeAsciiString('' + n); - stream.writeByte(Tags.TagOpenbrace); - map.forEach(function(value, key) { - serialize(writer, key); - serialize(writer, value); - }); + if (n > 0) { + stream.writeAsciiString('' + n); + stream.writeByte(Tags.TagOpenbrace); + map.forEach(function(value, key) { + serialize(writer, key); + serialize(writer, value); + }); + } + else { + stream.writeByte(Tags.TagOpenbrace); + } stream.writeByte(Tags.TagClosebrace); } @@ -423,10 +450,15 @@ function writeClass(writer, classname, fields) { stream.writeByte(Tags.TagQuote); stream.writeString(classname); stream.writeByte(Tags.TagQuote); - if (n > 0) stream.writeAsciiString('' + n); - stream.writeByte(Tags.TagOpenbrace); - for (var i = 0; i < n; i++) { - writeString(writer, fields[i]); + if (n > 0) { + stream.writeAsciiString('' + n); + stream.writeByte(Tags.TagOpenbrace); + for (var i = 0; i < n; i++) { + writeString(writer, fields[i]); + } + } + else { + stream.writeByte(Tags.TagOpenbrace); } stream.writeByte(Tags.TagClosebrace); var index = writer._fieldsref.length; diff --git a/lib/server/HttpServer.js b/lib/server/HttpServer.js index 5334b5c..fd030d4 100644 --- a/lib/server/HttpServer.js +++ b/lib/server/HttpServer.js @@ -13,12 +13,11 @@ * * * Hprose Http Server for Node.js. * * * - * LastModified: Aug 9, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); diff --git a/lib/server/HttpService.js b/lib/server/HttpService.js index bf8626b..54920e4 100644 --- a/lib/server/HttpService.js +++ b/lib/server/HttpService.js @@ -13,12 +13,11 @@ * * * Hprose Http Service for Node.js. * * * - * LastModified: Aug 10, 2015 * + * LastModified: Jun 6, 2018 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var fs = require('fs'); @@ -100,6 +99,7 @@ function HttpService() { function sendHeader(context) { var resp = context.response; + resp.statusCode = 200; resp.setHeader('Content-Type', 'text/plain'); if (_P3P) { resp.setHeader('P3P', @@ -178,7 +178,7 @@ function HttpService() { function setCrossDomainXmlContent(value) { _crossDomainXmlFile = null; - if (typeof(value) === 'string') value = new Buffer(value); + if (typeof(value) === 'string') { value = new Buffer(value); } _crossDomainXmlContent = value; } @@ -197,7 +197,7 @@ function HttpService() { function setClientAccessPolicyXmlContent(value) { _clientAccessPolicyXmlFile = null; - if (typeof(value) === 'string') value = new Buffer(value); + if (typeof(value) === 'string') { value = new Buffer(value); } _clientAccessPolicyXmlContent = value; } @@ -226,15 +226,17 @@ function HttpService() { }; request.socket.setTimeout(self.timeout); var bytes = new BytesIO(); + var done = new Future(); request.on('data', function(data) { bytes.write(data); }); request.on('end', function() { - if (_clientAccessPolicyXmlContent !== null && clientAccessPolicyXmlHandler(request, response)) return; - if (_crossDomainXmlContent !== null && crossDomainXmlHandler(request, response)) return; + if (_clientAccessPolicyXmlContent !== null && clientAccessPolicyXmlHandler(request, response)) { return; } + if (_crossDomainXmlContent !== null && crossDomainXmlHandler(request, response)) { return; } try { sendHeader(context); } catch (e) { send(self.endError(e, context), response); + done.resolve(true); return; } var result = ''; @@ -245,18 +247,20 @@ function HttpService() { result = self.defaultHandle(bytes.bytes, context); } send(result, response); + done.resolve(result); }); + return done; } Object.defineProperties(this, { onSendHeader: { get: getSendHeader, set: setSendHeader }, crossDomain: { get: isCrossDomainEnabled, set: setCrossDomainEnabled }, p3p: { get: isP3PEnabled, set: setP3PEnabled }, - get: { get: isGetEnabled, set: isGetEnabled }, + get: { get: isGetEnabled, set: setGetEnabled }, crossDomainXmlFile: { get: getCrossDomainXmlFile, set: setCrossDomainXmlFile }, crossDomainXmlContent: { get: getCrossDomainXmlContent, set: setCrossDomainXmlContent }, - clientAccessPolicyXmlFile: { get: getClientAccessPolicyXmlFile, set: getClientAccessPolicyXmlFile }, - clientAccessPolicyXmlContent: { get: getClientAccessPolicyXmlContent, set: getClientAccessPolicyXmlContent }, + clientAccessPolicyXmlFile: { get: getClientAccessPolicyXmlFile, set: setClientAccessPolicyXmlFile }, + clientAccessPolicyXmlContent: { get: getClientAccessPolicyXmlContent, set: setClientAccessPolicyXmlContent }, addAccessControlAllowOrigin: { value: addAccessControlAllowOrigin }, removeAccessControlAllowOrigin: { value: removeAccessControlAllowOrigin }, handle: { value: handle } diff --git a/lib/server/Server.js b/lib/server/Server.js index 14802db..6b65c42 100644 --- a/lib/server/Server.js +++ b/lib/server/Server.js @@ -13,12 +13,11 @@ * * * Hprose Server for Node.js. * * * - * LastModified: Jun 20, 2015 * + * LastModified: Aug 20, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var parse = require('url').parse; @@ -26,14 +25,14 @@ var HttpServer = global.hprose.HttpServer; var SocketServer = global.hprose.SocketServer; var WebSocketServer = global.hprose.WebSocketServer; -function create(uri, tlsOptions) { +function create(uri, tlsOptions, handler) { var parser = parse(uri); var protocol = parser.protocol; var options = {}; var port; if (protocol === 'http:' || protocol === 'https:') { - port = parseInt(parser.port); + port = parseInt(parser.port, 10); if (!port) { port = (protocol === 'http:' ? 80 : 443); } @@ -50,7 +49,7 @@ function create(uri, tlsOptions) { options.host = parser.hostname; } if (parser.port) { - options.port = parseInt(parser.port); + options.port = parseInt(parser.port, 10); } return new SocketServer(options, tlsOptions); } @@ -60,7 +59,7 @@ function create(uri, tlsOptions) { } if (protocol === 'ws:' || protocol === 'wss:') { - port = parseInt(parser.port); + port = parseInt(parser.port, 10); if (!port) { port = (protocol === 'http:' ? 80 : 443); } @@ -69,13 +68,13 @@ function create(uri, tlsOptions) { if (parser.path) { options.path = parser.path; } - return new WebSocketServer(options, tlsOptions); + return new WebSocketServer(options, tlsOptions, handler); } throw new Error('The ' + protocol + ' server isn\'t implemented.'); } -function Server(uri, tlsOptions) { - return create(uri, tlsOptions); +function Server(uri, tlsOptions, handler) { + return create(uri, tlsOptions, handler); } Object.defineProperty(Server, 'create', { value: create }); diff --git a/lib/server/Service.js b/lib/server/Service.js index 4b90e12..35337a0 100644 --- a/lib/server/Service.js +++ b/lib/server/Service.js @@ -13,19 +13,19 @@ * * * Hprose Service for Node.js. * * * - * LastModified: Aug 9, 2015 * + * LastModified: Apr 12, 2018 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); var EventEmitter = require('events').EventEmitter; +var isError = require('../common/isError'); var TimeoutError = require('../common/TimeoutError'); +var crypto = require('crypto'); -var setImmediate = global.setImmediate; var Future = global.hprose.Future; var ResultMode = global.hprose.ResultMode; var Tags = global.hprose.Tags; @@ -35,31 +35,42 @@ var Writer = global.hprose.Writer; function callService(args, context) { if (context.oneway) { - setImmediate(function() { - context.method.apply(context.scope, args); + process.nextTick(function() { + try { + Future.toPromise(context.method.apply(context.scope, args)); + } + catch (e) {} }); if (context.async) { args[args.length - 1](null); } return null; } - return context.method.apply(context.scope, args); + return Future.toPromise(context.method.apply(context.scope, args)); } function getFuncName(func, obj) { var f = func.toString(); - var funcname = f.substr(0, f.indexOf('(')).replace(/(^\s*function\s*)|(\s*$)/ig, ''); + var funcname = f.substr(0, f.indexOf('(')).replace(/(^\s*(async)?\s*function\*?\s*)|(\s*$)/ig, ''); if ((funcname === '') && obj) { for (var name in obj) { - if (obj[name] === func) return name; + if (obj[name] === func) { return name; } } } return funcname; } -var nextid = 0; function getNextId() { - return (nextid < 0x7fffffff) ? ++nextid : nextid = 0; + var result = new Future(); + crypto.randomBytes(16, function(err, buf) { + if (err) { + result.reject(err); + } + else { + result.resolve(buf.toString('hex')); + } + }); + return result; } function Service() { @@ -142,14 +153,14 @@ function Service() { } function sendError(error, context) { - if (!util.isError(error)) { + if (!isError(error)) { error = new Error(error); } try { self.emit('sendError', error, context); if (_onSendError !== null) { var e = _onSendError(error, context); - if (util.isError(e)) { + if (isError(e)) { error = e; } } @@ -175,17 +186,19 @@ function Service() { self.emit('beforeInvoke', name, args, context.byref, context); if (_onBeforeInvoke !== null) { var value = _onBeforeInvoke(name, args, context.byref, context); - if (util.isError(value)) throw value; + if (isError(value)) { throw value; } if (Future.isPromise(value)) { return value.then(function(e) { - if (util.isError(e)) throw e; + if (isError(e)) { throw e; } return invoke(name, args, context); - }).catch(function(e) { + }).then(null, function(e) { return sendError(e, context); }); } } - return invoke(name, args, context); + return invoke(name, args, context).then(null, function(e) { + return sendError(e, context); + }); } catch (e) { return sendError(e, context); @@ -202,33 +215,51 @@ function Service() { } if (context.async) { return Future.promise(function(resolve, reject) { - if (passContext) args.push(context); - args.push(function(result) { - if (util.isError(result)) { - reject(result); - } - else { - resolve(result); + if (passContext) { args.push(context); } + args.push(function() { + var args = arguments; + switch (args.length) { + case 0: resolve(undefined); break; + case 1: { + var result = args[0]; + if (isError(result)) { + reject(result); + } + else { + resolve(result); + } + break; + } + default: { + var err = args[0]; + var result = args[1]; + if (err == null) { + resolve(result); + } + else if (isError(err)) { + reject(err); + } + else { + resolve(err); + } + break; + } } }); callService(args, context); }); } else { - if (passContext) args.push(context); - return callService(args, context); + if (passContext) { args.push(context); } + return Future.toPromise(callService(args, context)); } } function invoke(name, args, context) { - var result = _invokeHandler(name, args, context); - if (Future.isPromise(result)) { - return result.then(function(result) { - if (util.isError(result)) throw result; - return afterInvoke(name, args, context, result); - }); - } - return afterInvoke(name, args, context, result); + return _invokeHandler(name, args, context).then(function(result) { + if (isError(result)) { throw result; } + return afterInvoke(name, args, context, result); + }); } function afterInvoke(name, args, context, result) { @@ -242,10 +273,10 @@ function Service() { self.emit('afterInvoke', name, args, context.byref, result, context); if (_onAfterInvoke !== null) { var value = _onAfterInvoke(name, args, context.byref, result, context); - if (util.isError(value)) throw value; + if (isError(value)) { throw value; } if (Future.isPromise(value)) { return value.then(function(e) { - if (util.isError(e)) throw e; + if (isError(e)) { throw e; } return doOutput(args, context, result); }); } @@ -288,20 +319,23 @@ function Service() { reader.reset(); var name = reader.readString(); var alias = name.toLowerCase(); + var cc = {}; + var key; + for (key in context) { cc[key] = context[key]; } var call = _calls[alias] || _calls['*']; if (call) { - for (var key in call) context[key] = call[key]; + for (key in call) { cc[key] = call[key]; } } var args = []; - context.byref = false; + cc.byref = false; tag = input.readByte(); if (tag === Tags.TagList) { - reader.useHarmonyMap = context.useHarmonyMap; + reader.useHarmonyMap = cc.useHarmonyMap; reader.reset(); args = reader.readListWithoutTag(); tag = input.readByte(); if (tag === Tags.TagTrue) { - context.byref = true; + cc.byref = true; tag = input.readByte(); } } @@ -311,10 +345,10 @@ function Service() { 'with following data: ' + input.toString()); } if (call) { - results.push(beforeInvoke(name, args, context)); + results.push(beforeInvoke(name, args, cc)); } else { - results.push(sendError(new Error('Can\'t find this function ' + name + '().'), context)); + results.push(sendError(new Error('Can\'t find this function ' + name + '().'), cc)); } } while (tag === Tags.TagCall); return Future.reduce(results, function(output, result) { @@ -326,7 +360,7 @@ function Service() { }); } - function doFunctionList(context) { + function doFunctionList() { var stream = new BytesIO(); var writer = new Writer(stream, true); stream.writeByte(Tags.TagFunctions); @@ -335,34 +369,43 @@ function Service() { return stream.bytes; } + function delayError(e, context) { + var err = endError(e, context); + if (_errorDelay > 0) { + return Future.delayed(_errorDelay, err); + } + else { + return Promise.value(err); + } + } + function beforeFilterHandler(request, context) { var response; try { request = inputFilter(request, context); - response = _afterFilterHandler(request, context); - } - catch (e) { - response = endError(e, context); - if (_errorDelay > 0) { - response = Future.delayed(_errorDelay, response); - } - } - if (Future.isPromise(response)) { - return response.then(function(response) { - return outputFilter(response, context); + response = _afterFilterHandler(request, context).then(null, function(e) { + return delayError(e, context); }); } - else { - return outputFilter(response, context); + catch (e) { + response = delayError(e, context); } + return response.then(function(value) { + return outputFilter(value, context); + }); } function afterFilterHandler(request, context) { - var input = new BytesIO(request); - switch (input.readByte()) { - case Tags.TagCall: return doInvoke(input, context); - case Tags.TagEnd: return doFunctionList(context); - default: throw new Error('Wrong Request: \r\n' + BytesIO.toString(request)); + try { + var input = new BytesIO(request); + switch (input.readByte()) { + case Tags.TagCall: return doInvoke(input, context); + case Tags.TagEnd: return Future.value(doFunctionList(context)); + default: throw new Error('Wrong Request: \r\n' + BytesIO.toString(request)); + } + } + catch (e) { + return Future.error(e); } } @@ -473,6 +516,17 @@ function Service() { return true; } + function remove(alias) { + var name = alias.toLowerCase(); + if (_calls[name]) { + var index = _names.indexOf(alias); + if (index >= 0) { + _names.splice(index, 1); + } + delete _calls[name]; + } + } + function addFunction(func, alias, options) { if (typeof(func) !== 'function') { throw new Error('Argument func must be a function'); @@ -705,7 +759,7 @@ function Service() { else if (typeof(args[0]) === 'string') { addFunction(global[args[0]], args[0]); } - else if (util.isArray(args[0])) { + else if (Array.isArray(args[0])) { addFunctions(args[0]); } else { @@ -722,8 +776,8 @@ function Service() { typeof(args[1]) === 'string') { addFunction(global[args[0]], args[1]); } - else if (util.isArray(args[0])) { - if (util.isArray(args[1])) { + else if (Array.isArray(args[0])) { + if (Array.isArray(args[1])) { addFunctions(args[0], args[1]); } else { @@ -750,8 +804,8 @@ function Service() { addFunction(global[args[0]], args[2]); } } - else if (util.isArray(args[0])) { - if (util.isArray(args[2]) && !args[1]) { + else if (Array.isArray(args[0])) { + if (Array.isArray(args[2]) && !args[1]) { addFunctions(args[0], args[2]); } else { @@ -777,7 +831,7 @@ function Service() { else if (typeof(args[0]) === 'string') { addAsyncFunction(global[args[0]], args[0]); } - else if (util.isArray(args[0])) { + else if (Array.isArray(args[0])) { addAsyncFunctions(args[0]); } else { @@ -794,8 +848,8 @@ function Service() { typeof(args[1]) === 'string') { addAsyncFunction(global[args[0]], args[1]); } - else if (util.isArray(args[0])) { - if (util.isArray(args[1])) { + else if (Array.isArray(args[0])) { + if (Array.isArray(args[1])) { addAsyncFunctions(args[0], args[1]); } else { @@ -822,8 +876,8 @@ function Service() { addAsyncFunction(global[args[0]], args[2]); } } - else if (util.isArray(args[0])) { - if (util.isArray(args[2]) && !args[1]) { + else if (Array.isArray(args[0])) { + if (Array.isArray(args[2]) && !args[1]) { addAsyncFunctions(args[0], args[2]); } else { @@ -839,15 +893,20 @@ function Service() { throw new Error('Wrong arguments'); } + function getTopics(topic) { + if (!(topic in _topics)) { + throw new Error('topic "' + topic + '" is not published.'); + } + return _topics[topic]; + } function delTimer(topics, id) { - if ('timer' in topics[id]) { - global.clearTimeout(topics[id].timer); - delete topics[id].timer; + var t = topics[id]; + if ('timer' in t) { + global.clearTimeout(t.timer); + delete t.timer; } } - - function offline(topic, id) { - var topics = getTopics(topic); + function offline(topics, topic, id) { delTimer(topics, id); var messages = topics[id].messages; delete topics[id]; @@ -861,12 +920,17 @@ function Service() { self.emit('unsubscribe', topic, id, self); } } - function resetTimer(topic, id) { - var topics = getTopics(topic); + function setTimer(topics, topic, id) { + var t = topics[id]; + if (!('timer' in t)) { + t.timer = global.setTimeout(function() { + offline(topics, topic, id); + }, t.heartbeat); + } + } + function resetTimer(topics, topic, id) { delTimer(topics, id); - topics[id].timer = global.setTimeout(function() { - offline(topic, id); - }, topics[id].heartbeat); + setTimer(topics, topic, id); } function setRequestTimer(topic, id, request, timeout) { var topics = getTopics(topic); @@ -874,30 +938,28 @@ function Service() { return request.timeout(timeout).catchError(function(e) { if (e instanceof TimeoutError) { var checkoffline = function() { - topics[id].timer = global.setTimeout( + var t = topics[id]; + t.timer = global.setTimeout( checkoffline, - topics[id].heartbeat + t.heartbeat ); - if (topics[id].count < 0) { - offline(topic, id); + if (t.count < 0) { + offline(topics, topic, id); } else { - topics[id].count--; + t.count--; } }; checkoffline(); } - else { - topics[id].count--; - } }); } return request; } function publish(topic, options) { - if (util.isArray(topic)) { + if (Array.isArray(topic)) { topic.forEach(function(t) { - publish(t, timeout); + publish(t, options); }); return; } @@ -909,7 +971,7 @@ function Service() { _topics[topic] = {}; _events[topic] = events; addFunction(function(id) { - var topics = _topics[topic]; + var topics = getTopics(topic); if (id in topics) { if (topics[id].count < 0) { topics[id].count = 0; @@ -918,7 +980,7 @@ function Service() { if (messages.length > 0) { var message = messages.shift(); message.detector.resolve(true); - resetTimer(topic, id); + resetTimer(topics, topic, id); return message.result; } else { @@ -928,7 +990,7 @@ function Service() { } else { topics[id] = { messages: [], count: 1, heartbeat: heartbeat }; - setImmediate(function() { + process.nextTick(function() { if (_events[topic] instanceof EventEmitter) { _events[topic].emit('subscribe', id, self); } @@ -938,7 +1000,7 @@ function Service() { }); } if ('request' in topics[id]) { - topics[id].request.resolve(); + topics[id].request.resolve(null); } var request = new Future(); request.whenComplete(function() { topics[id].count--; }); @@ -946,13 +1008,13 @@ function Service() { return setRequestTimer(topic, id, request, timeout); }, topic); } - function getTopics(topic) { - if (!(topic in _topics)) { - throw new Error('topic "' + topic + '" is not published.'); - } - return _topics[topic]; - } function _push(topic, id, result) { + if (Future.isPromise(result)) { + var __push = function(result) { + return _push(topic, id, result); + } + return result.then(__push, __push); + } var topics = getTopics(topic); if (!(id in topics)) { return Future.value(false); @@ -960,12 +1022,13 @@ function Service() { if ('request' in topics[id]) { topics[id].request.resolve(result); delete topics[id].request; + setTimer(topics, topic, id); return Future.value(true); } else { var detector = new Future(); topics[id].messages.push({ detector: detector, result: result }); - resetTimer(topic, id); + setTimer(topics, topic, id); return detector; } } @@ -1024,7 +1087,7 @@ function Service() { var args = arguments; var argc = args.length; var id, result; - if (argc < 2 && argc > 3) { + if (argc < 2 || argc > 3) { throw new Error('Wrong number of arguments'); } if (argc === 2) { @@ -1038,7 +1101,7 @@ function Service() { var topics = getTopics(topic); for (id in topics) { _push(topic, id, result); } } - else if (util.isArray(id)) { + else if (Array.isArray(id)) { id.forEach(function(id) { _push(topic, id, result); }); } else { @@ -1050,7 +1113,7 @@ function Service() { _beforeFilterHandler = _beforeFilterHandlers.reduceRight( function(next, handler) { return function(request, context) { - return handler(request, context, next); + return Future.toPromise(handler(request, context, next)); }; }, beforeFilterHandler); } @@ -1059,7 +1122,7 @@ function Service() { _afterFilterHandler = _afterFilterHandlers.reduceRight( function(next, handler) { return function(request, context) { - return handler(request, context, next); + return Future.toPromise(handler(request, context, next)); }; }, afterFilterHandler); } @@ -1068,7 +1131,7 @@ function Service() { _invokeHandler = _invokeHandlers.reduceRight( function(next, handler) { return function(name, args, context) { - return handler(name, args, context, next); + return Future.toPromise(handler(name, args, context, next)); }; }, invokeHandler); } @@ -1076,6 +1139,10 @@ function Service() { addInvokeHandler(handler); return self; } + function getNames() { + return _names; + } + var beforeFilter = Object.create(null, { use: { value: function(handler) { addBeforeFilterHandler(handler); @@ -1102,6 +1169,7 @@ function Service() { debug: { get: isDebugEnabled, set: setDebugEnabled }, simple: { get: getSimpleMode, set: setSimpleMode }, passContext: { get: getPassContext, set: setPassContext }, + errorDelay: { get: getErrorDelay, set: setErrorDelay }, filter: { get: getFilter, set: setFilter }, addFilter: { value: addFilter }, removeFilter: { value: removeFilter }, @@ -1129,6 +1197,7 @@ function Service() { idlist: { value: idlist }, exist: { value: exist }, use: { value: use }, + getNames: { value: getNames }, beforeFilter: { value: beforeFilter }, afterFilter: { value: afterFilter } }); diff --git a/lib/server/SocketServer.js b/lib/server/SocketServer.js index c09dbe6..41d638e 100644 --- a/lib/server/SocketServer.js +++ b/lib/server/SocketServer.js @@ -13,12 +13,11 @@ * * * Hprose Socket Server for Node.js. * * * - * LastModified: Aug 9, 2015 * + * LastModified: Mar 3, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); diff --git a/lib/server/SocketService.js b/lib/server/SocketService.js index 841f13a..6859e6a 100644 --- a/lib/server/SocketService.js +++ b/lib/server/SocketService.js @@ -13,21 +13,21 @@ * * * Hprose Socket Service for Node.js. * * * - * LastModified: Aug 9, 2015 * + * LastModified: Sep 17, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); -var setImmediate = global.setImmediate; var Service = global.hprose.Service; var BytesIO = global.hprose.BytesIO; var Future = global.hprose.Future; +function noop() {} + function SocketService() { Service.call(this); @@ -139,7 +139,7 @@ function SocketService() { }; try { self.emit('accept', context); - if (_onAccept) _onAccept(context); + if (_onAccept) { _onAccept(context); } } catch(e) { socket.end(); @@ -148,16 +148,18 @@ function SocketService() { socket.on('close', function() { try { self.emit('close', context); - if (_onClose) _onClose(context); + if (_onClose) { _onClose(context); } } catch(e) {} }); - socket.on('end', function(e) { }); + socket.on('end', noop); socket.on('error', function(e) { try { - self.emit('sendError', e, context); - if (self.onSendError) { - self.onSendError(e, context); + if (e.code != "EPIPE") { + self.emit('sendError', e, context); + if (self.onSendError) { + self.onSendError(e, context); + } } } catch(e) {} diff --git a/lib/server/WebSocketServer.js b/lib/server/WebSocketServer.js index ae1d74c..4513e32 100644 --- a/lib/server/WebSocketServer.js +++ b/lib/server/WebSocketServer.js @@ -13,12 +13,11 @@ * * * Hprose WebSocket Server for Node.js. * * * - * LastModified: Aug 9, 2015 * + * LastModified: Aug 20, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); @@ -27,12 +26,13 @@ var http = require('http'); var https = require('https'); var WebSocketService = global.hprose.WebSocketService; -function WebSocketServer(options, tlsOptions) { +function WebSocketServer(options, tlsOptions, handler) { WebSocketService.call(this); var self = this; - var httpserver = (tlsOptions ? - https.createServer(tlsOptions, self.handle) : - http.createServer(self.handle)); + if (!handler) handler = this.handle; + var httpserver = tlsOptions ? + https.createServer(tlsOptions, handler) : + http.createServer(handler); var host = options.host; var port = options.port; delete options.host; @@ -47,7 +47,7 @@ function WebSocketServer(options, tlsOptions) { server: self.server, userdata:{} }; - if (socket) context.socket = socket; + if (socket) { context.socket = socket; } try { self.emit('sendError', e, context); if (self.onSendError) { diff --git a/lib/server/WebSocketService.js b/lib/server/WebSocketService.js index 28faf3e..0779cee 100644 --- a/lib/server/WebSocketService.js +++ b/lib/server/WebSocketService.js @@ -13,17 +13,15 @@ * * * Hprose WebSocket Service for Node.js. * * * - * LastModified: Aug 9, 2015 * + * LastModified: Sep 30, 2016 * * Author: Ma Bingyao * * * \**********************************************************/ -/*jshint node:true, eqeqeq:true */ 'use strict'; var util = require('util'); -var setImmediate = global.setImmediate; var HttpService = global.hprose.HttpService; var BytesIO = global.hprose.BytesIO; var Future = global.hprose.Future; @@ -101,7 +99,7 @@ function WebSocketService() { }; try { self.emit('accept', context); - if (_onAccept) _onAccept(context); + if (_onAccept) { _onAccept(context); } } catch(e) { ws.close(); @@ -110,12 +108,10 @@ function WebSocketService() { ws.on('close', function() { try { self.emit('close',context); - if (_onClose) _onClose(context); + if (_onClose) { _onClose(context); } } catch(e) {} }); - var bytes = new BytesIO(); - var dataLength = -1; ws.on('error', function(e) { try { self.emit('sendError', e, context); @@ -129,7 +125,7 @@ function WebSocketService() { var bytes = new BytesIO(data); var id = bytes.readInt32BE(); var request = bytes.read(bytes.length - 4); - setImmediate(function() { + process.nextTick(function() { var context = { httpserver: self.httpserver, server: self.server, diff --git a/lib/utils/regenerator-runtime.js b/lib/utils/regenerator-runtime.js new file mode 100644 index 0000000..4ec72f1 --- /dev/null +++ b/lib/utils/regenerator-runtime.js @@ -0,0 +1,669 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * https://raw.github.com/facebook/regenerator/master/LICENSE file. An + * additional grant of patent rights can be found in the PATENTS file in + * the same directory. + */ + +!(function(global) { + "use strict"; + + var hasOwn = Object.prototype.hasOwnProperty; + var undefined; // More compressible than void 0. + var $Symbol = typeof Symbol === "function" ? Symbol : {}; + var iteratorSymbol = $Symbol.iterator || "@@iterator"; + var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; + + var inModule = typeof module === "object"; + var runtime = global.regeneratorRuntime; + if (runtime) { + if (inModule) { + // If regeneratorRuntime is defined globally and we're in a module, + // make the exports object identical to regeneratorRuntime. + module.exports = runtime; + } + // Don't bother evaluating the rest of this file if the runtime was + // already defined globally. + return; + } + + // Define the runtime globally (as expected by generated code) as either + // module.exports (if we're in a module) or a new, empty object. + runtime = global.regeneratorRuntime = inModule ? module.exports : {}; + + function wrap(innerFn, outerFn, self, tryLocsList) { + // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator. + var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator; + var generator = Object.create(protoGenerator.prototype); + var context = new Context(tryLocsList || []); + + // The ._invoke method unifies the implementations of the .next, + // .throw, and .return methods. + generator._invoke = makeInvokeMethod(innerFn, self, context); + + return generator; + } + runtime.wrap = wrap; + + // Try/catch helper to minimize deoptimizations. Returns a completion + // record like context.tryEntries[i].completion. This interface could + // have been (and was previously) designed to take a closure to be + // invoked without arguments, but in all the cases we care about we + // already have an existing method we want to call, so there's no need + // to create a new function object. We can even get away with assuming + // the method takes exactly one argument, since that happens to be true + // in every case, so we don't have to touch the arguments object. The + // only additional allocation required is the completion record, which + // has a stable shape and so hopefully should be cheap to allocate. + function tryCatch(fn, obj, arg) { + try { + return { type: "normal", arg: fn.call(obj, arg) }; + } catch (err) { + return { type: "throw", arg: err }; + } + } + + var GenStateSuspendedStart = "suspendedStart"; + var GenStateSuspendedYield = "suspendedYield"; + var GenStateExecuting = "executing"; + var GenStateCompleted = "completed"; + + // Returning this object from the innerFn has the same effect as + // breaking out of the dispatch switch statement. + var ContinueSentinel = {}; + + // Dummy constructor functions that we use as the .constructor and + // .constructor.prototype properties for functions that return Generator + // objects. For full spec compliance, you may wish to configure your + // minifier not to mangle the names of these two functions. + function Generator() {} + function GeneratorFunction() {} + function GeneratorFunctionPrototype() {} + + var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype; + GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; + GeneratorFunctionPrototype.constructor = GeneratorFunction; + GeneratorFunctionPrototype[toStringTagSymbol] = GeneratorFunction.displayName = "GeneratorFunction"; + + // Helper for defining the .next, .throw, and .return methods of the + // Iterator interface in terms of a single ._invoke method. + function defineIteratorMethods(prototype) { + ["next", "throw", "return"].forEach(function(method) { + prototype[method] = function(arg) { + return this._invoke(method, arg); + }; + }); + } + + runtime.isGeneratorFunction = function(genFun) { + var ctor = typeof genFun === "function" && genFun.constructor; + return ctor + ? ctor === GeneratorFunction || + // For the native GeneratorFunction constructor, the best we can + // do is to check its .name property. + (ctor.displayName || ctor.name) === "GeneratorFunction" + : false; + }; + + runtime.mark = function(genFun) { + if (Object.setPrototypeOf) { + Object.setPrototypeOf(genFun, GeneratorFunctionPrototype); + } else { + genFun.__proto__ = GeneratorFunctionPrototype; + if (!(toStringTagSymbol in genFun)) { + genFun[toStringTagSymbol] = "GeneratorFunction"; + } + } + genFun.prototype = Object.create(Gp); + return genFun; + }; + + // Within the body of any async function, `await x` is transformed to + // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test + // `value instanceof AwaitArgument` to determine if the yielded value is + // meant to be awaited. Some may consider the name of this method too + // cutesy, but they are curmudgeons. + runtime.awrap = function(arg) { + return new AwaitArgument(arg); + }; + + function AwaitArgument(arg) { + this.arg = arg; + } + + function AsyncIterator(generator) { + function invoke(method, arg, resolve, reject) { + var record = tryCatch(generator[method], generator, arg); + if (record.type === "throw") { + reject(record.arg); + } else { + var result = record.arg; + var value = result.value; + if (value instanceof AwaitArgument) { + return Promise.resolve(value.arg).then(function(value) { + invoke("next", value, resolve, reject); + }, function(err) { + invoke("throw", err, resolve, reject); + }); + } + + return Promise.resolve(value).then(function(unwrapped) { + // When a yielded Promise is resolved, its final value becomes + // the .value of the Promise<{value,done}> result for the + // current iteration. If the Promise is rejected, however, the + // result for this iteration will be rejected with the same + // reason. Note that rejections of yielded Promises are not + // thrown back into the generator function, as is the case + // when an awaited Promise is rejected. This difference in + // behavior between yield and await is important, because it + // allows the consumer to decide what to do with the yielded + // rejection (swallow it and continue, manually .throw it back + // into the generator, abandon iteration, whatever). With + // await, by contrast, there is no opportunity to examine the + // rejection reason outside the generator function, so the + // only option is to throw it from the await expression, and + // let the generator function handle the exception. + result.value = unwrapped; + resolve(result); + }, reject); + } + } + + if (typeof process === "object" && process.domain) { + invoke = process.domain.bind(invoke); + } + + var previousPromise; + + function enqueue(method, arg) { + function callInvokeWithMethodAndArg() { + return new Promise(function(resolve, reject) { + invoke(method, arg, resolve, reject); + }); + } + + return previousPromise = + // If enqueue has been called before, then we want to wait until + // all previous Promises have been resolved before calling invoke, + // so that results are always delivered in the correct order. If + // enqueue has not been called before, then it is important to + // call invoke immediately, without waiting on a callback to fire, + // so that the async generator function has the opportunity to do + // any necessary setup in a predictable way. This predictability + // is why the Promise constructor synchronously invokes its + // executor callback, and why async functions synchronously + // execute code before the first await. Since we implement simple + // async functions in terms of async generators, it is especially + // important to get this right, even though it requires care. + previousPromise ? previousPromise.then( + callInvokeWithMethodAndArg, + // Avoid propagating failures to Promises returned by later + // invocations of the iterator. + callInvokeWithMethodAndArg + ) : callInvokeWithMethodAndArg(); + } + + // Define the unified helper method that is used to implement .next, + // .throw, and .return (see defineIteratorMethods). + this._invoke = enqueue; + } + + defineIteratorMethods(AsyncIterator.prototype); + + // Note that simple async functions are implemented on top of + // AsyncIterator objects; they just return a Promise for the value of + // the final result produced by the iterator. + runtime.async = function(innerFn, outerFn, self, tryLocsList) { + var iter = new AsyncIterator( + wrap(innerFn, outerFn, self, tryLocsList) + ); + + return runtime.isGeneratorFunction(outerFn) + ? iter // If outerFn is a generator, return the full iterator. + : iter.next().then(function(result) { + return result.done ? result.value : iter.next(); + }); + }; + + function makeInvokeMethod(innerFn, self, context) { + var state = GenStateSuspendedStart; + + return function invoke(method, arg) { + if (state === GenStateExecuting) { + throw new Error("Generator is already running"); + } + + if (state === GenStateCompleted) { + if (method === "throw") { + throw arg; + } + + // Be forgiving, per 25.3.3.3.3 of the spec: + // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume + return doneResult(); + } + + while (true) { + var delegate = context.delegate; + if (delegate) { + if (method === "return" || + (method === "throw" && delegate.iterator[method] === undefined)) { + // A return or throw (when the delegate iterator has no throw + // method) always terminates the yield* loop. + context.delegate = null; + + // If the delegate iterator has a return method, give it a + // chance to clean up. + var returnMethod = delegate.iterator["return"]; + if (returnMethod) { + var record = tryCatch(returnMethod, delegate.iterator, arg); + if (record.type === "throw") { + // If the return method threw an exception, let that + // exception prevail over the original return or throw. + method = "throw"; + arg = record.arg; + continue; + } + } + + if (method === "return") { + // Continue with the outer return, now that the delegate + // iterator has been terminated. + continue; + } + } + + var record = tryCatch( + delegate.iterator[method], + delegate.iterator, + arg + ); + + if (record.type === "throw") { + context.delegate = null; + + // Like returning generator.throw(uncaught), but without the + // overhead of an extra function call. + method = "throw"; + arg = record.arg; + continue; + } + + // Delegate generator ran and handled its own exceptions so + // regardless of what the method was, we continue as if it is + // "next" with an undefined arg. + method = "next"; + arg = undefined; + + var info = record.arg; + if (info.done) { + context[delegate.resultName] = info.value; + context.next = delegate.nextLoc; + } else { + state = GenStateSuspendedYield; + return info; + } + + context.delegate = null; + } + + if (method === "next") { + // Setting context._sent for legacy support of Babel's + // function.sent implementation. + context.sent = context._sent = arg; + + } else if (method === "throw") { + if (state === GenStateSuspendedStart) { + state = GenStateCompleted; + throw arg; + } + + if (context.dispatchException(arg)) { + // If the dispatched exception was caught by a catch block, + // then let that catch block handle the exception normally. + method = "next"; + arg = undefined; + } + + } else if (method === "return") { + context.abrupt("return", arg); + } + + state = GenStateExecuting; + + var record = tryCatch(innerFn, self, context); + if (record.type === "normal") { + // If an exception is thrown from innerFn, we leave state === + // GenStateExecuting and loop back for another invocation. + state = context.done + ? GenStateCompleted + : GenStateSuspendedYield; + + var info = { + value: record.arg, + done: context.done + }; + + if (record.arg === ContinueSentinel) { + if (context.delegate && method === "next") { + // Deliberately forget the last sent value so that we don't + // accidentally pass it on to the delegate. + arg = undefined; + } + } else { + return info; + } + + } else if (record.type === "throw") { + state = GenStateCompleted; + // Dispatch the exception by looping back around to the + // context.dispatchException(arg) call above. + method = "throw"; + arg = record.arg; + } + } + }; + } + + // Define Generator.prototype.{next,throw,return} in terms of the + // unified ._invoke helper method. + defineIteratorMethods(Gp); + + Gp[iteratorSymbol] = function() { + return this; + }; + + Gp[toStringTagSymbol] = "Generator"; + + Gp.toString = function() { + return "[object Generator]"; + }; + + function pushTryEntry(locs) { + var entry = { tryLoc: locs[0] }; + + if (1 in locs) { + entry.catchLoc = locs[1]; + } + + if (2 in locs) { + entry.finallyLoc = locs[2]; + entry.afterLoc = locs[3]; + } + + this.tryEntries.push(entry); + } + + function resetTryEntry(entry) { + var record = entry.completion || {}; + record.type = "normal"; + delete record.arg; + entry.completion = record; + } + + function Context(tryLocsList) { + // The root entry object (effectively a try statement without a catch + // or a finally block) gives us a place to store values thrown from + // locations where there is no enclosing try statement. + this.tryEntries = [{ tryLoc: "root" }]; + tryLocsList.forEach(pushTryEntry, this); + this.reset(true); + } + + runtime.keys = function(object) { + var keys = []; + for (var key in object) { + keys.push(key); + } + keys.reverse(); + + // Rather than returning an object with a next method, we keep + // things simple and return the next function itself. + return function next() { + while (keys.length) { + var key = keys.pop(); + if (key in object) { + next.value = key; + next.done = false; + return next; + } + } + + // To avoid creating an additional object, we just hang the .value + // and .done properties off the next function object itself. This + // also ensures that the minifier will not anonymize the function. + next.done = true; + return next; + }; + }; + + function values(iterable) { + if (iterable) { + var iteratorMethod = iterable[iteratorSymbol]; + if (iteratorMethod) { + return iteratorMethod.call(iterable); + } + + if (typeof iterable.next === "function") { + return iterable; + } + + if (!isNaN(iterable.length)) { + var i = -1, next = function next() { + while (++i < iterable.length) { + if (hasOwn.call(iterable, i)) { + next.value = iterable[i]; + next.done = false; + return next; + } + } + + next.value = undefined; + next.done = true; + + return next; + }; + + return next.next = next; + } + } + + // Return an iterator with no values. + return { next: doneResult }; + } + runtime.values = values; + + function doneResult() { + return { value: undefined, done: true }; + } + + Context.prototype = { + constructor: Context, + + reset: function(skipTempReset) { + this.prev = 0; + this.next = 0; + // Resetting context._sent for legacy support of Babel's + // function.sent implementation. + this.sent = this._sent = undefined; + this.done = false; + this.delegate = null; + + this.tryEntries.forEach(resetTryEntry); + + if (!skipTempReset) { + for (var name in this) { + // Not sure about the optimal order of these conditions: + if (name.charAt(0) === "t" && + hasOwn.call(this, name) && + !isNaN(+name.slice(1))) { + this[name] = undefined; + } + } + } + }, + + stop: function() { + this.done = true; + + var rootEntry = this.tryEntries[0]; + var rootRecord = rootEntry.completion; + if (rootRecord.type === "throw") { + throw rootRecord.arg; + } + + return this.rval; + }, + + dispatchException: function(exception) { + if (this.done) { + throw exception; + } + + var context = this; + function handle(loc, caught) { + record.type = "throw"; + record.arg = exception; + context.next = loc; + return !!caught; + } + + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + var record = entry.completion; + + if (entry.tryLoc === "root") { + // Exception thrown outside of any try block that could handle + // it, so set the completion value of the entire function to + // throw the exception. + return handle("end"); + } + + if (entry.tryLoc <= this.prev) { + var hasCatch = hasOwn.call(entry, "catchLoc"); + var hasFinally = hasOwn.call(entry, "finallyLoc"); + + if (hasCatch && hasFinally) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } else if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + + } else if (hasCatch) { + if (this.prev < entry.catchLoc) { + return handle(entry.catchLoc, true); + } + + } else if (hasFinally) { + if (this.prev < entry.finallyLoc) { + return handle(entry.finallyLoc); + } + + } else { + throw new Error("try statement without catch or finally"); + } + } + } + }, + + abrupt: function(type, arg) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + if (entry.tryLoc <= this.prev && + hasOwn.call(entry, "finallyLoc") && + this.prev < entry.finallyLoc) { + var finallyEntry = entry; + break; + } + } + + if (finallyEntry && + (type === "break" || + type === "continue") && + finallyEntry.tryLoc <= arg && + arg <= finallyEntry.finallyLoc) { + // Ignore the finally entry if control is not jumping to a + // location outside the try/catch block. + finallyEntry = null; + } + + var record = finallyEntry ? finallyEntry.completion : {}; + record.type = type; + record.arg = arg; + + if (finallyEntry) { + this.next = finallyEntry.finallyLoc; + } else { + this.complete(record); + } + + return ContinueSentinel; + }, + + complete: function(record, afterLoc) { + if (record.type === "throw") { + throw record.arg; + } + + if (record.type === "break" || + record.type === "continue") { + this.next = record.arg; + } else if (record.type === "return") { + this.rval = record.arg; + this.next = "end"; + } else if (record.type === "normal" && afterLoc) { + this.next = afterLoc; + } + }, + + finish: function(finallyLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + if (entry.finallyLoc === finallyLoc) { + this.complete(entry.completion, entry.afterLoc); + resetTryEntry(entry); + return ContinueSentinel; + } + } + }, + + "catch": function(tryLoc) { + for (var i = this.tryEntries.length - 1; i >= 0; --i) { + var entry = this.tryEntries[i]; + if (entry.tryLoc === tryLoc) { + var record = entry.completion; + if (record.type === "throw") { + var thrown = record.arg; + resetTryEntry(entry); + } + return thrown; + } + } + + // The context.catch method must only be called with a location + // argument that corresponds to a known catch block. + throw new Error("illegal catch attempt"); + }, + + delegateYield: function(iterable, resultName, nextLoc) { + this.delegate = { + iterator: values(iterable), + resultName: resultName, + nextLoc: nextLoc + }; + + return ContinueSentinel; + } + }; +})( + // Among the various tricks for obtaining a reference to the global + // object, this seems to be the most reliable technique that does not + // use indirect eval (which violates Content Security Policy). + typeof global === "object" ? global : + typeof window === "object" ? window : + typeof self === "object" ? self : this +); diff --git a/package.json b/package.json index 158b26f..0266944 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hprose", - "version": "2.0.5", + "version": "2.0.51", "homepage": "https://github.com/hprose/hprose-nodejs", "description": "hprose for node.js", "keywords": [ @@ -50,9 +50,9 @@ "lib": "lib/" }, "main": "lib/hprose.js", - "optionalDependencies": { "ws": "*" }, + "optionalDependencies": { "ws": ">=4.0.0" }, "devDependencies": { - "promises-plus-tests": "*" + "promises-aplus-tests": "*" }, "scripts": { "aplus-tests": "promises-aplus-tests lib/hprose"