├── .gitignore ├── .travis.yml ├── bin └── common-node ├── examples ├── traceur.js ├── hello.js ├── sleep.js ├── static.js ├── http.js ├── chat.js ├── spawn.js ├── twitter.js └── base64.js ├── benchmarks ├── set-timeout │ ├── node.js │ ├── common-node.js │ └── ringo.js ├── hello-world │ ├── common-node.js │ ├── node.js │ └── ringo.js ├── buffer-alloc │ ├── common-node.js │ ├── node.js │ └── ringo.js ├── file-read │ ├── node.js │ ├── ringo.js │ └── common-node.js ├── file-stream │ ├── common-node.js │ ├── ringo.js │ └── node.js ├── string-alloc │ ├── node.js │ ├── common-node.js │ └── ringo.js ├── no-alloc │ ├── ringo.js │ ├── common-node.js │ └── node.js ├── run ├── graph └── run.js ├── test ├── binary │ ├── all.js │ ├── bytearray-encodings-tests.js │ ├── bytestring-encodings-tests.js │ ├── bytestring-tests.js │ └── bytearray-tests.js ├── jsgi.js ├── all.js ├── fs-base │ ├── all.js │ ├── dirname.js │ ├── normal.js │ ├── extension.js │ ├── relative.js │ ├── path.js │ ├── resolve.js │ ├── main.js │ └── iterator.js ├── subprocess.js ├── http.js └── io.js ├── lib ├── all.js ├── global.js ├── console.js ├── run.js ├── test.js ├── assert.js ├── httpserver.js ├── system.js ├── socket.js ├── subprocess.js ├── httpclient.js └── ringo │ └── httpclient.js ├── tools └── jsdoc │ ├── hosted.html │ ├── static.html │ ├── style.css │ └── jsdoc.css ├── doc ├── static │ ├── style.css │ ├── jsdoc.css │ └── jsdoc.js ├── all │ └── index.html ├── test │ └── index.html ├── index.html ├── jsgi │ └── index.html ├── system │ └── index.html └── httpclient │ └── index.html ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | -------------------------------------------------------------------------------- /bin/common-node: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | process.argv.shift(); 3 | process.execPath = 'common-node'; 4 | require('../lib/run')(); 5 | 6 | -------------------------------------------------------------------------------- /examples/traceur.js: -------------------------------------------------------------------------------- 1 | var {HttpClient} = require('httpclient'); 2 | 3 | console.log(new HttpClient({ 4 | url: 'http://google.com' 5 | }).finish().body.read(null).decodeToString()); -------------------------------------------------------------------------------- /benchmarks/set-timeout/node.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | 3 | http.createServer(function(req, res) { 4 | setTimeout(function() { 5 | res.writeHead(200); 6 | res.end(); 7 | }, 20); 8 | }).listen(8080); -------------------------------------------------------------------------------- /benchmarks/set-timeout/common-node.js: -------------------------------------------------------------------------------- 1 | var sleep = require('system').sleep; 2 | 3 | exports.app = function() { 4 | sleep(20); 5 | return { 6 | status:200, 7 | headers:{}, 8 | body:[] 9 | }; 10 | }; -------------------------------------------------------------------------------- /examples/hello.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Hello World example. 3 | */ 4 | 5 | exports.app = function(request) { 6 | return { 7 | status: 200, 8 | headers: {}, 9 | body: ['Hello World!\n'] 10 | }; 11 | }; -------------------------------------------------------------------------------- /benchmarks/hello-world/common-node.js: -------------------------------------------------------------------------------- 1 | exports.app = function() { 2 | return { 3 | status:200, 4 | headers:{ 5 | 'Content-Type':'text/plain; charset=utf-8' 6 | }, 7 | body:['Hello World!\n'] 8 | }; 9 | }; -------------------------------------------------------------------------------- /benchmarks/hello-world/node.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | 3 | http.createServer(function(req, res) { 4 | res.writeHead(200, {"Content-Type":"text/plain; charset=utf-8"}); 5 | res.end('Hello World!\n'); 6 | }).listen(8080); -------------------------------------------------------------------------------- /benchmarks/set-timeout/ringo.js: -------------------------------------------------------------------------------- 1 | exports.app = function() { 2 | java.lang.Thread.sleep(100); 3 | return { 4 | status : 200, 5 | headers : {}, 6 | body : [] 7 | }; 8 | }; 9 | 10 | if (require.main === module) { 11 | require("ringo/httpserver").main(module.id); 12 | } 13 | -------------------------------------------------------------------------------- /benchmarks/buffer-alloc/common-node.js: -------------------------------------------------------------------------------- 1 | var ByteArray = require('binary').ByteArray; 2 | 3 | var n = 128 * 1024; 4 | exports.app = function(request) { 5 | var b = new ByteArray(n); 6 | return { 7 | status:200, 8 | headers:{ 9 | "Content-Type":"text/plain" 10 | }, 11 | body:[b] 12 | }; 13 | }; -------------------------------------------------------------------------------- /benchmarks/buffer-alloc/node.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var Buffer = require('buffer').Buffer; 3 | 4 | var n = 128 * 1024; 5 | http.createServer(function(req, res) { 6 | var b = new Buffer(n); 7 | b.fill(0); 8 | res.writeHead(200, { 9 | "Content-Type":"text/plain" 10 | }); 11 | res.end(b); 12 | }).listen(8080); -------------------------------------------------------------------------------- /benchmarks/hello-world/ringo.js: -------------------------------------------------------------------------------- 1 | exports.app = function() { 2 | var response = { 3 | status: 200, 4 | headers: { 5 | 'Content-Type': 'text/plain' 6 | }, 7 | body: ['Hello World!\n'] 8 | }; 9 | return response; 10 | }; 11 | 12 | if(require.main === module) { 13 | require("ringo/httpserver").main(module.id); 14 | } 15 | -------------------------------------------------------------------------------- /examples/sleep.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Sleep example. Sleep for one second before returning a 3 | * response. 4 | */ 5 | var sleep = require('system').sleep; 6 | 7 | exports.app = function(request) { 8 | sleep(1000); 9 | return { 10 | status: 200, 11 | headers: {}, 12 | body: ['Hello Sleep!\n'] 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /benchmarks/file-read/node.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var fs = require('fs'); 3 | var Buffer = require('buffer').Buffer; 4 | 5 | fs.writeFileSync('test.dat', new Buffer(128 * 1024)); 6 | http.createServer(function(req, res) { 7 | res.writeHead(200, {"Content-Type":"text/plain; charset=utf-8'"}); 8 | res.end(fs.readFileSync('test.dat')); 9 | }).listen(8080); -------------------------------------------------------------------------------- /benchmarks/file-stream/common-node.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-base'); 2 | var ByteArray = require('binary').ByteArray; 3 | 4 | fs.write('test.dat', new ByteArray(128 * 1024, false)); 5 | exports.app = function() { 6 | return { 7 | status:200, 8 | headers:{'Content-Type':'text/plain; charset=utf-8'}, 9 | body:fs.openRaw('test.dat') 10 | }; 11 | }; -------------------------------------------------------------------------------- /benchmarks/file-stream/ringo.js: -------------------------------------------------------------------------------- 1 | var openRaw = require('fs').openRaw; 2 | 3 | exports.app = function() { 4 | return { 5 | status:200, 6 | headers:{ 7 | 'Content-Type':'text/plain' 8 | }, 9 | body:openRaw('../README.md') 10 | }; 11 | }; 12 | 13 | if (require.main === module) { 14 | require("ringo/httpserver").main(module.id); 15 | } -------------------------------------------------------------------------------- /benchmarks/file-read/ringo.js: -------------------------------------------------------------------------------- 1 | var read = require('fs').read; 2 | 3 | exports.app = function() { 4 | return { 5 | status:200, 6 | headers:{ 7 | 'Content-Type':'text/plain' 8 | }, 9 | body:[read('../README.md', {binary:true})] 10 | }; 11 | }; 12 | 13 | if (require.main === module) { 14 | require("ringo/httpserver").main(module.id); 15 | } -------------------------------------------------------------------------------- /benchmarks/file-read/common-node.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-base'); 2 | var ByteArray = require('binary').ByteArray; 3 | 4 | fs.write('test.dat', new ByteArray(128 * 1024, false)); 5 | exports.app = function() { 6 | return { 7 | status:200, 8 | headers:{'Content-Type':'text/plain; charset=utf-8'}, 9 | body:[fs.read('test.dat', {binary:true})] 10 | }; 11 | }; -------------------------------------------------------------------------------- /test/binary/all.js: -------------------------------------------------------------------------------- 1 | exports['ByteArray'] = require('./bytearray-tests'); 2 | exports['ByteArray Encodings'] = require('./bytearray-encodings-tests'); 3 | exports['ByteString'] = require('./bytestring-tests'); 4 | exports['ByteString Encodings'] = require('./bytestring-encodings-tests'); 5 | 6 | if (require.main == module) { 7 | require("../../lib/test").run(exports); 8 | } 9 | -------------------------------------------------------------------------------- /examples/static.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Serve a static file from disk. The source code of the current 3 | * module, i.e. this file is served. 4 | */ 5 | 6 | var openRaw = require('fs-base').openRaw; 7 | 8 | exports.app = function(request) { 9 | return { 10 | status: 200, 11 | headers: {}, 12 | body: openRaw(module.filename) 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/all.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Collection of all modules. 3 | */ 4 | 5 | [ 6 | 'assert', 7 | 'binary', 8 | 'fs-base', 9 | 'global', 10 | 'httpclient', 11 | 'httpserver', 12 | 'io', 13 | 'run', 14 | 'socket', 15 | 'subprocess', 16 | 'system', 17 | 'test', 18 | 'ringo/httpclient' 19 | ].forEach(function(path) { 20 | exports[path] = require('./' + path); 21 | }); -------------------------------------------------------------------------------- /benchmarks/string-alloc/node.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var Buffer = require('buffer').Buffer; 3 | 4 | var n = 128 * 1024; 5 | var b = new Buffer(n); 6 | for (var i = 0; i < n; i++) { 7 | b[i] = Math.floor(32 + (127 - 32) * Math.random()); 8 | } 9 | http.createServer(function(req, res) { 10 | res.writeHead(200, { 11 | "Content-Type":"text/plain" 12 | }); 13 | res.end(b.toString("ascii")); 14 | }).listen(8080); 15 | -------------------------------------------------------------------------------- /test/jsgi.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var jsgi = require('../lib/jsgi').jsgi; 3 | 4 | exports.testServer = function() { 5 | http.createServer(jsgi(function(request) { 6 | return { 7 | status: 200, 8 | headers: { 9 | "Content-Type": "text/plain" 10 | }, 11 | body: request.input 12 | }; 13 | })).listen(8080); 14 | }; 15 | 16 | if (require.main == module) { 17 | require("../lib/test").run(exports); 18 | } 19 | -------------------------------------------------------------------------------- /benchmarks/string-alloc/common-node.js: -------------------------------------------------------------------------------- 1 | var ByteArray = require('binary').ByteArray; 2 | 3 | var n = 128 * 1024; 4 | var b = new ByteArray(n); 5 | for (var i = 0; i < n; i++) { 6 | b[i] = Math.floor(32 + (127 - 32) * Math.random()); 7 | } 8 | exports.app = function(request) { 9 | return { 10 | status:200, 11 | headers:{ 12 | "Content-Type":"text/plain" 13 | }, 14 | body:[b.decodeToString("ascii")] 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /test/all.js: -------------------------------------------------------------------------------- 1 | //exports['Assert'] 2 | //exports['Console'] 3 | //exports['System'] 4 | exports['Binary'] = require('./binary/all'); 5 | exports['IO'] = require('./io'); 6 | exports['Filesystem'] = require('./fs-base/all'); 7 | // exports['JSGI'] = require('./jsgi'); 8 | exports['HTTP'] = require('./http'); 9 | exports['Subprocess'] = require('./subprocess'); 10 | 11 | if (require.main === module) { 12 | require("../lib/test").run(exports); 13 | } -------------------------------------------------------------------------------- /test/fs-base/all.js: -------------------------------------------------------------------------------- 1 | exports.main = require('./main'); 2 | exports.path = require('./path'); 3 | exports.dirname = require('./dirname'); 4 | exports.extension = require('./extension'); 5 | exports.iterator = require('./iterator'); 6 | exports.normal = require('./normal'); 7 | exports.relative = require('./relative'); 8 | exports.resolve = require('./resolve'); 9 | 10 | if (require.main === module) { 11 | require("../../lib/test").run(exports); 12 | } -------------------------------------------------------------------------------- /benchmarks/buffer-alloc/ringo.js: -------------------------------------------------------------------------------- 1 | var ByteArray = require('binary').ByteArray; 2 | 3 | exports.app = function(request) { 4 | var n = 1024, b = new ByteArray(n); 5 | for( var i = 1; i < 50; i++) 6 | b = new ByteArray(n); 7 | var response = { 8 | status: 200, 9 | headers: { 10 | "Content-Type": "text/plain" 11 | }, 12 | body: [b] 13 | }; 14 | return response; 15 | }; 16 | 17 | if(require.main === module) { 18 | require("ringo/httpserver").main(module.id); 19 | } 20 | -------------------------------------------------------------------------------- /benchmarks/no-alloc/ringo.js: -------------------------------------------------------------------------------- 1 | var ByteArray = require('binary').ByteArray; 2 | 3 | var n = 1024; 4 | var b = new ByteArray(n); 5 | for( var i = 0; i < n; i++) 6 | b[i] = 100; 7 | 8 | exports.app = function(request) { 9 | var response = { 10 | status: 200, 11 | headers: { 12 | "Content-Type": "text/plain" 13 | }, 14 | body: [b] 15 | }; 16 | return response; 17 | }; 18 | 19 | if(require.main === module) { 20 | require("ringo/httpserver").main(module.id); 21 | } 22 | -------------------------------------------------------------------------------- /examples/http.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview HTTP proxy example. Note that rather than first getting the 3 | * response, storing it in a string and then passing it on, we are using the 4 | * Stream directly and are piping data through as it arrives without unnecessary 5 | * buffering. 6 | */ 7 | 8 | var HttpClient = require('httpclient').HttpClient; 9 | 10 | exports.app = function(req) { 11 | req.url = 'http://nodejs.org'; 12 | return new HttpClient(req).finish(); 13 | }; 14 | -------------------------------------------------------------------------------- /benchmarks/no-alloc/common-node.js: -------------------------------------------------------------------------------- 1 | var ByteArray = require('binary').ByteArray; 2 | 3 | var m = 128; 4 | var n = 1024; 5 | var d = []; 6 | for (var i = 0; i < m; i++) { 7 | var b = new ByteArray(n); 8 | for (var j = 0; j < n; j++) { 9 | b[j] = Math.floor(256 * Math.random()); 10 | } 11 | d.push(b); 12 | } 13 | exports.app = function(request) { 14 | return { 15 | status:200, 16 | headers:{ 17 | "Content-Type":"text/plain" 18 | }, 19 | body:d 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /benchmarks/no-alloc/node.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var Buffer = require('buffer').Buffer; 3 | 4 | var m = 128; 5 | var n = 1024; 6 | var d = []; 7 | for (var i = 0; i < m; i++) { 8 | var b = new Buffer(n); 9 | for (var j = 0; j < n; j++) { 10 | b[j] = Math.floor(256 * Math.random()); 11 | } 12 | d.push(b); 13 | } 14 | http.createServer(function(req, res) { 15 | res.writeHead(200, {"Content-Type":"text/plain"}); 16 | d.forEach(function(b) { 17 | res.write(b); 18 | }); 19 | res.end(); 20 | }).listen(8080); 21 | 22 | -------------------------------------------------------------------------------- /benchmarks/string-alloc/ringo.js: -------------------------------------------------------------------------------- 1 | var ByteArray = require('binary').ByteArray; 2 | 3 | var n = 1024; 4 | var b = new ByteArray(n); 5 | for( var i = 0; i < n; i++) 6 | b[i] = 100; 7 | 8 | exports.app = function(request) { 9 | for( var i = 1; i <= 50; i++) 10 | b.decodeToString("ascii"); 11 | var response = { 12 | status: 200, 13 | headers: { 14 | "Content-Type": "text/plain" 15 | }, 16 | body: [b] 17 | }; 18 | return response; 19 | }; 20 | 21 | if(require.main === module) { 22 | require("ringo/httpserver").main(module.id); 23 | } 24 | -------------------------------------------------------------------------------- /examples/chat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Implementation of a Telnet chat server. After running, telnet 3 | * to port 8080. 4 | */ 5 | 6 | var server = new require('socket').Socket().bind('localhost', 8080), clients = [], client; 7 | while(clients.push(client = server.accept().getStream())) { 8 | spawn(function() { 9 | for( var stream = client, line; (line = stream.read(null)).length;) { 10 | clients.forEach(function(c) { 11 | (stream == c) || c.write(line); 12 | }); 13 | } 14 | clients.splice(clients.indexOf(stream), 1); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /examples/spawn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Spawn example. Spawns a new fiber which sleeps for 10 seconds 3 | * before printing to the console while immediately returning an 4 | * HTTP response. 5 | */ 6 | 7 | var system = require('system'); 8 | var spawn = system.spawn, sleep = system.sleep; 9 | 10 | exports.app = function(request) { 11 | spawn(function() { 12 | sleep(10000); 13 | console.log('Hello Server!'); 14 | }); 15 | return { 16 | status: 200, 17 | headers: {}, 18 | body: ['Hello Client!\n'] 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /benchmarks/file-stream/node.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var fs = require('fs'); 3 | var Buffer = require('buffer').Buffer; 4 | 5 | fs.writeFileSync('test.dat', new Buffer(128 * 1024)); 6 | http.createServer(function(req, res) { 7 | res.writeHead(200, {"Content-Type":"text/plain; charset=utf-8"}); 8 | var input = fs.createReadStream('test.dat').on('error', function() { 9 | res.end(); 10 | }).on('end', function() { 11 | res.end(); 12 | }).on('close', function() { 13 | res.end(); 14 | }).on('readable', function() { 15 | res.write(input.read()); 16 | }); 17 | }).listen(8080); -------------------------------------------------------------------------------- /test/fs-base/dirname.js: -------------------------------------------------------------------------------- 1 | var assert = require('../../lib/assert'); 2 | var fs = require('../../lib/fs-base'); 3 | 4 | var tests; 5 | tests = [['', '.'], ['.', '.'], ['foo', '.'], ['foo/bar', 'foo']]; 6 | 7 | tests.forEach(function(dirs) { 8 | exports['test "' + dirs[0] + '"'] = function() { 9 | var actual = fs.directory(dirs[0]); 10 | assert.strictEqual(dirs[1], actual); 11 | }; 12 | }); 13 | 14 | tests.forEach(function(dirs) { 15 | exports['testPath "' + dirs[0] + '"'] = function() { 16 | var actual = fs.path(dirs[0]).directory(); 17 | assert.strictEqual(dirs[1], actual.toString()); 18 | }; 19 | }); 20 | 21 | if (require.main === module) { 22 | require("../../lib/test").run(exports); 23 | } -------------------------------------------------------------------------------- /tools/jsdoc/hosted.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{title}} 6 | 7 | 8 | 9 | {{{head}}} 10 | 11 | 12 | 13 |
14 | {{{content}}} 15 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tools/jsdoc/static.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{title}} 6 | 7 | 8 | 9 | {{{head}}} 10 | 11 | 12 | 13 |
14 | {{{content}}} 15 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /doc/static/style.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | min-width: 850px; 8 | font-family: sans-serif; 9 | font-size: 80%; 10 | line-height: 170%; 11 | color: #EEEEEE; 12 | background: none repeat scroll 0 0 #22252A; 13 | } 14 | 15 | a { 16 | color: #CCDD55; 17 | text-decoration: none; 18 | } 19 | 20 | h1 { 21 | padding: 14px 0; 22 | } 23 | 24 | div.wrap { 25 | margin: 0 auto; 26 | width: 800px; 27 | } 28 | 29 | div.main { 30 | clear: both; 31 | float: left; 32 | width: 530px; 33 | margin: 0 10px; 34 | padding: 5px; 35 | } 36 | 37 | div.menu { 38 | float: right; 39 | padding-top: 6em; 40 | } 41 | 42 | .hidden { 43 | visibility:hidden; 44 | display:none; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /tools/jsdoc/style.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | min-width: 850px; 8 | font-family: sans-serif; 9 | font-size: 80%; 10 | line-height: 170%; 11 | color: #EEEEEE; 12 | background: none repeat scroll 0 0 #22252A; 13 | } 14 | 15 | a { 16 | color: #CCDD55; 17 | text-decoration: none; 18 | } 19 | 20 | h1 { 21 | padding: 14px 0; 22 | } 23 | 24 | div.wrap { 25 | margin: 0 auto; 26 | width: 800px; 27 | } 28 | 29 | div.main { 30 | clear: both; 31 | float: left; 32 | width: 530px; 33 | margin: 0 10px; 34 | padding: 5px; 35 | } 36 | 37 | div.menu { 38 | float: right; 39 | padding-top: 6em; 40 | } 41 | 42 | .hidden { 43 | visibility:hidden; 44 | display:none; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /test/fs-base/normal.js: -------------------------------------------------------------------------------- 1 | var assert = require('../../lib/assert'); 2 | var fs = require('../../lib/fs-base'); 3 | var separator = fs.separator; 4 | 5 | var tests = [ 6 | ['', ''], ['.', ''], ['./', ''], ['../', '../'], ['../a', '../a'], 7 | ['../a/', '../a/'], ['a/..', ''], ['a/../', ''], ['a/../b', 'b'], 8 | ['a/../b/', 'b/'] 9 | ]; 10 | 11 | tests.forEach(function(args) { 12 | exports['test "' + args[0] + '"'] = function() { 13 | var result = fs.normal(localize(args[0])); 14 | assert.strictEqual(localize(args[1]), result); 15 | }; 16 | }); 17 | 18 | tests.forEach(function(args) { 19 | exports['testPath "' + args[0] + '"'] = function() { 20 | var result = fs.path(localize(args[0])).normal(); 21 | assert.strictEqual(localize(args[1]), result.toString()); 22 | }; 23 | }); 24 | 25 | // adapt path to the platform we're running on 26 | function localize(path) { 27 | return path.replace(/\//g, separator); 28 | } 29 | 30 | if (require.main === module) { 31 | require("../../lib/test").run(exports); 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"common-node", 3 | "keywords":[ 4 | "commonjs" 5 | ], 6 | "description":"Synchronous CommonJS compatibility layer using node-fibers", 7 | "version":"0.10.21", 8 | "author":{ 9 | "name":"Oleg Podsechin", 10 | "email":"oleg@ionsquare.com" 11 | }, 12 | "contributors":[ 13 | { 14 | "name":"Alex Lam", 15 | "email":"alex.lam@nikao-tech.com" 16 | }, 17 | { 18 | "name":"Oleg Podsechin", 19 | "email":"oleg@ionsquare.com" 20 | } 21 | ], 22 | "directories":{ 23 | "lib":"./lib", 24 | "example":"./examples" 25 | }, 26 | "repository":{ 27 | "type":"git", 28 | "url":"git://github.com/olegp/common-node.git" 29 | }, 30 | "homepage":"http://github.com/olegp/common-node/", 31 | "main":"./lib/all.js", 32 | "scripts":{ 33 | "test":"node test/all.js" 34 | }, 35 | "bin":{ 36 | "common-node":"./bin/common-node" 37 | }, 38 | "engines":{ 39 | "node":">=0.10.0" 40 | }, 41 | "dependencies":{ 42 | "fibers":"1.0.2" 43 | }, 44 | "preferGlobal":true, 45 | "licenses":[ 46 | { 47 | "type":"MIT" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /test/fs-base/extension.js: -------------------------------------------------------------------------------- 1 | var assert = require('../../lib/assert'); 2 | var fs = require('../../lib/fs-base'); 3 | 4 | var tests = [ 5 | ['', ''], ['.', ''], ['..', ''], ['.a', ''], ['..a', ''], 6 | ['.a.b', '.b'], ['a.b', '.b'], ['a.b.c', '.c'], ['/', ''], ['/.', ''], 7 | ['/..', ''], ['/..a', ''], ['/.a.b', '.b'], ['/a.b', '.b'], 8 | ['/a.b.c', '.c'], ['foo/', ''], ['foo/.', ''], ['foo/..', ''], 9 | ['foo/..a', ''], ['foo/.a.b', '.b'], ['foo/a.b', '.b'], 10 | ['foo/a.b.c', '.c'], ['/foo/', ''], ['/foo/.', ''], ['/foo/..', ''], 11 | ['/foo/..a', ''], ['/foo/.a.b', '.b'], ['/foo/a.b', '.b'], 12 | ['/foo/a.b.c', '.c'] 13 | ]; 14 | 15 | tests.forEach(function(dirs) { 16 | exports['test "' + dirs[0] + '"'] = function() { 17 | var actual = fs.extension(dirs[0]); 18 | assert.strictEqual(dirs[1], actual); 19 | }; 20 | }); 21 | 22 | tests.forEach(function(dirs) { 23 | exports['testPath "' + dirs[0] + '"'] = function() { 24 | var actual = fs.path(dirs[0]).extension(); 25 | assert.strictEqual(dirs[1], actual.toString()); 26 | }; 27 | }); 28 | 29 | if (require.main === module) { 30 | require("../../lib/test").run(exports); 31 | } -------------------------------------------------------------------------------- /examples/twitter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Twitter Streaming API usage example. Returns streaming search 3 | * results. To quickly see results search for "test". Run with 4 | * 5 | *
 6 |  * common-node twitter.js username password keyword
 7 |  * 
8 | */ 9 | 10 | var HttpClient = require('httpclient').HttpClient; 11 | var TextStream = require('io').TextStream; 12 | var encode = require('./base64').encode; 13 | 14 | // make an HTTP request and wrap its response stream with TextStream, which lets 15 | // us read one line at a time 16 | var stream = new TextStream(new HttpClient({ 17 | method: 'POST', 18 | url: 'https://stream.twitter.com/1/statuses/filter.json', 19 | headers: { 20 | 'Authorization': 'Basic ' + encode(system.args[2] + ':' + system.args[3]), 21 | 'Content-Type': 'application/x-www-form-urlencoded' 22 | }, 23 | body: ['track=' + system.args[4]], 24 | timeout: 10000 25 | }).finish().body); 26 | 27 | var line; 28 | while(true) { 29 | // returns an empty string in case of EOF or '\n' in case of empty line 30 | line = stream.readLine(); 31 | // EOF 32 | if(!line.length) 33 | break; 34 | // make sure this isn't an empty line 35 | if(line.length > 1) { 36 | var message = JSON.parse(line); 37 | console.log(message.text); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/fs-base/relative.js: -------------------------------------------------------------------------------- 1 | var assert = require('../../lib/assert'); 2 | var fs = require('../../lib/fs-base'); 3 | var separator = fs.separator; 4 | 5 | var tests = [ 6 | ['', '', ''], ['.', '', ''], ['', '.', ''], ['.', '.', ''], 7 | ['', '..', '../'], ['', '../', '../'], ['a', 'b', 'b'], 8 | ['../a', '../b', 'b'], ['../a/b', '../a/c', 'c'], ['a/b', '..', '../../'], 9 | ['a/b', 'c', '../c'], ['a/b', 'c/d', '../c/d'] 10 | ]; 11 | 12 | tests.forEach(function(args) { 13 | var source = args[0], target = args[1], expected = args[2]; 14 | var name = '"' + source + '" -> "' + target + '" = "' + expected + '"'; 15 | exports['test ' + name] = function() { 16 | var actual = fs.relative(source, target); 17 | // expect returned paths to use system-dependent file separator 18 | assert.strictEqual(expected.replace(/\//g, separator), actual); 19 | }; 20 | }); 21 | 22 | tests.forEach(function(args) { 23 | var source = args[0], target = args[1], expected = args[2]; 24 | var name = '"' + source + '" -> "' + target + '" = "' + expected + '"'; 25 | exports['testPath ' + name] = function() { 26 | var actual = fs.path(source).relative(target); 27 | // expect returned paths to use system-dependent file separator 28 | assert.strictEqual(expected.replace(/\//g, separator), actual.toString()); 29 | }; 30 | }); 31 | 32 | if (require.main === module) { 33 | require("../../lib/test").run(exports); 34 | } -------------------------------------------------------------------------------- /benchmarks/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # based on http://github.com/senchalabs/connect/tree/master/benchmarks/ 3 | 4 | ABFLAGS=${ABFLAGS-"-n 50000 -c 50"} 5 | ADDR=${ADDR-http://0.0.0.0:8080/} 6 | AB=${AB-ab} 7 | 8 | # 9 | # Log 10 | # 11 | # 12 | # 13 | 14 | log(){ 15 | echo "... $@" 16 | } 17 | 18 | # 19 | # Benchmark the given and [path]. 20 | # 21 | # [path] 22 | # 23 | 24 | bm(){ 25 | local dir=$1; 26 | for platform in common-node node ringo 27 | do 28 | local flags="" 29 | if [ $platform = "ringo" ]; then 30 | # disable module reloading on ringo 31 | flags="-p" 32 | fi 33 | $platform $flags $dir/$platform.js & 34 | local pid=$! 35 | sleep 2 36 | local dirname=results/$dir 37 | mkdir -p $dirname 38 | # jvm needs warmup time to reach final speed. If you run smaller 39 | # batches you'll need more runs for warmup. 40 | for runs in {1..2} 41 | do 42 | $AB $ABFLAGS -g $dirname/$platform.dat $ADDR/$path > $dirname/$platform.out 43 | sleep 1 44 | done 45 | log $(cat $dirname/$platform.out | grep Requests) 46 | kill $pid 47 | sleep 2 48 | done 49 | } 50 | 51 | # Make ./results 52 | mkdir -p results 53 | 54 | # Store flags 55 | echo "$ABFLAGS" > results/flags 56 | 57 | # Run benchmarks 58 | log $AB $ABFLAGS $ADDR 59 | bm buffer-alloc 60 | bm file-read 61 | bm file-stream 62 | bm hello-world 63 | bm no-alloc 64 | bm set-timeout 65 | bm string-alloc -------------------------------------------------------------------------------- /test/fs-base/path.js: -------------------------------------------------------------------------------- 1 | var assert = require("../../lib/assert"); 2 | var test = require('../../lib/test'); 3 | var fs = require("../../lib/fs-base"); 4 | var ByteString = require('../../lib/binary').ByteString; 5 | 6 | function getContents(file) { 7 | return require("fs").readFileSync(file, "utf8"); 8 | } 9 | 10 | exports.testCopy = test.fs(2, function(file1, file2) { 11 | fs.path(file1).write(new ByteString('abcd')); 12 | fs.path(file1).copy(file2); 13 | var resource = getContents(file2); 14 | 15 | assert.strictEqual(resource, 'abcd'); 16 | }); 17 | 18 | exports.testOpen = function() { 19 | var resource = getContents('./lib/assert.js'); 20 | var io = fs.path('./lib/assert.js').open(); 21 | 22 | assert.strictEqual(io.read().length, resource.length); 23 | }; 24 | 25 | exports.testOpenRaw = function() { 26 | var resource = getContents('./lib/assert.js'); 27 | var io = fs.path('./lib/assert.js').openRaw(); 28 | 29 | assert.strictEqual(io.read().decodeToString().length, resource.length); 30 | }; 31 | 32 | exports.testRead = function() { 33 | var resource = getContents('./lib/assert.js'); 34 | var file = fs.path('./lib/assert.js').read(); 35 | 36 | assert.strictEqual(file.length, resource.length); 37 | }; 38 | 39 | exports.testWrite = test.fs(1, function(file) { 40 | fs.path(file).write(new ByteString('test')); 41 | var resource = getContents(file); 42 | 43 | assert.strictEqual('test', resource); 44 | }); 45 | 46 | if (require.main === module) { 47 | require("../../lib/test").run(exports); 48 | } -------------------------------------------------------------------------------- /lib/global.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Provides global functions 3 | * import, include, export and spawn for RingoJS compatibility.

4 | */ 5 | 6 | /** 7 | * Load a module and include all its properties in the calling scope. 8 | * 9 | * @param {String} moduleName the module name such as 'core.object' 10 | */ 11 | global.include = exports.include = function(moduleName) { 12 | var module = this.require(moduleName); 13 | for( var key in module) { 14 | this[key] = module[key]; 15 | } 16 | }; 17 | 18 | /** 19 | * Define the properties to be exported. 20 | * 21 | * @param name one or more names of exported properties 22 | */ 23 | global.export = exports.export = function() { 24 | var module = this; 25 | var exports = this.exports; 26 | if(!exports || typeof exports != "object") { 27 | // this should never happen 28 | exports = {}; 29 | Object.defineProperty(module, "exports", { 30 | value: exports 31 | }); 32 | } 33 | Array.forEach(arguments, function(name) { 34 | Object.defineProperty(exports, name, { 35 | get: function() { 36 | return module[name]; 37 | }, 38 | enumerable: true 39 | }); 40 | }); 41 | }; 42 | 43 | // load system module 44 | var system = global.system = require('./system'); 45 | 46 | /** 47 | * spawn() from the system module. Spawns a new thread. 48 | * 49 | * @param {Function} run entry point of the new thread. 50 | */ 51 | global.spawn = exports.spawn = system.spawn; 52 | 53 | // make our fiber instance available to other packages 54 | global.Fiber = require("fibers"); 55 | 56 | // TODO add getResource 57 | -------------------------------------------------------------------------------- /test/fs-base/resolve.js: -------------------------------------------------------------------------------- 1 | var assert = require('../../lib/assert'); 2 | var fs = require('../../lib/fs-base'); 3 | var separator = fs.separator, roots = [separator]; 4 | 5 | var tests = [ 6 | [['/'], '/'], [['/a'], '/a'], [['/a/'], '/a/'], 7 | [['/a', '/b'], '/b'], [['/a', '/b/'], '/b/'], [['/', 'a'], '/a'], 8 | [['/', 'a/'], '/a/'], [['/a', 'a'], '/a'], [['/a', 'a/'], '/a/'], 9 | [['/a/', 'a'], '/a/a'], [['/a/', 'a/'], '/a/a/'], [['..'], '../'], 10 | [['..', 'a'], '../a'], [['..', 'a/'], '../a/'], [['.'], ''], 11 | [['.', 'a'], 'a'], [['.', 'a/'], 'a/'], [['a', '.'], ''], 12 | [['a', '.', 'a'], 'a'], [['a', '.', 'a/'], 'a/'], [['a', '..'], '../'], 13 | [['a', '..', 'a'], '../a'], [['a', '..', 'a/'], '../a/'], 14 | [['a/', '..'], ''], [['a/', '..', 'a'], 'a'], [['a/', '..', 'a/'], 'a/'], 15 | [['a/b', ''], 'a/b'] 16 | ]; 17 | 18 | tests.forEach(function(args) { 19 | exports['test ' + JSON.stringify(args[0])] = function() { 20 | var result = fs.resolve.apply(null, args[0].map(localize)); 21 | assert.strictEqual(localize(args[1]), result); 22 | }; 23 | }); 24 | 25 | tests.forEach(function(args) { 26 | exports['testPath ' + JSON.stringify(args[0])] = function() { 27 | args[0] = args[0].map(localize); 28 | var path = fs.path(args[0].shift()); 29 | var result = path.resolve.apply(path, args[0]); 30 | assert.strictEqual(localize(args[1]), result.toString()); 31 | }; 32 | }); 33 | 34 | function localize(path) { 35 | return path.replace(/^\//, roots[0]).replace(/\//g, separator); 36 | } 37 | 38 | if (require.main === module) { 39 | require("../../lib/test").run(exports); 40 | } -------------------------------------------------------------------------------- /test/fs-base/main.js: -------------------------------------------------------------------------------- 1 | var assert = require("../../lib/assert"); 2 | var test = require('../../lib/test'); 3 | var fs = require("../../lib/fs-base"); 4 | var ByteString = require('../../lib/binary').ByteString; 5 | 6 | function getContents(file) { 7 | return require("fs").readFileSync(file, "utf8"); 8 | } 9 | 10 | exports.testCopy = test.fs(2, function(file1, file2) { 11 | fs.write(file1, new ByteString('abcd')); 12 | fs.copy(file1, file2); 13 | var resource = getContents(file2); 14 | 15 | assert.strictEqual(resource, 'abcd'); 16 | }); 17 | 18 | exports.testOpen = function() { 19 | var resource = getContents('./lib/assert.js'); 20 | var io = fs.open('./lib/assert.js'); 21 | 22 | assert.strictEqual(io.read().length, resource.length); 23 | }; 24 | 25 | exports.testOpenRaw = function() { 26 | var resource = getContents('./lib/assert.js'); 27 | var io = fs.openRaw('./lib/assert.js'); 28 | 29 | assert.strictEqual(io.read().decodeToString().length, resource.length); 30 | }; 31 | 32 | exports.testRead = function() { 33 | var resource = getContents('./lib/assert.js'); 34 | var file = fs.read('./lib/assert.js'); 35 | 36 | assert.strictEqual(file.length, resource.length); 37 | }; 38 | 39 | exports.testWrite = test.fs(1, function(file) { 40 | fs.write(file, new ByteString('test')); 41 | var resource = getContents(file); 42 | assert.strictEqual('test', resource); 43 | 44 | fs.write(file, 'test'); 45 | var resource = getContents(file); 46 | assert.strictEqual('test', resource); 47 | 48 | fs.write(file, ''); 49 | fs.write(file, new ByteString('test'), {append:true}); 50 | fs.write(file, 'test', {append:true}); 51 | var resource = getContents(file); 52 | assert.strictEqual('testtest', resource); 53 | }); 54 | 55 | if (require.main === module) { 56 | require("../../lib/test").run(exports); 57 | } -------------------------------------------------------------------------------- /doc/static/jsdoc.css: -------------------------------------------------------------------------------- 1 | pre.code { 2 | background-color: #abc; 3 | border-width: 1px; 4 | border-color: #9ab; 5 | border-style: solid; 6 | padding: 0.8em; 7 | line-height: 145%; 8 | } 9 | 10 | table td { 11 | vertical-align: top; 12 | padding: 0 10px; 13 | } 14 | 15 | h4 { 16 | margin: 5px 0; 17 | color: green; 18 | font-weight: bold; 19 | } 20 | 21 | .dimmed { 22 | color: #333; 23 | font-weight: 500; 24 | } 25 | 26 | .paramname { 27 | font-weight:bold; 28 | } 29 | 30 | .extrainfo { 31 | padding:0 12px; 32 | } 33 | 34 | .leftnav, .repositorylist, .propertyoverview { 35 | list-style-type: none; 36 | padding: 2px; 37 | } 38 | 39 | .docitem { 40 | } 41 | 42 | .docitem .itemtitle { 43 | font-size:large; 44 | text-decoration:none; 45 | } 46 | 47 | .light { 48 | font-weight: normal; 49 | } 50 | 51 | .docitem.highlight { 52 | border-left: 4px solid green; 53 | padding: 0 0 0 6px; 54 | } 55 | 56 | ul.modulelist { 57 | list-style-type:none; 58 | margin: 0; 59 | padding-left: 10px; 60 | } 61 | 62 | .repositoryname { 63 | font-weight:bold; 64 | } 65 | 66 | .moduleheader { 67 | padding: 8px; 68 | } 69 | 70 | h2, h3 { 71 | margin-bottom: 5px; 72 | } 73 | 74 | h3 { 75 | font-size: small; 76 | } 77 | 78 | ul { 79 | margin: 5px; 80 | } 81 | 82 | hr { 83 | border: 0; 84 | color: #999; 85 | background-color: #999; 86 | height: 1px; 87 | } 88 | 89 | .classoverview { 90 | /* background-color: #f0ffc0; #f7ffe5; */ 91 | padding: 5px 10px; 92 | margin: 15px 0; 93 | } 94 | 95 | .modulename { 96 | /* background-color: #f1ffcc; */ 97 | } 98 | 99 | .showmore { 100 | color: blue; 101 | cursor: pointer; 102 | } 103 | -------------------------------------------------------------------------------- /tools/jsdoc/jsdoc.css: -------------------------------------------------------------------------------- 1 | pre.code { 2 | background-color: #abc; 3 | border-width: 1px; 4 | border-color: #9ab; 5 | border-style: solid; 6 | padding: 0.8em; 7 | line-height: 145%; 8 | } 9 | 10 | table td { 11 | vertical-align: top; 12 | padding: 0 10px; 13 | } 14 | 15 | h4 { 16 | margin: 5px 0; 17 | color: green; 18 | font-weight: bold; 19 | } 20 | 21 | .dimmed { 22 | color: #333; 23 | font-weight: 500; 24 | } 25 | 26 | .paramname { 27 | font-weight:bold; 28 | } 29 | 30 | .extrainfo { 31 | padding:0 12px; 32 | } 33 | 34 | .leftnav, .repositorylist, .propertyoverview { 35 | list-style-type: none; 36 | padding: 2px; 37 | } 38 | 39 | .docitem { 40 | } 41 | 42 | .docitem .itemtitle { 43 | font-size:large; 44 | text-decoration:none; 45 | } 46 | 47 | .light { 48 | font-weight: normal; 49 | } 50 | 51 | .docitem.highlight { 52 | border-left: 4px solid green; 53 | padding: 0 0 0 6px; 54 | } 55 | 56 | ul.modulelist { 57 | list-style-type:none; 58 | margin: 0; 59 | padding-left: 10px; 60 | } 61 | 62 | .repositoryname { 63 | font-weight:bold; 64 | } 65 | 66 | .moduleheader { 67 | padding: 8px; 68 | } 69 | 70 | h2, h3 { 71 | margin-bottom: 5px; 72 | } 73 | 74 | h3 { 75 | font-size: small; 76 | } 77 | 78 | ul { 79 | margin: 5px; 80 | } 81 | 82 | hr { 83 | border: 0; 84 | color: #999; 85 | background-color: #999; 86 | height: 1px; 87 | } 88 | 89 | .classoverview { 90 | /* background-color: #f0ffc0; #f7ffe5; */ 91 | padding: 5px 10px; 92 | margin: 15px 0; 93 | } 94 | 95 | .modulename { 96 | /* background-color: #f1ffcc; */ 97 | } 98 | 99 | .showmore { 100 | color: blue; 101 | cursor: pointer; 102 | } 103 | -------------------------------------------------------------------------------- /doc/static/jsdoc.js: -------------------------------------------------------------------------------- 1 | function jsdocSetup() { 2 | 3 | // filter given ul $list by text query 4 | var filterList = function(query, $list) { 5 | if (!query) { 6 | $('li', $list).show(); 7 | return false; 8 | } 9 | var found = false; 10 | $('li', $list).each(function() { 11 | var $item = $(this); 12 | if ($item.html().toLowerCase().indexOf(query) < 0) { 13 | $item.hide(); 14 | } else { 15 | $item.show(); 16 | found = true; 17 | } 18 | }); 19 | return found; 20 | }; 21 | 22 | // search module list 23 | function doFilter() { 24 | var query = $.trim($(this).val().toLowerCase()); 25 | /* if (sessionStorage) { 26 | sessionStorage.jsdocQuery = query; 27 | } */ 28 | filterList(query, $('.jsdoc-leftnav')); 29 | } 30 | $("#jsdoc-leftnavsearch").change(doFilter).keyup(doFilter).click(doFilter).focus(); 31 | 32 | // hide all but first paragraph from module fileoverview 33 | var $showMoreLink = $('.jsdoc-showmore'); 34 | $(".jsdoc-fileoverview").each(function(idx, overview) { 35 | var $overview = $(overview); 36 | var $allButFirstElement = $overview.children().not(":first-child"); 37 | if ($allButFirstElement.length) { 38 | $allButFirstElement.hide(); 39 | $overview.append($showMoreLink.clone()); 40 | } 41 | return; 42 | }); 43 | $(".jsdoc-showmore").click(function() { 44 | $(this).hide().siblings().show(); 45 | }); 46 | 47 | // load query string from storage if any 48 | /* var query = sessionStorage && sessionStorage.jsdocQuery; 49 | if (query) { 50 | $("#jsdoc-leftnavsearch").val(query).trigger('keyup'); 51 | } */ 52 | } 53 | -------------------------------------------------------------------------------- /benchmarks/graph: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # based on http://github.com/senchalabs/connect/tree/master/benchmarks/ 3 | 4 | COL=${COL-9} 5 | ABFLAGS=$(cat results/flags) 6 | 7 | # Log 8 | # 9 | # 10 | # 11 | 12 | log(){ 13 | echo "... $@" 14 | } 15 | 16 | # 17 | # Output gnuplot script for line graph. 18 | # 19 | # <node> <connect> 20 | # 21 | 22 | function line() { 23 | cat <<-EOF 24 | set terminal png # enhanced size 600 380 fname "Sans" fsize 11 25 | set output "results/graphs/$1.png" 26 | set title "$2 ($ABFLAGS)" 27 | # set size 0.97,0.97 28 | set grid y 29 | set key left top 30 | set xlabel "requests" 31 | set ylabel "response time (ms)" 32 | plot "$3" using $COL smooth sbezier with lines title "common", \\ 33 | "$4" using $COL smooth sbezier with lines title "connect", \\ 34 | "$5" using $COL smooth sbezier with lines title "ringo" 35 | EOF 36 | } 37 | 38 | # 39 | # Output gnuplot script for bar graph. 40 | # 41 | # <title> <node> <connect> 42 | # 43 | 44 | function bar() { 45 | cat <<-EOF 46 | set terminal png 47 | set output "results/graphs/$1.rps.png" 48 | set title "$1 $ABFLAGS" 49 | set size 0.7,0.5 50 | set grid y 51 | set key left top 52 | set ylabel "requests per second" 53 | plot "$1.rps.dat" using 2: xtic(1) with histogram title "" 54 | EOF 55 | } 56 | 57 | # 58 | # Graph the output of the given <dir>. 59 | # 60 | # <dir> 61 | # 62 | 63 | function graph(){ 64 | log graphing $1 65 | local dir=results/$1 66 | line $1 "$2" \ 67 | $dir/common-node.dat \ 68 | $dir/node.dat \ 69 | $dir/ringo.dat \ 70 | > results/$1.p 71 | gnuplot results/$1.p 72 | } 73 | 74 | # Make ./results/graphs 75 | mkdir -p results/graphs 76 | 77 | graph buffer-alloc "buffer alloc" 78 | graph file-read "file read" 79 | graph file-stream "file stream" 80 | graph hello-world "hello world" 81 | graph no-alloc "no alloc" 82 | graph set-timeout "set timeout" 83 | graph static-file "static file" 84 | graph string-alloc "string alloc" -------------------------------------------------------------------------------- /doc/all/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html > 2 | 3 | <html lang="de" dir="ltr"> 4 | <head> 5 | <title>all - Common Node 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 |

Module all

24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /lib/console.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview This module provides a familiar console object for logging and 3 | * debugging defined in [CommonJS 4 | * Console/A](http://wiki.commonjs.org/wiki/Console) 5 | */ 6 | 7 | /** 8 | * Logs a message to the console. 9 | * 10 | * The first argument to log may be a string containing printf-like 11 | * placeholders. Otherwise, multipel arguments will be concatenated separated by 12 | * spaces. 13 | * 14 | * @param msg... one or more message arguments 15 | * 16 | * @name log 17 | */ 18 | 19 | /** 20 | * Logs a message with the visual "error" representation, including the file 21 | * name and line number of the calling code. 22 | * 23 | * @param msg... one or more message arguments 24 | * @function 25 | * 26 | * @name error 27 | */ 28 | 29 | /** 30 | * Logs a message with the visual "warn" representation, including the file name 31 | * and line number of the calling code. 32 | * 33 | * @param msg... one or more message arguments 34 | * @function 35 | * 36 | * @name warn 37 | */ 38 | 39 | /** 40 | * Logs a message with the visual "info" representation, including the file name 41 | * and line number of the calling code. 42 | * 43 | * @param {...} msg... one or more message arguments 44 | * @function 45 | * 46 | * @name info 47 | */ 48 | 49 | /** 50 | * Prints a stack trace of JavaScript execution at the point where it is called. 51 | * 52 | * @param {...} msg... optional message arguments 53 | * @function 54 | * 55 | * @name trace 56 | */ 57 | 58 | /** 59 | * Tests that an expression is true and throws an exception if not. 60 | * 61 | * @param expression the expression to test 62 | * @param {...} msg... one or more error messages 63 | * @function 64 | * 65 | * @name assert 66 | */ 67 | 68 | /** 69 | * Creates a new timer under the given name. Call `console.timeEnd(name)` with 70 | * the same name to stop the timer and log the time elapsed. 71 | * 72 | * @param {String} name the timer name 73 | * 74 | * @name time 75 | */ 76 | 77 | /** 78 | * Stops a timer created by a call to `console.time(name)` and logs the time 79 | * elapsed. 80 | * 81 | * @param {String} name the timer name 82 | * 83 | * @name timeEnd 84 | */ 85 | 86 | /** 87 | * Prints a list of all properties of an object. 88 | * 89 | * @param {Object} obj the object 90 | * 91 | * @name dir 92 | */ 93 | -------------------------------------------------------------------------------- /lib/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Bootstraps Common Node. 3 | */ 4 | 5 | var remap = { 6 | 'fs':'fs-base', 7 | 'ringo/httpserver':'httpserver', 8 | 'ringo/subprocess':'subprocess' 9 | }; 10 | 11 | /** 12 | * Runs the specified module. 13 | * 14 | * @param main the module to run 15 | */ 16 | var run = module.exports = function(main) { 17 | var options = {}, argv = []; 18 | process.argv.forEach(function(arg) { 19 | if (!arg.indexOf('-')) { 20 | options[arg.substr(1)] = true; 21 | } else { 22 | argv.push(arg); 23 | } 24 | }); 25 | var path = require('path'); 26 | var all = require('./all'); 27 | var proto = require('module').prototype; 28 | var protoRequire = proto.require; 29 | proto.require = function(name) { 30 | if (options.r) { 31 | name = remap[name] || name; 32 | } 33 | return all[name] || protoRequire.call(this, name); 34 | }; 35 | var mainPath = main || argv[1]; 36 | if (typeof mainPath === 'string') { 37 | mainPath = path.resolve(process.cwd(), mainPath); 38 | } else { 39 | mainPath = process.cwd(); 40 | } 41 | var protoLoad = proto.load; 42 | proto.load = function(filename) { 43 | if (filename === mainPath) 44 | process.mainModule = this; 45 | return protoLoad.call(this, filename); 46 | }; 47 | 48 | if (options.u) { 49 | process.on('uncaughtException', function(err) { 50 | console.log(err.stack); 51 | }); 52 | } 53 | 54 | if (options.v) { 55 | spawn(function() { 56 | var fs = require('./fs-base'); 57 | var p = fs.resolve(fs.directory(module.filename), 'package.json'); 58 | console.log('v' + JSON.parse(fs.openRaw(p).read().decodeToString()).version); 59 | }); 60 | } else if (options.e) { 61 | argv.shift(); 62 | spawn(function() { 63 | argv.forEach(function(e) { 64 | eval(e); 65 | }); 66 | }); 67 | } else if (argv.length > 1) { 68 | spawn(function() { 69 | if (typeof main === 'function') { 70 | main(); 71 | } else { 72 | var module = require(mainPath); 73 | if (module.app) { 74 | var httpserver = require('./httpserver'); 75 | if (!httpserver.started) { 76 | httpserver.main(module.app, argv[2], {cluster:argv[3]}); 77 | } 78 | } 79 | } 80 | }); 81 | } else { 82 | var system = require('./system'); 83 | require = function(name) { 84 | try { 85 | return proto.require.call(this, name); 86 | } catch (e) { 87 | return protoRequire.call(this, path.resolve(process.cwd(), name)); 88 | } 89 | }; 90 | var line; 91 | spawn(function() { 92 | do { 93 | system.stdout.write('> '); 94 | line = system.stdin.readLine(); 95 | if (line) { 96 | try { 97 | console.log(eval(line)); 98 | } catch (e) { 99 | console.log(e.stack); 100 | } 101 | } 102 | } while (line); 103 | }); 104 | } 105 | }; 106 | 107 | if (require.main === module) { 108 | process.argv.shift(); // remove node, which is first arg 109 | run(); 110 | } -------------------------------------------------------------------------------- /test/fs-base/iterator.js: -------------------------------------------------------------------------------- 1 | var assert = require('../../lib/assert'); 2 | var fs = require('../../lib/fs-base'); 3 | 4 | /* 5 | * a decorator that passes a path object corresponding to the test name and 6 | * removes any files created therein afterward 7 | */ 8 | var Test = function(block) { 9 | var exported = null; 10 | exported = function() { 11 | for (var name in exports) { 12 | if (exports[name] === exported) { 13 | try { 14 | var path = fs.path(fs.directory(require.resolve('./iterator')), name); 15 | block(path); 16 | } finally { 17 | if (path.exists()) { 18 | path.removeTree(); 19 | } 20 | } 21 | } 22 | } 23 | }; 24 | return exported; 25 | }; 26 | 27 | exports.testPrintReadLine = Test(function(path) { 28 | var stream = path.open('w'); 29 | 30 | stream.print('hello'); 31 | stream.print('world'); 32 | stream.close(); 33 | 34 | stream = path.open('r'); 35 | assert.strictEqual('hello\n', stream.readLine()); 36 | assert.strictEqual('world\n', stream.readLine()); 37 | assert.strictEqual('', stream.readLine()); 38 | stream.close(); 39 | }); 40 | 41 | exports.testPrintReadLineChain = Test(function(path) { 42 | var stream = path.open('w'); 43 | stream.print('hello').print('world'); 44 | stream.close(); 45 | stream = path.open('r'); 46 | assert.strictEqual('hello\n', stream.readLine()); 47 | assert.strictEqual('world\n', stream.readLine()); 48 | assert.strictEqual('', stream.readLine()); 49 | stream.close(); 50 | }); 51 | 52 | exports.testReadLines = Test(function(path) { 53 | var stream = path.open('w'); 54 | stream.print('hello').print('world'); 55 | stream.close(); 56 | stream = path.open('r'); 57 | assert.deepEqual(['hello\n', 'world\n'], stream.readLines()); 58 | stream.close(); 59 | }); 60 | 61 | exports.testForEach = Test(function(path) { 62 | var output = path.open('w'); 63 | var input = path.open('r'); 64 | output.print('1'); 65 | output.print('1'); 66 | var count = 0; 67 | input.forEach(function(line) { 68 | assert.strictEqual('1', line); 69 | count++; 70 | }); 71 | assert.strictEqual(2, count); 72 | 73 | input.close(); 74 | output.print('2').print('2'); 75 | input = path.open('r'); 76 | count = 0; 77 | input.forEach(function(line) { 78 | assert.strictEqual(count < 2 ? '1' : '2', line); 79 | count++; 80 | }); 81 | assert.strictEqual(4, count); 82 | 83 | output.close(); 84 | input.close(); 85 | }); 86 | 87 | exports.testNext = Test(function(path) { 88 | path.open('w').print('1').print('2').close(); 89 | var iterator = path.open(); 90 | assert.strictEqual('1', iterator.next()); 91 | assert.strictEqual('2', iterator.next()); 92 | assert.throws(function() { 93 | iterator.next(); 94 | }); 95 | iterator.close(); 96 | }); 97 | 98 | exports.testIterator = Test(function(path) { 99 | path.open('w').print('1').print('2').close(); 100 | var iterator = path.open().iterator(); 101 | assert.strictEqual('1', iterator.next()); 102 | assert.strictEqual('2', iterator.next()); 103 | assert.throws(function() { 104 | iterator.next(); 105 | }); 106 | iterator.close(); 107 | }); 108 | 109 | if (require.main === module) { 110 | require("../../lib/test").run(exports); 111 | } -------------------------------------------------------------------------------- /lib/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Runs tests as defined in [CommonJS Unit 3 | * Testing](http://wiki.commonjs.org/wiki/Unit_Testing/1.0). 4 | */ 5 | var fs = require('fs'); 6 | var system = require('./system'); 7 | var inspect = require('util').inspect; 8 | 9 | function formatException(e) { 10 | var msg = ""; 11 | if ('message' in e) { 12 | msg += '"' + e.message + '"'; 13 | } 14 | if ('operator' in e) { 15 | msg += '(' + inspect(e.actual); 16 | msg += ' ' + e.operator + ' '; 17 | msg += inspect(e.expected) + ')'; 18 | } 19 | return msg; 20 | } 21 | 22 | function testModule(module, breakOnException) { 23 | var failures = []; 24 | for (var test in module) { 25 | var element = module[test]; 26 | if (typeof element === "function") { 27 | try { 28 | element(); 29 | console.log('PASSED ' + test); 30 | } catch (e) { 31 | failures.push(test); 32 | if (breakOnException) 33 | break; 34 | console.log(' FAILED ' + test + '\n ' + formatException(e)); 35 | } 36 | } else { 37 | console.log('\n#' + test); 38 | testModule(element).forEach(function(name) { 39 | failures.push(test + '\t' + name); 40 | }); 41 | } 42 | } 43 | return failures; 44 | } 45 | 46 | function getRandomInt(min, max) { 47 | return Math.floor(Math.random() * (max - min + 1)) + min; 48 | } 49 | 50 | exports.fs = function(numOfFiles, test) { 51 | return function() { 52 | var dir = './tmp'; 53 | if (!fs.existsSync(dir)) { 54 | fs.mkdirSync(dir); 55 | } 56 | var files = []; 57 | for (var i = 0; i < numOfFiles; i++) { 58 | files.push(dir + '/test' + getRandomInt(0, 1000000)); 59 | } 60 | try { 61 | test.apply(null, files); 62 | } finally { 63 | files.forEach(function(file) { 64 | if (fs.existsSync(file)) { 65 | fs.unlinkSync(file); 66 | } 67 | }); 68 | system.sleep(100); 69 | try { 70 | fs.rmdirSync(dir); 71 | } catch (e) { 72 | console.warn('test directory is not removed'); 73 | } 74 | } 75 | }; 76 | }; 77 | 78 | /** 79 | * Runs the test function in the module provided. If the module doesn't export 80 | * function, recursively looks inside the exported objects and runs them as 81 | * tests as well. See 'test/all.js' for an example. 82 | * 83 | * @param {Object} tests The module containing the tests. 84 | * @param {Boolean} breakOnException Stop upon first failed test. 85 | */ 86 | exports.run = function(tests, breakOnException) { 87 | var domain = require('domain').create(); 88 | domain.on('error', function(err) { 89 | console.log(err.stack); 90 | }); 91 | domain.run(function() { 92 | system.spawn(function() { 93 | var failures = testModule(tests, breakOnException); 94 | console.log(); 95 | console.log(); 96 | if (failures.length === 0) { 97 | console.log('#### All Tests Passed ####'); 98 | } else { 99 | console.log('!!!! Some Test Failed !!!!'); 100 | console.log(failures.join('\n')); 101 | } 102 | process.exit(failures.length > 0 ? 1 : 0); 103 | }); 104 | }); 105 | }; 106 | 107 | if (require.main === module) { 108 | exports.run(require(process.argv[2])); 109 | } 110 | -------------------------------------------------------------------------------- /examples/base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Base 64 decoding and encoding methods in pure JavaScript. 3 | */ 4 | 5 | var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 6 | 7 | // public method for encoding 8 | exports.encode = function (input) { 9 | var output = ""; 10 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 11 | var i = 0; 12 | 13 | input = utf8_encode(input); 14 | 15 | while (i < input.length) { 16 | 17 | chr1 = input.charCodeAt(i++); 18 | chr2 = input.charCodeAt(i++); 19 | chr3 = input.charCodeAt(i++); 20 | 21 | enc1 = chr1 >> 2; 22 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 23 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 24 | enc4 = chr3 & 63; 25 | 26 | if (isNaN(chr2)) { 27 | enc3 = enc4 = 64; 28 | } else if (isNaN(chr3)) { 29 | enc4 = 64; 30 | } 31 | 32 | output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); 33 | 34 | } 35 | 36 | return output; 37 | }; 38 | 39 | // public method for decoding 40 | exports.decode = function (input) { 41 | var output = ""; 42 | var chr1, chr2, chr3; 43 | var enc1, enc2, enc3, enc4; 44 | var i = 0; 45 | 46 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 47 | 48 | while (i < input.length) { 49 | 50 | enc1 = keyStr.indexOf(input.charAt(i++)); 51 | enc2 = keyStr.indexOf(input.charAt(i++)); 52 | enc3 = keyStr.indexOf(input.charAt(i++)); 53 | enc4 = keyStr.indexOf(input.charAt(i++)); 54 | 55 | chr1 = (enc1 << 2) | (enc2 >> 4); 56 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 57 | chr3 = ((enc3 & 3) << 6) | enc4; 58 | 59 | output = output + String.fromCharCode(chr1); 60 | 61 | if (enc3 != 64) { 62 | output = output + String.fromCharCode(chr2); 63 | } 64 | if (enc4 != 64) { 65 | output = output + String.fromCharCode(chr3); 66 | } 67 | 68 | } 69 | 70 | output = utf8_decode(output); 71 | 72 | return output; 73 | 74 | }; 75 | 76 | // private method for UTF-8 encoding 77 | function utf8_encode(string) { 78 | string = string.replace(/\r\n/g,"\n"); 79 | var utftext = ""; 80 | 81 | for (var n = 0; n < string.length; n++) { 82 | 83 | var c = string.charCodeAt(n); 84 | 85 | if (c < 128) { 86 | utftext += String.fromCharCode(c); 87 | } 88 | else if((c > 127) && (c < 2048)) { 89 | utftext += String.fromCharCode((c >> 6) | 192); 90 | utftext += String.fromCharCode((c & 63) | 128); 91 | } 92 | else { 93 | utftext += String.fromCharCode((c >> 12) | 224); 94 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 95 | utftext += String.fromCharCode((c & 63) | 128); 96 | } 97 | 98 | } 99 | 100 | return utftext; 101 | } 102 | 103 | // private method for UTF-8 decoding 104 | function utf8_decode(utftext) { 105 | var string = ""; 106 | var i = 0; 107 | var c = c1 = c2 = 0; 108 | 109 | while ( i < utftext.length ) { 110 | 111 | c = utftext.charCodeAt(i); 112 | 113 | if (c < 128) { 114 | string += String.fromCharCode(c); 115 | i++; 116 | } 117 | else if((c > 191) && (c < 224)) { 118 | c2 = utftext.charCodeAt(i+1); 119 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 120 | i += 2; 121 | } 122 | else { 123 | c2 = utftext.charCodeAt(i+1); 124 | c3 = utftext.charCodeAt(i+2); 125 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 126 | i += 3; 127 | } 128 | 129 | } 130 | 131 | return string; 132 | } 133 | -------------------------------------------------------------------------------- /test/binary/bytearray-encodings-tests.js: -------------------------------------------------------------------------------- 1 | var assert = require("../../lib/assert"); 2 | var binary = require("../../lib/binary"); 3 | var Binary = binary.Binary; 4 | var ByteArray = binary.ByteArray; 5 | 6 | exports.testByteArrayConstructorEncodings = function() { 7 | // ByteString(string, charset) 8 | // Convert a string. The ByteString will contain string encoded with charset. 9 | var testString = "hello world"; 10 | var b = new ByteArray(testString, "US-ASCII"); 11 | assert.strictEqual(testString.length, b.length); 12 | b.length = 678; 13 | assert.strictEqual(678, b.length); 14 | assert.strictEqual(testString.charCodeAt(0), b.get(0)); 15 | assert.strictEqual(testString.charCodeAt(testString.length - 1), b.get(testString.length - 1)); 16 | assert.strictEqual(0, b.get(677)); 17 | }; 18 | 19 | exports.testToByteArrayEncodings = function() { 20 | var testString = "I ♥ JS"; 21 | var b = testString.toByteArray("UTF-16"); 22 | var c = b.decodeToString("UTF-8"); 23 | assert.ok(typeof c === 'string'); 24 | assert.notStrictEqual(testString, c); 25 | var d = c.toByteArray("UTF-8"); 26 | assert.strictEqual(testString, d.decodeToString("UTF-16")); 27 | }; 28 | 29 | exports.testToByteStringEncodings = function() { 30 | var testString = "I ♥ JS"; 31 | var b = testString.toByteArray("UTF-16"); 32 | var c = b.decodeToString("UTF-8"); 33 | assert.ok(typeof c === 'string'); 34 | assert.notStrictEqual(testString, c); 35 | var d = c.toByteString("UTF-8"); 36 | assert.strictEqual(testString, d.decodeToString("UTF-16")); 37 | }; 38 | 39 | exports.testToArrayEncodings = function() { 40 | var a1; 41 | 42 | a1 = new ByteArray("\u0024\u00A2\u20AC", "UTF-8").toArray("UTF-8"); 43 | assert.strictEqual(3, a1.length); 44 | assert.strictEqual(0x24, a1[0]); 45 | assert.strictEqual(0xA2, a1[1]); 46 | assert.strictEqual(0x20AC, a1[2]); 47 | 48 | a1 = new ByteArray("\u0024\u00A2\u20AC", "UTF-16").toArray("UTF-16"); 49 | assert.strictEqual(3, a1.length); 50 | assert.strictEqual(0x24, a1[0]); 51 | assert.strictEqual(0xA2, a1[1]); 52 | assert.strictEqual(0x20AC, a1[2]); 53 | }; 54 | 55 | exports.testDecodeToString = function() { 56 | assert.strictEqual("hello world", new ByteArray("hello world", "US-ASCII") 57 | .decodeToString("US-ASCII")); 58 | 59 | assert.strictEqual("I ♥ JS", new ByteArray("I ♥ JS", "UTF-8") 60 | .decodeToString("UTF-8")); 61 | 62 | assert.strictEqual("\u0024", new ByteArray([0x24]).decodeToString("UTF-8")); 63 | assert.strictEqual("\u00A2", new ByteArray([0xC2, 0xA2]) 64 | .decodeToString("UTF-8")); 65 | assert.strictEqual("\u20AC", new ByteArray([0xE2, 0x82, 0xAC]) 66 | .decodeToString("UTF-8")); 67 | 68 | assert.strictEqual("\u0024", new ByteArray("\u0024", "UTF-8") 69 | .decodeToString("UTF-8")); 70 | assert.strictEqual("\u00A2", new ByteArray("\u00A2", "UTF-8") 71 | .decodeToString("UTF-8")); 72 | assert.strictEqual("\u20AC", new ByteArray("\u20AC", "UTF-8") 73 | .decodeToString("UTF-8")); 74 | assert.strictEqual("\u10ABCD", new ByteArray("\u10ABCD", "UTF-8") 75 | .decodeToString("UTF-8")); 76 | 77 | assert.strictEqual("\u0024", new ByteArray("\u0024", "UTF-16") 78 | .decodeToString("UTF-16")); 79 | assert.strictEqual("\u00A2", new ByteArray("\u00A2", "UTF-16") 80 | .decodeToString("UTF-16")); 81 | assert.strictEqual("\u20AC", new ByteArray("\u20AC", "UTF-16") 82 | .decodeToString("UTF-16")); 83 | assert.strictEqual("\u10ABCD", new ByteArray("\u10ABCD", "UTF-16") 84 | .decodeToString("UTF-16")); 85 | }; 86 | 87 | if (require.main === module) { 88 | require("../../lib/test").run(exports); 89 | } -------------------------------------------------------------------------------- /benchmarks/run.js: -------------------------------------------------------------------------------- 1 | var readline = require("readline"); 2 | var http = require("http"); 3 | var spawn = require("child_process").spawn; 4 | 5 | var options = { 6 | host:'localhost', 7 | port:8080, 8 | agent:new http.Agent() 9 | }; 10 | var launchers = { 11 | c:function(test) { 12 | var benchmark = test + '/common-node.js'; 13 | console.log('Launching "' + benchmark + '"...'); 14 | return spawn('node', ['../lib/run', benchmark], { 15 | stdio:'inherit' 16 | }); 17 | }, 18 | n:function(test) { 19 | var benchmark = test + '/node.js'; 20 | console.log('Launching "' + benchmark + '"...'); 21 | return spawn('node', [benchmark], { 22 | stdio:'inherit' 23 | }); 24 | } 25 | }; 26 | var running = true; 27 | 28 | function noop() { 29 | } 30 | 31 | function poke() { 32 | if (running) { 33 | var first = true; 34 | function next() { 35 | if (first) { 36 | first = false; 37 | count++; 38 | setTimeout(poke, 0); 39 | } 40 | } 41 | function err() { 42 | if (first) { 43 | first = false; 44 | error++; 45 | setTimeout(poke, 1000); 46 | } 47 | } 48 | http.get(options, function(response) { 49 | response.on('error', err).on('end', next).on('close', err).resume(); 50 | }).on('error', err); 51 | } 52 | } 53 | 54 | if (require.main === module) { 55 | var launcher = process.argv[2] && process.argv[2].length > 0 && launchers[process.argv[2].charAt(0).toLowerCase()]; 56 | if (process.argv.length < 3 || !launcher) { 57 | console.log('node run.js []'); 58 | process.exit(1); 59 | } 60 | var child = launcher(process.argv[3]); 61 | process.on('uncaughtException', function(e) { 62 | console.error(e.stack); 63 | child.kill('SIGINT'); 64 | process.exit(1); 65 | }); 66 | 67 | var past = Date.now(); 68 | var count = 0; 69 | var error = 0; 70 | var n = process.argv[4] || 20; 71 | console.log('Concurrency = ' + n); 72 | options.agent.maxSockets = n; 73 | for (var i = 0; i < n; i++) { 74 | setTimeout(poke, 1000); 75 | } 76 | 77 | var cur = 0, max = 10, pts = []; 78 | pts.sum = 0; 79 | pts.sqSum = 0; 80 | 81 | function pad(string, length) { 82 | return (new Array(Math.max(length + 1 - string.length, 0))).join(' ') + string; 83 | } 84 | 85 | var previous = 0; 86 | var id = setInterval(function() { 87 | var now = Date.now(); 88 | var next = count; 89 | var p = 1e3 * (next - previous) / (now - past); 90 | var q = pts[cur] || 0; 91 | pts.sum += p - q; 92 | pts.sqSum += Math.pow(p, 2) - Math.pow(q, 2); 93 | pts[cur] = p; 94 | process.stdout.clearLine(); 95 | process.stdout.cursorTo(0); 96 | var s = pad(pts[cur].toFixed(2), 8); 97 | var mean = pts.sum / pts.length; 98 | s += ' ' + pad(mean.toFixed(2), 8); 99 | var sd = Math.sqrt(pts.sqSum / pts.length - Math.pow(mean, 2)); 100 | s += ' +/- ' + pad(sd.toFixed(2), 6); 101 | process.stdout.write(s + ' req/s (' + error + ')'); 102 | past = now; 103 | previous = next; 104 | cur = (cur + 1) % max; 105 | }, 3000); 106 | 107 | var r = readline.createInterface({ 108 | input:process.stdin, 109 | output:process.stdout 110 | }); 111 | r.question('Press ENTER to terminate...\n', function() { 112 | running = false; 113 | r.close(); 114 | console.log('Cleaning up...'); 115 | clearInterval(id); 116 | setTimeout(function() { 117 | child.kill('SIGINT'); 118 | }, 5000); 119 | }); 120 | } -------------------------------------------------------------------------------- /test/binary/bytestring-encodings-tests.js: -------------------------------------------------------------------------------- 1 | var assert = require("../../lib/assert"); 2 | var binary = require("../../lib/binary"); 3 | var ByteString = binary.ByteString; 4 | 5 | exports.testByteStringConstructorEncodings = function() { 6 | // ByteString(string, charset) 7 | // Convert a string. The ByteString will contain string encoded with charset. 8 | var testString = "hello world"; 9 | var b4 = new ByteString(testString, "US-ASCII"); 10 | assert.strictEqual(testString.length, b4.length); 11 | b4.length = 123; 12 | assert.strictEqual(testString.length, b4.length); 13 | assert.strictEqual(testString.charCodeAt(0), b4.get(0)); 14 | assert.strictEqual(testString.charCodeAt(testString.length - 1), b4.get(testString.length - 1)); 15 | }; 16 | 17 | exports.testToByteArrayEncodings = function() { 18 | var testString = "I ♥ JS"; 19 | var b = testString.toByteString("UTF-16"); 20 | var c = b.decodeToString("UTF-8"); 21 | assert.ok(typeof c === 'string'); 22 | assert.notStrictEqual(testString, c); 23 | var d = c.toByteArray("UTF-8"); 24 | assert.strictEqual(testString, d.decodeToString("UTF-16")); 25 | }; 26 | 27 | exports.testToByteStringEncodings = function() { 28 | var testString = "I ♥ JS"; 29 | var b = testString.toByteString("UTF-16"); 30 | var c = b.decodeToString("UTF-8"); 31 | assert.ok(typeof c === 'string'); 32 | assert.notStrictEqual(testString, c); 33 | var d = c.toByteString("UTF-8"); 34 | assert.strictEqual(testString, d.decodeToString("UTF-16")); 35 | }; 36 | 37 | exports.testToArrayEncodings = function() { 38 | var a1; 39 | 40 | a1 = new ByteString("\u0024\u00A2\u20AC", "UTF-8").toArray("UTF-8"); 41 | assert.strictEqual(3, a1.length); 42 | assert.strictEqual(0x24, a1[0]); 43 | assert.strictEqual(0xA2, a1[1]); 44 | assert.strictEqual(0x20AC, a1[2]); 45 | 46 | a1 = new ByteString("\u0024\u00A2\u20AC", "UTF-16").toArray("UTF-16"); 47 | assert.strictEqual(3, a1.length); 48 | assert.strictEqual(0x24, a1[0]); 49 | assert.strictEqual(0xA2, a1[1]); 50 | assert.strictEqual(0x20AC, a1[2]); 51 | 52 | }; 53 | 54 | exports.testDecodeToString = function() { 55 | assert.strictEqual("hello world", new ByteString("hello world", "US-ASCII") 56 | .decodeToString("US-ASCII")); 57 | 58 | assert.strictEqual("I ♥ JS", new ByteString("I ♥ JS", "UTF-8") 59 | .decodeToString("UTF-8")); 60 | 61 | assert.strictEqual("\u0024", new ByteString([0x24]).decodeToString("UTF-8")); 62 | assert.strictEqual("\u00A2", new ByteString([0xC2, 0xA2]) 63 | .decodeToString("UTF-8")); 64 | assert.strictEqual("\u20AC", new ByteString([0xE2, 0x82, 0xAC]) 65 | .decodeToString("UTF-8")); 66 | 67 | assert.strictEqual("\u0024", new ByteString("\u0024", "UTF-8") 68 | .decodeToString("UTF-8")); 69 | assert.strictEqual("\u00A2", new ByteString("\u00A2", "UTF-8") 70 | .decodeToString("UTF-8")); 71 | assert.strictEqual("\u20AC", new ByteString("\u20AC", "UTF-8") 72 | .decodeToString("UTF-8")); 73 | assert.strictEqual("\u10ABCD", new ByteString("\u10ABCD", "UTF-8") 74 | .decodeToString("UTF-8")); 75 | 76 | assert.strictEqual("\u0024", new ByteString("\u0024", "UTF-16") 77 | .decodeToString("UTF-16")); 78 | assert.strictEqual("\u00A2", new ByteString("\u00A2", "UTF-16") 79 | .decodeToString("UTF-16")); 80 | assert.strictEqual("\u20AC", new ByteString("\u20AC", "UTF-16") 81 | .decodeToString("UTF-16")); 82 | assert.strictEqual("\u10ABCD", new ByteString("\u10ABCD", "UTF-16") 83 | .decodeToString("UTF-16")); 84 | }; 85 | 86 | exports.testStringToByteString = function() { 87 | assert.strictEqual("hello world", "hello world".toByteString("US-ASCII") 88 | .decodeToString("US-ASCII")); 89 | assert.strictEqual("I ♥ JS", "I ♥ JS".toByteString("UTF-8").decodeToString( 90 | "UTF-8")); 91 | }; 92 | 93 | if (require.main === module) { 94 | require("../../lib/test").run(exports); 95 | } -------------------------------------------------------------------------------- /lib/assert.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview Assertion library covering [CommonJS Unit 3 | * Testing](http://wiki.commonjs.org/wiki/Unit_Testing/1.0). 4 | */ 5 | 6 | module.exports = require('assert'); 7 | 8 | /******************************************************************************* 9 | * **** E R R O R S ***** 10 | ******************************************************************************/ 11 | 12 | /** 13 | * Constructs a new AssertionError instance 14 | * 15 | * @class Instances of this class represent an assertion error 16 | * @param {Object} 17 | * options An object containing error details 18 | * @param.message {String} The error message 19 | * @param.actual {Object} The actual value 20 | * @param.expected {Object} The expected value 21 | * @constructor 22 | * @augments Error 23 | * 24 | * @name AssertionError 25 | */ 26 | 27 | /** 28 | * Creates a new ArgumentsError instance 29 | * 30 | * @class Instances of this class represent an error thrown if insufficient 31 | * arguments have been passed to an assertion function 32 | * @param {String} 33 | * message The exception message 34 | * @returns A newly created ArgumentsError instance 35 | * @constructor 36 | * 37 | * @name ArgumentsError 38 | */ 39 | 40 | /******************************************************************************* 41 | * **** C O M M O N J S A S S E R T I O N M E T H O D S ***** 42 | ******************************************************************************/ 43 | 44 | /** 45 | * Checks if the value passed as argument is truthy. 46 | * 47 | * @param {Object} 48 | * value The value to check for truthiness 49 | * @throws ArgumentsError 50 | * @throws AssertionError 51 | * 52 | * @name ok 53 | */ 54 | 55 | 56 | /** 57 | * Checks if the values passed as arguments are equal. 58 | * 59 | * @param {Object} 60 | * actual The actual value 61 | * @param {Object} 62 | * expected The expected value 63 | * @throws ArgumentsError 64 | * @throws AssertionError 65 | * 66 | * @name equal 67 | */ 68 | 69 | /** 70 | * Checks if the values passed as arguments are not equal. 71 | * 72 | * @param {Object} 73 | * actual The actual value 74 | * @param {Object} 75 | * expected The expected value 76 | * @throws ArgumentsError 77 | * @throws AssertionError 78 | * 79 | * @name notEqual 80 | */ 81 | 82 | /** 83 | * Checks if the values passed as arguments are deep equal 84 | * 85 | * @param {Object} 86 | * actual The actual value 87 | * @param {Object} 88 | * expected The expected value 89 | * @throws ArgumentsError 90 | * @throws AssertionError 91 | * 92 | * @name deepEqual 93 | */ 94 | 95 | /** 96 | * Checks if the values passed as arguments are not deep equal 97 | * 98 | * @param {Object} 99 | * actual The actual value 100 | * @param {Object} 101 | * expected The expected value 102 | * @throws ArgumentsError 103 | * @throws AssertionError 104 | * 105 | * @name notDeepEqual 106 | */ 107 | 108 | /** 109 | * Checks if the values passed as arguments are strictly equal 110 | * 111 | * @param {Object} 112 | * actual The actual value 113 | * @param {Object} 114 | * expected The expected value 115 | * @throws ArgumentsError 116 | * @throws AssertionError 117 | * 118 | * @name strictEqual 119 | */ 120 | 121 | /** 122 | * Checks if the values passed as arguments are not strictly equal 123 | * 124 | * @param {Object} 125 | * actual The actual value 126 | * @param {Object} 127 | * expected The expected value 128 | * @throws ArgumentsError 129 | * @throws AssertionError 130 | * 131 | * @name notStrictEqual 132 | */ 133 | 134 | /** 135 | * Checks if the function passed as argument throws a defined exception. 136 | * 137 | * @param {Object} 138 | * func The function to call 139 | * @param {Object} 140 | * expectedError Optional object expected to be thrown when executing 141 | * the function 142 | * @throws ArgumentsError 143 | * @throws AssertionError 144 | * 145 | * @name throws 146 | */ -------------------------------------------------------------------------------- /lib/httpserver.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview HTTP server interface as defined in [CommonJS JSGI 3 | * 0.3](http://wiki.commonjs.org/wiki/JSGI/Level0/A/Draft2). 4 | */ 5 | var Fiber = require('fibers'); 6 | var fs = require('fs').read; 7 | var parse = require('url').parse; 8 | var http = require('http'); 9 | var https = require('https'); 10 | var cluster = require('cluster'); 11 | var cpus = require('os').cpus().length; 12 | var system = require('./system'); 13 | var Binary = require('./binary').Binary; 14 | var Stream = require('./io').Stream; 15 | 16 | http.IncomingMessage.prototype._addHeaderLine = function(field, values, dest) { 17 | if (!dest) { 18 | dest = this.complete ? this.trailers : this.headers; 19 | } 20 | field = field.toLowerCase(); 21 | values = values.split(', '); 22 | if (field in dest) { 23 | Array.prototype.push.apply(dest[field], values); 24 | } else { 25 | dest[field] = values; 26 | } 27 | }; 28 | 29 | function receive(request) { 30 | var url = parse(request.url); 31 | var host, port; 32 | var c = request.connection; 33 | var h = request.headers.host; 34 | if (h && h.length) { 35 | h = h[0].split(':'); 36 | host = h[0]; 37 | port = +h[1] || 80; 38 | } else { 39 | host = c.localAddress; 40 | port = c.localPort; 41 | } 42 | return { 43 | version:[request.httpVersionMajor, request.httpVersionMinor], 44 | method:request.method, 45 | scriptName:'', 46 | pathInfo:url.pathname, 47 | queryString:url.search ? url.search.substr(1) : "", 48 | host:host, 49 | port:port, 50 | scheme:'http', 51 | input:new Stream(request), 52 | headers:request.headers, 53 | jsgi:{ 54 | version:[0, 3], 55 | errors:system.stderr, 56 | multithread:false, 57 | multiprocess:false, 58 | runOnce:false, 59 | async:false, 60 | cgi:false 61 | }, 62 | env:{}, 63 | // non standard fields below 64 | remoteAddress:c.remoteAddress, 65 | remotePort:c.remotePort 66 | }; 67 | } 68 | 69 | var CHARSET_PATTERN = /charset=([^;]+)/; 70 | function getCharset(headers) { 71 | var value = CHARSET_PATTERN.exec(headers['Content-Type']); 72 | return value && value[1]; 73 | } 74 | 75 | function send(r, response) { 76 | var body = r.body; 77 | var headers = r.headers || {}; 78 | response.writeHead(r.status, headers); 79 | var charset = getCharset(headers); 80 | body.forEach(function(part) { 81 | if (!(part instanceof Binary)) { 82 | part = part.toByteString(charset); 83 | } 84 | response.write(part.buffer); 85 | }); 86 | if (body.close) { 87 | body.close(); 88 | } 89 | response.end(); 90 | } 91 | 92 | var jsgi = exports.jsgi = function(app) { 93 | return function(request, response) { 94 | system.spawn(function() { 95 | try { 96 | send(app(receive(request)), response); 97 | } catch (e) { 98 | console.error(e.stack); 99 | send({status:503, body:[], headers:{}}, response); 100 | } 101 | request.resume(); 102 | }); 103 | }; 104 | }; 105 | 106 | exports.started = false; 107 | var main = exports.main = function(module, port, options) { 108 | if (cluster.isMaster && options && options.cluster) { 109 | if (options.cluster === 0) { 110 | options.cluster = cpus; 111 | } 112 | for (var i = 0; i < options.cluster; i++) { 113 | cluster.fork(process.env); 114 | } 115 | } else { 116 | options = options || {}; 117 | var app; 118 | if (typeof module === "function") { 119 | app = module; 120 | } else { 121 | app = module.app ? module.app : require(module).app; 122 | } 123 | if (!app) { 124 | throw new Error('No app found'); 125 | } 126 | port = port || 8080; 127 | var server; 128 | if ('key' in options && 'cert' in options) { 129 | server = https.createServer(options, jsgi(app)); 130 | } else { 131 | server = http.createServer(jsgi(app)); 132 | } 133 | server.timeout = options.timeout || 0; 134 | server.listen(port); 135 | exports.started = true; 136 | } 137 | }; -------------------------------------------------------------------------------- /lib/system.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview System module compliant with the [CommonJS 3 | * System/1.0](http://wiki.commonjs.org/wiki/System/1.0) 4 | * specification. Additional non-standard methods for sleeping on 5 | * the current thread, spawning a new thread and spawning 6 | * processes are provided. 7 | */ 8 | var Fiber = require('fibers'); 9 | var io = require('./io'); 10 | var Stream = io.Stream, TextStream = io.TextStream; 11 | 12 | /** 13 | * A TextStream to read from stdin. 14 | * 15 | * @name stdin 16 | */ 17 | exports.stdin = new TextStream(new Stream(process.stdin)); 18 | 19 | /** 20 | * A TextStream to write to stdout. 21 | * 22 | * @name stdout 23 | */ 24 | exports.stdout = new TextStream(new Stream(process.stdout)); 25 | 26 | /** 27 | * A TextStream to write to stderr. 28 | * 29 | * @name stderr 30 | */ 31 | exports.stderr = new TextStream(new Stream(process.stderr)); 32 | 33 | /** 34 | * A utility function to write to stdout. 35 | */ 36 | exports.print = function() { 37 | exports.stdout.print.apply(exports.stdout, arguments); 38 | }; 39 | 40 | /** 41 | * An array of strings representing the command line arguments passed to the 42 | * running script. 43 | */ 44 | exports.args = process.argv || []; 45 | 46 | /** 47 | * An object containing our environment variables. 48 | */ 49 | exports.env = process.env; 50 | 51 | /** 52 | * Terminates the current process. 53 | * 54 | * @param {number} 55 | * status The exit status, defaults to 0. 56 | */ 57 | exports.exit = function(status) { 58 | process.exit(status); 59 | }; 60 | 61 | // non-standard methods below 62 | 63 | /** 64 | * Used to register a callback which is called when the current process 65 | * terminates. 66 | * 67 | * @param {Function} 68 | * callback The callback to call. 69 | */ 70 | exports.onexit = function(callback) { 71 | process.on('SIGINT', function() { 72 | spawn(callback); 73 | }); 74 | }; 75 | 76 | /** 77 | * Spawns a new thread. 78 | * 79 | * @param {Function} 80 | * run entry point of the new thread. 81 | */ 82 | var spawn = exports.spawn = function(run) { 83 | // TODO bring in line with RingoJS 84 | var fiber = Fiber(function() { 85 | try { 86 | run(); 87 | } catch (e) { 88 | console.error(e.stack); 89 | } 90 | }); 91 | fiber.run(); 92 | return fiber; 93 | }; 94 | 95 | exports.parallel = function(fibers) { 96 | var fiber = Fiber.current; 97 | var index = 0; 98 | fibers.forEach(function(r) { 99 | Fiber(function() { 100 | fiber.run([index ++, r()]); 101 | }).run(); 102 | }); 103 | var result = []; 104 | while(index --) { 105 | var r = Fiber.yield(); 106 | result[r[0]] = r[1]; 107 | } 108 | return result; 109 | } 110 | 111 | /** 112 | * Suspends the current process for the specified number of milliseconds. 113 | * 114 | * @param {Number} 115 | * milliseconds The number of milliseconds to sleep. 116 | */ 117 | exports.sleep = function(milliseconds) { 118 | // TODO bring in line with RingoJS 119 | var fiber = Fiber.current; 120 | setTimeout(function() { 121 | fiber.run(); 122 | }, milliseconds); 123 | Fiber.yield(); 124 | }; 125 | 126 | global.getResource = function(name) { 127 | // TODO cache resources? 128 | var fs = require('fs-base'); 129 | var resource = { 130 | lastModified : function() { 131 | return fs.lastModified(name); 132 | }, 133 | length : fs.exists(name) ? fs.size(name) : 0, 134 | getInputStream : function() { 135 | return fs.openRaw(name); 136 | }, 137 | getContent : function() { 138 | return this.content; 139 | }, 140 | exists : function() { 141 | return fs.exists(name); 142 | } 143 | }; 144 | 145 | Object.defineProperty(resource, "content", { 146 | get : function() { 147 | return fs.openRaw(name).read().decodeToString(); 148 | } 149 | }); 150 | 151 | return resource; 152 | } 153 | 154 | global.getRepository = function(name) { 155 | var fs = require('fs-base'); 156 | return { 157 | _base : name, 158 | getResource : function(name) { 159 | return getResource(fs.normal(fs.join(this._base, name))); 160 | }, 161 | setRoot : function() { 162 | } 163 | }; 164 | } 165 | -------------------------------------------------------------------------------- /doc/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test - Common Node 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 |

Module test

24 |
25 |

Runs tests as defined in 26 | CommonJS Unit Testing.

27 | 28 | 29 |
30 | 31 |
32 | 33 |
34 |

Functions

35 |
    36 |
  • 37 | run(tests) 38 |
  • 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 |

50 | run 51 | (tests)

52 |
53 |

Runs the test function in the module provided. If the module 54 | doesn't export function, recursively looks inside the exported 55 | objects and runs them as tests as well. See 'test/all.js' for an example.

56 | 57 | 58 |
59 |
60 |

Parameters

61 | 62 | 63 | 64 | 65 | 66 | 67 |
ObjecttestsThe module containing the tests.
68 |
69 |
70 |
71 |
72 | 73 |
74 |
75 |
76 | 77 | 78 | 79 | 100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /test/subprocess.js: -------------------------------------------------------------------------------- 1 | var assert = require("../lib/assert"); 2 | var test = require('../lib/test'); 3 | var p = require('../lib/subprocess'); 4 | var io = require('../lib/io'); 5 | var system = require('../lib/system'); 6 | var MemoryStream = io.MemoryStream; 7 | 8 | function hijack(run) { 9 | var stdout = system.stdout; 10 | var stderr = system.stderr; 11 | var out = new MemoryStream(); 12 | var err = new MemoryStream(); 13 | system.stdout = {raw:out}; 14 | system.stderr = {raw:err}; 15 | try { 16 | run(); 17 | return {out:out.content.decodeToString(), err:err.content.decodeToString()}; 18 | } finally { 19 | system.stdout = stdout; 20 | system.stderr = stderr; 21 | } 22 | } 23 | 24 | exports.testCreateProcessText = function() { 25 | [ 26 | {command:'node -v'}, 27 | {command:['node', '-v']}, 28 | {command:['node', '-v'], encoding:'utf-8'} 29 | ].forEach(function(args) { 30 | var proc = p.createProcess(args); 31 | assert.strictEqual(proc.wait(), 0); 32 | assert.strictEqual(proc.stdout.read().trim(), process.version); 33 | assert.strictEqual(proc.stderr.read().length, 0); 34 | }); 35 | }; 36 | 37 | exports.testCreateProcessBinary = function() { 38 | [ 39 | {command:'node -v', binary:true}, 40 | {command:['node', '-v'], binary:true}, 41 | {command:['node', '-v'], binary:true, encoding:'utf-8'} 42 | ].forEach(function(args) { 43 | var proc = p.createProcess(args); 44 | assert.strictEqual(proc.wait(), 0); 45 | assert.strictEqual(proc.stdout.read().decodeToString().trim(), process.version); 46 | assert.strictEqual(proc.stderr.read().length, 0); 47 | }); 48 | }; 49 | 50 | exports.testCreateProcessError = function() { 51 | [ 52 | {command:'node nosuchscriptfile'}, 53 | {command:['node', 'nosuchscriptfile']}, 54 | {command:['node', 'nosuchscriptfile'], encoding:'utf-8'}, 55 | {command:'node nosuchscriptfile', binary:true}, 56 | {command:['node', 'nosuchscriptfile'], binary:true}, 57 | {command:['node', 'nosuchscriptfile'], binary:true, encoding:'utf-8'} 58 | ].forEach(function(args) { 59 | var proc = p.createProcess(args); 60 | assert.notStrictEqual(proc.wait(), 0); 61 | assert.strictEqual(proc.stdout.read().length, 0); 62 | assert.notStrictEqual(proc.stderr.read().length, 0); 63 | }); 64 | var proc = p.createProcess({command:'nosuchprocess'}); 65 | assert.notStrictEqual(proc.wait(), 0); 66 | }; 67 | 68 | exports.testCommandText = function() { 69 | [ 70 | ['node -v'], 71 | ['node', '-v'], 72 | ['node', '-v', {encoding:'utf-8'}] 73 | ].forEach(function(args) { 74 | assert.strictEqual(p.command.apply(null, args).trim(), process.version); 75 | }); 76 | }; 77 | 78 | exports.testCommandBinary = function() { 79 | [ 80 | ['node -v', {binary:true}], 81 | ['node', '-v', {binary:true}], 82 | ['node', '-v', {binary:true, encoding:'utf-8'}] 83 | ].forEach(function(args) { 84 | assert.strictEqual(p.command.apply(null, args).decodeToString().trim(), process.version); 85 | }); 86 | }; 87 | 88 | exports.testCommandError = function() { 89 | [ 90 | ['node nosuchscriptfile'], 91 | ['node', 'nosuchscriptfile'], 92 | ['node', 'nosuchscriptfile', {encoding:'utf-8'}], 93 | ['node nosuchscriptfile', {binary:true}], 94 | ['node', 'nosuchscriptfile', {binary:true}], 95 | ['node', 'nosuchscriptfile', {binary:true, encoding:'utf-8'}] 96 | ].forEach(function(args) { 97 | assert.throws(function() { 98 | p.command.apply(null, args); 99 | }); 100 | }); 101 | assert.throws(function() { 102 | p.command('nosuchprocess'); 103 | }); 104 | }; 105 | 106 | exports.testSystem = function() { 107 | [ 108 | ['node -v'], 109 | ['node', '-v'], 110 | ['node', '-v', {encoding:'utf-8'}], 111 | ['node -v', {binary:true}], 112 | ['node', '-v', {binary:true}], 113 | ['node', '-v', {binary:true, encoding:'utf-8'}] 114 | ].forEach(function(args) { 115 | var std = hijack(function() { 116 | assert.strictEqual(p.system.apply(null, args), 0); 117 | }); 118 | assert.strictEqual(std.out.trim(), process.version); 119 | assert.strictEqual(std.err.length, 0); 120 | }); 121 | }; 122 | 123 | exports.testSystemError = function() { 124 | [ 125 | ['node nosuchscriptfile'], 126 | ['node', 'nosuchscriptfile'], 127 | ['node', 'nosuchscriptfile', {encoding:'utf-8'}], 128 | ['node nosuchscriptfile', {binary:true}], 129 | ['node', 'nosuchscriptfile', {binary:true}], 130 | ['node', 'nosuchscriptfile', {binary:true, encoding:'utf-8'}] 131 | ].forEach(function(args) { 132 | var std = hijack(function() { 133 | assert.notStrictEqual(p.system.apply(null, args), 0); 134 | }); 135 | assert.strictEqual(std.out.length, 0); 136 | assert.notStrictEqual(std.err.length, 0); 137 | }); 138 | var std = hijack(function() { 139 | assert.notStrictEqual(p.system('nosuchprocess'), 0); 140 | }); 141 | assert.strictEqual(std.out.length, 0); 142 | assert.strictEqual(std.err.length, 0); 143 | }; 144 | 145 | if (require.main === module) { 146 | test.run(exports); 147 | } -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module overview - Common Node 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 |

Common Node

24 | 25 |
    26 |
  • 27 | 28 |

    all

    29 |
    30 | 31 | 32 | 33 |
    34 | 35 |
  • 36 |
  • 37 | 38 |

    assert

    39 |
    40 |

    Assertion library covering 41 | CommonJS Unit Testing.

    42 | 43 | 44 |
    45 | 46 |
  • 47 |
  • 48 | 49 |

    binary

    50 |
    51 |

    Binary, ByteArray and ByteString classes as defined in 52 | CommonJS Binary/B.

    53 | 54 | 55 |
    56 | 57 |
  • 58 |
  • 59 | 60 |

    fs-base

    61 |
    62 |

    File and path related functionality as 63 | defined in CommonJS 64 | Filesystem/A .

    65 | 66 | 67 |
    68 | 69 |
  • 70 |
  • 71 | 72 |

    httpclient

    73 |
    74 |

    HTTP Client as defined in 75 | CommonJS HTTP Client/A.

    76 | 77 | 78 |
    79 | 80 |
  • 81 |
  • 82 | 83 |

    io

    84 |
    85 |

    Stream and TextStream classes as per 86 | CommonJS IO/A 87 | as well as a ByteArray based in memory MemoryStream.

    88 | 89 | 90 |
    91 | 92 |
  • 93 |
  • 94 | 95 |

    jsgi

    96 |
    97 |

    HTTP server interface as defined in CommonJS JSGI 0.3.

    98 | 99 | 100 |
    101 | 102 |
  • 103 |
  • 104 | 105 |

    system

    106 |
    107 |

    System module 108 | compliant with the CommonJS 109 | System/1.0 specification. Additional non-standard methods for sleeping on the current thread, 110 | spawning a new thread and spawning processes are provided.

    111 | 112 | 113 |
    114 | 115 |
  • 116 |
  • 117 | 118 |

    test

    119 |
    120 |

    Runs tests as defined in 121 | CommonJS Unit Testing.

    122 | 123 | 124 |
    125 | 126 |
  • 127 |
128 | 129 |
130 | 131 |
132 | 133 | .. show more 134 |
135 |
136 | 137 | 138 | 139 | 140 | 141 | 142 | 163 |
164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /doc/jsgi/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jsgi - Common Node 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 |

Module jsgi

24 |
25 |

HTTP server interface as defined in CommonJS JSGI 0.3.

26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 |

Functions

34 |
    35 |
  • 36 | jsgi(app) 37 |
  • 38 |
  • 39 | run(module, port, options) 40 |
  • 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 |
51 |

52 | jsgi 53 | (app)

54 |
55 | 56 | 57 | 58 |
59 |
60 |

Parameters

61 | 62 | 63 | 64 | 65 | 66 | 67 |
app
68 |
69 |
70 |
71 |
72 | 73 | 74 |
75 |
76 |

77 | run 78 | (module, port, options)

79 |
80 | 81 | 82 | 83 |
84 |
85 |

Parameters

86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
module
port
options
103 |
104 |
105 |
106 |
107 | 108 |
109 |
110 |
111 | 112 | 113 | 114 | 135 |
136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /test/http.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var assert = require("../lib/assert"); 3 | var HttpClient = require("../lib/httpclient").HttpClient; 4 | var ByteString = require("../lib/binary").ByteString; 5 | var Stream = require("../lib/io").Stream; 6 | 7 | function getStream(file) { 8 | return new Stream(fs.createReadStream(file)); 9 | } 10 | 11 | function getContents(file) { 12 | return fs.readFileSync(file, "utf8"); 13 | } 14 | 15 | var resource = getContents('./lib/assert.js'); 16 | 17 | require("../lib/httpserver").main(function(request) { 18 | return app(request); 19 | }); 20 | 21 | var app; 22 | 23 | function checkHeaders(original, response) { 24 | for (var key in original) { 25 | var h1 = original[key]; 26 | if (!Array.isArray(h1)) { 27 | h1 = h1.split(', '); 28 | } 29 | var h2 = response[key.toLowerCase()]; 30 | assert.deepEqual(h1, h2); 31 | } 32 | } 33 | 34 | exports.testSetHeader = function() { 35 | var headers = { 36 | 'Accept-Encoding':'gzip', 37 | 'Content-Type':'text/plain', 38 | 'Cache-Control':['max-age=42', 'must-revalidate', 'private'] 39 | }; 40 | var request = new HttpClient({ 41 | method:"GET", 42 | url:"http://localhost:8080/", 43 | headers:headers 44 | }); 45 | 46 | var expected = {}; 47 | for (var key in headers) { 48 | var value = headers[key]; 49 | var newKey = key.toLowerCase(); 50 | var newValue = Array.isArray(value) ? value.concat('changed') : value + ', changed'; 51 | request.setHeader(newKey, newValue); 52 | expected[newKey] = newValue; 53 | } 54 | assert.deepEqual(expected, request._headers); 55 | }; 56 | 57 | exports.testWrite = function() { 58 | var data = 'Hello\nWorld!'.toByteString(); 59 | var headers = { 60 | 'Content-Type':'text/plain', 61 | 'Cache-Control':'max-age=42, must-revalidate, private' 62 | }; 63 | var headers2 = { 64 | 'Content-Type':'text/plain', 65 | 'Content-Length':47, 66 | 'Cache-Control':'max-age=42, must-revalidate, private' 67 | }; 68 | 69 | assert.throws(function() { 70 | var request = new HttpClient({ 71 | method:"GET", 72 | url:"http://localhost:8080/", 73 | body:data 74 | }); 75 | request.write(data); 76 | }); 77 | assert.throws(function() { 78 | var request = new HttpClient({ 79 | method:"GET", 80 | url:"http://localhost:8080/", 81 | headers:headers, 82 | body:data 83 | }); 84 | request.write(data); 85 | }); 86 | assert.throws(function() { 87 | var request = new HttpClient({ 88 | method:"GET", 89 | url:"http://localhost:8080/", 90 | headers:headers2, 91 | body:data 92 | }); 93 | request.write(data); 94 | }); 95 | 96 | var request = new HttpClient({ 97 | method:"GET", 98 | url:"http://localhost:8080/", 99 | body:[] 100 | }); 101 | request.write(data); 102 | assert.ok(!request._headers.hasOwnProperty('Content-Length')); 103 | request = new HttpClient({ 104 | method:"GET", 105 | url:"http://localhost:8080/", 106 | headers:headers, 107 | body:[] 108 | }); 109 | request.write(data); 110 | assert.ok(!request._headers.hasOwnProperty('Content-Length')); 111 | 112 | request = new HttpClient({ 113 | method:"GET", 114 | url:"http://localhost:8080/", 115 | headers:headers2, 116 | body:[] 117 | }); 118 | request.write(data); 119 | assert.strictEqual(request._headers['Content-Length'], 47 + data.length); 120 | }; 121 | 122 | exports.testRead = function() { 123 | assert.throws(function() { 124 | new HttpClient({ 125 | method:"GET", 126 | url:"http://localhost:8080/" 127 | }).read(); 128 | }); 129 | }; 130 | 131 | exports.testGet = function() { 132 | var code = 200; 133 | var method = "gEt"; 134 | var content = "Hello\nWorld!"; 135 | var headers = { 136 | 'Content-Type':'text/plain', 137 | 'Cache-Control':'max-age=42, must-revalidate, private' 138 | }; 139 | app = function(request) { 140 | var status = code; 141 | if (request.method !== method.toUpperCase()) { 142 | status = 599; 143 | } 144 | try { 145 | checkHeaders(headers, request.headers); 146 | } catch (e) { 147 | status = 598; 148 | } 149 | return { 150 | status:status, 151 | headers:headers, 152 | body:[content] 153 | }; 154 | }; 155 | 156 | var response = new HttpClient({ 157 | method:method, 158 | url:"http://localhost:8080/", 159 | headers:headers 160 | }).finish(); 161 | assert.notStrictEqual(599, response.status, 'request method mismatch'); 162 | assert.notStrictEqual(598, response.status, 'request header mismatch'); 163 | assert.strictEqual(code, response.status); 164 | checkHeaders(headers, response.headers); 165 | assert.strictEqual(content, response.body.read().decodeToString()); 166 | response.body.close(); 167 | }; 168 | 169 | exports.testPost = function() { 170 | var code = 301; 171 | var method = "POST"; 172 | var content = "Hello\nWorld!"; 173 | var headers = { 174 | 'Content-Type':'text/plain;charset=utf-16', 175 | 'Cache-Control':['max-age=42', 'must-revalidate', 'private'] 176 | }; 177 | app = function(request) { 178 | var status = code; 179 | if (request.method !== method.toUpperCase()) { 180 | status = 599; 181 | } 182 | try { 183 | checkHeaders(headers, request.headers); 184 | } catch (e) { 185 | status = 598; 186 | } 187 | return { 188 | status:status, 189 | headers:headers, 190 | body:[content] 191 | }; 192 | }; 193 | 194 | var response = new HttpClient({ 195 | method:method, 196 | url:"http://localhost:8080/", 197 | headers:headers 198 | }).connect().read(); 199 | assert.notStrictEqual(599, response.status, 'request method mismatch'); 200 | assert.notStrictEqual(598, response.status, 'request header mismatch'); 201 | assert.strictEqual(code, response.status); 202 | checkHeaders(headers, response.headers); 203 | assert.strictEqual(content, response.body.read().decodeToString('UTF-16')); 204 | response.body.close(); 205 | }; 206 | 207 | exports.testPut = function() { 208 | var code = 409; 209 | var method = "put"; 210 | var headers = { 211 | 'Content-Type':'text/plain' 212 | }; 213 | app = function(request) { 214 | var status = code; 215 | if (request.method !== method.toUpperCase()) { 216 | status = 599; 217 | } 218 | try { 219 | checkHeaders(headers, request.headers); 220 | } catch (e) { 221 | status = 598; 222 | } 223 | return { 224 | status:status, 225 | headers:headers, 226 | body:getStream('./lib/assert.js') 227 | }; 228 | }; 229 | 230 | var response = new HttpClient({ 231 | method:method, 232 | url:"http://localhost:8080/", 233 | headers:headers 234 | }).connect().read(); 235 | assert.notStrictEqual(599, response.status, 'request method mismatch'); 236 | assert.notStrictEqual(598, response.status, 'request header mismatch'); 237 | assert.strictEqual(code, response.status); 238 | checkHeaders(headers, response.headers); 239 | assert.strictEqual(resource.toString(), response.body.read().decodeToString()); 240 | response.body.close(); 241 | }; 242 | 243 | exports.testHttpsGet = function() { 244 | var response = new HttpClient({ 245 | method:"GET", 246 | url:"https://github.com/" 247 | }).finish(); 248 | response.body.close(); 249 | assert.strictEqual(response.status, 200); 250 | }; 251 | 252 | if (require.main === module) { 253 | require("../lib/test").run(exports); 254 | } -------------------------------------------------------------------------------- /lib/socket.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Socket class as defined in the [CommonJS 3 | * Sockets/A](http://wiki.commonjs.org/wiki/Sockets/A) proposal. 4 | */ 5 | var Fiber = require('fibers'); 6 | var net = require('net'); 7 | var io = require('./io'); 8 | 9 | exports.Socket = Socket; 10 | 11 | var families = { 12 | AF_INET: "tcp4", 13 | AF_INET6: "tcp6", 14 | AF_UNIX: "unix" 15 | }; 16 | 17 | /** 18 | * The Socket class is used to create a blocking socket. 19 | * 20 | * @constructor 21 | * @this {Socket} 22 | * @param family AF_INET (default), AF_INET6 or AF_UNIX 23 | * @param type SOCK_STREAM for TCP (default) or SOCK_DGRAM for UDP 24 | */ 25 | function Socket(family, type) { 26 | if(!(this instanceof Socket)) 27 | return new Socket(family, type); 28 | 29 | if(family instanceof net.Socket) { 30 | this.client = family; 31 | } else { 32 | this.type = families[type]; 33 | this.guts = {}; 34 | } 35 | } 36 | 37 | /** 38 | * Initiate a connection on a socket. Connect to a remote port on the specified 39 | * host with a connection timeout. Throws an exception in case of failure. 40 | * 41 | * @param host IP address or hostname 42 | * @param port port number or service name 43 | * @param timeout timeout value (default X microseconds) 44 | */ 45 | Socket.prototype.connect = function(host, port, timeout) { 46 | var fiber = Fiber.current; 47 | // TODO Unix sockets 48 | // TODO UDP sockets 49 | // TODO TLS sockets 50 | var client = this.client = net.createConnection(port, host); 51 | client.on('connect', function() { 52 | fiber.run(); 53 | }); 54 | client.on('error', function(error) { 55 | fiber.run(error); 56 | }); 57 | var result = Fiber.yield(); 58 | if(result instanceof Error) { 59 | throw new Error(result.message); 60 | } 61 | return this; 62 | }; 63 | 64 | /** 65 | * When a new socket is created, it has no address bound to it. Bind assigns the 66 | * specified address (also known as name) to the socket. Throws an exception in 67 | * case of failure. 68 | * 69 | * @param host address (interface) to which the socket will be bound. If address 70 | * is omitted, any address is will match. 71 | * @param port port number or service name to which socket is to be bound. If 72 | * port is undefined, the socket wont bind to any port. 73 | */ 74 | Socket.prototype.bind = function(host, port) { 75 | var server = this.server = net.createServer(); 76 | var fiber = Fiber.current; 77 | server.on('error', function(error) { 78 | // on error EADDRINUSE 79 | fiber.run(error); 80 | }); 81 | // TODO leave out port or host to use defaults / random 82 | server.listen(port, host, function() { 83 | fiber.run(); 84 | }); 85 | // TODO add a listener to queue up connections until accept is called 86 | var result = Fiber.yield(); 87 | if(result instanceof Error) { 88 | throw new Error(result.message); 89 | } 90 | return this; 91 | }; 92 | 93 | //TODO add functions to retrieve port and hostname the socket is bound to 94 | 95 | /** 96 | * Accept a connection on a socket. Returns a new (connected) Socket. 97 | */ 98 | Socket.prototype.accept = function() { 99 | // TODO grab an already accepted connection if there are any? 100 | var fiber = Fiber.current; 101 | var onconnection = function(socket) { 102 | fiber.run(socket); 103 | }; 104 | this.server.on('connection', onconnection); 105 | var result = Fiber.yield(); 106 | this.server.removeListener('connection', onconnection); 107 | return new Socket(result); 108 | }; 109 | 110 | /** 111 | * Listen for incoming connections on a socket (use before an accept). Throws an 112 | * exception in case of failure. 113 | */ 114 | Socket.prototype.listen = function() { 115 | // TODO figure out what the purpose of this is? 116 | return this; 117 | }; 118 | 119 | /** 120 | * Shut down part of a full-duplex connection on a socket. If [what] is SHUT_RD, 121 | * further receives will be disallowed. If [what] is SHUT_WR further sends will 122 | * be disallowed. If what is SHUT_RDWR, further sends and receives will be 123 | * disallowed. 124 | * 125 | * @param what SHUT_RD, SHUT_WR or SHUT_RDWR 126 | */ 127 | Socket.prototype.shutdown = function(what) { 128 | // ??? 129 | }; 130 | 131 | /** 132 | * Close the socket immediately 133 | */ 134 | Socket.prototype.close = function() { 135 | // socket.end([data], [encoding]); 136 | // socket.destroy(); 137 | }; 138 | 139 | Socket.prototype.getStream = function() { 140 | if(!this.stream) { 141 | this.stream = new io.Stream(this.client || this.server); 142 | } 143 | return this.stream; 144 | }; 145 | 146 | /** 147 | * Receive a block of bytes from the socket. Returns block of bytes read. 148 | * 149 | * @param maxlen number of bytes to read. Default: X bytes 150 | */ 151 | Socket.prototype.read = function(maxlen) { 152 | return this.getStream().read(maxlen); 153 | }; 154 | 155 | /** 156 | * Receive a block of bytes from the socket. Returns block of bytes read. May 157 | * receive fewer bytes than requested even if the end of the stream hasn’t been 158 | * reached. 159 | * 160 | * @param maxlen number of bytes to read. Default: X bytes 161 | */ 162 | Socket.prototype.receive = function(maxlen) { 163 | // TODO no clear what to do here, but the solution below is clearly broken; we 164 | // could just deprecate this method 165 | return this.getStream().read(null); 166 | }; 167 | 168 | /** 169 | * Send a block of bytes to the socket. 170 | * 171 | * @param data block of bytes to send 172 | */ 173 | Socket.prototype.send = function(data) { 174 | // socket.write(data, [encoding], [callback]) 175 | // socket.write(data, [encoding], [fileDescriptor], [callback]) 176 | // TODO deal with the fileDescriptor case 177 | this.getStream().write(data); 178 | return this; 179 | }; 180 | 181 | /** 182 | * Send a block of bytes to the socket. May send only a part of the data even if 183 | * the peer hasn’t closed connection. 184 | * 185 | * @param data block of bytes to send 186 | */ 187 | Socket.prototype.write = function(data) { 188 | // socket.write(data, [encoding], [callback]) 189 | // socket.write(data, [encoding], [fileDescriptor], [callback]) 190 | // TODO deal with the fileDescriptor case 191 | // TODO this is clearly broken - we could just deprecate this method 192 | this.getStream().write(data); 193 | return this; 194 | }; 195 | 196 | /** 197 | * Receive all data from socket. Returns object with ip_address and port of 198 | * sender along with data properties. 199 | * 200 | * @param maxlen number of bytes to read. Default: X bytes 201 | */ 202 | Socket.prototype.receiveFrom = function(maxlen) { 203 | // UDP? 204 | }; 205 | 206 | /** 207 | * Send a block of data to a specific host and port. 208 | * 209 | * @param host IP address or hostname 210 | * @param port port number or service name 211 | * @param data block of bytes to send 212 | */ 213 | Socket.prototype.sendTo = function(host, port, data) { 214 | // UDP? 215 | }; 216 | 217 | /** 218 | * Sends a complete File object across a socket. 219 | * 220 | * @param file a File object 221 | * @deprecated 222 | */ 223 | Socket.prototype.sendFile = function(file) { 224 | throw new Error('sendFile is deprecated'); 225 | }; 226 | 227 | /** 228 | * Set socket option value to socket option name. 229 | * 230 | * @param option 231 | * @param value 232 | */ 233 | Socket.prototype.setOption = function(option, value) { 234 | this.guts[option] = value; 235 | return this; 236 | }; 237 | 238 | /** 239 | * Get socket option value, option should be socket option name. 240 | */ 241 | Socket.prototype.getOption = function() { 242 | return this.guts[option]; 243 | }; 244 | -------------------------------------------------------------------------------- /lib/subprocess.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview A module for spawning processes, connecting to their 3 | * input/output/errput and returning their response codes. 4 | */ 5 | var Fiber = require('fibers'); 6 | var childProcess = require('child_process'); 7 | 8 | var io = require('./io'); 9 | var Stream = io.Stream, TextStream = io.TextStream; 10 | var system = require('./system'); 11 | 12 | function parseArguments(args) { 13 | args = Array.prototype.slice.call(args); 14 | // arguments may end with a {dir: "", env: {}} object 15 | var opts = (args.length > 1 && args[args.length - 1] instanceof Object) ? args.pop() : {}; 16 | // make command either a single string or an array of strings 17 | opts.command = args.length === 1 ? args[0] : args; 18 | return opts; 19 | } 20 | 21 | function asyncCopy(from, to) { 22 | system.spawn(function() { 23 | from.copy(to); 24 | }); 25 | } 26 | 27 | /** 28 | * Low-level function to spawn a new process. The function takes an object 29 | * argument containing the following properties where all properties except 30 | * command are optional: 31 | * 32 | * `command` a string or array of strings containing the command to execute 33 | * 34 | * `dir` the directory to run the process in 35 | * 36 | * `env` alternative environment variables. If null the process inherits the 37 | * environment of the current process. 38 | * 39 | * `binary` a boolean flag that uses raw binary streams instead of text streams 40 | * 41 | * `encoding` the character encoding to use for text streams 42 | * 43 | * @param {Object} args an object containing the process command and options. 44 | * @returns a Process object 45 | * @see #Process 46 | */ 47 | var createProcess = exports.createProcess = function(args) { 48 | var binary = args.binary, encoding = args.encoding; 49 | var procArgs = args.command; 50 | if (typeof procArgs === 'string') { 51 | procArgs = procArgs.split(/\s+/g); 52 | } else { 53 | procArgs = Array.prototype.map.call(procArgs, String); 54 | } 55 | 56 | var code; 57 | var waiting; 58 | 59 | function handleExit(c) { 60 | code = c; 61 | if (waiting) { 62 | var fiber = waiting; 63 | waiting = null; 64 | fiber.run(); 65 | } 66 | } 67 | 68 | var child = childProcess.spawn(procArgs.shift(), procArgs, { 69 | cwd:args.dir || process.cwd(), 70 | env:args.env || system.env 71 | }).on('error', function(e) { 72 | // Node.js won't end these streams upon error 73 | stdout._end(); 74 | stderr._end(); 75 | handleExit(e.code); 76 | }).on('exit', handleExit); 77 | 78 | var stdin = new Stream(child.stdin); 79 | var stdout = new Stream(child.stdout); 80 | var stderr = new Stream(child.stderr); 81 | 82 | function wrapIO(stream) { 83 | return binary ? stream : new TextStream(stream, { 84 | charset:encoding 85 | }); 86 | } 87 | 88 | /** 89 | * The Process object can be used to control and obtain information about a 90 | * subprocess started using [createProcess()](#createProcess). 91 | * 92 | * @name Process 93 | * @class Process 94 | */ 95 | return { 96 | /** 97 | * The PID of the child process. Note: this is not available in Ringo. 98 | * 99 | * @name Process.prototype.pid 100 | */ 101 | pid:child.pid, 102 | /** 103 | * The process's input stream. 104 | * 105 | * @name Process.prototype.stdin 106 | */ 107 | stdin:wrapIO(stdin), 108 | /** 109 | * The process's output stream. 110 | * 111 | * @name Process.prototype.stdout 112 | */ 113 | stdout:wrapIO(stdout), 114 | /** 115 | * The process's error stream. 116 | * 117 | * @name Process.prototype.stderr 118 | */ 119 | stderr:wrapIO(stderr), 120 | /** 121 | * Wait for the process to terminate and return its exit status. 122 | * 123 | * @name Process.prototype.wait 124 | * @function 125 | */ 126 | wait:function() { 127 | if (typeof code === 'undefined') { 128 | waiting = Fiber.current; 129 | Fiber.yield(); 130 | } 131 | return code; 132 | }, 133 | /** 134 | * Kills the subprocess. 135 | * 136 | * @name Process.prototype.kill 137 | * @param signal SIGTERM, SIGHUP 138 | * @function 139 | */ 140 | kill:function(signal) { 141 | child.kill(signal); 142 | }, 143 | /** 144 | * Connects the process's steams to the argument streams and starts threads 145 | * to copy the data asynchronously. 146 | * 147 | * @param {Stream} input output stream to connect to the process's input stream 148 | * @param {Stream} output input stream to connect to the process's output stream 149 | * @param {Stream} errput input stream to connect to the process's error stream 150 | * @name Process.prototype.connect 151 | */ 152 | connect:function(input, output, errput) { 153 | // TODO test connect 154 | if (input) { 155 | asyncCopy(input, stdin); 156 | } 157 | if (output) { 158 | asyncCopy(stdout, output); 159 | } 160 | if (errput) { 161 | asyncCopy(stderr, errput); 162 | } 163 | } 164 | }; 165 | }; 166 | 167 | /** 168 | * Executes a given command and returns the standard output. If the exit status 169 | * is non-zero, throws an Error. 170 | * 171 | * @param {String} command ... command and optional arguments as single or 172 | * multiple string parameters 173 | * @param {Object} [options] options object. This may contain a `dir` string 174 | * property specifying the directory to run the process in and a `env` object 175 | * property specifying additional environment variable mappings. 176 | * @returns String the standard output of the command 177 | */ 178 | var command = exports.command = function(command, options) { 179 | var args = parseArguments(arguments); 180 | var process = createProcess(args); 181 | var status = process.wait(); 182 | if (status !== 0) { 183 | throw new Error("(" + status + ")\n" + process.stderr.read()); 184 | } 185 | return process.stdout.read(); 186 | }; 187 | 188 | /** 189 | * Executes a given command, attached to this process's output and error 190 | * streams, and returns the exit status. 191 | * 192 | * @param {String} command ... command and optional arguments as single or 193 | * multiple string parameters 194 | * @param {Object} [options] options object. This may contain a `dir` string 195 | * property specifying the directory to run the process in and a `env` object 196 | * property specifying additional environment variable mappings. 197 | * @returns Number exit status 198 | */ 199 | exports.system = function(command, options) { 200 | var args = parseArguments(arguments); 201 | var process = createProcess(args); 202 | var output = system.stdout.raw; 203 | var errput = system.stderr.raw; 204 | process.connect(null, output, errput); 205 | return process.wait(); 206 | }; 207 | 208 | /** 209 | * Executes a given command quietly and returns the exit status. 210 | * 211 | * @param {String} command ... command and optional arguments as single or 212 | * multiple string parameters 213 | * @param {Object} [options] options object. This may contain a `dir` string 214 | * property specifying the directory to run the process in and a `env` object 215 | * property specifying additional environment variable mappings. 216 | * @returns Number exit status 217 | * @name status 218 | */ 219 | var status = exports.status = function(command, options) { 220 | var process = createProcess(parseArguments(arguments)); 221 | process.connect(null, dummyStream(), dummyStream()); 222 | return process.wait(); 223 | }; 224 | 225 | function dummyStream() { 226 | return { 227 | writable:function() { 228 | return true; 229 | }, 230 | readable:function() { 231 | return false; 232 | }, 233 | seekable:function() { 234 | return false; 235 | }, 236 | write:function() { 237 | return this; 238 | }, 239 | flush:function() { 240 | return this; 241 | }, 242 | close:function() { 243 | return this; 244 | } 245 | }; 246 | } -------------------------------------------------------------------------------- /test/io.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var assert = require("../lib/assert"); 3 | var io = require('../lib/io'); 4 | var test = require('../lib/test'); 5 | var Stream = io.Stream; 6 | var MemoryStream = io.MemoryStream; 7 | var TextStream = io.TextStream; 8 | var binary = require('../lib/binary'); 9 | var ByteArray = binary.ByteArray; 10 | var ByteString = binary.ByteString; 11 | 12 | function getStream(file) { 13 | return new Stream(fs.createReadStream(file)); 14 | } 15 | 16 | function getContents(file) { 17 | return fs.readFileSync(file, "utf8"); 18 | } 19 | 20 | var resource = getContents('./lib/assert.js'); 21 | 22 | exports.testStreamReadFixed = function() { 23 | var io = getStream('./lib/io.js'); 24 | var bytes = io.read(7); 25 | assert.strictEqual(bytes.length, 7); 26 | assert.strictEqual(bytes.decodeToString(), resource.substr(0, 7)); 27 | bytes = io.read(5); 28 | assert.strictEqual(bytes.length, 5); 29 | assert.strictEqual(bytes.decodeToString(), resource.substr(7, 5)); 30 | }; 31 | 32 | exports.testStreamReadIndefinite = function() { 33 | var io = getStream('./lib/assert.js'); 34 | var bytes = io.read(); 35 | assert.strictEqual(bytes.length, resource.length); 36 | assert.strictEqual(bytes.decodeToString(), resource); 37 | }; 38 | 39 | exports.testStreamForEach = function() { 40 | var io = getStream('./lib/assert.js'); 41 | var str = ""; 42 | var read = 0; 43 | io.forEach(function(data) { 44 | read += data.length; 45 | str += data.decodeToString(); 46 | }); 47 | assert.strictEqual(read, resource.length); 48 | assert.strictEqual(str, resource); 49 | }; 50 | 51 | exports.testStreamReadInto = function() { 52 | var io = getStream('./lib/assert.js'); 53 | var bytes = new ByteArray(7); 54 | io.readInto(bytes); 55 | assert.equal(bytes.decodeToString(), resource.substring(0, 7)); 56 | }; 57 | 58 | exports.testStreamWrite = test.fs(1, function(file) { 59 | var io = new Stream(fs.createWriteStream(file)); 60 | io.write(new ByteArray('test')); 61 | io.flush(); 62 | io.close(); 63 | assert.strictEqual('test', getContents(file)); 64 | }); 65 | 66 | function checkRW(io, readable, writable) { 67 | assert.strictEqual(io.readable(), readable); 68 | assert.strictEqual(io.writable(), writable); 69 | } 70 | 71 | function checkCursors(io, position, length) { 72 | assert.strictEqual(io.position, position); 73 | assert.strictEqual(io.length, length); 74 | } 75 | 76 | function noWrite(io) { 77 | assert.throws(function() { 78 | io.write(''); 79 | }); 80 | } 81 | 82 | function write(io) { 83 | io.write(resource); 84 | checkCursors(io, resource.length, resource.length); 85 | io.write(new ByteArray(resource)); 86 | checkCursors(io, resource.length * 2, resource.length * 2); 87 | io.position = 0; 88 | io.write(new ByteString(resource)); 89 | checkCursors(io, resource.length, resource.length * 2); 90 | } 91 | 92 | function read(io) { 93 | var position = io.position; 94 | var length = io.length; 95 | var bytes = io.read(resource.length); 96 | assert.strictEqual(bytes.length, resource.length); 97 | assert.strictEqual(bytes.decodeToString(), resource); 98 | checkCursors(io, position + resource.length, length); 99 | var bytes = new ByteArray(7); 100 | io.position = position; 101 | io.readInto(bytes); 102 | assert.equal(bytes.decodeToString(), resource.substring(0, 7)); 103 | checkCursors(io, position + 7, length); 104 | } 105 | 106 | exports.testMemoryStreamEmpty = function() { 107 | [new MemoryStream(), new MemoryStream(resource.length)].forEach(function(io) { 108 | checkRW(io, true, true); 109 | checkCursors(io, 0, 0); 110 | write(io); 111 | read(io); 112 | var length = io.length; 113 | io.position = 42; 114 | checkCursors(io, 42, length); 115 | io.length = 1; 116 | checkCursors(io, 1, 1); 117 | io.position = 42; 118 | checkCursors(io, 1, 1); 119 | io.close(); 120 | noWrite(io); 121 | }); 122 | }; 123 | 124 | exports.testMemoryStreamArray = function() { 125 | var io = new MemoryStream(new ByteArray(resource)); 126 | checkRW(io, true, true); 127 | checkCursors(io, 0, resource.length); 128 | write(io); 129 | read(io); 130 | var length = io.length; 131 | io.position = 42; 132 | checkCursors(io, 42, length); 133 | io.length = 1; 134 | checkCursors(io, 1, 1); 135 | io.position = 42; 136 | checkCursors(io, 1, 1); 137 | io.close(); 138 | noWrite(io); 139 | }; 140 | 141 | exports.testMemoryStreamString = function() { 142 | var io = new MemoryStream(new ByteString(resource)); 143 | checkRW(io, true, false); 144 | checkCursors(io, 0, resource.length); 145 | read(io); 146 | var length = io.length; 147 | io.position = 42; 148 | checkCursors(io, 42, length); 149 | io.length = 1; 150 | checkCursors(io, 42, length); 151 | noWrite(io); 152 | }; 153 | 154 | exports.testTextStreamRead = function() { 155 | var io = new TextStream(getStream('./lib/assert.js')); 156 | var text = io.read(); 157 | assert.strictEqual(text.length, resource.length); 158 | assert.strictEqual(text, resource); 159 | }; 160 | 161 | exports.testTextStreamForEach = function() { 162 | var resource = getContents('./lib/assert.js'); 163 | var io = new TextStream(getStream('./lib/assert.js')); 164 | var trimmed = resource.replace(/\n$/, ''); 165 | var lines = trimmed.split(/\n/g); 166 | var i = 0; 167 | io.forEach(function(read) { 168 | assert.strictEqual(read, lines[i]); 169 | i++; 170 | }); 171 | assert.strictEqual(i, lines.length); 172 | }; 173 | 174 | exports.testTextStreamReadLine = function() { 175 | var resource = getContents('./lib/assert.js'); 176 | var io = new TextStream(getStream('./lib/assert.js')); 177 | var trimmed = resource.replace(/\n$/, ''); 178 | var lines = trimmed.split(/\n/g); 179 | var last = resource === trimmed ? '' : '\n'; 180 | for (var i = 0; i < lines.length; i++) { 181 | var line = lines[i] + (i === lines.length - 1 ? last : '\n'); 182 | assert.strictEqual(io.readLine(), line); 183 | } 184 | assert.strictEqual(io.readLine(), ''); 185 | }; 186 | 187 | exports.testTextStreamReadLines = function() { 188 | var resource = getContents('./lib/assert.js'); 189 | var io = new TextStream(getStream('./lib/assert.js')); 190 | var read = io.readLines(); 191 | var trimmed = resource.replace(/\n$/, ''); 192 | var lines = trimmed.split(/\n/g); 193 | assert.strictEqual(read.length, lines.length); 194 | var last = resource === trimmed ? '' : '\n'; 195 | for (var i = 0; i < lines.length; i++) { 196 | var line = lines[i] + (i === lines.length - 1 ? last : '\n'); 197 | assert.strictEqual(read[i], line); 198 | } 199 | }; 200 | 201 | exports.testTextStreamWrite = function() { 202 | var resource = getContents('./lib/assert.js'); 203 | var stream = new MemoryStream(); 204 | var io = new TextStream(stream); 205 | io.write(resource); 206 | assert.strictEqual(stream.content.decodeToString().length, resource.length); 207 | assert.strictEqual(stream.content.decodeToString(), resource); 208 | }; 209 | 210 | exports.testTextStreamWriteLine = function() { 211 | var resource = getContents('./lib/assert.js'); 212 | var stream = new MemoryStream(); 213 | var io = new TextStream(stream); 214 | var trimmed = resource.replace(/\n$/, ''); 215 | trimmed.split(/\n/g).forEach(function(line) { 216 | io.writeLine(line); 217 | }); 218 | resource = trimmed + '\n'; 219 | assert.strictEqual(stream.content.decodeToString().length, resource.length); 220 | assert.strictEqual(stream.content.decodeToString(), resource); 221 | }; 222 | 223 | exports.testTextStreamWriteLines = function() { 224 | var resource = getContents('./lib/assert.js'); 225 | var stream = new MemoryStream(); 226 | var io = new TextStream(stream); 227 | var trimmed = resource.replace(/\n$/, ''); 228 | io.writeLines(trimmed.split(/\n/g)); 229 | resource = trimmed + '\n'; 230 | assert.strictEqual(stream.content.decodeToString().length, resource.length); 231 | assert.strictEqual(stream.content.decodeToString(), resource); 232 | }; 233 | 234 | if (require.main === module) { 235 | test.run(exports); 236 | } -------------------------------------------------------------------------------- /lib/httpclient.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview HTTP Client as defined in [CommonJS HTTP 3 | * Client/A](http://wiki.commonjs.org/wiki/HTTP_Client/A). 4 | */ 5 | var Fiber = require('fibers'); 6 | var Stream = require('./io').Stream; 7 | var zlib = require('zlib'); 8 | 9 | var protocols = { 10 | http:require('http'), 11 | https:require('https') 12 | }; 13 | var ports = { 14 | http:80, 15 | https:443 16 | }; 17 | Object.keys(protocols).forEach(function(key) { 18 | protocols[key].globalAgent.maxSockets = 1 / 0; 19 | }); 20 | 21 | exports.HttpClient = HttpClient; 22 | 23 | var URI_PARTS = ["source", "protocol", "authority", "domain", "port", "path", "directoryPath", "fileName", "query", "anchor"]; 24 | var URI_REGEX = /^(?:([^:\/?#.]+):)?(?:\/\/)?(([^:\/?#]*)(?::(\d*))?)((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[\?#]|$)))*\/?)?([^?#\/]*))?(?:\?([^#]*))?(?:#(.*))?/; 25 | /** 26 | * parseUri JS v0.1.1, by Steven Levithan Splits any 27 | * well-formed URI into the following parts (all are optional): 28 | * - source (since the exec method returns the entire match as key 0, we might as well use it) 29 | * - protocol (i.e., scheme) 30 | * - authority (includes both the domain and port) 31 | * - domain (i.e., host; can be an IP address) 32 | * - port 33 | * - path (includes both the directory path and filename) 34 | * - directoryPath (supports directories with periods, and without a trailing backslash) 35 | * - fileName 36 | * - query (does not include the leading question mark) 37 | * - anchor (i.e., fragment) 38 | * 39 | * @param {String} sourceUri 40 | * 41 | * @ignore 42 | */ 43 | function parseUri(sourceUri) { 44 | var uriParts = URI_REGEX.exec(sourceUri), uri = {}; 45 | 46 | for (var i = 0; i < URI_PARTS.length; i++) { 47 | uri[URI_PARTS[i]] = (uriParts[i] ? uriParts[i] : ""); 48 | } 49 | 50 | /* 51 | * Always end directoryPath with a trailing backslash if a path was present in 52 | * the source URI Note that a trailing backslash is NOT automatically inserted 53 | * within or appended to the "path" key 54 | */ 55 | if (uri.directoryPath.length > 0) { 56 | uri.directoryPath = uri.directoryPath.replace(/\/?$/, "/"); 57 | } 58 | 59 | return uri; 60 | } 61 | 62 | /** 63 | * If called as a simple function, then return a new HTTPClient(settings). Set 64 | * all protected members to the default values. If a settings object is 65 | * included, call this.set(settings). 66 | * 67 | * @constructor 68 | * @this {HttpClient} 69 | * @param {Object} [settings] the settings object 70 | */ 71 | function HttpClient(settings) { 72 | if (!(this instanceof HttpClient)) { 73 | return new HttpClient(settings); 74 | } 75 | /** @ignore */ 76 | this._opts = { 77 | method:"GET" 78 | }; 79 | /** @ignore */ 80 | this._headers = {}; 81 | /** @ignore */ 82 | this._body = []; 83 | if (settings) { 84 | this.setOptions(settings); 85 | } 86 | } 87 | 88 | /** 89 | * Set the body, headers, method, or url, or any combination thereof in the 90 | * settings object. Attribute validity is enforced. 91 | * 92 | * @param {Object} settings the settings object 93 | */ 94 | HttpClient.prototype.setOptions = function(settings) { 95 | for (var key in settings) { 96 | if (settings.hasOwnProperty(key)) { 97 | this.setOption(key, settings[key]); 98 | } 99 | } 100 | return this; 101 | }; 102 | 103 | /** 104 | * Set the body, headers, method, or url, or any combination thereof in the 105 | * settings object. Attribute validity is enforced. 106 | * 107 | * @param {String} key one of body, headers, method or url 108 | * @param {Object} val the value to set 109 | */ 110 | HttpClient.prototype.set = HttpClient.prototype.setOption = function(key, val) { 111 | switch (key) { 112 | case "headers": 113 | if (typeof val !== 'object') { 114 | throw new Error("HttpClient: headers must be a simple object."); 115 | } else { 116 | this.setHeaders(val); 117 | } 118 | break; 119 | case "body": 120 | if (typeof val.forEach !== 'function') { 121 | throw new Error("HttpClient: body must be iterable."); 122 | } else { 123 | this._body = val; 124 | } 125 | break; 126 | default: 127 | this._opts[key] = val; 128 | break; 129 | } 130 | return this; 131 | }; 132 | 133 | /** 134 | * Set a bunch of headers expressed as name-value pairs. 135 | * 136 | * @param headers headers to set 137 | */ 138 | HttpClient.prototype.setHeaders = function(headers) { 139 | for (var h in headers) { 140 | if (headers.hasOwnProperty(h)) { 141 | this.setHeader(h, headers[h]); 142 | } 143 | } 144 | return this; 145 | }; 146 | 147 | /** 148 | * Set a header on the header object in a case-insensitive manner. That is, if 149 | * the user sets "content-type", and then later sets "Content-Type", then the 150 | * first setting is lost. 151 | * 152 | * @param {String} key header name 153 | * @param {String} val header value 154 | */ 155 | HttpClient.prototype.setHeader = function(key, val) { 156 | for (var name in this._headers) { 157 | if (name.toLowerCase() === key.toLowerCase()) { 158 | delete this._headers[name]; 159 | } 160 | } 161 | this._headers[key] = val; 162 | return this; 163 | }; 164 | 165 | /** 166 | * Append data to the outgoing request, that is, to the iterable body object. 167 | * This method returns an error if a body was provided earlier via settings that 168 | * does not implement push. 169 | * 170 | * @param data {Binary} object to write 171 | */ 172 | HttpClient.prototype.write = function(data) { 173 | if (!this._body.push) { 174 | throw new Error("body does not have push()"); 175 | } else { 176 | this._body.push(data); 177 | if (this._headers.hasOwnProperty('Content-Length')) { 178 | this._headers['Content-Length'] += data.length; 179 | } 180 | } 181 | return this; 182 | }; 183 | 184 | /** 185 | * Open the connection to the URL using the method supplied. If the method or 186 | * url is missing, throw an error. After connecting, write() will have no 187 | * effect. 188 | */ 189 | HttpClient.prototype.connect = function() { 190 | var uri = parseUri(this._opts.url); 191 | var path = uri.path || '/'; 192 | if (uri.query) { 193 | path += '?' + uri.query; 194 | } 195 | var options = { 196 | method:this._opts.method, 197 | host:uri.domain, 198 | port:uri.port || ports[uri.protocol], 199 | path:path, 200 | headers:this._headers 201 | }; 202 | if (this._opts.hasOwnProperty('rejectUnauthorized')) { 203 | options.rejectUnauthorized = this._opts.rejectUnauthorized; 204 | } 205 | var that = this; 206 | var req = protocols[uri.protocol].request(options, function(response) { 207 | that._response = response; 208 | if (that._waiting) { 209 | var fiber = that._waiting; 210 | delete that._waiting; 211 | fiber.run(); 212 | } 213 | }).on('error', function(error) { 214 | that._response = error; 215 | if (that._waiting) { 216 | var fiber = that._waiting; 217 | delete that._waiting; 218 | fiber.run(); 219 | } 220 | }); 221 | if (this._opts.timeout) { 222 | req.setTimeout(this._opts.timeout, function() { 223 | req.abort(); 224 | }); 225 | } 226 | this._body.forEach(function(block) { 227 | req.write(block.buffer || block); 228 | }); 229 | req.end(); 230 | this._connected = true; 231 | return this; 232 | }; 233 | 234 | /** 235 | * Read the request and return a JSGI-style object consisting of 236 | * {status:Integer, headers:Object, body:Iterable}. Calling read() 237 | * does not block the application until the request is completed, but it does 238 | * open the input stream such that the data can be read. 239 | */ 240 | HttpClient.prototype.read = function() { 241 | if (!this._connected) { 242 | throw new Error("connect() not called yet"); 243 | } else { 244 | return new Response(this); 245 | } 246 | }; 247 | 248 | /** 249 | * Alias for .connect().read() 250 | */ 251 | HttpClient.prototype.finish = function() { 252 | return this.connect().read(); 253 | }; 254 | 255 | /** 256 | * @param {HttpClient} client 257 | * 258 | * @ignore 259 | */ 260 | function Response(client) { 261 | this.client = client; 262 | return this; 263 | } 264 | 265 | Object.defineProperty(Response.prototype, '_response', { 266 | get:function() { 267 | if (!this.client._response) { 268 | this.client._waiting = Fiber.current; 269 | Fiber.yield(); 270 | } 271 | var result = this.client._response; 272 | if (result instanceof Error) { 273 | throw new Error(result.message); 274 | } else { 275 | return result; 276 | } 277 | } 278 | }); 279 | 280 | Object.defineProperty(Response.prototype, 'status', { 281 | get:function() { 282 | return this._response.statusCode; 283 | } 284 | }); 285 | 286 | Object.defineProperty(Response.prototype, 'headers', { 287 | get:function() { 288 | return this._response.headers; 289 | } 290 | }); 291 | 292 | Object.defineProperty(Response.prototype, 'body', { 293 | get:function() { 294 | if (!this._body) { 295 | var response = this._response; 296 | if ((this.headers['content-encoding'] || [])[0] === 'gzip') { 297 | response = response.pipe(zlib.createGunzip()); 298 | response.writable = false; 299 | } 300 | this._body = new Stream(response); 301 | } 302 | return this._body; 303 | } 304 | }); -------------------------------------------------------------------------------- /test/binary/bytestring-tests.js: -------------------------------------------------------------------------------- 1 | var assert = require("../../lib/assert"); 2 | var binary = require("../../lib/binary"); 3 | var Binary = binary.Binary; 4 | var ByteString = binary.ByteString; 5 | var ByteArray = binary.ByteArray; 6 | 7 | exports.testConstructor = function() { 8 | var testArray = [1, 2, 3, 4]; 9 | 10 | // ByteString() 11 | // Construct an empty byte string. 12 | var b1 = new ByteString(); 13 | assert.ok(b1 instanceof Binary, "not instanceof Binary"); 14 | assert.ok(b1 instanceof ByteString, "not instanceof ByteString"); 15 | assert.strictEqual(0, b1.length); 16 | b1.length = 123; 17 | assert.strictEqual(0, b1.length); 18 | // ByteString(byteString) 19 | // Copies byteString. 20 | var b2 = new ByteString(new ByteString(testArray)); 21 | assert.strictEqual(testArray.length, b2.length); 22 | b2.length = 123; 23 | assert.strictEqual(testArray.length, b2.length); 24 | assert.strictEqual(1, b2.get(0)); 25 | assert.strictEqual(4, b2.get(3)); 26 | 27 | // ByteString(byteArray) 28 | // Use the contents of byteArray. 29 | var b2 = new ByteString(new ByteArray(testArray)); 30 | assert.strictEqual(testArray.length, b2.length); 31 | b2.length = 123; 32 | assert.strictEqual(testArray.length, b2.length); 33 | assert.strictEqual(1, b2.get(0)); // failing 34 | assert.strictEqual(4, b2.get(3)); 35 | 36 | // ByteString(arrayOfNumbers) 37 | // Use the numbers in arrayOfNumbers as the bytes. 38 | var b3 = new ByteString(testArray); 39 | assert.strictEqual(testArray.length, b3.length); 40 | b3.length = 123; 41 | assert.strictEqual(testArray.length, b3.length); 42 | assert.strictEqual(1, b3.get(0)); 43 | assert.strictEqual(4, b3.get(3)); 44 | }; 45 | 46 | // exports.testJoin = function() { 47 | // } 48 | 49 | exports.testToByteArray = function() { 50 | var b1 = new ByteString([1, 2, 3]), b2 = b1.toByteArray(); 51 | 52 | assert.ok(b2 instanceof ByteArray, "not instanceof ByteArray"); 53 | assert.strictEqual(b1.length, b2.length); 54 | assert.strictEqual(b1.get(0), b2.get(0)); 55 | assert.strictEqual(b1.get(2), b2.get(2)); 56 | }; 57 | 58 | exports.testToByteString = function() { 59 | var b1 = new ByteString([1, 2, 3]), b2 = b1.toByteString(); 60 | 61 | assert.strictEqual(b1.length, b2.length); 62 | assert.strictEqual(b1.get(0), b2.get(0)); 63 | assert.strictEqual(b1.get(2), b2.get(2)); 64 | }; 65 | 66 | exports.testToArray = function() { 67 | var testArray = [0, 1, 254, 255], b1 = new ByteString(testArray); 68 | var a1 = b1.toArray(); 69 | 70 | assert.strictEqual(testArray.length, a1.length); 71 | for (var i = 0; i < testArray.length; i++) 72 | assert.strictEqual(testArray[i], a1[i]); 73 | }; 74 | 75 | exports.testToString = function() { 76 | // the format of the resulting string isn't specified, but it shouldn't be 77 | // the decoded string 78 | var testString = '', testArray = []; 79 | for (var i = 0; i < 128; i++) { 80 | testString += 'A'; 81 | testArray.push(65); 82 | } 83 | 84 | var resultString = new ByteString(testArray).toString(); 85 | 86 | assert.ok(resultString.length < 100); 87 | assert.ok(resultString !== testString); 88 | }; 89 | 90 | exports.testIndexOf = function() { 91 | var b1 = new ByteString([0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); 92 | 93 | assert.strictEqual(-1, b1.indexOf(-1)); 94 | 95 | assert.strictEqual(0, b1.indexOf(0)); 96 | assert.strictEqual(5, b1.indexOf(5)); 97 | assert.strictEqual(-1, b1.indexOf(12)); 98 | 99 | assert.strictEqual(6, b1.indexOf(0, 6)); 100 | assert.strictEqual(11, b1.indexOf(5, 6)); 101 | assert.strictEqual(-1, b1.indexOf(12, 6)); 102 | 103 | assert.strictEqual(0, b1.indexOf(0, 0, 3)); 104 | assert.strictEqual(-1, b1.indexOf(5, 0, 3)); 105 | assert.strictEqual(-1, b1.indexOf(12, 0, 3)); 106 | }; 107 | 108 | exports.testLastIndexOf = function() { 109 | var b1 = new ByteString([0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); 110 | 111 | assert.strictEqual(-1, b1.lastIndexOf(-1)); 112 | 113 | assert.strictEqual(6, b1.lastIndexOf(0)); 114 | assert.strictEqual(11, b1.lastIndexOf(5)); 115 | assert.strictEqual(-1, b1.lastIndexOf(12)); 116 | 117 | assert.strictEqual(0, b1.lastIndexOf(0, 0, 6)); 118 | assert.strictEqual(5, b1.lastIndexOf(5, 0, 6)); 119 | assert.strictEqual(-1, b1.lastIndexOf(12, 0, 6)); 120 | 121 | assert.strictEqual(6, b1.lastIndexOf(0, 6, 9)); 122 | assert.strictEqual(-1, b1.lastIndexOf(5, 6, 9)); 123 | assert.strictEqual(-1, b1.lastIndexOf(12, 6, 9)); 124 | }; 125 | 126 | exports.testCharCodeAt = function() { 127 | var b1 = new ByteString([0, 1, 2, 3, 4, 255]); 128 | 129 | assert.ok(isNaN(b1.charCodeAt(-1))); 130 | assert.strictEqual(0, b1.charCodeAt(0)); 131 | assert.strictEqual(255, b1.charCodeAt(5)); 132 | assert.ok(isNaN(b1.charCodeAt(6))); 133 | }; 134 | 135 | // identical to charCodeAt, test anyway? 136 | exports.testGet = function() { 137 | var b1 = new ByteString([0, 1, 2, 3, 4, 255]); 138 | 139 | assert.ok(isNaN(b1.get(-1))); 140 | assert.strictEqual(0, b1.get(0)); 141 | assert.strictEqual(255, b1.get(5)); 142 | assert.ok(isNaN(b1.get(6))); 143 | }; 144 | 145 | exports.testByteAt = function() { 146 | var b1 = new ByteString([0, 1, 2, 3, 4, 255]), b2; 147 | b2 = b1.byteAt(-1); 148 | assert.strictEqual(0, b2.length); 149 | b2 = b1.byteAt(0); 150 | assert.strictEqual(1, b2.length); 151 | assert.strictEqual(0, b2.get(0)); 152 | b2 = b1.byteAt(5); 153 | assert.strictEqual(1, b2.length); 154 | assert.strictEqual(255, b2.get(0)); 155 | b2 = b1.byteAt(6); 156 | assert.strictEqual(0, b2.length); 157 | }; 158 | 159 | // identical to byteAt, test anyway? 160 | exports.testCharAt = function() { 161 | var b1 = new ByteString([0, 1, 2, 3, 4, 255]), b2; 162 | 163 | b2 = b1.charAt(-1); 164 | assert.strictEqual(0, b2.length); 165 | b2 = b1.charAt(0); 166 | assert.strictEqual(1, b2.length); 167 | assert.strictEqual(0, b2.get(0)); 168 | b2 = b1.charAt(5); 169 | assert.strictEqual(1, b2.length); 170 | assert.strictEqual(255, b2.get(0)); 171 | b2 = b1.charAt(6); 172 | assert.strictEqual(0, b2.length); 173 | }; 174 | 175 | exports.testSplit = function() { 176 | var b1 = new ByteString([0, 1, 2, 3, 4, 5]), a1; 177 | 178 | a1 = b1.split([]); 179 | assert.strictEqual(1, a1.length); 180 | assert.ok(a1[0] instanceof ByteString); 181 | assert.strictEqual(6, a1[0].length); 182 | assert.strictEqual(0, a1[0].get(0)); 183 | assert.strictEqual(5, a1[0].get(5)); 184 | 185 | a1 = b1.split([2]); 186 | assert.strictEqual(2, a1.length); 187 | assert.ok(a1[0] instanceof ByteString); 188 | assert.strictEqual(2, a1[0].length); 189 | assert.strictEqual(0, a1[0].get(0)); 190 | assert.strictEqual(1, a1[0].get(1)); 191 | assert.strictEqual(3, a1[1].length); 192 | assert.strictEqual(3, a1[1].get(0)); 193 | assert.strictEqual(5, a1[1].get(2)); 194 | 195 | a1 = b1.split([2], { 196 | includeDelimiter:true 197 | }); 198 | assert.strictEqual(3, a1.length); 199 | assert.ok(a1[0] instanceof ByteString); 200 | assert.strictEqual(2, a1[0].length); 201 | assert.strictEqual(0, a1[0].get(0)); 202 | assert.strictEqual(1, a1[0].get(1)); 203 | assert.strictEqual(1, a1[1].length); 204 | assert.strictEqual(2, a1[1].get(0)); 205 | assert.strictEqual(3, a1[2].length); 206 | assert.strictEqual(3, a1[2].get(0)); 207 | assert.strictEqual(5, a1[2].get(2)); 208 | 209 | a1 = b1.split(new ByteString([2, 3])); 210 | assert.strictEqual(2, a1.length); 211 | assert.ok(a1[0] instanceof ByteString); 212 | assert.strictEqual(2, a1[0].length); 213 | assert.strictEqual(0, a1[0].get(0)); 214 | assert.strictEqual(1, a1[0].get(1)); 215 | assert.strictEqual(2, a1[1].length); 216 | assert.strictEqual(4, a1[1].get(0)); 217 | assert.strictEqual(5, a1[1].get(1)); 218 | }; 219 | 220 | exports.testConcat = function() { 221 | var a = []; 222 | var b = new ByteString(); 223 | 224 | var a1 = [1, 2, 3]; 225 | var b1 = b.concat(new ByteString(a1)); 226 | a1 = a.concat(a1); 227 | assert.strictEqual(a1.length, b1.length); 228 | assert.deepEqual(a1, b1.toArray()); 229 | 230 | var a2 = [4, 5, 6]; 231 | var b2 = b1.concat(new ByteString(a2)); 232 | a2 = a1.concat(a2); 233 | assert.strictEqual(a2.length, b2.length); 234 | assert.deepEqual(a2, b2.toArray()); 235 | 236 | var a3 = a2.concat(a, a1, a2, [], []); 237 | var b3 = b2.concat(b, b1, b2, new ByteString(), new ByteArray()); 238 | assert.strictEqual(a3.length, b3.length); 239 | assert.deepEqual(a3, b3.toArray()); 240 | }; 241 | 242 | exports.testSlice = function() { 243 | var b1 = new ByteString([0, 1, 2, 3, 4, 5]), b2; 244 | 245 | b2 = b1.slice(); 246 | assert.strictEqual(6, b2.length); 247 | assert.strictEqual(0, b2.get(0)); 248 | assert.strictEqual(5, b2.get(5)); 249 | 250 | b2 = b1.slice(0); 251 | assert.strictEqual(6, b2.length); 252 | assert.strictEqual(0, b2.get(0)); 253 | assert.strictEqual(5, b2.get(5)); 254 | 255 | b2 = b1.slice(1, 4); 256 | assert.strictEqual(3, b2.length); 257 | assert.strictEqual(1, b2.get(0)); 258 | assert.strictEqual(3, b2.get(2)); 259 | 260 | b2 = b1.slice(0, -1); 261 | assert.strictEqual(5, b2.length); 262 | assert.strictEqual(0, b2.get(0)); 263 | assert.strictEqual(4, b2.get(4)); 264 | 265 | b2 = b1.slice(-3, -1); 266 | 267 | assert.strictEqual(2, b2.length); 268 | assert.strictEqual(3, b2.get(0)); 269 | assert.strictEqual(4, b2.get(1)); 270 | 271 | b2 = b1.slice(9, 10); 272 | assert.strictEqual(0, b2.length); 273 | }; 274 | 275 | exports.testNewless = function() { 276 | assert.strictEqual(1, ByteString([0]).length); 277 | // assert.strictEqual(2, ByteString([0, 1], 0, 2).length); 278 | }; 279 | 280 | if (require.main === module) { 281 | require("../../lib/test").run(exports); 282 | } -------------------------------------------------------------------------------- /test/binary/bytearray-tests.js: -------------------------------------------------------------------------------- 1 | var assert = require("../../lib/assert"); 2 | var binary = require("../../lib/binary"); 3 | var Binary = binary.Binary; 4 | var ByteString = binary.ByteString; 5 | var ByteArray = binary.ByteArray; 6 | 7 | exports.testConstructor = function() { 8 | var testArray = [1, 2, 3, 4], b; 9 | 10 | // ByteArray() 11 | // New, empty ByteArray. 12 | b = new ByteArray(); 13 | // assert.isTrue(b instanceof Binary, "not instanceof Binary"); 14 | assert.ok(b instanceof ByteArray, "not instanceof ByteArray"); 15 | assert.strictEqual(0, b.length); 16 | b.length = 123; 17 | assert.strictEqual(123, b.length); 18 | assert.strictEqual(0, b.get(4)); 19 | 20 | // ByteArray(length) 21 | // New ByteArray filled with length zero bytes. 22 | b = new ByteArray(10); 23 | assert.strictEqual(10, b.length); 24 | for (var i = 0; i < 10; i++) 25 | assert.strictEqual(0, b.get(i)); 26 | assert.ok(isNaN(b.get(10))); 27 | b.length = 234; 28 | assert.strictEqual(234, b.length); 29 | assert.strictEqual(0, b.get(10)); 30 | assert.strictEqual(0, b.get(233)); 31 | assert.ok(isNaN(b.get(234))); 32 | 33 | // ByteArray(byteString) 34 | // Copy contents of byteString. 35 | b = new ByteArray(new ByteString(testArray)); 36 | assert.strictEqual(testArray.length, b.length); 37 | b.length = 345; 38 | assert.strictEqual(345, b.length); 39 | assert.strictEqual(1, b.get(0)); 40 | assert.strictEqual(4, b.get(3)); 41 | assert.strictEqual(0, b.get(4)); 42 | 43 | // ByteArray(byteArray) 44 | // Copy byteArray. 45 | b = new ByteArray(new ByteArray(testArray)); 46 | assert.strictEqual(testArray.length, b.length); 47 | b.length = 456; 48 | assert.strictEqual(456, b.length); 49 | assert.strictEqual(1, b.get(0)); 50 | assert.strictEqual(4, b.get(3)); 51 | assert.strictEqual(0, b.get(4)); 52 | 53 | // ByteArray(arrayOfBytes) 54 | // Use numbers in arrayOfBytes as contents. 55 | b = new ByteArray(testArray); 56 | assert.strictEqual(testArray.length, b.length); 57 | b.length = 567; 58 | assert.strictEqual(567, b.length); 59 | assert.strictEqual(1, b.get(0)); 60 | assert.strictEqual(4, b.get(3)); 61 | assert.strictEqual(0, b.get(4)); 62 | }; 63 | 64 | exports.testResizing = function() { 65 | var b1 = new ByteArray([0, 1, 2, 3, 4, 5, 6]); 66 | assert.strictEqual(7, b1.length); 67 | assert.ok(isNaN(b1.get(7))); 68 | 69 | b1.length = 10; 70 | assert.strictEqual(10, b1.length, "Length should change to 10"); 71 | assert.strictEqual(5, b1.get(5)); 72 | assert.strictEqual(0, b1.get(7)); 73 | 74 | b1.length = 3; 75 | assert.strictEqual(3, b1.length, "Length should change to 3"); 76 | assert.strictEqual(0, b1.get(0)); 77 | assert.ok(isNaN(b1.get(3))); 78 | 79 | b1.length = 9; 80 | assert.strictEqual(9, b1.length, "Length should change to 9"); 81 | assert.strictEqual(0, b1.get(0)); 82 | assert.strictEqual(0, b1.get(4)); 83 | }; 84 | 85 | exports.testToByteArray = function() { 86 | var b1 = new ByteArray([1, 2, 3]), b2 = b1.toByteArray(); 87 | 88 | assert.ok(b2 instanceof ByteArray, "not instanceof ByteArray"); 89 | assert.strictEqual(b1.length, b2.length); 90 | assert.strictEqual(b1.get(0), b2.get(0)); 91 | assert.strictEqual(b1.get(2), b2.get(2)); 92 | 93 | assert.strictEqual(1, b1.get(0)); 94 | assert.strictEqual(1, b2.get(0)); 95 | 96 | b1.set(0, 10); 97 | 98 | assert.strictEqual(10, b1.get(0)); 99 | assert.strictEqual(1, b2.get(0)); 100 | }; 101 | 102 | exports.testToByteString = function() { 103 | var b1 = new ByteArray([1, 2, 3]), b2 = b1.toByteString(); 104 | 105 | assert.strictEqual(b1.length, b2.length); 106 | assert.strictEqual(b1.get(0), b2.get(0)); 107 | assert.strictEqual(b1.get(2), b2.get(2)); 108 | 109 | assert.strictEqual(1, b1.get(0)); 110 | assert.strictEqual(1, b2.get(0)); 111 | 112 | b1.set(0, 10); 113 | 114 | assert.strictEqual(10, b1.get(0)); 115 | assert.strictEqual(1, b2.get(0)); 116 | }; 117 | 118 | exports.testToArray = function() { 119 | var testArray = [0, 1, 254, 255], b1 = new ByteArray(testArray), a1 = b1 120 | .toArray(); 121 | 122 | assert.strictEqual(testArray.length, a1.length); 123 | for (var i = 0; i < testArray.length; i++) 124 | assert.strictEqual(testArray[i], a1[i]); 125 | }; 126 | 127 | exports.testToString = function() { 128 | // the format of the resulting string isn't specified, but it shouldn't be the 129 | // decoded string 130 | var testString = '', testArray = []; 131 | for (var i = 0; i < 128; i++) { 132 | testString += 'A'; 133 | testArray.push(65); 134 | } 135 | 136 | var resultString = new ByteArray(testArray).toString(); 137 | 138 | assert.ok(resultString.length < 100); 139 | assert.ok(resultString !== testString); 140 | }; 141 | 142 | exports.testIndexOf = function() { 143 | var b1 = new ByteArray([0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); 144 | 145 | assert.strictEqual(-1, b1.indexOf(-1)); 146 | 147 | assert.strictEqual(0, b1.indexOf(0)); 148 | assert.strictEqual(5, b1.indexOf(5)); 149 | assert.strictEqual(-1, b1.indexOf(12)); 150 | 151 | assert.strictEqual(6, b1.indexOf(0, 6)); 152 | assert.strictEqual(11, b1.indexOf(5, 6)); 153 | assert.strictEqual(-1, b1.indexOf(12, 6)); 154 | 155 | assert.strictEqual(0, b1.indexOf(0, 0, 3)); 156 | assert.strictEqual(-1, b1.indexOf(5, 0, 3)); 157 | assert.strictEqual(-1, b1.indexOf(12, 0, 3)); 158 | }; 159 | 160 | exports.testLastIndexOf = function() { 161 | var b1 = new ByteArray([0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]); 162 | 163 | assert.strictEqual(-1, b1.lastIndexOf(-1)); 164 | 165 | assert.strictEqual(6, b1.lastIndexOf(0)); 166 | assert.strictEqual(11, b1.lastIndexOf(5)); 167 | assert.strictEqual(-1, b1.lastIndexOf(12)); 168 | 169 | assert.strictEqual(0, b1.lastIndexOf(0, 0, 6)); 170 | assert.strictEqual(5, b1.lastIndexOf(5, 0, 6)); 171 | assert.strictEqual(-1, b1.lastIndexOf(12, 0, 6)); 172 | 173 | assert.strictEqual(6, b1.lastIndexOf(0, 6, 9)); 174 | assert.strictEqual(-1, b1.lastIndexOf(5, 6, 9)); 175 | assert.strictEqual(-1, b1.lastIndexOf(12, 6, 9)); 176 | }; 177 | 178 | exports.testReverse = function() { 179 | var testArray = [0, 1, 2, 3, 4, 5, 6]; 180 | 181 | var b1 = new ByteArray(testArray), b2 = b1.reverse(); 182 | 183 | assert.strictEqual(b1, b2); 184 | assert.strictEqual(b1.length, b2.length); 185 | for (var i = 0; i < testArray.length; i++) 186 | assert.strictEqual(testArray[i], b2.get(testArray.length - i - 1)); 187 | 188 | testArray = [0, 1, 2, 3, 4, 5, 6, 7]; 189 | 190 | b1 = new ByteArray(testArray); 191 | b2 = b1.reverse(); 192 | 193 | assert.strictEqual(b1, b2); 194 | assert.strictEqual(b1.length, b2.length); 195 | for (var i = 0; i < testArray.length; i++) 196 | assert.strictEqual(testArray[i], b2.get(testArray.length - i - 1)); 197 | }; 198 | 199 | exports.testSort = function() { 200 | var testArray = []; 201 | for (var i = 0; i < 1000; i++) 202 | testArray.push(Math.floor(Math.random() * 256)); 203 | 204 | var a = new ByteArray(testArray); 205 | a.sort(); 206 | 207 | for (var i = 1; i < a.length; i++) 208 | assert.ok(a.get(i - 1) <= a.get(i), "index=" + i + "(" + a.get(i - 1) + "," 209 | + a.get(i) + ")"); 210 | }; 211 | 212 | exports.testSortCustom = function() { 213 | var testArray = []; 214 | for (var i = 0; i < 1000; i++) 215 | testArray.push(Math.floor(Math.random() * 256)); 216 | 217 | var a = new ByteArray(testArray); 218 | a.sort(function(o1, o2) { 219 | return o2 - o1; 220 | }); 221 | 222 | for (var i = 1; i < a.length; i++) 223 | assert.ok(a.get(i - 1) >= a.get(i), "index=" + i + "(" + a.get(i - 1) + "," 224 | + a.get(i) + ")"); 225 | }; 226 | 227 | exports.testSplit = function() { 228 | var b1 = new ByteArray([0, 1, 2, 3, 4, 5]), a1; 229 | 230 | a1 = b1.split([]); 231 | assert.strictEqual(1, a1.length); 232 | assert.ok(a1[0] instanceof ByteArray); 233 | assert.strictEqual(6, a1[0].length); 234 | assert.strictEqual(0, a1[0].get(0)); 235 | assert.strictEqual(5, a1[0].get(5)); 236 | 237 | a1 = b1.split([2]); 238 | assert.strictEqual(2, a1.length); 239 | assert.ok(a1[0] instanceof ByteArray); 240 | assert.strictEqual(2, a1[0].length); 241 | assert.strictEqual(0, a1[0].get(0)); 242 | assert.strictEqual(1, a1[0].get(1)); 243 | assert.strictEqual(3, a1[1].length); 244 | assert.strictEqual(3, a1[1].get(0)); 245 | assert.strictEqual(5, a1[1].get(2)); 246 | 247 | a1 = b1.split([2], { 248 | includeDelimiter:true 249 | }); 250 | assert.strictEqual(3, a1.length); 251 | assert.ok(a1[0] instanceof ByteArray); 252 | assert.strictEqual(2, a1[0].length); 253 | assert.strictEqual(0, a1[0].get(0)); 254 | assert.strictEqual(1, a1[0].get(1)); 255 | assert.strictEqual(1, a1[1].length); 256 | assert.strictEqual(2, a1[1].get(0)); 257 | assert.strictEqual(3, a1[2].length); 258 | assert.strictEqual(3, a1[2].get(0)); 259 | assert.strictEqual(5, a1[2].get(2)); 260 | 261 | a1 = b1.split(new ByteArray([2, 3])); 262 | assert.strictEqual(2, a1.length); 263 | assert.ok(a1[0] instanceof ByteArray); 264 | assert.strictEqual(2, a1[0].length); 265 | assert.strictEqual(0, a1[0].get(0)); 266 | assert.strictEqual(1, a1[0].get(1)); 267 | assert.strictEqual(2, a1[1].length); 268 | assert.strictEqual(4, a1[1].get(0)); 269 | assert.strictEqual(5, a1[1].get(1)); 270 | }; 271 | 272 | exports.testForEach = function() { 273 | var b = new ByteArray([2, 3, 4, 5]), log = [], item; 274 | 275 | var thisObj = {}; 276 | 277 | b.forEach(function() { 278 | log.push({ 279 | thisObj:this, 280 | args:arguments 281 | }); 282 | }, thisObj); 283 | 284 | assert.strictEqual(4, log.length, "block called for each item"); 285 | 286 | item = log[0]; 287 | assert.ok(thisObj === item.thisObj, "block called with correct thisObj"); 288 | assert.strictEqual(3, item.args.length, "block called with three args"); 289 | assert.strictEqual(b.get(0), item.args[0], "block called with correct item 0"); 290 | 291 | item = log[3]; 292 | assert.strictEqual(b.get(3), item.args[0], "block called with correct item 3"); 293 | 294 | }; 295 | 296 | exports.testConcat = function() { 297 | var a = []; 298 | var b = new ByteArray(); 299 | 300 | var a1 = [1, 2, 3]; 301 | var b1 = b.concat(new ByteArray(a1)); 302 | a1 = a.concat(a1); 303 | assert.strictEqual(a1.length, b1.length); 304 | assert.deepEqual(a1, b1.toArray()); 305 | 306 | var a2 = [4, 5, 6]; 307 | var b2 = b1.concat(new ByteString(a2)); 308 | a2 = a1.concat(a2); 309 | assert.strictEqual(a2.length, b2.length); 310 | assert.deepEqual(a2, b2.toArray()); 311 | 312 | var a3 = a2.concat(a, a1, a2, [], []); 313 | var b3 = b2.concat(b, b1, b2, new ByteString(), new ByteArray()); 314 | assert.strictEqual(a3.length, b3.length); 315 | assert.deepEqual(a3, b3.toArray()); 316 | }; 317 | 318 | exports.testSlice = function() { 319 | [ 320 | [], 321 | [3, 6], 322 | [3, 4], 323 | [3, 3], 324 | [3, 2], 325 | [7], 326 | [3, -2], 327 | [-2], 328 | [50], 329 | [-100, 100], 330 | ["foo"], 331 | ["foo", "bar"], 332 | ["foo", 4], 333 | [3, "bar"] 334 | ].forEach(function(test) { 335 | var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; 336 | var b = new ByteArray(a); 337 | var s = a.slice.apply(a, test); 338 | var t = b.slice.apply(b, test); 339 | assert.ok(t instanceof ByteArray, test); 340 | assert.strictEqual(s.length, t.length, test); 341 | assert.deepEqual(s, t.toArray(), test); 342 | }); 343 | }; 344 | 345 | exports.testSplice = function() { 346 | var c = [42, 47]; 347 | var tests = []; 348 | [ 349 | [], 350 | [3, 6], 351 | [3, 4], 352 | [3, 3], 353 | [3, 2], 354 | [7], 355 | [3, -2], 356 | [-2], 357 | [50], 358 | [-100, 100], 359 | ["foo"], 360 | ["foo", "bar"], 361 | ["foo", 4], 362 | [3, "bar"] 363 | ].forEach(function(test) { 364 | tests.push(test.concat()); 365 | test[0] |= 0; 366 | test[1] |= 0; 367 | tests.push(test.concat(c)); 368 | }); 369 | tests.forEach(function(test) { 370 | var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; 371 | var b = new ByteArray(a); 372 | var s = a.splice.apply(a, test); 373 | var t = b.splice.apply(b, test); 374 | assert.strictEqual(a.length, b.length, 'modified: ' + test); 375 | assert.deepEqual(a, b.toArray(), 'modified: ' + test); 376 | assert.ok(t instanceof ByteArray, test); 377 | assert.strictEqual(s.length, t.length, 'removed: ' + test); 378 | assert.deepEqual(s, t.toArray(), 'removed: ' + test); 379 | }); 380 | }; 381 | 382 | if (require.main === module) { 383 | require("../../lib/test").run(exports); 384 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Common Node 2 | 3 | This package implements a number of [CommonJS](http://www.commonjs.org) 4 | proposals on top of [Node.js](http://nodejs.org) using 5 | [node-fibers](https://github.com/laverdet/node-fibers). Fibers are used to emulate multi-threading within a single process, allowing one to use a synchronous programming style and as a result: 6 | 7 | * write fast CRUD webapps that run on Node.js without messy callbacks and with very little overhead 8 | * run [webapps, middleware and libraries](https://github.com/olegp/common-node/wiki) written for [RingoJS](http://ringojs.org), [Narwhal](http://narwhaljs.org/), [Akshell](http://www.akshell.com), [Erbix](http://www.erbix.com/), [Wakanda](http://www.wakanda.org/), [v8cgi](http://code.google.com/p/v8cgi/) and [other implementations](http://wiki.commonjs.org/wiki/Implementations) 9 | * mix & match synchronous/asynchronous styles and use the best tool for the job by writing maintainable business logic in a synchronous manner 10 | * write concise, legible shell scripts 11 | 12 | For an example of a production app using Common Node, check out [StartHQ](https://starthq.com). 13 | 14 | If you have a spare 20 minutes, you can also check out [this presentation](http://www.slideshare.net/olegp/server-side-javascript-going-all-the-way) (audio included). 15 | If you have any questions about Common Node, or [`mongo-sync`](https://github.com/olegp/mongo-sync), [`stick`](https://github.com/olegp/stick) and other libraries built on top of it, please post them to the [Common Node mailing list](https://groups.google.com/forum/#!forum/common-node) or IRC, channel [#common-node](irc://irc.freenode.net/common-node) on Freenode. 16 | 17 | For a real world application using Common Node, take a look at the [Minimal CMS](https://github.com/olegp/mcms). 18 | 19 | The following modules are included: 20 | 21 | * assert - [Unit Testing/1.0](http://wiki.commonjs.org/wiki/Unit_Testing/1.0) (available in Node) & a suite for running tests 22 | * console - [Console](http://wiki.commonjs.org/wiki/Console) - partial implementation available in Node 23 | * system - [System/1.0](http://wiki.commonjs.org/wiki/System/1.0) 24 | * binary - [Binary/B](http://wiki.commonjs.org/wiki/Binary/B) 25 | * io - [IO/A](http://wiki.commonjs.org/wiki/IO/A), including a `ByteArray` backed `MemoryStream` 26 | * fs - [Filesystem/A](http://wiki.commonjs.org/wiki/Filesystem/A) 27 | * httpserver - [JSGI 0.3](http://wiki.commonjs.org/wiki/JSGI/Level0/A/Draft2) compatible HTTP server 28 | * httpclient - [HTTP Client/A](http://wiki.commonjs.org/wiki/HTTP_Client/A) 29 | * ringo/httpclient - [RingoJS HttpClient](http://ringojs.org/api/master/ringo/httpclient/) wrapper for `httpclient` 30 | * subprocess - methods for launching subprocesses modeled after Ringo 31 | * socket - [Sockets/A](http://wiki.commonjs.org/wiki/Sockets/A) (work in progress) 32 | 33 | ### Installation 34 | 35 | If you don't already have them, [install Node version 0.10.0 or later](https://github.com/joyent/node/wiki/Installation) (previous versions for Node going back as far as 0.4.0 are also available in NPM) and [Node Package Manager](http://npmjs.org). It's also highly recommended that you have your $NODE_PATH variable [set correctly](https://github.com/olegp/common-node/issues/20). 36 | 37 | Install `common-node` as a global package: 38 | 39 | 40 | ```bash 41 | [sudo] npm -g install common-node 42 | ``` 43 | 44 | Run the "Hello World" example: 45 | 46 | 47 | ```bash 48 | common-node $NODE_PATH/common-node/examples/hello.js 49 | ``` 50 | 51 | You shouldn't see any output. To test that it's working, make an HTTP request from another prompt: 52 | 53 | 54 | ```bash 55 | curl http://localhost:8080/ 56 | ``` 57 | 58 | Note: by default the port used is 8080 - to change this add the port number you want as the last command line argument. 59 | 60 | ### Examples 61 | 62 | A number of examples are available in [common-node/examples](https://github.com/olegp/common-node/tree/master/examples): 63 | 64 | * `hello.js` - Hello World webapp using JSGI to return a simple text response 65 | * `static.js` - streams a static file as a response 66 | * `http.js` - makes an HTTP request to another server and returns the result in the response 67 | * `sleep.js` - sleeps for one second before returning a response 68 | * `spawn.js` - spawns a new fiber which prints to stdout ten seconds after processing a request 69 | * `twitter.js` - an example of using Twitter's streaming API, uses HttpClient & TextStream for reading one line at a time 70 | * `chat.js` - Telnet chat server, compare this to an [async implementation](http://pastebin.com/Rhbbr6Tf) 71 | 72 | For more usage examples, please refer to the tests in the [common-node/test](https://github.com/olegp/common-node/tree/master/test) directory. 73 | 74 | If you're looking for an Express like framework that works with Common Node, take a look at [Stick](https://github.com/olegp/stick/). There's also the [Notes example webapp](https://github.com/olegp/notes) which uses Stick and a MongoDB data store. 75 | 76 | Common Node also works well with [CoffeeScript](http://coffeescript.org/), check out [this example](https://gist.github.com/1447709). 77 | 78 | ### Documentation 79 | 80 | The API reference is available at 81 | 82 | To generate the documentation, install RingoJS and run: 83 | 84 | 85 | ```bash 86 | ringo-doc -n "Common Node" -s ./lib -d ./doc --file-urls 87 | ``` 88 | 89 | ### Tests 90 | 91 | [![Build Status](https://travis-ci.org/olegp/common-node.svg?branch=master)](https://travis-ci.org/olegp/common-node) 92 | 93 | To run the unit tests run: 94 | 95 | 96 | ```bash 97 | node test/all.js 98 | ``` 99 | 100 | You can also run individual tests or sets of tests, for example: 101 | 102 | 103 | ```bash 104 | node test/io.js 105 | node test/fs-base/all.js 106 | ``` 107 | 108 | ### Benchmarks 109 | 110 | Although `common-node` is optimized for development efficiency rather than performance, 111 | a number of benchmarks are included in [common-node/benchmarks](https://github.com/olegp/common-node/tree/master/benchmarks). 112 | A `common-node` version, an asynchronous Node version using Connect & a RingoJS version of each benchmark is provided. 113 | The scripts are based on the [Ringo/Node benchmark scripts](https://github.com/hns/ringo-node-benchmark), with a couple of additions. 114 | 115 | The benchmarks have the following dependencies: 116 | 117 | * `connect` for Node which can be installed via npm 118 | * `ringo` which can be installed by following the instructions in the [RingoJS repository](https://github.com/ringo/ringojs) 119 | * `ab` - installed with `sudo apt-get install apache2-utils` on Debian/Ubuntu 120 | * `gnuplot` - installed with `sudo apt-get install gnuplot-nox` 121 | 122 | To run them and generate graphs, execute the following command: 123 | 124 | 125 | ```bash 126 | cd benchmarks/; ./run; ./graph 127 | ``` 128 | 129 | This will generate PNG images of the graphs in `benchmarks/results/graphs/`. Some results are provided below: 130 | 131 | * [buffer-alloc](http://olegp.github.com/common-node/graphs/buffer-alloc.png) - creates a number of byte arrays per request; the difference in performance compared to Node is due to the [array being cleared](https://github.com/olegp/common-node/issues/6) 132 | * [hello-world](http://olegp.github.com/common-node/graphs/hello-world.png) - returns a dynamically generated string 133 | * [no-alloc](http://olegp.github.com/common-node/graphs/no-alloc.png) 134 | * [parse-json](http://olegp.github.com/common-node/graphs/parse-json.png) 135 | * [set-timeout](http://olegp.github.com/common-node/graphs/set-timeout.png) - sleep for 100ms before returning a response 136 | * [static-file](http://olegp.github.com/common-node/graphs/static-file.png) - returns a file served from the file system 137 | * [string-alloc](http://olegp.github.com/common-node/graphs/string-alloc.png) 138 | 139 | As you can see from the results and given no profiling or optimization work has been done so far, there's room for improvement. 140 | Any patches or suggestions on how to improve performance would be greatly appreciated. 141 | 142 | ### Embedding 143 | 144 | You can use Common Node without invoking the `common-node` binary. There are two ways to do this: 145 | 146 | * the bootstrapped approach allows you to run your Common Node code as is, but prevents you from accessing core Node modules such as `fs` (by over-riding that with Common Node's `fs` module instead) 147 | * the mixed mode approach allows you to use Commo Node along side your existing Node specific code 148 | 149 | #### Bootstrapped 150 | 151 | To bootstrap Common Node & assuming your app's entry point is in `main.js`, simply add an `index.js` with the following contents to the same directory: 152 | 153 | 154 | ```js 155 | require('common-node').run('./main'); 156 | ``` 157 | 158 | Then, instead of starting your app with `common-node main.js`, run `node index.js`. 159 | 160 | #### Mixed Mode 161 | 162 | To use Common Node alongside your existing Node code, you will need to: 163 | 164 | * run your app with `node` instead of `common-node` 165 | * change the way in which you require modules from `var io = require('io');` to `var io = require('common-node').io;` or update your NODE_PATH to include `common-node/lib` (see `bin/common-node` for an example) 166 | * make sure any synchronous code is inside a fiber that has been created with `spawn` (in the example below each call of exports.app runs in a new fiber) 167 | 168 | For example the following modified version of `examples/http.js` can be run directly via `node http.js` 169 | 170 | 171 | ```js 172 | var HttpClient = require('common-node').httpclient.HttpClient; 173 | 174 | exports.app = function(request) { 175 | return { 176 | status: 200, 177 | headers: {}, 178 | body: new HttpClient({ 179 | url: 'http://google.com' 180 | }).finish().body 181 | }; 182 | }; 183 | 184 | if (require.main == module) { 185 | require('common-node').run(exports); 186 | } 187 | ``` 188 | 189 | ### Contributing 190 | 191 | To contribute to this project, you can start by trying to run the tests on your system and posting any failures on the issue tracker. It is advised that you use the version in master for doing this, which you can install via: 192 | 193 | 194 | ```bash 195 | npm install -g https://github.com/olegp/common-node/tarball/master 196 | ``` 197 | 198 | If you run into any issues along the way, whether related to using this library 199 | in your own project or to the documentation, please post your comments on the [issue tracker](https://github.com/olegp/common-node/issues/). The tracker is also a great place to contribute ideas and find out what needs doing. 200 | 201 | If you're coming from Ringo or Narwhal, please try running the tests for some of your existing packages. If the tests pass and the package is compatible with Common Node, feel free to add it to the [wiki](https://github.com/olegp/common-node/wiki). 202 | 203 | To find specific issues not listed on the tracker, you can run the following command from inside the `common-node` directory. 204 | 205 | 206 | ```bash 207 | grep 'TODO' lib/* 208 | ``` 209 | 210 | To contribute code: fork the project, make and commit your changes and send a pull request. 211 | 212 | A number of higher level goals, such as descriptions of packages that would complement Common Node are listed on the [TODO wiki page](https://github.com/olegp/common-node/wiki/TODO). 213 | 214 | ### Maintainers 215 | 216 | * [Alex Lam](http://github.com/alexlamsl) 217 | * [Oleg Podsechin](http://github.com/olegp) 218 | 219 | ### Acknowledgements 220 | 221 | * Marcel Laverdet for [node-fibers](https://github.com/laverdet/node-fibers) 222 | * Hannes Wallnoefer and others from [RingoJS](http://ringojs.org) - this project uses a number of its tests & the Ringo code was used as a starting point for some modules 223 | * Kris Kowal, Tom Robinson, Isaac Schlueter for [Narwhal](http://narwhaljs.org/) - this project used a number of its modules as a starting point and some methods in e.g. Binary have been copied as is 224 | * everybody on the [CommonJS](http://groups.google.com/group/commonjs) mailing list 225 | 226 | ### License 227 | 228 | (The MIT License) 229 | 230 | Copyright (c) 2011+ Oleg Podsechin 231 | 232 | Permission is hereby granted, free of charge, to any person obtaining 233 | a copy of this software and associated documentation files (the 234 | 'Software'), to deal in the Software without restriction, including 235 | without limitation the rights to use, copy, modify, merge, publish, 236 | distribute, sublicense, and/or sell copies of the Software, and to 237 | permit persons to whom the Software is furnished to do so, subject to 238 | the following conditions: 239 | 240 | The above copyright notice and this permission notice shall be 241 | included in all copies or substantial portions of the Software. 242 | 243 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 244 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 245 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 246 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 247 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 248 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 249 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 250 | 251 | -------------------------------------------------------------------------------- /doc/system/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | system - Common Node 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 |

Module system

24 |
25 |

System module 26 | compliant with the CommonJS 27 | System/1.0 specification. Additional non-standard methods for sleeping on the current thread, 28 | spawning a new thread and spawning processes are provided.

29 | 30 | 31 |
32 | 33 |
34 | 35 |
36 |

Functions

37 | 57 |
58 |
59 |

Properties

60 | 77 |
78 |
79 |
80 |
81 |
82 |
83 | 84 |
85 |
86 |

87 | args 88 |

89 |
90 |

An array of strings representing the command line arguments passed to the running script.

91 | 92 | 93 |
94 |
95 |
96 |
97 | 98 | 99 |
100 |
101 |

102 | command 103 | ()

104 |
105 | 106 | 107 | 108 |
109 |
110 |
111 |
112 | 113 | 114 |
115 |
116 |

117 | createProcess 118 | ()

119 |
120 | 121 | 122 | 123 |
124 |
125 |
126 |
127 | 128 | 129 |
130 |
131 |

132 | env 133 |

134 |
135 |

An object containing our environment variables.

136 | 137 | 138 |
139 |
140 |
141 |
142 | 143 | 144 |
145 |
146 |

147 | exit 148 | (status)

149 |
150 |

Terminates the current process.

151 | 152 | 153 |
154 |
155 |

Parameters

156 | 157 | 158 | 159 | 160 | 161 | 162 |
numberstatusThe exit status, defaults to 0.
163 |
164 |
165 |
166 |
167 | 168 | 169 |
170 |
171 |

172 | print 173 | ()

174 |
175 |

A utility function to write to stdout.

176 | 177 | 178 |
179 |
180 |
181 |
182 | 183 | 184 |
185 |
186 |

187 | sleep 188 | (milliseconds)

189 |
190 |

Suspends the current process for the specified number of milliseconds.

191 | 192 | 193 |
194 |
195 |

Parameters

196 | 197 | 198 | 199 | 200 | 201 | 202 |
NumbermillisecondsThe number of milliseconds to sleep.
203 |
204 |
205 |
206 |
207 | 208 | 209 |
210 |
211 |

212 | spawn 213 | (run)

214 |
215 |

Spawns a new thread.

216 | 217 | 218 |
219 |
220 |

Parameters

221 | 222 | 223 | 224 | 225 | 226 | 227 |
Functionrunentry point of the new thread.
228 |
229 |
230 |
231 |
232 | 233 | 234 |
235 |
236 |

237 | stderr 238 |

239 |
240 |

A TextStream to write to stderr.

241 | 242 | 243 |
244 |
245 |
246 |
247 | 248 | 249 |
250 |
251 |

252 | stdin 253 |

254 |
255 |

A TextStream to read from stdin.

256 | 257 | 258 |
259 |
260 |
261 |
262 | 263 | 264 |
265 |
266 |

267 | stdout 268 |

269 |
270 |

A TextStream to write to stdout.

271 | 272 | 273 |
274 |
275 |
276 |
277 | 278 |
279 |
280 |
281 | 282 | 283 | 284 | 305 |
306 | 307 | 308 | 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /doc/httpclient/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | httpclient - Common Node 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 |

Module httpclient

24 |
25 |

HTTP Client as defined in 26 | CommonJS HTTP Client/A.

27 | 28 | 29 |
30 | 31 |
32 | 33 |
34 |

Class HttpClient

35 |
36 |

Instance Methods

37 | 63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 |
71 |

72 | HttpClient 73 |

74 |
75 | 76 | 77 | 78 |
79 |
80 |
81 |
82 | 83 | 84 |
85 |
86 |

87 | HttpClient.prototype.connect 88 | ()

89 |
90 |

Open the connection to the URL using the method supplied. If the method or url is missing, throw an error. 91 | After connecting, write() will have no effect.

92 | 93 | 94 |
95 |
96 |
97 |
98 | 99 | 100 |
101 |
102 |

103 | HttpClient.prototype.finish 104 | ()

105 |
106 |

Alias for .connect().read()

107 | 108 | 109 |
110 |
111 |
112 |
113 | 114 | 115 |
116 |
117 |

118 | HttpClient.prototype.read 119 | ()

120 |
121 |

Read the request and return a JSGI-style object consisting of 122 | {status:Integer, headers:Objecct, body:Iterable}. 123 | Calling read() does not block the application until the request is completed, 124 | but it does open the input stream such that the data can be read.

125 | 126 | 127 |
128 |
129 |
130 |
131 | 132 | 133 |
134 |
135 |

136 | HttpClient.prototype.set 137 | (key, val)

138 |
139 |

Set the body, headers, method, or url, or any combination thereof in 140 | the settings object. Attribute validity is enforced.

141 | 142 | 143 |
144 |
145 |

Parameters

146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
keyone of body, headers, method or url
valthe value to set
158 |
159 |
160 |
161 |
162 | 163 | 164 |
165 |
166 |

167 | HttpClient.prototype.setHeader 168 | (key, val)

169 |
170 |

Set a header on the header object in a case-insensitive manner. 171 | That is, if the user sets "content-type", and then later sets "Content-Type", then the first setting is lost.

172 | 173 | 174 |
175 |
176 |

Parameters

177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 |
Stringkeyheader name
Stringvalheader value
189 |
190 |
191 |
192 |
193 | 194 | 195 |
196 |
197 |

198 | HttpClient.prototype.setHeaders 199 | (headers)

200 |
201 |

Set a bunch of headers expressed as name-value pairs.

202 | 203 | 204 |
205 |
206 |

Parameters

207 | 208 | 209 | 210 | 211 | 212 | 213 |
headersheaders to set
214 |
215 |
216 |
217 |
218 | 219 | 220 |
221 |
222 |

223 | HttpClient.prototype.setOption 224 | (key, val)

225 |
226 | 227 | 228 | 229 |
230 |
231 |

Parameters

232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 |
key
val
244 |
245 |
246 |
247 |
248 | 249 | 250 |
251 |
252 |

253 | HttpClient.prototype.write 254 | (data)

255 |
256 |

Append data to the outgoing request, that is, to the iterable body object. 257 | This method return an error if a body was provided earlier via settings 258 | that does not implement push.

259 | 260 | 261 |
262 |
263 |

Parameters

264 | 265 | 266 | 267 | 268 | 269 | 270 |
data{Binary} object to write
271 |
272 |
273 |
274 |
275 | 276 |
277 |
278 |
279 | 280 | 281 | 282 | 303 |
304 | 305 | 306 | 307 | 308 | 309 | 310 | -------------------------------------------------------------------------------- /lib/ringo/httpclient.js: -------------------------------------------------------------------------------- 1 | var io = require("../io"); 2 | var TextStream = io.TextStream, MemoryStream = io.MemoryStream; 3 | var Binary = require("../binary").Binary; 4 | var ByteArray = require("../binary").ByteArray; 5 | var HttpClient = require("../httpclient").HttpClient; 6 | 7 | /** 8 | * Creates a new object as the as the keywise union of the provided objects. 9 | * Whenever a key exists in a later object that already existed in an earlier 10 | * object, the according value of the earlier object takes precedence. 11 | * 12 | * @param {Object} objs ... The objects to merge 13 | */ 14 | function merge(objs) { 15 | var result = {}; 16 | for (var i = arguments.length; i > 0; --i) { 17 | var obj = arguments[i - 1]; 18 | for (var property in obj) { 19 | result[property] = obj[property]; 20 | } 21 | } 22 | return result; 23 | } 24 | 25 | /** 26 | * Encode an object's properties into an URL encoded string. 27 | * 28 | * @param {Object} 29 | * object an object 30 | * @returns {String} a string containing the URL encoded properties of the 31 | * object 32 | */ 33 | function urlEncode(object) { 34 | var buf = []; 35 | var key, value; 36 | for (key in object) { 37 | value = object[key]; 38 | if (value instanceof Array) { 39 | for (var i = 0; i < value.length; i++) { 40 | if (buf.length) 41 | buf.push("&"); 42 | buf.push(encodeURIComponent(key), "=", encodeURIComponent(value[i])); 43 | } 44 | } else { 45 | if (buf.length) 46 | buf.push("&"); 47 | buf.push(encodeURIComponent(key), "=", encodeURIComponent(value)); 48 | } 49 | } 50 | return buf.join(''); 51 | } 52 | 53 | /** 54 | * Returns an object for use as a HTTP header collection. The returned object 55 | * provides methods for setting, getting, and deleting its properties in a 56 | * case-insensitive and case-preserving way. 57 | * 58 | * This function can be used as mixin for an existing JavaScript object or as a 59 | * constructor. 60 | * 61 | * @param {Object} 62 | * headers an existing JS object. If undefined, a new object is created 63 | */ 64 | function Headers(headers) { 65 | // when is a duck a duck? 66 | if (headers && headers.get && headers.set) { 67 | return headers; 68 | } 69 | 70 | headers = headers || {}; 71 | var keys = {}; 72 | // populate internal lower case to original case map 73 | for (var key in headers) { 74 | keys[String(key).toLowerCase()] = key; 75 | } 76 | 77 | /** 78 | * Get the value of the header with the given name 79 | * 80 | * @param {String} 81 | * name the header name 82 | * @returns the header value 83 | * @name Headers.instance.get 84 | */ 85 | Object.defineProperty(headers, "get", { 86 | value:function(key) { 87 | if (key in this) { 88 | return this[key]; 89 | } else { 90 | return (key = keys[key.toLowerCase()]) && this[key]; 91 | } 92 | } 93 | }); 94 | 95 | /** 96 | * Set the header with the given name to the given value. 97 | * 98 | * @param {String} 99 | * name the header name 100 | * @param {String} 101 | * value the header value 102 | * @name Headers.instance.set 103 | */ 104 | Object.defineProperty(headers, "set", { 105 | value:function(key, value) { 106 | // JSGI uses \n as separator for mulitple headers 107 | value = value.replace(/\n/g, ""); 108 | var oldkey = keys[key.toLowerCase()]; 109 | if (oldkey) { 110 | delete this[oldkey]; 111 | } 112 | this[key] = value; 113 | keys[key.toLowerCase()] = key; 114 | } 115 | }); 116 | 117 | /** 118 | * Add a header with the given name and value. 119 | * 120 | * @param {String} 121 | * name the header name 122 | * @param {String} 123 | * value the header value 124 | * @name Headers.instance.add 125 | */ 126 | Object.defineProperty(headers, "add", { 127 | value:function(key, value) { 128 | // JSGI uses \n as separator for mulitple headers 129 | value = value.replace(/\n/g, ""); 130 | if (this[key]) { 131 | // shortcut 132 | this[key] = this[key] + "\n" + value; 133 | return; 134 | } 135 | var lowerkey = key.toLowerCase(); 136 | var oldkey = keys[lowerkey]; 137 | if (oldkey) { 138 | value = this[oldkey] + "\n" + value; 139 | if (key !== oldkey) 140 | delete this[oldkey]; 141 | } 142 | this[key] = value; 143 | keys[lowerkey] = key; 144 | } 145 | 146 | }); 147 | 148 | /** 149 | * Queries whether a header with the given name is set 150 | * 151 | * @param {String} 152 | * name the header name 153 | * @returns {Boolean} true if a header with this name is set 154 | * @name Headers.instance.contains 155 | */ 156 | Object.defineProperty(headers, "contains", { 157 | value:function(key) { 158 | return Boolean(key in this || (key = keys[key.toLowerCase()]) && key in this); 159 | } 160 | }); 161 | 162 | /** 163 | * Unsets any cookies with the given name 164 | * 165 | * @param {String} 166 | * name the header name 167 | * @name Headers.instance.unset 168 | */ 169 | Object.defineProperty(headers, "unset", { 170 | value:function(key) { 171 | key = key.toLowerCase(); 172 | if (key in keys) { 173 | delete this[keys[key]]; 174 | delete keys[key]; 175 | } 176 | } 177 | }); 178 | 179 | /** 180 | * Returns a string representation of the headers in MIME format. 181 | * 182 | * @returns {String} a string representation of the headers 183 | * @name Headers.instance.toString 184 | */ 185 | Object.defineProperty(headers, "toString", { 186 | value:function() { 187 | var buffer = new Buffer(); 188 | for (var key in this) { 189 | this[key].split("\n").forEach(function(value) { 190 | buffer.write(key).write(": ").writeln(value); 191 | }); 192 | } 193 | return buffer.toString(); 194 | } 195 | }); 196 | 197 | return headers; 198 | } 199 | 200 | /** 201 | * Defaults for options passable to to request() 202 | * 203 | * @param {Object} options 204 | */ 205 | var prepareOptions = function(options) { 206 | var defaultValues = { 207 | "data":{}, 208 | "headers":{}, 209 | "method":"GET", 210 | "followRedirects":true, 211 | "binary":false 212 | }; 213 | var opts = options ? merge(options, defaultValues) : defaultValues; 214 | Headers(opts.headers); 215 | opts.contentType = opts.contentType || opts.headers.get("Content-Type") 216 | || "application/x-www-form-urlencoded;charset=utf-8"; 217 | return opts; 218 | }; 219 | 220 | /** 221 | * Of the 4 arguments to get/post all but the first (url) are optional. This fn 222 | * puts the right arguments - depending on their type - into the options object 223 | * which can be used to call request() 224 | * 225 | * @param {Array} args Arguments Array 226 | * @returns {Object<{url, data, success, error}>} Object holding attributes for 227 | * call to request() 228 | */ 229 | var extractOptionalArguments = function(args) { 230 | var types = []; 231 | for (var key in args) { 232 | types.push(typeof (args[key])); 233 | } 234 | 235 | if (types[0] !== 'string') { 236 | throw new Error('first argument (url) must be string'); 237 | } 238 | 239 | if (args.length === 1) { 240 | return { 241 | url:args[0] 242 | }; 243 | } else if (args.length === 2) { 244 | if (types[1] === 'function') { 245 | return { 246 | url:args[0], 247 | success:args[1] 248 | }; 249 | } else { 250 | return { 251 | url:args[0], 252 | data:args[1] 253 | }; 254 | } 255 | throw new Error('two argument form must be (url, success) or (url, data)'); 256 | } else if (args.length === 3) { 257 | if (types[1] === 'function' && types[2] === 'function') { 258 | return { 259 | url:args[0], 260 | success:args[1], 261 | error:args[2] 262 | }; 263 | } else if (types[1] === 'object' && types[2] === 'function') { 264 | return { 265 | url:args[0], 266 | data:args[1], 267 | success:args[2] 268 | }; 269 | } else { 270 | throw new Error('three argument form must be (url, success, error) or (url, data, success)'); 271 | } 272 | } 273 | throw new Error('unknown arguments'); 274 | }; 275 | 276 | /** 277 | * @name Exchange 278 | * @param {String} 279 | * url The URL 280 | * @param {Object} 281 | * options The options 282 | * @param {Object} 283 | * callbacks An object containing success, error and complete callback 284 | * methods 285 | * @returns {Exchange} A newly constructed Exchange instance 286 | * @constructor 287 | */ 288 | var Exchange = function(url, options, callbacks) { 289 | var reqData = options.data; 290 | var httpclient = null; 291 | var url; 292 | var response; 293 | var responseContent; 294 | var responseContentBytes; 295 | var isDone = false; 296 | 297 | Object.defineProperties(this, { 298 | /** 299 | * The connection used by this Exchange instance 300 | * 301 | * @name Exchange.prototype.connection 302 | */ 303 | "connection":{ 304 | "get":function() { 305 | }, 306 | "enumerable":true 307 | }, 308 | /** 309 | * True if the request has completed, false otherwise 310 | * 311 | * @name Exchange.prototype.done 312 | */ 313 | "done":{ 314 | "get":function() { 315 | return isDone; 316 | }, 317 | enumerable:true 318 | }, 319 | /** 320 | * The response body as String 321 | * 322 | * @name Exchange.prototype.content 323 | */ 324 | "content":{ 325 | "get":function() { 326 | if (typeof responseContent !== 'undefined') { 327 | return responseContent; 328 | } 329 | return responseContent = this.contentBytes.decodeToString(this.encoding); 330 | }, 331 | "enumerable":true 332 | }, 333 | /** 334 | * The response body as ByteArray 335 | * 336 | * @name Exchange.prototype.contentBytes 337 | */ 338 | "contentBytes":{ 339 | "get":function() { 340 | return this.responseContentBytes; 341 | }, 342 | "enumerable":true 343 | } 344 | }); 345 | 346 | if ((options.method !== "POST" && options.method !== "PUT") || !(reqData instanceof Binary || reqData instanceof String)) { 347 | reqData = urlEncode(reqData); 348 | if (options.method === "POST" || options.method === "PUT") { 349 | options.headers["Content-Length"] = reqData.length; 350 | } 351 | if (typeof (reqData) === "string" && reqData.length > 0) { 352 | url += "?" + reqData; 353 | } 354 | } 355 | 356 | this.httpclient = new HttpClient({ 357 | method:options.method, 358 | url:url, 359 | headers:options.headers, 360 | body:[reqData], 361 | agent:false 362 | }); 363 | 364 | this.response = this.httpclient.finish(); 365 | this.responseContentBytes = this.response.body.read(); 366 | isDone = true; 367 | return this; 368 | }; 369 | 370 | Object.defineProperties(Exchange.prototype, { 371 | /** 372 | * The URL wrapped by this Exchange instance 373 | * 374 | * @type java.net.URL 375 | * @name Exchange.prototype.url 376 | */ 377 | "url":{ 378 | "get":function() { 379 | return this.url; 380 | }, 381 | "enumerable":true 382 | }, 383 | /** 384 | * The response status code 385 | * 386 | * @type Number 387 | * @name Exchange.prototype.status 388 | */ 389 | "status":{ 390 | "get":function() { 391 | return this.response.status; 392 | }, 393 | "enumerable":true 394 | }, 395 | /** 396 | * The response status message 397 | * 398 | * @type String 399 | * @name Exchange.prototype.message 400 | */ 401 | "message":{ 402 | "get":function() { 403 | }, 404 | "enumerable":true 405 | }, 406 | /** 407 | * The response headers 408 | * 409 | * @name Exchange.prototype.headers 410 | */ 411 | "headers":{ 412 | "get":function() { 413 | return this.response.headers; 414 | }, 415 | enumerable:true 416 | }, 417 | /** 418 | * The cookies set by the server 419 | * 420 | * @name Exchange.prototype.cookies 421 | */ 422 | "cookies":{ 423 | "get":function() { 424 | }, 425 | enumerable:true 426 | }, 427 | /** 428 | * The response encoding 429 | * 430 | * @type String 431 | * @name Exchange.prototype.encoding 432 | */ 433 | "encoding":{ 434 | "get":function() { 435 | }, 436 | "enumerable":true 437 | }, 438 | /** 439 | * The response content type 440 | * 441 | * @type String 442 | * @name Exchange.prototype.contentType 443 | */ 444 | "contentType":{ 445 | "get":function() { 446 | return this.httpclient.headers["Content-Type"]; 447 | }, 448 | "enumerable":true 449 | }, 450 | /** 451 | * The response content length 452 | * 453 | * @type Number 454 | * @name Exchange.prototype.contentLength 455 | */ 456 | "contentLength":{ 457 | "get":function() { 458 | return this.httpclient.headers["Content-Length"]; 459 | }, 460 | "enumerable":true 461 | } 462 | }); 463 | 464 | /** 465 | * Make a generic request. 466 | * 467 | * #### Generic request options 468 | * 469 | * The `options` object may contain the following properties: 470 | * - `url`: the request URL - `method`: request method such as GET or POST - 471 | * `data`: request data as string, object, or, for POST or PUT requests, Stream 472 | * or Binary. - `headers`: request headers - `username`: username for HTTP 473 | * authentication - `password`: password for HTTP authentication - 474 | * `contentType`: the contentType - `binary`: if true if content should be 475 | * delivered as binary, else it will be decoded to string 476 | * 477 | * #### Callbacks 478 | * 479 | * The `options` object may also contain the following callback functions: 480 | * - `complete`: called when the request is completed - `success`: called when 481 | * the request is completed successfully - `error`: called when the request is 482 | * completed with an error - `beforeSend`: called with the Exchange object as 483 | * argument before the request is sent 484 | * 485 | * The following arguments are passed to the `complete`, `success` and `part` 486 | * callbacks: 1. `content`: the content as String or ByteString 2. `status`: the 487 | * HTTP status code 3. `contentType`: the content type 4. `exchange`: the 488 | * exchange object 489 | * 490 | * The following arguments are passed to the `error` callback: 1. `message`: the 491 | * error message. This is either the message from an exception thrown during 492 | * request processing or an HTTP error message 2. `status`: the HTTP status 493 | * code. This is `0` if no response was received 3. `exchange`: the exchange 494 | * object 495 | * 496 | * @param {Object} 497 | * options 498 | * @returns {Exchange} exchange object 499 | * @see #get 500 | * @see #post 501 | * @see #put 502 | * @see #del 503 | */ 504 | var request = exports.request = function(options) { 505 | var opts = prepareOptions(options); 506 | return new Exchange(opts.url, { 507 | "method":opts.method, 508 | "data":opts.data, 509 | "headers":opts.headers, 510 | "username":opts.username, 511 | "password":opts.password, 512 | "contentType":opts.contentType, 513 | "followRedirects":opts.followRedirects, 514 | "binary":opts.binary 515 | }, { 516 | "beforeSend":opts.beforeSend, 517 | "success":opts.success, 518 | "complete":opts.complete, 519 | "error":opts.error 520 | }); 521 | }; 522 | 523 | /** 524 | * Creates an options object based on the arguments passed 525 | * 526 | * @param {String} 527 | * method The request method 528 | * @param {String} 529 | * url The URL 530 | * @param {String|Object|Stream|Binary} 531 | * data Optional data to send to the server 532 | * @param {Function} 533 | * success Optional success callback 534 | * @param {Function} 535 | * error Optional error callback 536 | * @returns An options object 537 | */ 538 | var createOptions = function(method, url, data, success, error) { 539 | var args = Array.prototype.slice.call(arguments, 1); 540 | if (args.length < 4) { 541 | var optArgs = extractOptionalArguments(args); 542 | var url = optArgs.url, data = optArgs.data, success = optArgs.success, error = optArgs.error; 543 | } 544 | return { 545 | method:method, 546 | url:url, 547 | data:data, 548 | success:success, 549 | error:error 550 | }; 551 | }; 552 | 553 | /** 554 | * Executes a GET request 555 | * 556 | * @param {String} 557 | * url The URL 558 | * @param {Object|String} 559 | * data The data to append as GET parameters to the URL 560 | * @param {Function} 561 | * success Optional success callback 562 | * @param {Function} 563 | * error Optional error callback 564 | * @returns The Exchange instance representing the request and response 565 | * @type Exchange 566 | */ 567 | var get = exports.get = function(url, data, success, error) { 568 | return request(createOptions("GET", url, data, success, error)); 569 | }; 570 | 571 | /** 572 | * Executes a POST request 573 | * 574 | * @param {String} 575 | * url The URL 576 | * @param {Object|String|Stream|Binary} 577 | * data The data to send to the server 578 | * @param {Function} 579 | * success Optional success callback 580 | * @param {Function} 581 | * error Optional error callback 582 | * @returns The Exchange instance representing the request and response 583 | * @type Exchange 584 | */ 585 | var post = exports.post = function(url, data, success, error) { 586 | return request(createOptions("POST", url, data, success, error)); 587 | }; 588 | 589 | /** 590 | * Executes a DELETE request 591 | * 592 | * @param {String} 593 | * url The URL 594 | * @param {Object|String} 595 | * data The data to append as GET parameters to the URL 596 | * @param {Function} 597 | * success Optional success callback 598 | * @param {Function} 599 | * error Optional error callback 600 | * @returns The Exchange instance representing the request and response 601 | * @type Exchange 602 | */ 603 | var del = exports.del = function(url, data, success, error) { 604 | return request(createOptions("DELETE", url, data, success, error)); 605 | }; 606 | 607 | /** 608 | * Executes a PUT request 609 | * 610 | * @param {String} 611 | * url The URL 612 | * @param {Object|String|Stream|Binary} 613 | * data The data send to the server 614 | * @param {Function} 615 | * success Optional success callback 616 | * @param {Function} 617 | * error Optional error callback 618 | * @returns The Exchange instance representing the request and response 619 | * @type Exchange 620 | */ 621 | var put = exports.put = function(url, data, success, error) { 622 | return request(createOptions("PUT", url, data, success, error)); 623 | }; 624 | --------------------------------------------------------------------------------