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.
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.
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 | [](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.
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.
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.
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.
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.