Compare commits

...

173 Commits
v0.8.1 ... v0.2

Author SHA1 Message Date
Ryan Dahl
86ddc0965a Apply V8 issue 851 to v0.2 for Mark Wubben
https://groups.google.com/d/topic/nodejs-dev/di7Jv1Umois/discussion
http://code.google.com/p/v8/source/detail?r=5364
http://codereview.chromium.org/3262001
2011-01-19 12:23:55 -08:00
Ryan Dahl
aab4f37e20 Fix test-net-connect-buffer
Change to end() behavior in 82afd0 was breaking it. end() should wait for
connection before dumping. Changed test-net-connect-timeout to use destroy()
instead.
2011-01-12 15:58:50 -08:00
Ryan Dahl
82afd0da35 Add test for connection timeouts
Also make socket.end() destroy connection if still trying to connect.
Previously was ignoring.
2011-01-12 11:12:28 -08:00
Ryan Dahl
f11e24b864 base64 decode should handle whitespace
painfully backported from v0.3 cf1db4f304
2011-01-03 15:02:32 -08:00
Ryan Dahl
6f8d78d84b Bump version to v0.2.6 2010-12-30 21:00:01 -08:00
Jorge Chamorro Bieling
ae91603dd7 Apple's threaded write()s bug
fixes test/simple/test-fs-sir-writes-alot.js on mac
2010-12-30 20:50:59 -08:00
Ryan Dahl
40b6197d9b Default to gcc in V8 build
Fixes detecting gcc toolchain in solaris (NODEJS-7)
2010-12-30 13:39:25 -08:00
Tom Hughes
ea3b78cd49 Don't access buffer data before initializing it.
Prevents valgrind from complaining and still tests that buffer data is
treated as unsigned.
2010-12-22 10:35:30 -08:00
Tom Hughes
a4c53533f6 Fix memory leak in node_crypto.cc.
Both HexDecode and unbase64 allocate buffers, which weren't being freed.
2010-12-22 10:35:27 -08:00
Tom Hughes
6681024a43 Fix memory corruption with unnamed AF_UNIX sockets.
AF_UNIX sockets can have a pathname, be unnamed, or abstract (Linux
only). If an unnamed socket is returned by getsockname, getpeername, or
accept, sun_path should not be inspected.
2010-12-22 10:35:23 -08:00
Ryan Dahl
6acf3b4858 Add toolchain=gcc to V8 build for Sun 2010-12-21 12:21:56 -08:00
Theo Schlossnagle
c3ce41d34f The following error can be thrown from accept on ECONNABORT. Instead, it should be ignored.
net:1100
        if (e.errno != EMFILE) throw e;
                               ^
Error: ECONNABORTED, Software caused connection abort
    at IOWatcher.callback (net:1098:24)
    at node.js:773:9
2010-12-21 10:48:16 -08:00
Ryan Dahl
30a3dfe2b9 Remove unnecessary call to X509_STORE_free 2010-12-01 09:29:27 -08:00
Ryan Dahl
02aaac892b Implement SecureContext destructor 2010-11-30 18:49:44 -08:00
Jeremy Martin
3bb61a913b Fix number of args emitted by EventEmitter during "fast case" (lte 3 args) 2010-11-29 17:32:57 -08:00
Ryan Dahl
06b600b200 Simplify state transitions in http.Client
Fixes new bug shown in test-http-allow-req-after-204-res.js pointed out by
Tom Carden <tom.carden@gmail.com>.
2010-11-29 14:35:26 -08:00
Brian White
3c7200ff66 Fix OpenSSL SSL_library_init function check on OpenBSD. 2010-11-18 21:48:36 -08:00
Brian White
a904d12306 Make sure raw mode is disabled when exiting a terminal-based REPL. 2010-11-17 18:48:44 -08:00
Ryan Dahl
74a1fc334e Bump version to 0.2.5 2010-11-16 21:46:59 -08:00
Ryan Dahl
44fa89cda8 Add ref to buffer during fs.write and fs.read
There was the possibility the buffer could be GCed while the eio_req was
pending.  Still needs test coverage for the fs.read() problem.

See:
http://groups.google.com/group/nodejs/browse_thread/thread/c11f8b683f37cef
2010-11-16 16:45:26 -08:00
Ryan Dahl
ba5ac87b12 Fix OS::GetExecutablePath for platform_none 2010-11-16 10:56:12 -08:00
isaacs
c73041b3d5 writeFile fixes
writeFileSync could exhibit pathological behavior when a buffer could
not be written to the file in a single write() call.

Also, writeFile was not quite as optimized as it could be.
2010-11-16 10:55:30 -08:00
Ben Noordhuis
8d578df4b6 Add --profile flag to configure script, enables gprof profiling. 2010-11-16 10:55:04 -08:00
Ryan Dahl
e006edab57 Fix segfault on test-crypto
Plus random cleanups. This code needs help.

Conflicts:

	src/node_crypto.cc
2010-11-16 10:53:06 -08:00
Ryan Dahl
9a066c99e4 Simplify REPL displayPrompt
Now that we insert \r into the stream and aren't switching back and forth
between termios modes, not need to worry about when to display the prompt.
2010-11-16 10:50:00 -08:00
Ryan Dahl
44db37859d Add writeFilter when in the readline
Switch \n with \r\n for all strings printed out.
Necessary for writev patch.
2010-11-16 10:49:26 -08:00
Ryan Dahl
7590075ea7 Remove -e from echo in test. Non-portable 2010-11-16 10:48:38 -08:00
Ryan Dahl
299eda8a6c Upgrade http-parser 2010-11-16 10:47:39 -08:00
Guillaume Tuton
ebbaa86e7a Set FD_CLOEXEC flag on stdio FDs before spawning.
With regression test.
2010-11-16 10:41:41 -08:00
Ryan Dahl
c6d214739a Remove unnecessary ref/unref in iowatcher cb 2010-11-16 10:41:25 -08:00
Ben Noordhuis
96e0615369 Make writes to process.env update the real environment. Tests included. 2010-11-16 10:41:15 -08:00
Chandra Sekar S
4e0c7dd3be Removed range read optimization as it doesn't work with libeio. 2010-11-16 10:39:02 -08:00
Ryan Dahl
67cc6d70a5 Make sure watcher.set() isn't being called when active
Conflicts:

	lib/dns.js
2010-11-16 10:38:23 -08:00
Ryan Dahl
825308a45c Fix argv[6] comment in node_file 2010-11-16 10:36:57 -08:00
Ryan Dahl
b6eeddeb17 Rewrite libeio After callback to use req->result instead of req->errorno for error checking
Conflicts:

	src/node_file.cc
2010-11-16 10:36:46 -08:00
Ryan Dahl
ae2087460b Abstract out a Server.prototype.pause method 2010-11-16 10:25:58 -08:00
Ryan Dahl
d2828ea2a5 Abstract out net.Server.prototype._rejectPending
Does the same timeout action for maxConnections as it does for EMFILE.

Conflicts:

	lib/net.js
2010-11-16 10:25:53 -08:00
Ryan Dahl
26b3f228ad Module-level EMFILE handling
All net servers now share the same dummy socket. The ulimit warning is
throttled for all servers.

Conflicts:

	lib/net.js
2010-11-16 10:24:48 -08:00
isaacs
3c9746de24 Handle cyclic links smarter in fs.realpath
Rather than aborting in the face of *any* repeated link in a given path,
instead only abort if such a cycle actually makes a given path unresolvable.

Test for this by doing a normal stat.  Still use the seenLinks object to
cache link contents so as to cut own a little bit on readlink calls.

Also add a pathological test that fails without the change to fs.js.
2010-11-16 10:22:46 -08:00
Jorge Chamorro Bieling
f76b65755f make "node --eval" eval in the global scope. 2010-11-16 10:22:30 -08:00
Ryan Dahl
b70149856d Add extra anti-DoS tech to net.Server
Conflicts:

	lib/net.js
2010-11-16 10:20:39 -08:00
Ryan Dahl
9634669cc7 Improve idle benchmarks 2010-11-16 10:14:19 -08:00
Ryan Dahl
ab86f597bc Gracefully handle EMFILE
Implementing a tip from Marc Lehmann:
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_accept_ing_wh

Keep an extra FD around for every server. When you hit EMFILE, destroy that
FD, accept a connection, close it; in this way you can clear the connection
queue and let people know that you're overload.

No more timeout needed.

Conflicts:

	lib/net.js
2010-11-16 10:12:53 -08:00
Ryan Dahl
c42a5cc8eb Fix test-http-buffer-sanity
Backported from 7e24a05cba
Add test/simple/test-http-curl-chunk-problem.js
2010-11-01 18:19:54 -07:00
Ryan Dahl
f8f675ba41 Add broken test passing a buffer through http 2010-11-01 18:19:53 -07:00
Ryan Dahl
428a670121 bump version to 0.2.4 2010-10-24 14:45:39 -07:00
Ryan Dahl
f7bc7fb031 Make sure Error object on exec() gets killed member
Also default to SIGTERM for destruction when exceeding timeout or buffer on
exec()

Back ported from v0.3; original commits:
bd8e4f656e
5a98fa4809
6570cd99e5
9bf2975f78
2010-10-24 12:57:57 -07:00
Ryan Dahl
d3e6834297 Add isatty for isaacs 2010-10-24 12:27:28 -07:00
Ryan Dahl
a958ebfca0 one more 'listening' race condition 2010-10-24 12:26:47 -07:00
Ryan Dahl
78afb12663 Fix a few 'listening' race conditions
in test-http-client-parse-error
2010-10-24 12:26:32 -07:00
Ryan Dahl
ad4fb5319b Do not spin on aceept() with EMFILE
When a server hit EMFILE it would continue to try to accept new connections
from the queue. This patch introduces a timeout of one second where it will
stop trying to accept new files. After the second is over it tries again.

This is a rather serious bug that has been effecting many highly concurrent
programs. It was introduced in 4593c0, version v0.2.0.

TODO: A test for this situation. Currently I test it like this

  termA% cd projects/node
  termA% ulimit -n 256
  termA% ./node benchmark/idle_server.js

  termB% cd projects/node
  termB% ./node benchmark/idle_clients.js

And watch how the server process behaves.
2010-10-24 12:25:04 -07:00
Tom Hughes
1e932ea930 Add signal handlers so we clean up before exiting.
Add SIGTERM and SIGINT signal handlers so that we run the exit handlers
before exiting when getting these signals. Fixes an issue where we
couldn't run vi after CTRL+C'ing node because the stdin fd was left
non-blocking.

Also the test from ceb5331a64
2010-10-23 19:14:08 -07:00
Vitali Lovich
ddb06cb552 Fix parsing of linux memory
If process name contains a space, this parsing fails for no good reason.
2010-10-23 19:08:22 -07:00
Ryan Dahl
60b5bcac0c Write write.txt into the tmpdir 2010-10-23 19:08:15 -07:00
Ryan Dahl
46eda290be Remove a confusing sentence in the docs 2010-10-23 19:08:05 -07:00
Tom Hughes
c19897b76a Add --max-stack-size flag.
v8 doesn't expose a command-line flag to set the stack size, so this
adds a new flag that node understands how to handle and uses v8's
ResourceConstraints API.
2010-10-23 18:32:20 -07:00
Ryan Dahl
8ec4870611 Fix race conditions in test-http-upgrade-client2 2010-10-23 18:30:17 -07:00
Ryan Dahl
8fb9cd2575 Fix test harness for Linux
Mostly just upgraded tools/test.py to the latest one that's in V8.  But also
fixing the before and after hooks to preserve the test/tmp directory so that
running tests manually usually works.
2010-10-23 18:30:04 -07:00
Ryan Dahl
006e283b78 Add idle connection test 2010-10-23 18:27:59 -07:00
Ryan Dahl
bdd82b7c5f Add 'make bench' script 2010-10-23 18:27:52 -07:00
Ryan Dahl
9f25dc1625 Improve benchmark/http_simple.js 2010-10-23 18:27:42 -07:00
Nathan Rajlich
511f80b3c8 http-parser: Allow whitespace in the 'Content-Length' header. 2010-10-23 18:26:46 -07:00
Joshua Peek
13dba0d1c1 Don't flush net writeQueue on end() if its still connecting 2010-10-23 18:26:07 -07:00
Ryan Dahl
97e3040202 Add future API for 'util' module 2010-10-23 18:18:24 -07:00
Rasmus Andersson
78fac9b863 environ symbol fix for Mac OS X 2010-10-23 18:16:40 -07:00
Ryan Dahl
969ee3dd83 Add flag to disable colors in REPL 2010-10-23 18:16:10 -07:00
Ryan Dahl
39a2a9d1b6 TCP clients should buffer writes before connection 2010-10-23 18:15:01 -07:00
TJ Holowaychuk
08f1bf42bb Added -e, --eval 2010-10-23 16:07:02 -07:00
Ryan Dahl
18ff6db648 ClearWeak on ObjectWraps. I /think/ this is the correct semantics 2010-10-23 16:06:51 -07:00
Ryan Dahl
dc103ae020 Bump version to v0.2.3 2010-10-02 06:05:56 -07:00
Ryan Dahl
fea3919d1b Fix zero length buffer bug for http res.end()
Reported by Kadir Pekel <kadirpekel@gmail.com>
2010-10-01 16:16:00 -07:00
Marco Rogers
b5dc54c6ed fix encoding option on ReadStream, updated test 2010-10-01 16:14:22 -07:00
Ryan Dahl
2fb393a768 Fix REPL crash on tabbing 'this.'
Thanks to Tim Becker for pointing this out.
2010-10-01 16:14:13 -07:00
Ryan Dahl
80974dc85b Drop reference to timer callback on clearTimeout
Reported here:
http://groups.google.com/group/nodejs-dev/browse_thread/thread/9e063d0938f99879

Would be good to test this somehow...
2010-10-01 16:13:59 -07:00
Evan Larkin
16f736200a fs.ReadStream: Passing null for file position on all reads except the first read of a range read. 2010-10-01 16:13:12 -07:00
Evan Larkin
d29e62d564 No longer using the global variable "stat" in unwatchFile 2010-10-01 16:11:42 -07:00
Ryan Dahl
41eca918e5 Add test for getting parse error from HTTP client
Made this test in response to this thread:
http://groups.google.com/group/nodejs/browse_thread/thread/f82835007a277de2/
But Node appears to be working correctly.
2010-10-01 16:11:17 -07:00
Ryan Dahl
45a01aff59 Move the http client's initParser() into prototype 2010-10-01 15:56:32 -07:00
Ryan Dahl
55aa7b17ab Put preprocessor defines into CPPFLAGS not compile flags... 2010-10-01 15:55:46 -07:00
Ryan Dahl
1165878e3c Fix timing on I/O benchmark 2010-10-01 15:54:49 -07:00
Ryan Dahl
b98958d7fe Add function_call benchmark 2010-10-01 15:54:44 -07:00
Fedor Indutny
448cbaf395 Fixed 'upgrade' event for httpclient
onend and ondata was cleaning on parser end
2010-10-01 15:54:27 -07:00
Tj Holowaychuk
b8eee9c6f0 Fixed fs.ReadStream() start: 0 bug 2010-10-01 15:53:53 -07:00
Mikeal Rogers
936a038bb7 HTTP: close connection on connection:close header.
rnewson found a good bug in keep-alive. we were only using the request
headers we send to enable/disable keep-alive but when the server sends
Connection: close we need to close down the connection regardless.

I wrote up a patch the Robert verified makes all his test client code work
now and I also added a new unittest for it.
2010-10-01 15:53:26 -07:00
Ryan Dahl
529cdad2dd Don't choose jobs based on processor
Leads to lots of builds with out-of-memory.
2010-10-01 15:53:02 -07:00
Ryan Dahl
7140933fcc More explicit openssl configure warning 2010-10-01 15:52:56 -07:00
Jorge Chamorro Bieling
27f6a20fdb sys.js: --needless Object.keys() --needless .map() in a single patch 2010-10-01 15:51:27 -07:00
Paul Querna
a30daa7b6c Fatal error out if OpenSSL was not explicitly disabled, we just couldn't autodetect it. 2010-10-01 15:50:32 -07:00
Jorge Chamorro Bieling
01035714a5 sys.js: sys.inspect: show function names 2010-10-01 15:50:18 -07:00
Ryan Dahl
f6f739b3a2 Pass correct message in HTTP client upgrade
Simplify and correct test.

Fix by Fedor Indutny.
2010-10-01 15:49:50 -07:00
Ryan Dahl
d7c1690c74 Revert requireNative changes: 4e6b9b0, d429033, 6abbfa0, bcad540 2010-10-01 15:45:21 -07:00
Ryan Dahl
7bf46bc980 bump version to v0.2.2 2010-09-17 11:34:16 -07:00
Ryan Dahl
bcbc52e257 ^c to get out of '...' in REPL 2010-09-17 11:23:08 -07:00
Ryan Dahl
c00a6a7169 Simplify REPL 2010-09-17 11:23:04 -07:00
Ryan Dahl
1d8b154e14 Safe constructors for fs.ReadStream and fs.WriteStream 2010-09-17 11:22:58 -07:00
Ryan Dahl
84f4ce742f Remove old versions of fs.read and fs.write from docs 2010-09-17 11:22:50 -07:00
Paul Querna
f9cc35fa57 Use the Apple recommended way of detecting OSX Versions
to enable KQueue, rather than deciding based on the compiler version.
2010-09-17 11:22:42 -07:00
Ryan Dahl
d506a42765 Handle writeStream errors in sys.pump 2010-09-17 11:21:59 -07:00
Russell Haering
09a41a7f9c Pass an error to the sys.pump callback if one occurs
- Add test case for pumping from unreadable stream.
- Document the sys.pump error handling behavior
2010-09-17 11:21:50 -07:00
Herbert Vojčík
8dcd6dccab Common subexpression in emit. 2010-09-17 11:20:52 -07:00
Herbert Vojčík
1375b8ed39 No need to do if (internalModuleCache...), it's in requireNative. 2010-09-17 11:20:32 -07:00
Herbert Vojčík
a3333e7393 Module system moved to the bottom, where only is it needed.
(this also splits the file into upper "setup" and lower "startup" sections)
2010-09-17 11:20:24 -07:00
Herbert Vojčík
d965640662 m.id unneccessary, id is enough. 2010-09-17 11:20:17 -07:00
Herbert Vojčík
ee253b374d Natives having their own self-contained minimalistic module system.
The main system is built upon this, and is optional, if only natives
are used in application (eg. node-core).

Natives not loaded into own context if NODE_MODULE_CONTEXTS=1.
This have its inner logic, if natives are seen just as lazy-loaded
parts of the core.
2010-09-17 11:19:33 -07:00
Ryan Dahl
b17b28531b shorten some lines in events.js 2010-09-17 11:18:59 -07:00
Ryan Dahl
10af67714b Optimize emit for two arguments 2010-09-17 11:18:54 -07:00
Ryan Dahl
180a04805b Make a list of known globals
And fix missing var!

It would be good to get this script running at the end of every test, so we
know that modules aren't leaking either - but it will require a lot
modification of the tests so that they themselves aren't leaking globals.
2010-09-17 11:18:42 -07:00
Ryan Dahl
ee21920571 Use child_process.exec rather than sys.exec 2010-09-17 11:17:33 -07:00
isaacs
66bce65d33 Bug in realpath with symlinks to absolute folder paths which have children.
Found by Cliffano Subagio
http://groups.google.com/group/nodejs/browse_thread/thread/f46f093938265ac0/387e14da08c7dd7b?
2010-09-17 11:17:26 -07:00
Tony Metzidis
2005ef9e37 Catch Exceptions thrown when openssl is disabled 2010-09-17 11:17:08 -07:00
Tony Metzidis
3816ebc85f - fix AttributeError on "use_openssl" when doing ./configure --without-ssl - error was: AttributeError: Values instance has no attribute 'use_openssl' 2010-09-17 11:17:00 -07:00
Ryan Dahl
4c5520a37e Fix style in node_object_wrap.h 2010-09-17 11:16:45 -07:00
Ryan Dahl
0ddad3fbc7 Fix style in readline 2010-09-17 11:12:01 -07:00
Ryan Dahl
db6a1788b2 Add SIGWINCH handler for readline 2010-09-17 11:11:52 -07:00
Ryan Dahl
1d17874a7d add to todo 2010-09-17 11:11:47 -07:00
Trent Mick
3191802605 add ANSI coloring option to sys.inspect and, by default, to the repl 2010-09-17 11:11:32 -07:00
Ryan Dahl
da235fa12c bump version to v0.2.1 2010-09-10 13:52:33 -07:00
Paul Querna
92fb664bfc Expose fingerproint from getPeerCertificate
Expose the SHA1 digest of the certificate as the fingerprint attribute in
the object returned by getPeerCertificate()
2010-09-10 13:31:46 -07:00
Ryan Dahl
7704fb9711 Fix fs.realpathSync('/') 2010-09-10 13:31:39 -07:00
isaacs
c099e274d2 Better temporary directory handling for tests.
Add a setUp and tearDown function to the test case class, and use it to
create and remove the test/tmp directory for each test.

TODO: amend other tests.
2010-09-10 13:31:34 -07:00
Ryan Dahl
c86f3c5d5c Don't use empty.js - breaks module test 2010-09-10 13:31:27 -07:00
Felix Geisendörfer
9b8577230f Simple benchmark for node's startup time 2010-09-10 13:31:21 -07:00
Ryan Dahl
9f6f26028f Use SetPointerInInternalField 2010-09-10 13:31:16 -07:00
Felix Geisendörfer
44f8756aab Fix: uncaughtException was broken for main module
See: 635986e433
2010-09-10 13:30:48 -07:00
Ryan Dahl
ae2f566fec Call Tick() after coming out of select()
Previously we would only call it before going into select(). This is needed
to fix test/simple/test-next-tick-ordering2.js.
2010-09-10 13:30:38 -07:00
isaacs
2f071ac7d1 Fix issue #262. Allow fs.realpath to traverse above the current working directory. 2010-09-08 17:56:34 -07:00
Tobie Langel
bd8ea2bb85 Test for ReadStream typo 2010-09-08 17:56:25 -07:00
Ryan Dahl
35682ac88e Fix style; undefined reference bug 2010-09-08 17:56:17 -07:00
Tobie Langel
5bbebf3593 Do not emit WriteStream's drain event before ws.write has been called. 2010-09-08 17:56:12 -07:00
Tobie Langel
0b9dab650e Avoid closing a WriteStream before it has been opened. 2010-09-08 17:55:58 -07:00
Tobie Langel
a6659e7612 Avoid missing ref error in WriteStream.prototype.destroy(). 2010-09-08 17:55:53 -07:00
Herbert Vojčík
727843eea1 Make test-global work with NODE_MODULE_CONTEXTS. 2010-09-08 17:55:44 -07:00
Herbert Vojčík
7e9a0173d7 Modifying test-global to accomodate v8 inter-context 'global' protection. 2010-09-08 17:55:38 -07:00
Herbert Vojčík
74b70c3cb1 Removing test-global-between-modules.
Using "global" to push data to require()d modules
not supported under NODE_MODULE_CONTEXTS=1.
2010-09-08 17:55:33 -07:00
Herbert Vojčík
d5deb4c4a3 Removed comment-out code. 2010-09-08 17:48:52 -07:00
Ryan Dahl
cc0164bc12 Increase ReadStream bufferSize to 64k 2010-09-08 17:48:45 -07:00
Trent Mick
ccd1304c5b Ctrl+W support for the REPL
FWIW, command-line style (delete back to whitespace) would be:
    leading = leading.replace(/\S+\s*$/, '');
2010-09-08 17:48:15 -07:00
Felix Geisendörfer
3d3d00d524 Test case showing a bug in nextTick ordering
nextTick should fire before setTimeout in this test, but it doesn't.
2010-09-08 17:48:02 -07:00
Ryan Dahl
dffa9e76a1 test-http-parser should not use private API 2010-09-08 17:47:36 -07:00
Ryan Dahl
90374797f0 Don't refer to private API in test-repl 2010-09-08 17:47:30 -07:00
Ryan Dahl
c1a4e10156 Special deepEquals for buffer 2010-09-08 17:47:23 -07:00
Ryan Dahl
b655ce5c08 typo: forceClose -> destroy for WriteStreams 2010-09-08 17:47:17 -07:00
Benjamin Thomas
b30b60717d Fix bug in process._tickCallback where callbacks can get abandoned.
Change process._tickCallback so that if a callback throws an error but
there are other callbacks after it, we indicate that
process._tickCallback needs to be ran again.

Currently, if a callback in process._tickCallback throws an error, and
that error is caught by an uncaughtException handler and
process.nextTick is never called again, then any other callbacks already
added to the nextTickQueue won't be called again.

Updated the next-tick-errors test to catch this scenario.
2010-09-08 17:46:58 -07:00
isaacs
b2dfea0361 Treat "//some_path" as pathname rather than hostname by default.
Note that "//" is still a special indicator for the hostname, and this does
not change the parsing of mailto: and other "slashless" url schemes.  It
does however remove some oddness in url.parse(req.url) which is the most
common use-case for the url.parse function.
2010-09-08 17:46:50 -07:00
Ryan Dahl
610743daee Make sure setInterval(cb, 0) loops infinitely 2010-09-08 17:46:43 -07:00
Ryan Dahl
df891c36a4 Fix style 2010-09-08 17:46:36 -07:00
Ryan Dahl
f35a8298b1 Fix style in test/simple/test-dgram-unix.js 2010-09-08 17:46:30 -07:00
Ryan Dahl
294c455678 Remove timer from test/simple/test-dgram-unix.js
Test running already has a timeout mechanism.
2010-09-08 17:46:24 -07:00
Marco Rogers
14f16ec592 fix for fs.readFile to return string when encoding specified on zero length read 2010-09-08 17:45:26 -07:00
Marco Rogers
cdee88051d Fixed async fs writes with length 0, it should fire the callback 2010-09-08 17:45:21 -07:00
Bradley Meck
b39b15d53f Allow Strings for ports on net.Server.listen 2010-09-08 17:45:07 -07:00
Fedor Indutny
b895568800 Constants should be readOnly and DontDelete 2010-09-08 17:44:40 -07:00
Ryan Dahl
ead58b9a93 Add failing uncaughtException test
FIXME
2010-09-08 17:44:30 -07:00
Johan Euphrosine
c306fa241c add readline support for meta-d 2010-09-08 17:44:13 -07:00
Benjamin Thomas
22f67f8585 Fix process.nextTick so thrown errors don't confuse it.
If the function for a process.nextTick throws an error, then the
splice() never removes that function from the nextTickQueue array.  This
makes sure the functions that have been run in _tickCallback get removed
regardless of errors.

Also add a test for this.
2010-09-08 17:43:47 -07:00
Ryan Dahl
416aa73946 Safe constructor: net.Server, net.Stream 2010-09-08 17:43:39 -07:00
Ryan Dahl
6a39a7ed83 Safe Constructor: Buffer 2010-09-08 17:43:32 -07:00
Ryan Dahl
ad960f4462 Safe constructors: http.Server and http.Client 2010-09-08 17:43:25 -07:00
Ryan Dahl
f78691e45c Only check for execinfo lib in freebsd
OpenEmbedded doesn't like it when you look in /usr/lib
2010-09-08 17:43:17 -07:00
Johan Euphrosine
3d8137c582 add test for readline putty support 2010-09-08 17:42:59 -07:00
Brian
c14c445cb6 Fix home/end keys in repl for putty/xterm. 2010-09-08 17:42:47 -07:00
Russell Haering
b9ca8435f5 Modify fs.open to use accept a callback without a mode 2010-09-08 17:42:36 -07:00
Johan Euphrosine
baa24bd40e add readline support for meta-f and meta-b 2010-09-08 17:42:23 -07:00
Johan Euphrosine
7a836ef813 add home/end support in rxvt and readline tests 2010-09-08 17:42:09 -07:00
Felix Geisendörfer
a2b9e02d83 Document WriteStream 'open' event 2010-09-08 17:41:59 -07:00
isaacs
3f7c791b13 Add testing items, and make npm lowercase 2010-09-08 17:40:59 -07:00
isaacs
53dac7c341 Missing 'var' in sys.inspect (Found by Oleg Slobodskoi) 2010-09-08 17:40:50 -07:00
Aria Stewart
c43ddf7acd Fix doc.js so that it doesn't misnest menu items in the TOC 2010-09-08 17:40:41 -07:00
Johan Euphrosine
699fdfaa43 fix home/end on GNU/Linux 2010-09-08 17:40:35 -07:00
Johan Euphrosine
8d2e79451e add tests for console.log arguments handling 2010-09-08 17:40:26 -07:00
Aria Stewart
760efc0758 s/HTTPS/SSL/ where appropriate 2010-09-08 17:39:55 -07:00
Ryan Dahl
1b1ad1d363 Improve appendix markdown 2010-09-08 17:39:38 -07:00
Ryan Dahl
c3eafdd3a4 Add appendix to docs 2010-09-08 17:39:28 -07:00
119 changed files with 3620 additions and 987 deletions

2
.gitignore vendored
View File

@@ -12,3 +12,5 @@ test/fixtures/hello.txt
tmp/
node
node_g
*.swp
.benchmark_reports

17
AUTHORS
View File

@@ -109,8 +109,25 @@ Chandra Sekar S <chandru.in@gmail.com>
Andrew Naylor <argon@mkbot.net>
Benjamin Kramer <benny.kra@gmail.com>
Danny Coates <dannycoates@gmail.com>
Samuel Shull <brickysam26@gmail.com>
Nick Stenning <nick@whiteink.com>
Bert Belder <bertbelder@gmail.com>
Trent Mick <trentm@gmail.com>
Fedor Indutny <fedor.indutny@gmail.com>
Illarionov Oleg <oleg@emby.ru>
Aria Stewart <aredridel@nbtsc.org>
Johan Euphrosine <proppy@aminche.com>
Russell Haering <russellhaering@gmail.com>
Bradley Meck <bradley.meck@gmail.com>
Tobie Langel <tobie.langel@gmail.com>
Tony Metzidis <tonym@tonym.us>
Jorge Chamorro Bieling <jorge@jorgechamorro.com>
Evan Larkin <evan.larkin.il.com>
Joshua Peek <josh@joshpeek.com>
Nathan Rajlich <nathan@tootallnate.net>
Tom Hughes <tom.hughes@palm.com>
Vitali Lovich <vitali.lovich@palm.com>
Guillaume Tuton <guillaume@tuton.fr>
Jeremy Martin <jmar777@gmail.com>
Theo Schlossnagle <jesus@omniti.com>

144
ChangeLog
View File

@@ -1,4 +1,140 @@
2010.08.20, Version 0.2.0
2010.12.30, Version 0.2.6
* Make sure raw mode is disabled when exiting a terminal-based REPL.
(Brian White)
* Fix OpenSSL SSL_library_init function check on OpenBSD.
(Brian White)
* Fix test-http-allow-req-after-204-res.js
* Fix length of arguments emitted by EventEmitter (Jeremy Martin)
* Fix unhandled ECONNABORTED (Theo Schlossnagle)
* Default to gcc for V8 build on Solaris (Trent Mick)
* Fix various memory leaks (Tom Hughes)
* Apple's threaded write()s bug (Jorge Chamorro Bieling)
2010.11.16, Version 0.2.5, 74a1fc334e486683d6da02fd918725d246ffc273
* Add ref to buffer during fs.write and fs.read. Sometimes buffers
would be GCed before making it to the thread pool.
* Fix http buffer pushing bug:
http://groups.google.com/group/nodejs/browse_thread/thread/f66cd3c960406919
* Gracefully handle EMFILE and server.maxConnections
* "node --eval" evals in the global scope. (Jorge Chamorro Bieling)
* Handle cyclic links smarter in fs.realpath (isaacs, Issue #167)
* Rewrite libeio After callback to use req->result instead of req->errorno
for error checking (Micheil Smith)
* Removed range read optimization as it doesn't work with libeio. (Chandra
Sekar S)
* Make writes to process.env update the real environment (Ben Noordhuis)
* Set FD_CLOEXEC flag on stdio FDs before spawning. (Guillaume Tuton)
* Upgrade http-parser
* Readline: Switch \n with \r\n for all strings printed out.
Simplify displayPrompt
* Fix segfault on test-crypto
* Add --profile flag to configure script, enables gprof profiling. (Ben
Noordhuis)
* writeFileSync could exhibit pathological behavior when a buffer could
not be written to the file in a single write() call. (isaacs)
* Fix OS::GetExecutablePath for platform_none shouldn't return garbage.
2010.10.24, Version 0.2.4, 428a67012158eb2ff478a0dc58336e85e4c6399a
* Add --eval to command line options (TJ Holowaychuk)
* net fixes
- TCP clients buffer writes before connection
- Don't flush net writeQueue on end() if its still connecting
(Joshua Peek)
- Do not spin on aceept() with EMFILE
* Add --max-stack-size flag. (Tom Hughes)
* Fixes to child_process.exec (timeouts and proper termination)
Default to SIGTERM instead of SIGKILL.
* Add signal handlers so we clean up before exiting. (Tom Hughes)
* Fix parsing of linux memory (Vitali Lovich)
* http-parser: Allow whitespace in the 'Content-Length' header.
(Nathan Rajlich)
* Add flag to disable colors in REPL
2010.10.02, Version 0.2.3, dc103ae020ecd6182aa2adb482ac72ea944130ee
* Fix require in REPL (bug introduced in v0.2.2)
* Pass correct message in client HTTP upgrade event.
(Fedor Indutny)
* Show function names in sys.inspect (Jorge Chamorro Bieling)
* In HTTP, close connection on the "connection:close" header.
(Mikeal Rogers)
* fs.ReadStream bug fixes (Tj Holowaychuk, Evan Larkin, Marco Rogers)
* Fix zero length buffer bug for http res.end()
2010.09.17, Version 0.2.2, 7bf46bc9808f4db98f1cf177d58a6ecf3a50b65d
* REPL improvements (Trent Mick)
* Fix bug in fs.realpath (Isaac Schlueter)
* sys.pump catches errors (Russell Haering)
2010.09.10, Version 0.2.1, da235fa12c208fc8243600e128db2c9b55624c5c
* REPL improvements (Johan Euphrosine, Brian White)
* nextTick bug fixes (Benjamin Thomas, Felix Geisendörfer,
Trent Mick)
* fs module bug fixes (Russell Haering, Marco Rogers, Tobie Langel,
Isaac Schlueter)
* Build script change for OpenEmbedded.
* Most constrctuors work without 'new' now.
* Allow Strings for ports on net.Server.listen (Bradley Meck)
* setInterval(cb, 0) loops infinitely
* Fixes for NODE_MODULE_CONTEXTS=1 (Herbert Vojčík)
* Expose fingerproint from getPeerCertificate (Paul Querna)
* API: forceClose -> destroy for WriteStreams
2010.08.20, Version 0.2.0, 9283e134e558900ba89d9a33c18a9bdedab07cb9
* process.title support for FreeBSD, Macintosh, Linux
@@ -116,7 +252,7 @@
* Upgrade http-parser, V8 to 2.2.21
2010.06.21, Version 0.1.99, a620b7298f68f68a855306437a3b60b650d61d78
2010.06.21, Version 0.1.99, a620b7298f68f68a855306437a3b60b650d61d78
* Datagram sockets (Paul Querna)
@@ -222,7 +358,7 @@
2010.05.06, Version 0.1.94, f711d5343b29d1e72e87107315708e40951a7826
* Look in /usr/local/lib/node for modules, so that there's a way
* Look in /usr/local/lib/node for modules, so that there's a way
to install modules globally (Issac Schlueter)
* SSL improvements (Rhys Jones, Paulo Matias)
@@ -242,7 +378,7 @@
* Bugfix: destroy() instead of end() http connection at end of
pipeline
* Bugfix: http.Client may be prematurely released back to the
* Bugfix: http.Client may be prematurely released back to the
free pool. (Thomas Lee)
* Upgrade V8 to 2.2.8

View File

@@ -36,9 +36,6 @@ test-pummel: all
test-internet: all
python tools/test.py internet
benchmark: all
build/default/node benchmark/run.js
# http://rtomayko.github.com/ronn
# gem install ronn
doc: doc/node.1 doc/api.html doc/index.html doc/changelog.html
@@ -85,4 +82,13 @@ dist: doc/node.1 doc/api.html
rm -rf $(TARNAME)
gzip -f -9 $(TARNAME).tar
.PHONY: benchmark clean docclean dist distclean check uninstall install all test test-all website-upload
bench:
benchmark/http_simple_bench.sh
bench-idle:
./node benchmark/idle_server.js &
sleep 1
./node benchmark/idle_clients.js &
.PHONY: bench clean docclean dist distclean check uninstall install all test test-all website-upload

4
TODO
View File

@@ -16,3 +16,7 @@
the maximum is reached it stops accepting new connections.
- compile under clang
- Use C++ style casts everywhere.
- fs.readFile[Sync] should not call stat() and use the length.
Test on Linux's /proc/sys/kernel/hostname
- Ruby-like Process#detach (is that possible?)
- stderr isn't flushing on exit

View File

@@ -1,6 +1,6 @@
for (var i = 0; i < 9000000; i++) {
for (var i = 0; i < 9e7; i++) {
b = new Buffer(10);
b[1] = 2
}

View File

@@ -0,0 +1,35 @@
var binding = require('./build/default/binding');
c = 0
function js() {
return c++; //(new Date()).getTime();
}
var cxx = binding.hello;
var i, N = 100000000;
console.log(js());
console.log(cxx());
var start = new Date();
for (i = 0; i < N; i++) {
js();
}
var jsDiff = new Date() - start;
console.log(N +" JS function calls: " + jsDiff);
var start = new Date();
for (i = 0; i < N; i++) {
cxx();
}
var cxxDiff = new Date() - start;
console.log(N +" C++ function calls: " + cxxDiff);
console.log("\nJS speedup " + (cxxDiff / jsDiff));

View File

@@ -0,0 +1,19 @@
#include <v8.h>
#include <node.h>
#include <time.h>
using namespace v8;
static int c = 0;
static Handle<Value> Hello(const Arguments& args) {
HandleScope scope;
//time_t tv = time(NULL);
return scope.Close(Integer::New(c++));
}
extern "C" void init (Handle<Object> target) {
HandleScope scope;
//target->Set(String::New("hello"), String::New("World"));
NODE_SET_METHOD(target, "hello", Hello);
}

View File

@@ -0,0 +1,15 @@
srcdir = '.'
blddir = 'build'
VERSION = '0.0.1'
def set_options(opt):
opt.tool_options('compiler_cxx')
def configure(conf):
conf.check_tool('compiler_cxx')
conf.check_tool('node_addon')
def build(bld):
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
obj.target = 'binding'
obj.source = 'binding.cc'

View File

@@ -1,24 +1,40 @@
path = require("path");
Buffer = require("buffer").Buffer;
exec = require("child_process").exec;
http = require("http");
port = parseInt(process.env.PORT || 8000);
var old = (process.argv[2] == 'old');
console.log('pid ' + process.pid);
http = require(old ? "http_old" : 'http');
if (old) console.log('old version');
fixed = ""
for (var i = 0; i < 20*1024; i++) {
fixed += "C";
}
var uname, rev;
exec('git rev-list -1 HEAD', function (e, stdout) {
if (e) {
console.error("Problem executing: 'git rev-list -1 HEAD'");
throw new Error(e);
}
rev = stdout.replace(/\s/g, '');
});
exec('uname -a', function (e, stdout) {
if (e) {
console.error("Problem executing: 'uname -a'");
throw new Error(e);
}
uname = stdout.replace(/[\r\n]/g, '');
});
stored = {};
storedBuffer = {};
http.createServer(function (req, res) {
var server = http.createServer(function (req, res) {
var commands = req.url.split("/");
var command = commands[1];
var body = "";
@@ -57,6 +73,9 @@ http.createServer(function (req, res) {
} else if (command == "fixed") {
body = fixed;
} else if (command == "info") {
body = 'rev=' + rev + '\nuname="' + uname + '"\n';
} else {
status = 404;
body = "not found\n";
@@ -64,17 +83,13 @@ http.createServer(function (req, res) {
var content_length = body.length.toString();
res.writeHead( status
, { "Content-Type": "text/plain"
, "Content-Length": content_length
}
);
if (old) {
res.write(body, 'ascii');
res.close();
} else {
res.end(body, 'ascii');
}
}).listen(port);
res.writeHead(status, { "Content-Type": "text/plain",
"Content-Length": content_length });
res.end(body);
});
server.listen(port, function () {
console.log('Listening at http://127.0.0.1:'+port+'/');
});
console.log('Listening at http://127.0.0.1:'+port+'/');

77
benchmark/http_simple_bench.sh Executable file
View File

@@ -0,0 +1,77 @@
#!/bin/sh
SERVER=127.0.0.1
PORT=8000
# You may want to configure your TCP settings to make many ports available
# to node and ab. On macintosh use:
# sudo sysctl -w net.inet.ip.portrange.first=32768
# sudo sysctl -w net.inet.tcp.msl=1000
if [ ! -d benchmark/ ]; then
echo "Run this script from the node root directory"
exit 1
fi
if [ $SERVER == "127.0.0.1" ]; then
./node benchmark/http_simple.js &
node_pid=$!
sleep 1
fi
info=`curl -s http://$SERVER:$PORT/info`
eval $info
date=`date "+%Y%m%d%H%M%S"`
ab_hello_world() {
local type="$1"
local ressize="$2"
if [ $type == "string" ]; then
local uri="bytes/$ressize"
else
local uri="buffer/$ressize"
fi
name="ab-hello-world-$type-$ressize"
dir=".benchmark_reports/$name/$rev/"
if [ ! -d $dir ]; then
mkdir -p $dir
fi
summary_fn="$dir/$date.summary"
data_fn="$dir/$date.data"
echo "Bench $name starts in 3 seconds..."
# let shit calm down
sleep 3
# hammer that as hard as it can for 10 seconds.
ab -g $data_fn -c 100 -t 10 http://$SERVER:$PORT/$uri > $summary_fn
# add our data about the server
echo >> $summary_fn
echo >> $summary_fn
echo "webserver-rev: $rev" >> $summary_fn
echo "webserver-uname: $uname" >> $summary_fn
grep Req $summary_fn
echo "Summary: $summary_fn"
echo
}
# 1k
ab_hello_world 'string' '1024'
ab_hello_world 'buffer' '1024'
# 100k
ab_hello_world 'string' '102400'
ab_hello_world 'buffer' '102400'
if [ ! -z $node_pid ]; then
kill -9 $node_pid
fi

49
benchmark/idle_clients.js Normal file
View File

@@ -0,0 +1,49 @@
net = require('net');
var errors = 0, connections = 0;
var lastClose = 0;
function connect () {
process.nextTick(function () {
var s = net.Stream();
var gotConnected = false;
s.connect(9000);
s.on('connect', function () {
gotConnected = true;
connections++;
connect();
});
s.on('close', function () {
if (gotConnected) connections--;
lastClose = new Date();
});
s.on('error', function () {
errors++;
});
});
}
connect();
var oldConnections, oldErrors;
// Try to start new connections every so often
setInterval(connect, 5000);
setInterval(function () {
if (oldConnections != connections) {
oldConnections = connections;
console.log("CLIENT %d connections: %d", process.pid, connections);
}
if (oldErrors != errors) {
oldErrors = errors;
console.log("CLIENT %d errors: %d", process.pid, errors);
}
}, 1000);

31
benchmark/idle_server.js Normal file
View File

@@ -0,0 +1,31 @@
net = require('net');
connections = 0;
var errors = 0;
server = net.Server(function (socket) {
socket.on('error', function () {
errors++;
});
});
//server.maxConnections = 128;
server.listen(9000);
var oldConnections, oldErrors;
setInterval(function () {
if (oldConnections != server.connections) {
oldConnections = server.connections;
console.log("SERVER %d connections: %d", process.pid, server.connections);
}
if (oldErrors != errors) {
oldErrors = errors;
console.log("SERVER %d errors: %d", process.pid, errors);
}
}, 1000);

View File

@@ -5,13 +5,15 @@
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
int tsize = 1000 * 1048576;
const char *path = "/tmp/wt.dat";
int c = 0;
char* bufit(size_t l)
{
@@ -24,7 +26,7 @@ void writetest(int size, size_t bsize)
{
int i;
char *buf = bufit(bsize);
clock_t start, end;
struct timeval start, end;
double elapsed;
double mbps;
@@ -34,9 +36,10 @@ void writetest(int size, size_t bsize)
exit(254);
}
start = clock();
assert(0 == gettimeofday(&start, NULL));
for (i = 0; i < size; i += bsize) {
int rv = write(fd, buf, bsize);
if (c++ % 2000 == 0) fprintf(stderr, ".");
if (rv < 0) {
perror("write failed");
exit(254);
@@ -48,10 +51,10 @@ void writetest(int size, size_t bsize)
fsync(fd);
#endif
close(fd);
end = clock();
elapsed = ((double) (end - start)) / CLOCKS_PER_SEC;
assert(0 == gettimeofday(&end, NULL));
elapsed = (end.tv_sec - start.tv_sec) + ((double)(end.tv_usec - start.tv_usec))/100000.;
mbps = ((tsize/elapsed)) / 1048576;
fprintf(stderr, "Wrote %d bytes in %03fs using %d byte buffers: %03fmB/s\n", size, elapsed, bsize, mbps);
fprintf(stderr, "\nWrote %d bytes in %03fs using %ld byte buffers: %03fmB/s\n", size, elapsed, bsize, mbps);
free(buf);
}
@@ -60,7 +63,7 @@ void readtest(int size, size_t bsize)
{
int i;
char *buf = bufit(bsize);
clock_t start, end;
struct timeval start, end;
double elapsed;
double mbps;
@@ -70,7 +73,7 @@ void readtest(int size, size_t bsize)
exit(254);
}
start = clock();
assert(0 == gettimeofday(&start, NULL));
for (i = 0; i < size; i += bsize) {
int rv = read(fd, buf, bsize);
if (rv < 0) {
@@ -79,10 +82,10 @@ void readtest(int size, size_t bsize)
}
}
close(fd);
end = clock();
elapsed = ((double) (end - start)) / CLOCKS_PER_SEC;
assert(0 == gettimeofday(&end, NULL));
elapsed = (end.tv_sec - start.tv_sec) + ((double)(end.tv_usec - start.tv_usec))/100000.;
mbps = ((tsize/elapsed)) / 1048576;
fprintf(stderr, "Read %d bytes in %03fs using %d byte buffers: %03fmB/s\n", size, elapsed, bsize, mbps);
fprintf(stderr, "Read %d bytes in %03fs using %ld byte buffers: %03fmB/s\n", size, elapsed, bsize, mbps);
free(buf);
}

View File

@@ -1,4 +1,5 @@
var fs = require('fs');
var sys = require('sys');
var Buffer = require('buffer').Buffer;
var path = "/tmp/wt.dat";
@@ -21,6 +22,8 @@ function once(emitter, name, cb) {
emitter.addListener(name, incb);
}
c = 0
function writetest(size, bsize) {
var s = fs.createWriteStream(path, {'flags': 'w', 'mode': 0644});
var remaining = size;
@@ -40,6 +43,7 @@ function writetest(size, bsize) {
s.on('drain', function () {
dowrite();
if (c++ % 2000 == 0) sys.print(".");
});
dowrite();

26
benchmark/startup.js Normal file
View File

@@ -0,0 +1,26 @@
var spawn = require('child_process').spawn,
path = require('path'),
emptyJsFile = path.join(__dirname, '../test/fixtures/semicolon.js'),
starts = 100,
i = 0,
start;
function startNode() {
var node = spawn(process.execPath || process.argv[0], [emptyJsFile]);
node.on('exit', function(exitCode) {
if (exitCode !== 0) {
throw new Error('Error during node startup');
}
i++;
if (i < starts) {
startNode();
} else{
var duration = +new Date - start;
console.log('Started node %d times in %s ms. %d ms / start.', starts, duration, duration / starts);
}
});
}
start = +new Date;
startNode();

View File

@@ -1,6 +1,6 @@
for (var i = 0; i < 9000000; i++) {
for (var i = 0; i < 9e7; i++) {
s = '01234567890';
s[1] = "a";
}

View File

@@ -39,10 +39,10 @@ like this for a request parser:
settings.on_path = my_path_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
settings.data = my_socket;
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
When data is received on the socket execute the parser and check for errors.

View File

@@ -19,16 +19,6 @@
* IN THE SOFTWARE.
*/
#include <http_parser.h>
#ifdef _WIN32
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int16 int32_t;
typedef unsigned __int32 uint32_t;
#else
#include <stdint.h>
#endif
#include <assert.h>
#include <stddef.h>
@@ -108,7 +98,7 @@ static const char *method_strings[] =
/* ' ', '_', '-' and all alpha-numeric ascii characters are accepted by acceptable_header.
The 'A'-'Z' are lower-cased. */
static const unsigned char acceptable_header[256] = {
static const char acceptable_header[256] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0, 0, 0, 0, 0, 0, 0, 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
@@ -143,7 +133,49 @@ static const unsigned char acceptable_header[256] = {
'x', 'y', 'z', 0, 0, 0, 0, 0 };
static const int unhex[256] =
/* Tokens as defined by rfc 2616. Also lowercases them.
* token = 1*<any CHAR except CTLs or separators>
* separators = "(" | ")" | "<" | ">" | "@"
* | "," | ";" | ":" | "\" | <">
* | "/" | "[" | "]" | "?" | "="
* | "{" | "}" | SP | HT
*/
static const char tokens[256] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0, 0, 0, 0, 0, 0, 0, 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
0, 0, 0, 0, 0, 0, 0, 0,
/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
0, 0, 0, 0, 0, 0, 0, 0,
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
0, 0, 0, 0, 0, 0, 0, 0,
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
' ', '!', '"', '#', '$', '%', '&', '\'',
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
0, 0, '*', '+', 0, '-', '.', '/',
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
'0', '1', '2', '3', '4', '5', '6', '7',
/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
'8', '9', 0, 0, 0, 0, 0, 0,
/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
'x', 'y', 'z', 0, 0, 0, '^', '_',
/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
'x', 'y', 'z', 0, '|', '}', '~', 0 };
static const int8_t unhex[256] =
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
@@ -155,7 +187,7 @@ static const int unhex[256] =
};
static const int normal_url_char[256] = {
static const uint8_t normal_url_char[256] = {
/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
0, 0, 0, 0, 0, 0, 0, 0,
/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
@@ -302,6 +334,7 @@ enum flags
#define CR '\r'
#define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20)
#define TOKEN(c) tokens[(unsigned char)c]
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
@@ -600,7 +633,7 @@ size_t http_parser_execute (http_parser *parser,
if (ch == ' ' && matcher[index] == '\0') {
state = s_req_spaces_before_url;
} else if (ch == matcher[index]) {
; // nada
; /* nada */
} else if (parser->method == HTTP_CONNECT) {
if (index == 1 && ch == 'H') {
parser->method = HTTP_CHECKOUT;
@@ -772,7 +805,7 @@ size_t http_parser_execute (http_parser *parser,
switch (ch) {
case '?':
break; // XXX ignore extra '?' ... is this right?
break; /* XXX ignore extra '?' ... is this right? */
case ' ':
CALLBACK(url);
state = s_req_http_start;
@@ -802,7 +835,7 @@ size_t http_parser_execute (http_parser *parser,
switch (ch) {
case '?':
// allow extra '?' in query string
/* allow extra '?' in query string */
break;
case ' ':
CALLBACK(url);
@@ -1006,9 +1039,9 @@ size_t http_parser_execute (http_parser *parser,
goto headers_almost_done;
}
c = LOWER(ch);
c = TOKEN(ch);
if (c < 'a' || 'z' < c) goto error;
if (!c) goto error;
MARK(header_field);
@@ -1041,7 +1074,7 @@ size_t http_parser_execute (http_parser *parser,
case s_header_field:
{
c = acceptable_header[(unsigned char)ch];
c = TOKEN(ch);
if (c) {
switch (header_state) {
@@ -1264,6 +1297,7 @@ size_t http_parser_execute (http_parser *parser,
break;
case h_content_length:
if (ch == ' ') break;
if (ch < '0' || ch > '9') goto error;
parser->content_length *= 10;
parser->content_length += ch - '0';
@@ -1376,7 +1410,7 @@ size_t http_parser_execute (http_parser *parser,
}
}
// Exit, the rest of the connect is in a different protocol.
/* Exit, the rest of the connect is in a different protocol. */
if (parser->upgrade) {
CALLBACK2(message_complete);
return (p - data);
@@ -1437,7 +1471,7 @@ size_t http_parser_execute (http_parser *parser,
{
assert(parser->flags & F_CHUNKED);
c = unhex[(int)ch];
c = unhex[(unsigned char)ch];
if (c == -1) goto error;
parser->content_length = c;
state = s_chunk_size;
@@ -1453,7 +1487,7 @@ size_t http_parser_execute (http_parser *parser,
break;
}
c = unhex[(int)ch];
c = unhex[(unsigned char)ch];
if (c == -1) {
if (ch == ';' || ch == ' ') {
@@ -1545,6 +1579,7 @@ size_t http_parser_execute (http_parser *parser,
return len;
error:
parser->state = s_dead;
return (p - data);
}

View File

@@ -26,11 +26,20 @@ extern "C" {
#include <sys/types.h>
#include <stdint.h>
#ifdef _WIN32
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned int size_t;
typedef int ssize_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run

View File

@@ -31,7 +31,7 @@
#undef FALSE
#define FALSE 0
#define MAX_HEADERS 10
#define MAX_HEADERS 13
#define MAX_ELEMENT_SIZE 500
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -833,6 +833,71 @@ const struct message responses[] =
,.body= "<xml>hello</xml>"
}
#define RES_FIELD_UNDERSCORE 10
/* Should handle spaces in header fields */
, {.name= "field underscore"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
"Server: Apache\r\n"
"Cache-Control: no-cache, must-revalidate\r\n"
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
"Set-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
"Vary: Accept-Encoding\r\n"
"_eep-Alive: timeout=45\r\n" /* semantic value ignored */
"_onnection: Keep-Alive\r\n" /* semantic value ignored */
"Transfer-Encoding: chunked\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n"
"\r\n"
"0\r\n\r\n"
,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.num_headers= 11
,.headers=
{ { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
, { "Server", "Apache" }
, { "Cache-Control", "no-cache, must-revalidate" }
, { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
, { "Set-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
, { "Vary", "Accept-Encoding" }
, { "_eep-Alive", "timeout=45" }
, { "_onnection", "Keep-Alive" }
, { "Transfer-Encoding", "chunked" }
, { "Content-Type", "text/html" }
, { "Connection", "close" }
}
,.body= ""
}
#define NON_ASCII_IN_STATUS_LINE 11
/* Should handle non-ASCII in status line */
, {.name= "non-ASCII in status line"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
"Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
"Content-Length: 0\r\n"
"Connection: close\r\n"
"\r\n"
,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 500
,.num_headers= 3
,.headers=
{ { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
, { "Content-Length", "0" }
, { "Connection", "close" }
}
,.body= ""
}
, {.name= NULL } /* sentinel */
};

18
deps/libeio/eio.c vendored
View File

@@ -230,6 +230,10 @@ static xmutex_t reslock = X_MUTEX_INIT;
static xmutex_t reqlock = X_MUTEX_INIT;
static xcond_t reqwait = X_COND_INIT;
#if defined (__APPLE__)
static xmutex_t apple_bug_writelock = X_MUTEX_INIT;
#endif
#if !HAVE_PREADWRITE
/*
* make our pread/pwrite emulation safe against themselves, but not against
@@ -1579,9 +1583,19 @@ static void eio_execute (etp_worker *self, eio_req *req)
req->result = req->offs >= 0
? pread (req->int1, req->ptr2, req->size, req->offs)
: read (req->int1, req->ptr2, req->size); break;
case EIO_WRITE: req->result = req->offs >= 0
case EIO_WRITE:
#if defined (__APPLE__)
pthread_mutex_lock (&apple_bug_writelock);
#endif
req->result = req->offs >= 0
? pwrite (req->int1, req->ptr2, req->size, req->offs)
: write (req->int1, req->ptr2, req->size); break;
: write (req->int1, req->ptr2, req->size);
#if defined (__APPLE__)
pthread_mutex_unlock (&apple_bug_writelock);
#endif
break;
case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break;
case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size, self); break;

2
deps/v8/SConstruct vendored
View File

@@ -657,7 +657,7 @@ def GuessToolchain(os):
elif 'msvc' in tools:
return 'msvc'
else:
return None
return 'gcc'
OS_GUESS = utils.GuessOS()

View File

@@ -2601,7 +2601,8 @@ bool JSObject::ReferencesObject(Object* obj) {
Object* JSObject::PreventExtensions() {
// If there are fast elements we normalize.
if (HasFastElements()) {
NormalizeElements();
Object* ok = NormalizeElements();
if (ok->IsFailure()) return ok;
}
// Make sure that we never go back to fast case.
element_dictionary()->set_requires_slow_elements();

View File

@@ -0,0 +1,32 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var i = 0;
for (var i = 0; i < 10000; i++) {
Object.freeze({});
assertFalse(JSON.stringify({x: null}).match(/\0/));
}

View File

@@ -860,8 +860,9 @@ Experimental
Read the data from `readableStream` and send it to the `writableStream`.
When `writeableStream.write(data)` returns `false` `readableStream` will be
paused until the `drain` event occurs on the `writableStream`. `callback` is
called when `writableStream` is closed.
paused until the `drain` event occurs on the `writableStream`. `callback` gets
an error as its only argument and is called when `writableStream` is closed or
when an error occurs.
## Timers
@@ -911,9 +912,6 @@ normally, `code` is the final exit code of the process, otherwise `null`. If
the process terminated due to receipt of a signal, `signal` is the string name
of the signal, otherwise `null`.
After this event is emitted, the `'output'` and `'error'` callbacks will no
longer be made.
See `waitpid(2)`.
### child.stdin
@@ -1057,14 +1055,14 @@ There is a second optional argument to specify several options. The default opti
{ encoding: 'utf8'
, timeout: 0
, maxBuffer: 200*1024
, killSignal: 'SIGKILL'
, killSignal: 'SIGTERM'
, cwd: null
, env: null
}
If `timeout` is greater than 0, then it will kill the child process
if it runs longer than `timeout` milliseconds. The child process is killed with
`killSignal` (default: `'SIGKILL'`). `maxBuffer` specifies the largest
`killSignal` (default: `'SIGTERM'`). `maxBuffer` specifies the largest
amount of data allowed on stdout or stderr - if this value is exceeded then
the child process is killed.
@@ -1447,19 +1445,6 @@ See pwrite(2).
The callback will be given two arguments `(err, written)` where `written`
specifies how many _bytes_ were written.
### fs.write(fd, str, position, encoding='utf8', [callback])
Write the entire string `str` using the given `encoding` to the file specified
by `fd`.
`position` refers to the offset from the beginning of the file where this data
should be written. If `position` is `null`, the data will be written at the
current position.
See pwrite(2).
The callback will be given two arguments `(err, written)` where `written`
specifies how many _bytes_ were written.
### fs.writeSync(fd, buffer, offset, length, position)
Synchronous version of buffer-based `fs.write()`. Returns the number of bytes written.
@@ -1483,19 +1468,6 @@ If `position` is `null`, data will be read from the current file position.
The callback is given the two arguments, `(err, bytesRead)`.
### fs.read(fd, length, position, encoding, [callback])
Read data from the file specified by `fd`.
`length` is an integer specifying the number of bytes to read.
`position` is an integer specifying where to begin reading from in the file.
If `position` is `null`, data will be read from the current file position.
`encoding` is the desired encoding of the string of data read in from `fd`.
The callback is given the three arguments, `(err, str, bytesRead)`.
### fs.readSync(fd, buffer, offset, length, position)
Synchronous version of buffer-based `fs.read`. Returns the number of `bytesRead`.
@@ -1607,6 +1579,12 @@ An example to read the last 10 bytes of a file which is 100 bytes long:
`WriteStream` is a `Writable Stream`.
### Event: 'open'
`function (fd) { }`
`fd` is the file descriptor used by the WriteStream.
### fs.createWriteStream(path, [options])
Returns a new WriteStream object (See `Writable Stream`).
@@ -2225,7 +2203,7 @@ See `connect()`.
`function () { }`
Emitted when a stream connection successfully establishes a HTTPS handshake with its peer.
Emitted when a stream connection successfully establishes an SSL handshake with its peer.
### Event: 'data'
@@ -2314,9 +2292,9 @@ received.
### stream.setSecure([credentials])
Enables HTTPS support for the stream, with the crypto module credentials specifying the private key and certificate of the stream, and optionally the CA certificates for use in peer authentication.
Enables SSL support for the stream, with the crypto module credentials specifying the private key and certificate of the stream, and optionally the CA certificates for use in peer authentication.
If the credentials hold one ore more CA certificates, then the stream will request for the peer to submit a client certificate as part of the HTTPS connection handshake. The validity and content of this can be accessed via verifyPeer() and getPeerCertificate().
If the credentials hold one ore more CA certificates, then the stream will request for the peer to submit a client certificate as part of the SSL connection handshake. The validity and content of this can be accessed via verifyPeer() and getPeerCertificate().
### stream.verifyPeer()
@@ -3284,3 +3262,49 @@ All Node addons must export a function called `init` with this signature:
For the moment, that is all the documentation on addons. Please see
<http://github.com/ry/node_postgres> for a real example.
## Appendix - Third Party Modules
There are many third party modules for Node. At the time of writing, August
2010, the master repository of modules is
http://github.com/ry/node/wiki/modules[the wiki page].
This appendix is intended as a SMALL guide to new-comers to help them
quickly find what are considered to be quality modules. It is not intended
to be a complete list. There may be better more complete modules found
elsewhere.
- Module Installer: [npm](http://github.com/isaacs/npm)
- HTTP Middleware: [Connect](http://github.com/senchalabs/connect)
- Web Framework: [Express](http://github.com/visionmedia/express)
- Web Sockets: [Socket.IO](http://github.com/LearnBoost/Socket.IO-node)
- HTML Parsing: [HTML5](http://github.com/aredridel/html5)
- [mDNS/Zeroconf/Bonjour](http://github.com/agnat/node_mdns)
- [RabbitMQ, AMQP](http://github.com/ry/node-amqp)
- [mysql](http://github.com/felixge/node-mysql)
- Serialization: [msgpack](http://github.com/pgriess/node-msgpack)
- Scraping: [Apricot](http://github.com/silentrob/Apricot)
- Debugger: [ndb](http://github.com/smtlaissezfaire/ndb) is a CLI debugger
[inspector](http://github.com/dannycoates/node-inspector) is a web based
tool.
- [pcap binding](http://github.com/mranney/node_pcap)
- [ncurses](http://github.com/mscdex/node-ncurses)
- Testing/TDD/BDD: [vows](http://vowsjs.org/),
[expresso](http://github.com/visionmedia/expresso),
[mjsunit.runner](http://github.com/tmpvar/mjsunit.runner)
Patches to this list are welcome.

View File

@@ -295,7 +295,7 @@
<body>
<div id="toc">
<div id="toctitle">Node v0.2.0</div>
<div id="toctitle">Node v0.2.6</div>
<noscript>JavaScript must be enabled in your browser to display the table of contents.</noscript>
</div>
<div id='man'>

View File

@@ -23,6 +23,10 @@ NodeDoc.generateToc = function()
cur_level = this.tagName.substr(1, 1);
if (last_level != 0 && cur_level <= last_level) {
html.push("</li>")
}
if (cur_level > last_level)
{
html.push('<ul><li>');
@@ -38,15 +42,11 @@ NodeDoc.generateToc = function()
}
html.push('<a href="#' + $this.attr('id') + '">' + $this.text().replace(/\(.*\)$/gi, '') + '</a>');
if (cur_level == last_level || cur_level > last_level)
{
html.push('</li>');
}
last_level = cur_level;
});
html.push('</ul>');
html.push('</li></ul>');
var $toc = $('#toc').append(html.join('')).find('ul li ul').each(function()
{
@@ -175,4 +175,4 @@ NodeDoc.setupSmoothScrolling = function()
}
});
};
NodeDoc.init();
NodeDoc.init();

View File

@@ -89,8 +89,13 @@ net.createServer(function (socket) {
<a href="http://github.com/ry/node/tree/master">git repo</a>
</p>
<p>
2010.08.20
<a href="http://nodejs.org/dist/node-v0.2.0.tar.gz">node-v0.2.0.tar.gz</a>
Stable: 2010.12.30
<a href="http://nodejs.org/dist/node-v0.2.6.tar.gz">node-v0.2.6.tar.gz</a>
</p>
<p>
Unstable: 2010.12.16
<a href="http://nodejs.org/dist/node-v0.3.2.tar.gz">node-v0.3.2.tar.gz</a>
</p>
<p>Historical: <a href="http://nodejs.org/dist">versions</a>, <a href="http://nodejs.org/docs">docs</a></p>

View File

@@ -131,6 +131,15 @@ function _deepEqual(actual, expected) {
if (actual === expected) {
return true;
} else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
if (actual.length != expected.length) return false;
for (var i = 0; i < actual.length; i++) {
if (actual[i] !== expected[i]) return false;
}
return true;
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (actual instanceof Date && expected instanceof Date) {

View File

@@ -27,7 +27,7 @@ exports.execFile = function (file /* args, options, callback */) {
var options = { encoding: 'utf8'
, timeout: 0
, maxBuffer: 200*1024
, killSignal: 'SIGKILL'
, killSignal: 'SIGTERM'
, cwd: null
, env: null
};
@@ -60,15 +60,43 @@ exports.execFile = function (file /* args, options, callback */) {
var stdout = "";
var stderr = "";
var killed = false;
var exited = false;
var timeoutId;
function exithandler (code, signal) {
if (exited) return;
exited = true;
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
if (!callback) return;
if (code === 0 && signal === null) {
callback(null, stdout, stderr);
} else {
var e = new Error("Command failed: " + stderr);
e.killed = child.killed || killed;
e.code = code;
e.signal = signal;
callback(e, stdout, stderr);
}
}
function kill () {
killed = true;
child.kill(options.killSignal);
process.nextTick(function () {
exithandler(null, options.killSignal)
});
}
if (options.timeout > 0) {
timeoutId = setTimeout(function () {
if (!killed) {
child.kill(options.killSignal);
killed = true;
timeoutId = null;
}
kill();
timeoutId = null;
}, options.timeout);
}
@@ -77,32 +105,19 @@ exports.execFile = function (file /* args, options, callback */) {
child.stdout.addListener("data", function (chunk) {
stdout += chunk;
if (!killed && stdout.length > options.maxBuffer) {
child.kill(options.killSignal);
killed = true;
if (stdout.length > options.maxBuffer) {
kill();
}
});
child.stderr.addListener("data", function (chunk) {
stderr += chunk;
if (!killed && stderr.length > options.maxBuffer) {
child.kill(options.killSignal);
killed = true
if (stderr.length > options.maxBuffer) {
kill();
}
});
child.addListener("exit", function (code, signal) {
if (timeoutId) clearTimeout(timeoutId);
if (code === 0 && signal === null) {
if (callback) callback(null, stdout, stderr);
} else {
var e = new Error("Command failed: " + stderr);
e.killed = killed;
e.code = code;
e.signal = signal;
if (callback) callback(e, stdout, stderr);
}
});
child.addListener("exit", exithandler);
return child;
};
@@ -113,6 +128,8 @@ function ChildProcess () {
var self = this;
this.killed = false;
var gotCHLD = false;
var exitCode;
var termSignal;
@@ -158,7 +175,11 @@ inherits(ChildProcess, EventEmitter);
ChildProcess.prototype.kill = function (sig) {
return this._internal.kill(sig);
if (this._internal.pid) {
this.killed = true;
sig = sig || 'SIGTERM';
return this._internal.kill(sig);
}
};

View File

@@ -11,10 +11,9 @@ timer.callback = function () {
var sockets = Object.keys(activeWatchers);
for (var i = 0, l = sockets.length; i < l; i++) {
var socket = sockets[i];
var s = parseInt(socket);
channel.processFD( watchers[socket].read ? s : dns.SOCKET_BAD
, watchers[socket].write ? s : dns.SOCKET_BAD
);
var s = parseInt(socket, 10);
channel.processFD(watchers[socket].read ? s : dns.SOCKET_BAD,
watchers[socket].write ? s : dns.SOCKET_BAD);
}
updateTimer();
}
@@ -50,20 +49,19 @@ var channel = new dns.Channel({SOCK_STATE_CB: function (socket, read, write) {
};
watcher.callback = function(read, write) {
channel.processFD( read ? socket : dns.SOCKET_BAD
, write ? socket : dns.SOCKET_BAD
);
channel.processFD(read ? socket : dns.SOCKET_BAD,
write ? socket : dns.SOCKET_BAD);
updateTimer();
}
}
watcher.set(socket, read == 1, write == 1);
watcher.stop();
if (!(read || write)) {
watcher.stop();
delete activeWatchers[socket];
return;
} else {
watcher.set(socket, read == 1, write == 1);
watcher.start();
activeWatchers[socket] = watcher;
}

View File

@@ -1,8 +1,8 @@
exports.EventEmitter = process.EventEmitter;
var EventEmitter = exports.EventEmitter = process.EventEmitter;
var isArray = Array.isArray;
process.EventEmitter.prototype.emit = function (type) {
EventEmitter.prototype.emit = function (type) {
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events || !this._events.error ||
@@ -18,27 +18,32 @@ process.EventEmitter.prototype.emit = function (type) {
}
if (!this._events) return false;
if (!this._events[type]) return false;
var handler = this._events[type];
if (!handler) return false;
if (typeof this._events[type] == 'function') {
if (arguments.length < 3) {
// fast case
this._events[type].call( this
, arguments[1]
, arguments[2]
);
} else {
if (typeof handler == 'function') {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
var args = Array.prototype.slice.call(arguments, 1);
this._events[type].apply(this, args);
default:
var args = Array.prototype.slice.call(arguments, 1);
handler.apply(this, args);
}
return true;
} else if (isArray(this._events[type])) {
} else if (isArray(handler)) {
var args = Array.prototype.slice.call(arguments, 1);
var listeners = this._events[type].slice(0);
var listeners = handler.slice();
for (var i = 0, l = listeners.length; i < l; i++) {
listeners[i].apply(this, args);
}
@@ -49,9 +54,9 @@ process.EventEmitter.prototype.emit = function (type) {
}
};
// process.EventEmitter is defined in src/node_events.cc
// process.EventEmitter.prototype.emit() is also defined there.
process.EventEmitter.prototype.addListener = function (type, listener) {
// EventEmitter is defined in src/node_events.cc
// EventEmitter.prototype.emit() is also defined there.
EventEmitter.prototype.addListener = function (type, listener) {
if ('function' !== typeof listener) {
throw new Error('addListener only takes instances of Function');
}
@@ -76,9 +81,9 @@ process.EventEmitter.prototype.addListener = function (type, listener) {
return this;
};
process.EventEmitter.prototype.on = process.EventEmitter.prototype.addListener;
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
process.EventEmitter.prototype.removeListener = function (type, listener) {
EventEmitter.prototype.removeListener = function (type, listener) {
if ('function' !== typeof listener) {
throw new Error('removeListener only takes instances of Function');
}
@@ -101,13 +106,13 @@ process.EventEmitter.prototype.removeListener = function (type, listener) {
return this;
};
process.EventEmitter.prototype.removeAllListeners = function (type) {
EventEmitter.prototype.removeAllListeners = function (type) {
// does not use listeners(), so no side effect of creating _events[type]
if (type && this._events && this._events[type]) this._events[type] = null;
return this;
};
process.EventEmitter.prototype.listeners = function (type) {
EventEmitter.prototype.listeners = function (type) {
if (!this._events) this._events = {};
if (!this._events[type]) this._events[type] = [];
if (!isArray(this._events[type])) {

345
lib/fs.js
View File

@@ -6,7 +6,7 @@ var binding = process.binding('fs');
var fs = exports;
var kMinPoolSpace = 128;
var kPoolSize = 40*1024;
var kPoolSize = 40 * 1024;
fs.Stats = binding.Stats;
@@ -56,7 +56,7 @@ fs.readFile = function (path, encoding_, callback) {
function doRead() {
if (size < 1) {
binding.close(fd);
callback(null, buffer);
callback(null, encoding ? '' : buffer);
return;
}
// position is offset or null so we can read files on unseekable mediums
@@ -140,8 +140,11 @@ fs.closeSync = function (fd) {
return binding.close(fd);
};
fs.open = function (path, flags, mode, callback) {
if (mode === undefined) { mode = 0666; }
fs.open = function (path, flags, mode_, callback) {
var mode = (typeof(mode_) == 'number' ? mode_ : 0666);
var callback_ = arguments[arguments.length - 1];
var callback = (typeof(callback_) == 'function' ? callback_ : null);
binding.open(path, stringToFlags(flags), mode, callback || noop);
};
@@ -208,7 +211,15 @@ fs.write = function (fd, buffer, offset, length, position, callback) {
offset = 0;
length = buffer.length;
}
if(!length) return;
if (!length) {
if (typeof callback == 'function') {
process.nextTick(function() {
callback(undefined, 0);
});
}
return;
}
binding.write(fd, buffer, offset, length, position, callback || noop);
};
@@ -363,17 +374,18 @@ fs.chownSync = function(path, uid, gid) {
return binding.chown(path, uid, gid);
};
function writeAll (fd, buffer, callback) {
fs.write(fd, buffer, 0, buffer.length, null, function (writeErr, written) {
function writeAll (fd, buffer, offset, length, callback) {
// write(fd, buffer, offset, length, position, callback)
fs.write(fd, buffer, offset, length, offset, function (writeErr, written) {
if (writeErr) {
fs.close(fd, function () {
if (callback) callback(writeErr);
});
} else {
if (written === buffer.length) {
if (written === length) {
fs.close(fd, callback);
} else {
writeAll(fd, buffer.slice(written), callback);
writeAll(fd, buffer, offset+written, length-written, callback);
}
}
});
@@ -388,18 +400,21 @@ fs.writeFile = function (path, data, encoding_, callback) {
if (callback) callback(openErr);
} else {
var buffer = Buffer.isBuffer(data) ? data : new Buffer(data, encoding);
writeAll(fd, buffer, callback);
writeAll(fd, buffer, 0, buffer.length, callback);
}
});
};
fs.writeFileSync = function (path, data, encoding) {
encoding = encoding || "utf8"; // default to utf8
var fd = fs.openSync(path, "w");
if (!Buffer.isBuffer(data)) {
data = new Buffer(data, encoding || "utf8")
}
var written = 0;
while (written < data.length) {
written += fs.writeSync(fd, data, 0, encoding);
data = data.slice(written);
var length = data.length;
//writeSync(fd, buffer, offset, length, position)
while (written < length) {
written += fs.writeSync(fd, data, written, length-written, written);
}
fs.closeSync(fd);
};
@@ -445,6 +460,7 @@ fs.watchFile = function (filename) {
};
fs.unwatchFile = function (filename) {
var stat;
if (statWatchers[filename]) {
stat = statWatchers[filename];
stat.stop();
@@ -458,132 +474,127 @@ var path = require('path');
var normalize = path.normalize;
var normalizeArray = path.normalizeArray;
fs.realpathSync = function (path) {
var seen_links = {}, knownHards = {}, buf, i = 0, part, x, stats;
if (path.charAt(0) !== '/') {
var cwd = process.cwd().split('/');
path = cwd.concat(path.split('/'));
path = normalizeArray(path);
i = cwd.length;
buf = [].concat(cwd);
} else {
path = normalizeArray(path.split('/'));
// realpath
// Not using realpath(2) because it's bad.
// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
fs.realpathSync = realpathSync;
fs.realpath = realpath;
function realpathSync (p) {
if (p.charAt(0) !== '/') {
p = path.join(process.cwd(), p);
}
p = p.split('/');
var buf = [ '' ];
var seenLinks = {};
var knownHard = {};
// walk down the path, swapping out linked pathparts for their real
// values, and pushing non-link path bits onto the buffer.
// then return the buffer.
// NB: path.length changes.
for (var i = 0; i < p.length; i ++) {
// skip over empty path parts.
if (p[i] === '') continue;
var part = buf.join('/')+'/'+p[i];
if (knownHard[part]) {
buf.push( p[i] );
continue;
}
var stat = fs.lstatSync(part);
if (!stat.isSymbolicLink()) {
// not a symlink. easy.
knownHard[ part ] = true;
buf.push(p[i]);
continue;
}
var id = stat.dev.toString(32)+':'+stat.ino.toString(32);
if (!seenLinks[id]) {
fs.statSync(part);
seenLinks[id] = fs.readlinkSync(part);
}
var target = seenLinks[id];
if (target.charAt(0) === '/') {
// absolute. Start over.
buf = [''];
p = path.normalizeArray(target.split('/').concat(p.slice(i + 1)));
i = 0;
continue;
}
// not absolute. join and splice.
target = target.split('/');
Array.prototype.splice.apply(p, [i, 1].concat(target));
p = path.normalizeArray(p);
i = 0;
buf = [''];
}
for (; i<path.length; i++) {
part = path.slice(0, i+1).join('/');
if (part.length !== 0) {
if (part in knownHards) {
buf.push(path[i]);
} else {
stats = fs.lstatSync(part);
if (stats.isSymbolicLink()) {
x = stats.dev.toString(32)+":"+stats.ino.toString(32);
if (x in seen_links)
throw new Error("cyclic link at "+part);
seen_links[x] = true;
part = fs.readlinkSync(part);
if (part.charAt(0) === '/') {
// absolute
path = normalizeArray(part.split('/'));
buf = [''];
i = 0;
} else {
// relative
Array.prototype.splice.apply(path, [i, 1].concat(part.split('/')));
part = normalizeArray(path);
var y = 0, L = Math.max(path.length, part.length), delta;
for (; y<L && path[y] === part[y]; y++);
if (y !== L) {
path = part;
delta = i-y;
i = y-1;
if (delta > 0) buf.splice(y, delta);
} else {
i--;
}
}
} else {
buf.push(path[i]);
knownHards[buf.join('/')] = true;
}
}
}
}
return buf.join('/');
return buf.join('/') || '/';
}
fs.realpath = function (path, callback) {
var seen_links = {}, knownHards = {}, buf = [''], i = 0, part, x;
if (path.charAt(0) !== '/') {
// assumes cwd is canonical
var cwd = process.cwd().split('/');
path = cwd.concat(path.split('/'));
path = normalizeArray(path);
i = cwd.length-1;
buf = [].concat(cwd);
} else {
path = normalizeArray(path.split('/'));
function realpath (p, cb) {
if (p.charAt(0) !== '/') {
p = path.join(process.cwd(), p);
}
function done(err) {
if (callback) {
if (!err) callback(err, buf.join('/'));
else callback(err);
p = p.split('/');
var buf = [ '' ];
var seenLinks = {};
var knownHard = {};
// walk down the path, swapping out linked pathparts for their real
// values, and pushing non-link path bits onto the buffer.
// then return the buffer.
// NB: path.length changes.
var i = 0;
var part;
LOOP();
function LOOP () {
i ++;
if (!(i < p.length)) return exit();
// skip over empty path parts.
if (p[i] === '') return process.nextTick(LOOP);
part = buf.join('/')+'/'+p[i];
if (knownHard[part]) {
buf.push( p[i] );
return process.nextTick(LOOP);
}
return fs.lstat(part, gotStat);
}
function next() {
if (++i === path.length) return done();
part = path.slice(0, i+1).join('/');
if (part.length === 0) return next();
if (part in knownHards) {
buf.push(path[i]);
next();
} else {
fs.lstat(part, function(err, stats){
if (err) return done(err);
if (stats.isSymbolicLink()) {
x = stats.dev.toString(32)+":"+stats.ino.toString(32);
if (x in seen_links)
return done(new Error("cyclic link at "+part));
seen_links[x] = true;
fs.readlink(part, function(err, npart){
if (err) return done(err);
part = npart;
if (part.charAt(0) === '/') {
// absolute
path = normalizeArray(part.split('/'));
buf = [''];
i = 0;
} else {
// relative
Array.prototype.splice.apply(path, [i, 1].concat(part.split('/')));
part = normalizeArray(path);
var y = 0, L = Math.max(path.length, part.length), delta;
for (; y<L && path[y] === part[y]; y++);
if (y !== L) {
path = part;
delta = i-y;
i = y-1; // resolve new node if needed
if (delta > 0) buf.splice(y, delta);
}
else {
i--; // resolve new node if needed
}
}
next();
}); // binding.readlink
}
else {
buf.push(path[i]);
knownHards[buf.join('/')] = true;
next();
}
}); // binding.lstat
function gotStat (er, stat) {
if (er) return cb(er);
if (!stat.isSymbolicLink()) {
// not a symlink. easy.
knownHard[ part ] = true;
buf.push(p[i]);
return process.nextTick(LOOP);
}
var id = stat.dev.toString(32)+':'+stat.ino.toString(32);
if (seenLinks[id]) return gotTarget(null, seenLinks[id]);
fs.stat(part, function (er) {
if (er) return cb(er)
fs.readlink(part, function (er, target) {
gotTarget(er, seenLinks[id] = target);
});
})
}
next();
};
function gotTarget (er, target) {
if (er) return cb(er);
if (target.charAt(0) === '/') {
// absolute. Start over.
buf = [''];
p = path.normalizeArray(target.split('/').concat(p.slice(i + 1)));
i = 0;
return process.nextTick(LOOP);
}
// not absolute. join and splice.
target = target.split('/');
Array.prototype.splice.apply(p, [i, 1].concat(target));
p = path.normalizeArray(p);
i = 0;
buf = [''];
return process.nextTick(LOOP);
}
function exit () {
cb(null, buf.join('/') || '/');
}
}
var pool;
function allocNewPool () {
@@ -598,8 +609,12 @@ fs.createReadStream = function(path, options) {
};
var ReadStream = fs.ReadStream = function(path, options) {
if (!(this instanceof ReadStream)) return new ReadStream(path, options);
events.EventEmitter.call(this);
var self = this;
this.path = path;
this.fd = null;
this.readable = true;
@@ -607,7 +622,7 @@ var ReadStream = fs.ReadStream = function(path, options) {
this.flags = 'r';
this.mode = 0666;
this.bufferSize = 4 * 1024;
this.bufferSize = 64 * 1024;
options = options || {};
@@ -618,13 +633,16 @@ var ReadStream = fs.ReadStream = function(path, options) {
this[key] = options[key];
}
if(this.start || this.end) {
if(this.start === undefined || this.end === undefined) {
self.emit('error', new Error('Both start and end are needed for range streaming.'));
} else if(this.start > this.end) {
self.emit('error', new Error('start must be <= end'));
if(this.encoding) this.setEncoding(this.encoding);
if (this.start !== undefined || this.end !== undefined) {
if (this.start === undefined || this.end === undefined) {
this.emit('error',
new Error('Both start and end are needed for range streaming.'));
} else if (this.start > this.end) {
this.emit('error', new Error('start must be <= end'));
} else {
this.firstRead = true;
this._firstRead = true;
}
}
@@ -632,7 +650,6 @@ var ReadStream = fs.ReadStream = function(path, options) {
return;
}
var self = this;
fs.open(this.path, this.flags, this.mode, function(err, fd) {
if (err) {
self.emit('error', err);
@@ -666,9 +683,9 @@ ReadStream.prototype._read = function () {
allocNewPool();
}
if(this.start && this.firstRead) {
this.pos = this.start;
this.firstRead = false;
if (self.start !== undefined && self._firstRead) {
self.pos = self.start;
self._firstRead = false;
}
// Grab another reference to the pool in the case that while we're in the
@@ -678,7 +695,7 @@ ReadStream.prototype._read = function () {
var toRead = Math.min(pool.length - pool.used, this.bufferSize);
var start = pool.used;
if(this.pos) {
if (this.pos !== undefined) {
toRead = Math.min(this.end - this.pos + 1, toRead);
}
@@ -714,9 +731,9 @@ ReadStream.prototype._read = function () {
self._read();
}
fs.read(self.fd, pool, pool.used, toRead, this.pos, afterRead);
fs.read(self.fd, pool, pool.used, toRead, self.pos, afterRead);
if(self.pos) {
if (self.pos !== undefined) {
self.pos += toRead;
}
pool.used += toRead;
@@ -792,6 +809,8 @@ fs.createWriteStream = function(path, options) {
};
var WriteStream = fs.WriteStream = function(path, options) {
if (!(this instanceof WriteStream)) return new WriteStream(path, options);
events.EventEmitter.call(this);
this.path = path;
@@ -829,7 +848,10 @@ WriteStream.prototype.flush = function () {
var self = this;
var args = this._queue.shift();
if (!args) return self.emit('drain');
if (!args) {
if (this.drainable) { self.emit('drain'); }
return;
}
this.busy = true;
@@ -885,6 +907,8 @@ WriteStream.prototype.write = function (data) {
throw new Error('stream not writeable');
}
this.drainable = true;
var cb;
if (typeof(arguments[arguments.length-1]) == 'function') {
cb = arguments[arguments.length-1];
@@ -934,22 +958,27 @@ WriteStream.prototype.forceClose = function (cb) {
};
WriteStream.prototype.forceClose = function (cb) {
WriteStream.prototype.destroy = function (cb) {
var self = this;
this.writeable = false;
fs.close(self.fd, function(err) {
if (err) {
if (cb) {
cb(err);
function close() {
fs.close(self.fd, function(err) {
if (err) {
if (cb) { cb(err); }
self.emit('error', err);
return;
}
self.emit('error', err);
return;
}
if (cb) { cb(null); }
self.emit('close');
});
}
if (cb) {
cb(null);
}
self.emit('close');
});
if (this.fd) {
close();
} else {
this.addListener('open', close);
}
};

View File

@@ -519,7 +519,7 @@ OutgoingMessage.prototype.end = function (data, encoding) {
if (!hot) {
if (this.chunkedEncoding) {
ret = this._send('0\r\n\r\n'); // Last chunk.
} else if (!data) {
} else {
// Force a flush, HACK.
ret = this._send('');
}
@@ -713,6 +713,7 @@ function httpSocketSetup (socket) {
function Server (requestListener) {
if (!(this instanceof Server)) return new Server(requestListener);
net.Server.call(this);
if(requestListener){
@@ -834,60 +835,31 @@ function connectionListener (socket) {
function Client ( ) {
if (!(this instanceof Client)) return new Client();
net.Stream.call(this);
var self = this;
// Possible states:
// - disconnected
// - connecting
// - connected
this._state = 'disconnected';
httpSocketSetup(self);
var parser;
function initParser () {
if (!parser) parser = parsers.alloc();
parser.reinitialize('response');
parser.socket = self;
parser.onIncoming = function (res) {
debug("incoming response!");
var req = self._outgoing[0];
// Responses to HEAD requests are AWFUL. Ask Ryan.
// A major oversight in HTTP. Hence this nastiness.
var isHeadResponse = req.method == "HEAD";
debug('isHeadResponse ' + isHeadResponse);
res.addListener('end', function ( ) {
debug("request complete disconnecting. readyState = " + self.readyState);
// For the moment we reconnect for every request. FIXME!
// All that should be required for keep-alive is to not reconnect,
// but outgoingFlush instead.
if (req.shouldKeepAlive) {
outgoingFlush(self)
self._outgoing.shift()
outgoingFlush(self)
} else {
self.end();
}
});
req.emit("response", res);
return isHeadResponse;
};
};
self.ondata = function (d, start, end) {
if (!parser) {
function onData(d, start, end) {
if (!self.parser) {
throw new Error("parser not initialized prior to Client.ondata call");
}
var ret = parser.execute(d, start, end - start);
var ret = self.parser.execute(d, start, end - start);
if (ret instanceof Error) {
self.destroy(ret);
} else if (parser.incoming && parser.incoming.upgrade) {
} else if (self.parser.incoming && self.parser.incoming.upgrade) {
var bytesParsed = ret;
self.ondata = null;
self.onend = null
var req = self._outgoing[0];
var req = self.parser.incoming;
var upgradeHead = d.slice(start + bytesParsed + 1, end);
@@ -901,41 +873,48 @@ function Client ( ) {
self.addListener("connect", function () {
debug('client connected');
self.ondata = onData;
self.onend = onEnd;
if (this.https) {
this.setSecure(this.credentials);
} else {
initParser();
self._state = "connected";
self._initParser();
debug('requests: ' + sys.inspect(self._outgoing));
outgoingFlush(self);
}
});
self.addListener("secure", function () {
initParser();
self._state = "connected";
self._initParser();
debug('requests: ' + sys.inspect(self._outgoing));
outgoingFlush(self);
});
self.onend = function () {
if (parser) parser.finish();
debug("self got end closing. readyState = " + self.readyState);
function onEnd() {
if (self.parser) self.parser.finish();
debug("CLIENT got end closing. state = " + self._state);
self.end();
};
self.addListener("close", function (e) {
self._state = "disconnected";
if (e) return;
debug("HTTP CLIENT onClose. readyState = " + self.readyState);
debug("CLIENT onClose. state = " + self._state);
// finally done with the request
self._outgoing.shift();
// If there are more requests to handle, reconnect.
if (self._outgoing.length) {
self._reconnect();
} else if (parser) {
parsers.free(parser);
parser = null;
self._ensureConnection();
} else if (self.parser) {
parsers.free(self.parser);
self.parser = null;
}
});
};
@@ -953,6 +932,46 @@ exports.createClient = function (port, host, https, credentials) {
};
Client.prototype._initParser = function () {
var self = this;
if (!self.parser) self.parser = parsers.alloc();
self.parser.reinitialize('response');
self.parser.socket = self;
self.parser.onIncoming = function (res) {
debug("incoming response!");
var req = self._outgoing[0];
// Responses to HEAD requests are AWFUL. Ask Ryan.
// A major oversight in HTTP. Hence this nastiness.
var isHeadResponse = req.method == "HEAD";
debug('isHeadResponse ' + isHeadResponse);
if (req.shouldKeepAlive && res.headers.connection === 'close') {
req.shouldKeepAlive = false;
}
res.addListener('end', function ( ) {
debug("CLIENT request complete disconnecting. state = " + self._state);
// For the moment we reconnect for every request. FIXME!
// All that should be required for keep-alive is to not reconnect,
// but outgoingFlush instead.
if (req.shouldKeepAlive) {
outgoingFlush(self)
self._outgoing.shift()
outgoingFlush(self)
} else {
self.end();
}
});
req.emit("response", res);
return isHeadResponse;
};
};
// This is called each time a request has been pushed completely to the
// socket. The message that was sent is still sitting at client._outgoing[0]
// it is our responsibility to shift it off.
@@ -971,17 +990,18 @@ Client.prototype._onOutgoingSent = function (message) {
//
// Instead, we just check if the connection is closed, and if so
// reconnect if we have pending messages.
if (this._outgoing.length && this.readyState == "closed") {
debug("HTTP client request flush. reconnect. readyState = " + this.readyState);
this._reconnect();
if (this._outgoing.length) {
debug("CLIENT request flush. ensure connection. state = " + this._state);
this._ensureConnection();
}
};
Client.prototype._reconnect = function () {
if (this.readyState === "closed") {
debug("HTTP CLIENT: reconnecting readyState = " + this.readyState);
Client.prototype._ensureConnection = function () {
if (this._state == 'disconnected') {
debug("CLIENT reconnecting state = " + this._state);
this.connect(this.port, this.host);
this._state = "connecting";
}
};
@@ -1015,7 +1035,7 @@ Client.prototype.request = function (method, url, headers) {
}
var req = new ClientRequest(this, method, url, headers);
this._outgoing.push(req);
if (this.readyState === 'closed') this._reconnect();
this._ensureConnection();
return req;
};

View File

@@ -513,6 +513,7 @@ function initStream (self) {
}
function Stream (fd, type) {
if (!(this instanceof Stream)) return new Stream(fd, type);
events.EventEmitter.call(this);
this.fd = null;
@@ -637,13 +638,21 @@ Object.defineProperty(Stream.prototype, 'readyState', {
// something was queued. If data was queued, then the "drain" event will
// signal when it has been finally flushed to socket.
Stream.prototype.write = function (data, encoding, fd) {
if (this._writeQueue && this._writeQueue.length) {
if (this._connecting || (this._writeQueue && this._writeQueue.length)) {
if (!this._writeQueue) {
this._writeQueue = [];
this._writeQueueEncoding = [];
this._writeQueueFD = [];
}
// Slow. There is already a write queue, so let's append to it.
if (this._writeQueueLast() === END_OF_FILE) {
throw new Error('Stream.end() called already; cannot write.');
}
if (typeof data == 'string' &&
this._writeQueue.length &&
typeof this._writeQueue[this._writeQueue.length-1] === 'string' &&
this._writeQueueEncoding[this._writeQueueEncoding.length-1] === encoding) {
// optimization - concat onto last
this._writeQueue[this._writeQueue.length-1] += data;
@@ -816,6 +825,8 @@ Stream.prototype.setEncoding = function (encoding) {
function doConnect (socket, port, host) {
timeout.active(socket);
try {
connect(socket.fd, port, host);
} catch (e) {
@@ -849,13 +860,21 @@ function doConnect (socket, port, host) {
socket.destroy(e);
return;
}
if (socket._writeQueue && socket._writeQueue.length) {
// Flush socket in case any writes are queued up while connecting.
// ugly
_doFlush.call(socket._writeWatcher);
}
} else if (errno != EINPROGRESS) {
socket.destroy(errnoException(errno, 'connect'));
}
};
}
function isPort (x) { return parseInt(x) >= 0; }
function toPort (x) { return (x = Number(x)) >= 0 ? x : false }
// var stream = new Stream();
@@ -868,13 +887,21 @@ Stream.prototype.connect = function () {
if (self.fd) throw new Error('Stream already opened');
if (!self._readWatcher) throw new Error('No readWatcher');
timeout.active(socket);
timeout.active(self);
self._connecting = true; // set false in doConnect
self.writable = true;
if (isPort(arguments[0])) {
var port = toPort(arguments[0])
if (port === false) {
// UNIX
self.fd = socket('unix');
self.type = 'unix';
setImplmentationMethods(this);
doConnect(self, arguments[0]);
} else {
// TCP
var port = arguments[0];
dns.lookup(arguments[1], function (err, ip, addressType) {
if (err) {
self.emit('error', err);
@@ -884,13 +911,6 @@ Stream.prototype.connect = function () {
doConnect(self, port, ip);
}
});
} else {
// UNIX
self.fd = socket('unix');
self.type = 'unix';
setImplmentationMethods(this);
doConnect(self, arguments[0]);
}
};
@@ -1025,13 +1045,16 @@ Stream.prototype.end = function (data, encoding) {
if (data) this.write(data, encoding);
if (this._writeQueueLast() !== END_OF_FILE) {
this._writeQueue.push(END_OF_FILE);
this.flush();
if (!this._connecting) {
this.flush();
}
}
}
};
function Server (listener) {
if (!(this instanceof Server)) return new Server(listener);
events.EventEmitter.call(this);
var self = this;
@@ -1041,21 +1064,57 @@ function Server (listener) {
self.connections = 0;
self.paused = false;
self.pauseTimeout = 1000;
function pause () {
// We've hit the maximum file limit. What to do?
// Let's try again in 1 second.
self.watcher.stop();
// If we're already paused, don't do another timeout.
if (self.paused) return;
setTimeout(function () {
self.paused = false;
// Make sure we haven't closed in the interim
if (typeof self.fd != 'number') return;
self.watcher.start();
}, self.pauseTimeout);
}
self.watcher = new IOWatcher();
self.watcher.host = self;
self.watcher.callback = function () {
// Just in case we don't have a dummy fd.
getDummyFD();
if (self._pauseTimer) {
// Somehow the watcher got started again. Need to wait until
// the timer finishes.
self.watcher.stop();
}
while (self.fd) {
try {
var peerInfo = accept(self.fd);
} catch (e) {
if (e.errno == EMFILE) return;
throw e;
if (e.errno != EMFILE) throw e;
// Gracefully reject pending clients by freeing up a file
// descriptor.
rescueEMFILE(function() {
self._rejectPending();
});
return;
}
if (!peerInfo) return;
if (self.maxConnections && self.connections >= self.maxConnections) {
// Accept and close the connection.
// Close the connection we just had
close(peerInfo.fd);
// Reject all other pending connectins.
self._rejectPending();
return;
}
@@ -1090,6 +1149,47 @@ exports.createServer = function (listener) {
};
// Just stop trying to accepting connections for a while.
// Useful for throttling against DoS attacks.
Server.prototype.pause = function (msecs) {
// We're already paused.
if (this._pauseTimer) return;
var self = this;
msecs = msecs || 1000;
this.watcher.stop();
// Wait a second before accepting more.
this._pauseTimer = setTimeout(function () {
// Our fd should still be there. If someone calls server.close() then
// the pauseTimer should be cleared.
assert(parseInt(self.fd) >= 0);
self._pauseTimer = null;
self.watcher.start();
}, msecs);
};
Server.prototype._rejectPending = function () {
var self = this;
var acceptCount = 0;
// Accept and close the waiting clients one at a time.
// Single threaded programming ftw.
while (true) {
peerInfo = accept(this.fd);
if (!peerInfo) return;
close(peerInfo.fd);
// Don't become DoS'd by incoming requests
if (++acceptCount > 50) {
this.pause();
return;
}
}
};
// Listen on a UNIX socket
// server.listen("/tmp/socket");
//
@@ -1107,7 +1207,8 @@ Server.prototype.listen = function () {
self.addListener('listening', lastArg);
}
if (!isPort(arguments[0])) {
var port = toPort(arguments[0])
if (port === false) {
// the first argument specifies a path
self.fd = socket('unix');
self.type = 'unix';
@@ -1142,13 +1243,12 @@ Server.prototype.listen = function () {
// The port can be found with server.address()
self.type = 'tcp4';
self.fd = socket(self.type);
bind(self.fd, arguments[0]);
bind(self.fd, port);
process.nextTick(function () {
self._doListen();
});
} else {
// the first argument is the port, the second an IP
var port = arguments[0];
dns.lookup(arguments[1], function (err, ip, addressType) {
if (err) {
self.emit('error', err);
@@ -1179,6 +1279,9 @@ Server.prototype._startWatcher = function () {
};
Server.prototype._doListen = function () {
// Ensure we have a dummy fd for EMFILE conditions.
getDummyFD();
listen(this.fd, 128);
this._startWatcher();
}
@@ -1199,6 +1302,11 @@ Server.prototype.close = function () {
close(self.fd);
self.fd = null;
if (self._pauseTimer) {
clearTimeout(self._pauseTimer);
self._pauseTimer = null;
}
if (self.type === "unix") {
fs.unlink(self.path, function () {
self.emit("close");
@@ -1208,4 +1316,33 @@ Server.prototype.close = function () {
}
};
// vim:ts=2 sw=2
var dummyFD = null;
var lastEMFILEWarning = 0;
// Ensures to have at least on free file-descriptor free.
// callback should only use 1 file descriptor and close it before end of call
function rescueEMFILE(callback) {
// Output a warning, but only at most every 5 seconds.
var now = new Date();
if (now - lastEMFILEWarning > 5000) {
console.error("(node) Hit max file limit. Increase 'ulimit -n'.");
lastEMFILEWarning = now;
}
if (dummyFD) {
close(dummyFD);
dummyFD = null;
callback();
getDummyFD();
}
}
function getDummyFD() {
if (!dummyFD) {
try {
dummyFD = socket("tcp");
} catch (e) {
dummyFD = null;
}
}
}

View File

@@ -15,47 +15,72 @@ var EventEmitter = require('events').EventEmitter;
var stdio = process.binding('stdio');
exports.createInterface = function (output, completer) {
return new Interface(output, completer);
};
function writeFilter (stream) {
if (stream._writeFiltered) return;
stream._writeFiltered = true;
stream._normalWrite = stream.write;
stream.write = function (d) {
var args = Array.prototype.slice.call(arguments);
if (typeof d == 'string') {
args[0] = d.replace(/([^\r])\n|^\n/g, '$1\r\n');
}
// TODO what about buffers?
return stream._normalWrite.apply(stream, args);
}
}
function Interface (output, completer) {
if (!(this instanceof Interface)) return new Interface(output, completer);
EventEmitter.call(this);
this.output = output;
this.completer = completer;
this.setPrompt("node> ");
this.setPrompt("> ");
this._tty = output.fd < 3;
this.enabled = output.fd < 3; // Looks like a TTY.
if (parseInt(process.env['NODE_NO_READLINE'])) {
this._tty = false;
this.enabled = false;
}
if (this._tty) {
if (this.enabled) {
// input refers to stdin
writeFilter(this.output);
writeFilter(process.stdout);
// Current line
this.line = "";
// Check process.env.TERM ?
stdio.setRawMode(true);
this._tty = true;
this.enabled = true;
// Cursor position on the line.
this.cursor = 0;
this.history = [];
this.historyIndex = -1;
exports.columns = process.binding('stdio').getColumns();
if (process.listeners("SIGWINCH").length === 0) {
process.on("SIGWINCH", function () {
exports.columns = process.binding('stdio').getColumns();
});
}
}
}
inherits(Interface, EventEmitter);
Interface.prototype.__defineGetter__("columns", function () {
if (this._tty) {
return stdio.getColumns();
}
return exports.columns;
});
Interface.prototype.setPrompt = function (prompt, length) {
@@ -65,7 +90,7 @@ Interface.prototype.setPrompt = function (prompt, length) {
Interface.prototype.prompt = function () {
if (this._tty) {
if (this.enabled) {
this.cursor = 0;
this._refreshLine();
} else {
@@ -93,8 +118,6 @@ Interface.prototype._addHistory = function () {
Interface.prototype._refreshLine = function () {
if (this._closed) return;
stdio.setRawMode(true);
// Cursor to left edge.
this.output.write('\x1b[0G');
@@ -111,7 +134,7 @@ Interface.prototype._refreshLine = function () {
Interface.prototype.close = function (d) {
if (this._tty) {
if (this.enabled) {
stdio.setRawMode(false);
}
this.emit('close');
@@ -121,7 +144,7 @@ Interface.prototype.close = function (d) {
Interface.prototype.write = function (d) {
if (this._closed) return;
return this._tty ? this._ttyWrite(d) : this._normalWrite(d);
return this.enabled ? this._ttyWrite(d) : this._normalWrite(d);
};
@@ -261,7 +284,12 @@ Interface.prototype._ttyWrite = function (b) {
/* ctrl+c */
case 3:
//process.kill(process.pid, "SIGINT");
this.close();
if (this.listeners('SIGINT').length) {
this.emit('SIGINT');
} else {
// default behavior, end the readline
this.close();
}
break;
case 4: // control-d, delete right or EOF
@@ -277,8 +305,7 @@ Interface.prototype._ttyWrite = function (b) {
case 13: /* enter */
var line = this._addHistory();
this.output.write('\n\x1b[0G');
stdio.setRawMode(false);
this.output.write('\r\n');
this.emit('line', line);
break;
@@ -332,6 +359,17 @@ Interface.prototype._ttyWrite = function (b) {
this._historyNext();
break;
case 23: // control-w, delete backwards to a word boundary
if (this.cursor !== 0) {
var leading = this.line.slice(0, this.cursor);
var match = leading.match(/\s?((\W+|\w+)\s*)$/);
leading = leading.slice(0, leading.length - match[1].length);
this.line = leading + this.line.slice(this.cursor, this.line.length);
this.cursor = leading.length;
this._refreshLine();
}
break;
case 9: // tab, completion
if (this.completer) {
this._tabComplete();
@@ -347,34 +385,104 @@ Interface.prototype._ttyWrite = function (b) {
return;
case 27: /* escape sequence */
if (b[1] === 98 && this.cursor > 0) { // meta-b - backward word
var next_word, next_non_word, previous_word, previous_non_word;
} else if (b[1] === 102 && this.cursor < this.line.length) { // meta-f - forward word
if (b[1] === 98 && this.cursor > 0) {
// meta-b - backward word
previous_word = this.line.slice(0, this.cursor)
.split('').reverse().join('')
.search(/\w/);
if (previous_word !== -1) {
previous_non_word = this.line.slice(0, this.cursor - previous_word)
.split('').reverse().join('')
.search(/\W/);
if (previous_non_word !== -1) {
this.cursor -= previous_word + previous_non_word;
this._refreshLine();
break;
}
}
this.cursor = 0;
this._refreshLine();
} else if (b[1] === 91 && b[2] === 68) { // left arrow
} else if (b[1] === 102 && this.cursor < this.line.length) {
// meta-f - forward word
next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
if (next_word !== -1) {
next_non_word =
this.line.slice(this.cursor + next_word, this.line.length)
.search(/\W/);
if (next_non_word !== -1) {
this.cursor += next_word + next_non_word;
this._refreshLine();
break;
}
}
this.cursor = this.line.length;
this._refreshLine();
} else if (b[1] === 100 && this.cursor < this.line.length) {
// meta-d delete forward word
next_word = this.line.slice(this.cursor, this.line.length).search(/\w/);
if (next_word !== -1) {
next_non_word =
this.line.slice(this.cursor + next_word, this.line.length)
.search(/\W/);
if (next_non_word !== -1) {
this.line =
this.line.slice(this.cursor + next_word + next_non_word);
this.cursor = 0;
this._refreshLine();
break;
}
}
this.line = '';
this.cursor = 0;
this._refreshLine();
} else if (b[1] === 91 && b[2] === 68) {
// left arrow
if (this.cursor > 0) {
this.cursor--;
this.output.write('\x1b[0D');
}
} else if (b[1] === 91 && b[2] === 67) { // right arrow
} else if (b[1] === 91 && b[2] === 67) {
// right arrow
if (this.cursor != this.line.length) {
this.cursor++;
this.output.write('\x1b[0C');
}
} else if (b[1] === 91 && b[2] === 72) { // home
} else if ((b[1] === 91 && b[2] === 72) ||
(b[1] === 79 && b[2] === 72) ||
(b[1] === 91 && b[2] === 55) ||
(b[1] === 91 && b[2] === 49 && (b[3] && b[3] === 126))) {
// home
this.cursor = 0;
this._refreshLine();
} else if (b[1] === 91 && b[2] === 70) { // end
} else if ((b[1] === 91 && b[2] === 70) ||
(b[1] === 79 && b[2] === 70) ||
(b[1] === 91 && b[2] === 56) ||
(b[1] === 91 && b[2] === 52 && (b[3] && b[3] === 126))) {
// end
this.cursor = this.line.length;
this._refreshLine();
} else if (b[1] === 91 && b[2] === 65) { // up arrow
} else if (b[1] === 91 && b[2] === 65) {
// up arrow
this._historyPrev();
} else if (b[1] === 91 && b[2] === 66) { // down arrow
} else if (b[1] === 91 && b[2] === 66) {
// down arrow
this._historyNext();
} else if (b[1] === 91 && b[2] === 51 && this.cursor < this.line.length) { // delete right
} else if (b[1] === 91 && b[2] === 51 && this.cursor < this.line.length) {
// delete right
this.line = this.line.slice(0, this.cursor) +
this.line.slice(this.cursor+1, this.line.length);
this._refreshLine();
}
break;

View File

@@ -20,6 +20,8 @@ var fs = require("fs");
var rl = require('readline');
var context;
var disableColors = process.env.NODE_DISABLE_COLORS ? true : false;
function cwdRequire (id) {
if (id.match(/^\.\.\//) || id.match(/^\.\//)) {
id = path.join(process.cwd(), id);
@@ -48,13 +50,31 @@ function REPLServer(prompt, stream) {
self.buffered_cmd = '';
self.stream = stream || process.openStdin();
self.prompt = prompt || "node> ";
self.prompt = prompt || "> ";
var rli = self.rli = rl.createInterface(self.stream, function (text) {
return self.complete(text);
});
if (rli.enabled && !disableColors) {
// Turn on ANSI coloring.
exports.writer = function(obj, showHidden, depth) {
return sys.inspect(obj, showHidden, depth, true);
}
}
rli.setPrompt(self.prompt);
rli.on("SIGINT", function () {
if (self.buffered_cmd && self.buffered_cmd.length > 0) {
rli.write("\n");
self.buffered_cmd = '';
self.displayPrompt();
} else {
rli.close();
}
});
self.stream.addListener("data", function (chunk) {
rli.write(chunk);
});
@@ -62,8 +82,6 @@ function REPLServer(prompt, stream) {
rli.addListener('line', function (cmd) {
cmd = trimWhitespace(cmd);
var flushed = true;
// Check to see if a REPL keyword was used. If it returns true,
// display next prompt and return.
if (self.parseREPLKeyword(cmd) === true) return;
@@ -78,7 +96,7 @@ function REPLServer(prompt, stream) {
var ret = evalcx(self.buffered_cmd, context, "repl");
if (ret !== undefined) {
context._ = ret;
flushed = self.stream.write(exports.writer(ret) + "\n");
self.stream.write(exports.writer(ret) + "\n");
}
self.buffered_cmd = '';
@@ -91,28 +109,14 @@ function REPLServer(prompt, stream) {
} catch (e) {
// On error: Print the error and clear the buffer
if (e.stack) {
flushed = self.stream.write(e.stack + "\n");
self.stream.write(e.stack + "\n");
} else {
flushed = self.stream.write(e.toString() + "\n");
self.stream.write(e.toString() + "\n");
}
self.buffered_cmd = '';
}
// need to make sure the buffer is flushed before displaying the prompt
// again. This is really ugly. Need to have callbacks from
// net.Stream.write()
if (flushed) {
self.displayPrompt();
} else {
self.displayPromptOnDrain = true;
}
});
self.stream.addListener('drain', function () {
if (self.displayPromptOnDrain) {
self.displayPrompt();
self.displayPromptOnDrain = false;
}
self.displayPrompt();
});
rli.addListener('close', function () {
@@ -130,7 +134,7 @@ exports.start = function (prompt, source) {
};
REPLServer.prototype.displayPrompt = function () {
this.rli.setPrompt(this.buffered_cmd.length ? '... ' : this.prompt);
this.rli.setPrompt(this.buffered_cmd.length ? '... ' : this.prompt);
this.rli.prompt();
};
@@ -300,7 +304,8 @@ REPLServer.prototype.complete = function (line) {
if (typeof obj === "object" || typeof obj === "function") {
memberGroups.push(Object.getOwnPropertyNames(obj));
}
var p = obj.constructor.prototype; // works for non-objects
// works for non-objects
var p = obj.constructor ? obj.constructor.prototype : null;
try {
var sentinel = 5;
while (p !== null) {
@@ -380,7 +385,8 @@ REPLServer.prototype.parseREPLKeyword = function (cmd) {
var self = this;
switch (cmd) {
case ".break":
case ".break":
// TODO remove me after 0.3.x
self.buffered_cmd = '';
self.displayPrompt();
return true;
@@ -391,10 +397,9 @@ REPLServer.prototype.parseREPLKeyword = function (cmd) {
self.displayPrompt();
return true;
case ".exit":
self.stream.destroy();
self.rli.close();
return true;
case ".help":
self.stream.write(".break\tSometimes you get stuck in a place you can't get out... This will get you out.\n");
self.stream.write(".clear\tBreak, and also clear the local context.\n");
self.stream.write(".exit\tExit the prompt\n");
self.stream.write(".help\tShow repl options\n");

View File

@@ -33,10 +33,51 @@ var error = exports.error = function (x) {
*
* @param {Object} value The object to print out
* @param {Boolean} showHidden Flag that shows hidden (not enumerable)
* properties of objects.
* properties of objects.
* @param {Number} depth Depth in which to descend in object. Default is 2.
* @param {Boolean} colors Flag to turn on ANSI escape codes to color the
* output. Default is false (no coloring).
*/
exports.inspect = function (obj, showHidden, depth) {
exports.inspect = function (obj, showHidden, depth, colors) {
var seen = [];
var stylize = function (str, styleType) {
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
var styles = { 'bold' : [1, 22]
, 'italic' : [3, 23]
, 'underline' : [4, 24]
, 'inverse' : [7, 27]
, 'white' : [37, 39]
, 'grey' : [90, 39]
, 'black' : [30, 39]
, 'blue' : [34, 39]
, 'cyan' : [36, 39]
, 'green' : [32, 39]
, 'magenta' : [35, 39]
, 'red' : [31, 39]
, 'yellow' : [33, 39]
};
var style = { "special": "grey"
, "number": "blue"
, "boolean": "blue"
, "undefined": "red"
, "null": "red"
, "string": "green"
, "date": "magenta"
//, "name": intentionally not styling
, "regexp": "cyan"
}[styleType];
if (style) {
return '\033[' + styles[style][0] + 'm' + str +
'\033[' + styles[style][1] + 'm';
} else {
return str;
}
};
if (! colors) {
stylize = function(str, styleType) { return str; };
}
function format(value, recurseTimes) {
// Provide a hook for user-specified inspect functions.
// Check that value is an object with an inspect function on it
@@ -50,39 +91,36 @@ exports.inspect = function (obj, showHidden, depth) {
// Primitive types cannot have properties
switch (typeof value) {
case 'undefined': return 'undefined';
case 'string': return JSON.stringify(value).replace(/'/g, "\\'")
.replace(/\\"/g, '"')
.replace(/(^"|"$)/g, "'");
case 'number': return '' + value;
case 'boolean': return '' + value;
case 'undefined': return stylize('undefined', 'undefined');
case 'string': return stylize(
JSON.stringify(value).replace(/'/g, "\\'")
.replace(/\\"/g, '"')
.replace(/(^"|"$)/g, "'"),
'string');
case 'number': return stylize('' + value, 'number');
case 'boolean': return stylize('' + value, 'boolean');
}
// For some reason typeof null is "object", so special case here.
if (value === null) {
return 'null';
return stylize('null', 'null');
}
// Look up the keys of the object.
if (showHidden) {
var keys = Object.getOwnPropertyNames(value).map(function (key) { return '' + key; });
} else {
var keys = Object.keys(value);
}
var visible_keys = Object.keys(value);
var keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys;
// Functions without properties can be shortcutted.
if (typeof value === 'function' && keys.length === 0) {
if (isRegExp(value)) {
return '' + value;
return stylize('' + value, 'regexp');
} else {
return '[Function]';
return stylize('[Function'+ (value.name ? ': '+ value.name : '')+ ']', 'special');
}
}
// Dates without properties can be shortcutted
if (isDate(value) && keys.length === 0) {
return value.toUTCString();
return stylize(value.toUTCString(), 'date');
}
var base, type, braces;
@@ -97,7 +135,7 @@ exports.inspect = function (obj, showHidden, depth) {
// Make functions say that they are functions
if (typeof value === 'function') {
base = (isRegExp(value)) ? ' ' + value : ' [Function]';
base = (isRegExp(value)) ? ' ' + value : ' [Function'+ (value.name ? ': '+ value.name : '')+ ']';
} else {
base = "";
}
@@ -115,24 +153,24 @@ exports.inspect = function (obj, showHidden, depth) {
if (recurseTimes < 0) {
if (isRegExp(value)) {
return '' + value;
return stylize('' + value, "regexp");
} else {
return "[Object]";
return stylize("[Object]", "special");
}
}
output = keys.map(function (key) {
var output = keys.map(function (key) {
var name, str;
if (value.__lookupGetter__) {
if (value.__lookupGetter__(key)) {
if (value.__lookupSetter__(key)) {
str = "[Getter/Setter]";
str = stylize("[Getter/Setter]", "special");
} else {
str = "[Getter]";
str = stylize("[Getter]", "special");
}
} else {
if (value.__lookupSetter__(key)) {
str = "[Setter]";
str = stylize("[Setter]", "special");
}
}
}
@@ -160,7 +198,7 @@ exports.inspect = function (obj, showHidden, depth) {
}
}
} else {
str = '[Circular]';
str = stylize('[Circular]', 'special');
}
}
if (typeof name === 'undefined') {
@@ -170,11 +208,13 @@ exports.inspect = function (obj, showHidden, depth) {
name = JSON.stringify('' + key);
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
name = name.substr(1, name.length-2);
name = stylize(name, "name");
}
else {
name = name.replace(/'/g, "\\'")
.replace(/\\"/g, '"')
.replace(/(^"|"$)/g, "'");
name = stylize(name, "string");
}
}
@@ -283,6 +323,15 @@ exports.exec = function () {
exports.pump = function (readStream, writeStream, callback) {
var callbackCalled = false;
function call (a, b, c) {
if (callback && !callbackCalled) {
callback(a, b, c);
callbackCalled = true;
}
}
if (!readStream.pause) readStream.pause = function () {readStream.emit("pause")};
if (!readStream.resume) readStream.resume = function () {readStream.emit("resume")};
@@ -307,7 +356,17 @@ exports.pump = function (readStream, writeStream, callback) {
});
readStream.addListener("close", function () {
if (callback) callback();
call();
});
readStream.addListener("error", function (err) {
writeStream.end();
call(err);
});
writeStream.addListener("error", function (err) {
readStream.destroy();
call(err);
});
};

View File

@@ -18,7 +18,7 @@ var protocolPattern = /^([a-z0-9]+:)/,
path = require("path"), // internal module, guaranteed to be loaded already.
querystring = require('querystring');
function urlParse (url, parseQueryString) {
function urlParse (url, parseQueryString, slashesDenoteHost) {
if (url && typeof(url) === "object" && url.href) return url;
var out = { href : url },
@@ -32,10 +32,15 @@ function urlParse (url, parseQueryString) {
}
// figure out if it's got a host
var slashes = rest.substr(0, 2) === "//";
if (slashes && !(proto && hostlessProtocol[proto])) {
rest = rest.substr(2);
out.slashes = true;
// user@server is *always* interpreted as a hostname, and url
// resolution will treat //foo/bar as host=foo,path=bar because that's
// how the browser resolves relative URLs.
if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
var slashes = rest.substr(0, 2) === "//";
if (slashes && !(proto && hostlessProtocol[proto])) {
rest = rest.substr(2);
out.slashes = true;
}
}
if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) {
// there's a hostname.
@@ -133,8 +138,8 @@ function urlResolve (source, relative) {
function urlResolveObject (source, relative) {
if (!source) return relative;
source = urlParse(urlFormat(source));
relative = urlParse(urlFormat(relative));
source = urlParse(urlFormat(source), false, true);
relative = urlParse(urlFormat(relative), false, true);
// hash is always overridden, no matter what.
source.hash = relative.hash;

View File

@@ -49,7 +49,12 @@
using namespace v8;
# ifdef __APPLE__
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
# else
extern char **environ;
# endif
namespace node {
@@ -68,12 +73,16 @@ static Persistent<String> listeners_symbol;
static Persistent<String> uncaught_exception_symbol;
static Persistent<String> emit_symbol;
static char *eval_string = NULL;
static int option_end_index = 0;
static bool use_debug_agent = false;
static bool debug_wait_connect = false;
static int debug_port=5858;
static int max_stack_size = 0;
static ev_prepare next_tick_watcher;
static ev_check check_tick_watcher;
static ev_prepare prepare_tick_watcher;
static ev_idle tick_spinner;
static bool need_tick_cb;
static Persistent<String> tick_callback_sym;
@@ -164,6 +173,11 @@ static void Check(EV_P_ ev_check *watcher, int revents) {
static Handle<Value> NeedTickCallback(const Arguments& args) {
HandleScope scope;
need_tick_cb = true;
// TODO: this tick_spinner shouldn't be necessary. An ev_prepare should be
// sufficent, the problem is only in the case of the very last "tick" -
// there is nothing left to do in the event loop and libev will exit. The
// ev_prepare callback isn't called before exiting. Thus we start this
// tick_spinner to keep the event loop alive long enough to handle it.
ev_idle_start(EV_DEFAULT_UC_ &tick_spinner);
return Undefined();
}
@@ -175,10 +189,7 @@ static void Spin(EV_P_ ev_idle *watcher, int revents) {
}
static void Tick(EV_P_ ev_prepare *watcher, int revents) {
assert(watcher == &next_tick_watcher);
assert(revents == EV_PREPARE);
static void Tick(void) {
// Avoid entering a V8 scope.
if (!need_tick_cb) return;
@@ -207,6 +218,20 @@ static void Tick(EV_P_ ev_prepare *watcher, int revents) {
}
static void PrepareTick(EV_P_ ev_prepare *watcher, int revents) {
assert(watcher == &prepare_tick_watcher);
assert(revents == EV_PREPARE);
Tick();
}
static void CheckTick(EV_P_ ev_check *watcher, int revents) {
assert(watcher == &check_tick_watcher);
assert(revents == EV_CHECK);
Tick();
}
static void DoPoll(EV_P_ ev_idle *watcher, int revents) {
assert(watcher == &eio_poller);
assert(revents == EV_IDLE);
@@ -1495,6 +1520,7 @@ static Handle<Value> Binding(const Arguments& args) {
exports->Set(String::New("tcp"), String::New(native_tcp));
exports->Set(String::New("url"), String::New(native_url));
exports->Set(String::New("utils"), String::New(native_utils));
exports->Set(String::New("util"), String::New(native_sys));
exports->Set(String::New("path"), String::New(native_path));
exports->Set(String::New("string_decoder"), String::New(native_string_decoder));
binding_cache->Set(module, exports);
@@ -1524,6 +1550,69 @@ static void ProcessTitleSetter(Local<String> property,
}
static Handle<Value> EnvGetter(Local<String> property,
const AccessorInfo& info) {
String::Utf8Value key(property);
const char* val = getenv(*key);
if (val) {
HandleScope scope;
return scope.Close(String::New(val));
}
return Undefined();
}
static Handle<Value> EnvSetter(Local<String> property,
Local<Value> value,
const AccessorInfo& info) {
String::Utf8Value key(property);
String::Utf8Value val(value);
setenv(*key, *val, 1);
return value;
}
static Handle<Integer> EnvQuery(Local<String> property,
const AccessorInfo& info) {
String::Utf8Value key(property);
if (getenv(*key)) {
HandleScope scope;
return scope.Close(Integer::New(None));
}
return Handle<Integer>();
}
static Handle<Boolean> EnvDeleter(Local<String> property,
const AccessorInfo& info) {
String::Utf8Value key(property);
if (getenv(*key)) {
unsetenv(*key); // prototyped as `void unsetenv(const char*)` on some platforms
return True();
}
return False();
}
static Handle<Array> EnvEnumerator(const AccessorInfo& info) {
HandleScope scope;
int size = 0;
while (environ[size]) size++;
Local<Array> env = Array::New(size);
for (int i = 0; i < size; ++i) {
const char* var = environ[i];
const char* s = strchr(var, '=');
const int length = s ? s - var : strlen(var);
env->Set(i, String::New(var, length));
}
return scope.Close(env);
}
static void Load(int argc, char *argv[]) {
HandleScope scope;
@@ -1576,32 +1665,28 @@ static void Load(int argc, char *argv[]) {
process->Set(String::NewSymbol("argv"), arguments);
// create process.env
Local<Object> env = Object::New();
for (i = 0; environ[i]; i++) {
// skip entries without a '=' character
for (j = 0; environ[i][j] && environ[i][j] != '='; j++) { ; }
// create the v8 objects
Local<String> field = String::New(environ[i], j);
Local<String> value = Local<String>();
if (environ[i][j] == '=') {
value = String::New(environ[i]+j+1);
}
// assign them
env->Set(field, value);
}
Local<ObjectTemplate> envTemplate = ObjectTemplate::New();
envTemplate->SetNamedPropertyHandler(EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, Undefined());
// assign process.ENV
Local<Object> env = envTemplate->NewInstance();
process->Set(String::NewSymbol("ENV"), env);
process->Set(String::NewSymbol("env"), env);
process->Set(String::NewSymbol("pid"), Integer::New(getpid()));
// -e, --eval
if (eval_string) {
process->Set(String::NewSymbol("_eval"), String::New(eval_string));
}
size_t size = 2*PATH_MAX;
char execPath[size];
if (OS::GetExecutablePath(execPath, &size) != 0) {
// as a last ditch effort, fallback on argv[0] ?
process->Set(String::NewSymbol("execPath"), String::New(argv[0]));
} else {
process->Set(String::NewSymbol("execPath"), String::New(execPath));
process->Set(String::NewSymbol("execPath"), String::New(execPath, size));
}
@@ -1707,21 +1792,23 @@ static void ParseDebugOpt(const char* arg) {
static void PrintHelp() {
printf("Usage: node [options] script.js [arguments] \n"
"Options:\n"
" -v, --version print node's version\n"
" --debug[=port] enable remote debugging via given TCP port\n"
" without stopping the execution\n"
" --debug-brk[=port] as above, but break in script.js and\n"
" wait for remote debugger to connect\n"
" --v8-options print v8 command line options\n"
" --vars print various compiled-in variables\n"
" -v, --version print node's version\n"
" --debug[=port] enable remote debugging via given TCP port\n"
" without stopping the execution\n"
" --debug-brk[=port] as above, but break in script.js and\n"
" wait for remote debugger to connect\n"
" --v8-options print v8 command line options\n"
" --vars print various compiled-in variables\n"
" --max-stack-size=val set max v8 stack size (bytes)\n"
"\n"
"Enviromental variables:\n"
"NODE_PATH ':'-separated list of directories\n"
" prefixed to the module search path,\n"
" require.paths.\n"
"NODE_DEBUG Print additional debugging output.\n"
"NODE_MODULE_CONTEXTS Set to 1 to load modules in their own\n"
" global contexts.\n"
"NODE_PATH ':'-separated list of directories\n"
" prefixed to the module search path,\n"
" require.paths.\n"
"NODE_DEBUG Print additional debugging output.\n"
"NODE_MODULE_CONTEXTS Set to 1 to load modules in their own\n"
" global contexts.\n"
"NODE_DISABLE_COLORS Set to 1 to disable colors in the REPL\n"
"\n"
"Documentation can be found at http://nodejs.org/api.html"
" or with 'man node'\n");
@@ -1744,9 +1831,21 @@ static void ParseArgs(int *argc, char **argv) {
printf("NODE_PREFIX: %s\n", NODE_PREFIX);
printf("NODE_CFLAGS: %s\n", NODE_CFLAGS);
exit(0);
} else if (strstr(arg, "--max-stack-size=") == arg) {
const char *p = 0;
p = 1 + strchr(arg, '=');
max_stack_size = atoi(p);
argv[i] = const_cast<char*>("");
} else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
PrintHelp();
exit(0);
} else if (strcmp(arg, "--eval") == 0 || strcmp(arg, "-e") == 0) {
if (*argc <= i + 1) {
fprintf(stderr, "Error: --eval requires an argument\n");
exit(1);
}
argv[i] = const_cast<char*>("");
eval_string = argv[++i];
} else if (strcmp(arg, "--v8-options") == 0) {
argv[i] = const_cast<char*>("--help");
} else if (argv[i][0] != '-') {
@@ -1758,6 +1857,21 @@ static void ParseArgs(int *argc, char **argv) {
}
static void SignalExit(int signal) {
exit(1);
}
static int RegisterSignalHandler(int signal, void (*handler)(int)) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
sigfillset(&sa.sa_mask);
return sigaction(signal, &sa, NULL);
}
static void AtExit() {
node::Stdio::Flush();
node::Stdio::DisableRawMode(STDIN_FILENO);
@@ -1788,13 +1902,26 @@ int main(int argc, char *argv[]) {
v8argv[node::option_end_index] = const_cast<char*>("--expose_debug_as");
v8argv[node::option_end_index + 1] = const_cast<char*>("v8debug");
}
// For the normal stack which moves from high to low addresses when frames
// are pushed, we can compute the limit as stack_size bytes below the
// the address of a stack variable (e.g. &stack_var) as an approximation
// of the start of the stack (we're assuming that we haven't pushed a lot
// of frames yet).
if (node::max_stack_size != 0) {
uint32_t stack_var;
ResourceConstraints constraints;
uint32_t *stack_limit = &stack_var - (node::max_stack_size / sizeof(uint32_t));
constraints.set_stack_limit(stack_limit);
SetResourceConstraints(&constraints); // Must be done before V8::Initialize
}
V8::SetFlagsFromCommandLine(&v8argc, v8argv, false);
// Ignore SIGPIPE
struct sigaction sa;
bzero(&sa, sizeof(sa));
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
node::RegisterSignalHandler(SIGPIPE, SIG_IGN);
node::RegisterSignalHandler(SIGINT, node::SignalExit);
node::RegisterSignalHandler(SIGTERM, node::SignalExit);
// Initialize the default ev loop.
@@ -1802,14 +1929,18 @@ int main(int argc, char *argv[]) {
// TODO(Ryan) I'm experiencing abnormally high load using Solaris's
// EVBACKEND_PORT. Temporarally forcing select() until I debug.
ev_default_loop(EVBACKEND_POLL);
#elif defined(__APPLE_CC__) && __APPLE_CC__ >= 5659
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
ev_default_loop(EVBACKEND_KQUEUE);
#else
ev_default_loop(EVFLAG_AUTO);
#endif
ev_prepare_init(&node::next_tick_watcher, node::Tick);
ev_prepare_start(EV_DEFAULT_UC_ &node::next_tick_watcher);
ev_prepare_init(&node::prepare_tick_watcher, node::PrepareTick);
ev_prepare_start(EV_DEFAULT_UC_ &node::prepare_tick_watcher);
ev_unref(EV_DEFAULT_UC);
ev_check_init(&node::check_tick_watcher, node::CheckTick);
ev_check_start(EV_DEFAULT_UC_ &node::check_tick_watcher);
ev_unref(EV_DEFAULT_UC);
ev_idle_init(&node::tick_spinner, node::Spin);

View File

@@ -25,7 +25,8 @@ namespace node {
#define NODE_DEFINE_CONSTANT(target, constant) \
(target)->Set(v8::String::NewSymbol(#constant), \
v8::Integer::New(constant))
v8::Integer::New(constant), \
static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete))
#define NODE_SET_METHOD(obj, name, callback) \
obj->Set(v8::String::NewSymbol(name), \

View File

@@ -49,9 +49,20 @@ var nextTickQueue = [];
process._tickCallback = function () {
var l = nextTickQueue.length;
if (l === 0) return;
for (var i = 0; i < l; i++) {
nextTickQueue[i]();
try {
for (var i = 0; i < l; i++) {
nextTickQueue[i]();
}
}
catch(e) {
nextTickQueue.splice(0, i+1);
if (i+1 < l) {
process._needTickCallback();
}
throw e;
}
nextTickQueue.splice(0, l);
};
@@ -187,17 +198,6 @@ var module = (function () {
return;
}
/* if (dirs.length == 0) {
if (callback) {
callback();
} else {
return; // sync returns null
}
} //no need for this, eventually move simpler if to traverser
// question: what with /absolute/id when dirs.length is 0?
// if "load it anyway", then this ^^^ code is wrong, but omitting it
// makes it right*/
var nextLoc = traverser(id, id.charAt(0) === '/' ? [''] : dirs);
var fs = requireNative('fs');
@@ -433,6 +433,7 @@ var module = (function () {
sandbox.__filename = filename;
sandbox.__dirname = dirname;
sandbox.module = self;
sandbox.global = sandbox;
sandbox.root = root;
Script.runInNewContext(content, sandbox, filename);
@@ -533,8 +534,8 @@ var events = module.requireNative('events');
// Signal Handlers
(function() {
var signalWatchers = {};
addListener = process.addListener,
removeListener = process.removeListener;
var addListener = process.addListener;
var removeListener = process.removeListener;
function isSignal (event) {
return event.slice(0, 3) === 'SIG' && process.hasOwnProperty(event);
@@ -596,12 +597,13 @@ global.setTimeout = function (callback, after) {
global.setInterval = function (callback, repeat) {
var timer = new process.Timer();
addTimerListener.apply(timer, arguments);
timer.start(repeat, repeat);
timer.start(repeat, repeat ? repeat : 1);
return timer;
};
global.clearTimeout = function (timer) {
if (timer instanceof process.Timer) {
timer.callback = null;
timer.stop();
}
};
@@ -744,16 +746,23 @@ if (process.argv[0].indexOf('/') > 0) {
}
if (process.argv[1]) {
// Load module
if (process.argv[1].charAt(0) != "/" && !(/^http:\/\//).exec(process.argv[1])) {
process.argv[1] = path.join(cwd, process.argv[1]);
}
// REMOVEME: nextTick should not be necessary. This hack to get
// test/simple/test-exception-handler2.js working.
process.nextTick(function() {
module.runMain();
});
module.runMain();
} else if (process._eval) {
// -e, --eval
var indirectEval= eval; // so the eval happens in global scope.
if (process._eval) console.log(indirectEval(process._eval));
} else {
// No arguments, run the repl
var repl = module.requireNative('repl');
console.log("Type '.help' for options.");
repl.start();
// REPL
module.requireNative('repl').start();
}
// All our arguments are loaded. We've evaluated all of the scripts. We

View File

@@ -156,6 +156,16 @@ Buffer* Buffer::New(size_t size) {
Handle<Value> Buffer::New(const Arguments &args) {
HandleScope scope;
if (!args.IsConstructCall()) {
Local<Value> argv[10];
for (int i = 0; i < MIN(args.Length(), 10); i++) {
argv[i] = args[i];
}
Local<Object> instance =
constructor_template->GetFunction()->NewInstance(args.Length(), argv);
return scope.Close(instance);
}
Buffer *buffer;
if (args[0]->IsInt32()) {
// var buffer = new Buffer(1024);
@@ -210,11 +220,19 @@ Handle<Value> Buffer::New(const Arguments &args) {
TryCatch try_catch;
write->Call(args.This(), 2, argv);
Local<Value> length_val = write->Call(args.This(), 2, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
assert(length_val->IsInt32());
size_t length = static_cast<size_t>(length_val->Int32Value());
assert(length <= buffer->length());
buffer->length_ = length;
assert(buffer->length() == length);
args.This()->Set(length_symbol, length_val);
}
return args.This();
@@ -307,15 +325,16 @@ static const char *base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static const int unbase64_table[] =
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-2,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63
,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63
,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1
,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14
,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1
,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40
,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1
};
#define unbase64(x) unbase64_table[(int)(x)]
Handle<Value> Buffer::Base64Slice(const Arguments &args) {
@@ -525,16 +544,21 @@ Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
return scope.Close(Integer::New(written));
}
// var bytesWritten = buffer.base64Write(string, offset);
// var bytesWritten = buffer.base64Write(string, offset, [maxLength]);
Handle<Value> Buffer::Base64Write(const Arguments &args) {
HandleScope scope;
assert(unbase64_table['/'] == 63);
assert(unbase64_table['+'] == 62);
assert(unbase64_table['T'] == 19);
assert(unbase64_table['Z'] == 25);
assert(unbase64_table['t'] == 45);
assert(unbase64_table['z'] == 51);
assert(unbase64('/') == 63);
assert(unbase64('+') == 62);
assert(unbase64('T') == 19);
assert(unbase64('Z') == 25);
assert(unbase64('t') == 45);
assert(unbase64('z') == 51);
assert(unbase64(' ') == -2);
assert(unbase64('\n') == -2);
assert(unbase64('\r') == -2);
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
@@ -564,29 +588,47 @@ Handle<Value> Buffer::Base64Write(const Arguments &args) {
}
char a, b, c, d;
char *dst = buffer->data();
char* start = buffer->data();
char* dst = start;
const char *src = *s;
const char *const srcEnd = src + s.length();
while (src < srcEnd) {
const int remaining = srcEnd - src;
if (remaining == 0 || *src == '=') break;
a = unbase64_table[*src++];
int remaining = srcEnd - src;
if (remaining == 1 || *src == '=') break;
b = unbase64_table[*src++];
while (unbase64(*src) < 0 && src < srcEnd) {
src++;
remaining--;
}
if (remaining == 0 || *src == '=') break;
a = unbase64(*src++);
while (unbase64(*src) < 0 && src < srcEnd) {
src++;
remaining--;
}
if (remaining <= 1 || *src == '=') break;
b = unbase64(*src++);
*dst++ = (a << 2) | ((b & 0x30) >> 4);
if (remaining == 2 || *src == '=') break;
c = unbase64_table[*src++];
while (unbase64(*src) < 0 && src < srcEnd) {
src++;
remaining--;
}
if (remaining <= 2 || *src == '=') break;
c = unbase64(*src++);
*dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2);
if (remaining == 3 || *src == '=') break;
d = unbase64_table[*src++];
while (unbase64(*src) < 0 && src < srcEnd) {
src++;
remaining--;
}
if (remaining <= 3 || *src == '=') break;
d = unbase64(*src++);
*dst++ = ((c & 0x03) << 6) | (d & 0x3F);
}
return scope.Close(Integer::New(size));
return scope.Close(Integer::New(dst - start));
}
@@ -701,6 +743,22 @@ Handle<Value> Buffer::ByteLength(const Arguments &args) {
}
Handle<Value> Buffer::MakeFastBuffer(const Arguments &args) {
HandleScope scope;
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
Local<Object> fast_buffer = args[1]->ToObject();;
uint32_t offset = args[2]->Uint32Value();
uint32_t length = args[3]->Uint32Value();
fast_buffer->SetIndexedPropertiesToPixelData((uint8_t*)buffer->data() + offset,
length);
return Undefined();
}
void Buffer::Initialize(Handle<Object> target) {
HandleScope scope;
@@ -731,6 +789,9 @@ void Buffer::Initialize(Handle<Object> target) {
NODE_SET_METHOD(constructor_template->GetFunction(),
"byteLength",
Buffer::ByteLength);
NODE_SET_METHOD(constructor_template->GetFunction(),
"makeFastBuffer",
Buffer::MakeFastBuffer);
target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction());
}

View File

@@ -61,6 +61,7 @@ class Buffer : public ObjectWrap {
static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);
static v8::Handle<v8::Value> Utf8Write(const v8::Arguments &args);
static v8::Handle<v8::Value> ByteLength(const v8::Arguments &args);
static v8::Handle<v8::Value> MakeFastBuffer(const v8::Arguments &args);
static v8::Handle<v8::Value> Unpack(const v8::Arguments &args);
static v8::Handle<v8::Value> Copy(const v8::Arguments &args);

View File

@@ -13,7 +13,12 @@
#include <sys/wait.h>
#endif
# ifdef __APPLE__
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
# else
extern char **environ;
# endif
namespace node {
@@ -34,6 +39,16 @@ static inline int SetNonBlocking(int fd) {
}
static inline int SetCloseOnExec(int fd) {
int flags = fcntl(fd, F_GETFD, 0);
int r = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
if (r != 0) {
perror("SetCloseOnExec()");
}
return r;
}
static inline int ResetFlags(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
// blocking
@@ -226,6 +241,22 @@ int ChildProcess::Spawn(const char *file,
return -1;
}
// Set the close-on-exec FD flag
if (custom_fds[0] == -1) {
SetCloseOnExec(stdin_pipe[0]);
SetCloseOnExec(stdin_pipe[1]);
}
if (custom_fds[1] == -1) {
SetCloseOnExec(stdout_pipe[0]);
SetCloseOnExec(stdout_pipe[1]);
}
if (custom_fds[2] == -1) {
SetCloseOnExec(stderr_pipe[0]);
SetCloseOnExec(stderr_pipe[1]);
}
// Save environ in the case that we get it clobbered
// by the child process.
char **save_our_env = environ;
@@ -286,8 +317,7 @@ int ChildProcess::Spawn(const char *file,
close(stdin_pipe[0]);
stdio_fds[0] = stdin_pipe[1];
SetNonBlocking(stdin_pipe[1]);
}
else {
} else {
stdio_fds[0] = custom_fds[0];
}
@@ -295,8 +325,7 @@ int ChildProcess::Spawn(const char *file,
close(stdout_pipe[1]);
stdio_fds[1] = stdout_pipe[0];
SetNonBlocking(stdout_pipe[0]);
}
else {
} else {
stdio_fds[1] = custom_fds[1];
}
@@ -304,8 +333,7 @@ int ChildProcess::Spawn(const char *file,
close(stderr_pipe[1]);
stdio_fds[2] = stderr_pipe[0];
SetNonBlocking(stderr_pipe[0]);
}
else {
} else {
stdio_fds[2] = custom_fds[2];
}

View File

@@ -25,6 +25,7 @@ static Persistent<String> subject_symbol;
static Persistent<String> issuer_symbol;
static Persistent<String> valid_from_symbol;
static Persistent<String> valid_to_symbol;
static Persistent<String> fingerprint_symbol;
static Persistent<String> name_symbol;
static Persistent<String> version_symbol;
@@ -224,6 +225,8 @@ Handle<Value> SecureContext::Close(const Arguments& args) {
if (sc->pCtx != NULL) {
SSL_CTX_free(sc->pCtx);
sc->pCtx = NULL;
sc->caStore = NULL;
return True();
}
return False();
@@ -540,6 +543,28 @@ Handle<Value> SecureStream::GetPeerCertificate(const Arguments& args) {
BIO_free(bio);
info->Set(valid_to_symbol, String::New(buf));
unsigned int md_size, i;
unsigned char md[EVP_MAX_MD_SIZE];
if (X509_digest(peer_cert, EVP_sha1(), md, &md_size)) {
const char hex[] = "0123456789ABCDEF";
char fingerprint[EVP_MAX_MD_SIZE * 3];
for (i=0; i<md_size; i++) {
fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4];
fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)];
fingerprint[(3*i)+2] = ':';
}
if (md_size > 0) {
fingerprint[(3*(md_size-1))+2] = '\0';
}
else {
fingerprint[0] = '\0';
}
info->Set(fingerprint_symbol, String::New(fingerprint));
}
X509_free(peer_cert);
}
return scope.Close(info);
@@ -659,24 +684,23 @@ static void HexDecode(unsigned char *input,
void base64(unsigned char *input, int length, char** buf64, int* buf64_len) {
BIO *bmem, *b64;
BUF_MEM *bptr;
int len;
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
len = BIO_write(b64, input, length);
int len = BIO_write(b64, input, length);
assert(len == length);
BIO_flush(b64);
int r = BIO_flush(b64);
assert(r == 1);
BUF_MEM *bptr;
BIO_get_mem_ptr(b64, &bptr);
*buf64_len = bptr->length;
*buf64 = new char[*buf64_len+1];
memcpy(*buf64, bptr->data, bptr->length);
memcpy(*buf64, bptr->data, *buf64_len);
char* b = *buf64;
b[bptr->length] = 0;
b[*buf64_len] = 0;
BIO_free_all(b64);
}
@@ -1386,19 +1410,19 @@ class Decipher : public ObjectWrap {
if (alloc_buf) {
delete [] buf;
alloc_buf = false;
}
buf = ciphertext;
len = ciphertext_len;
alloc_buf = true;
} else if (strcasecmp(*encoding, "base64") == 0) {
unbase64((unsigned char*)buf, len, (char **)&ciphertext, &ciphertext_len);
if (alloc_buf) {
delete [] buf;
alloc_buf = false;
}
buf = ciphertext;
len = ciphertext_len;
alloc_buf = true;
} else if (strcasecmp(*encoding, "binary") == 0) {
// Binary - do nothing
@@ -1766,15 +1790,6 @@ class Hash : public ObjectWrap {
return 1;
}
int HashDigest(unsigned char** md_value, unsigned int *md_len) {
if (!initialised_) return 0;
*md_value = new unsigned char[EVP_MAX_MD_SIZE];
EVP_DigestFinal_ex(&mdctx, *md_value, md_len);
EVP_MD_CTX_cleanup(&mdctx);
initialised_ = false;
return 1;
}
protected:
@@ -1825,22 +1840,27 @@ class Hash : public ObjectWrap {
}
static Handle<Value> HashDigest(const Arguments& args) {
Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
HandleScope scope;
unsigned char* md_value;
Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
if (!hash->initialised_) {
return ThrowException(Exception::Error(String::New("Not initialized")));
}
unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len;
char* md_hexdigest;
int md_hex_len;
Local<Value> outString ;
int r = hash->HashDigest(&md_value, &md_len);
EVP_DigestFinal_ex(&hash->mdctx, md_value, &md_len);
EVP_MD_CTX_cleanup(&hash->mdctx);
hash->initialised_ = false;
if (md_len == 0 || r == 0) {
if (md_len == 0) {
return scope.Close(String::New(""));
}
Local<Value> outString;
if (args.Length() == 0 || !args[0]->IsString()) {
// Binary
outString = Encode(md_value, md_len, BINARY);
@@ -1848,10 +1868,14 @@ class Hash : public ObjectWrap {
String::Utf8Value encoding(args[0]->ToString());
if (strcasecmp(*encoding, "hex") == 0) {
// Hex encoding
char* md_hexdigest;
int md_hex_len;
HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
} else if (strcasecmp(*encoding, "base64") == 0) {
char* md_hexdigest;
int md_hex_len;
base64(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
@@ -1862,9 +1886,8 @@ class Hash : public ObjectWrap {
"can be binary, hex or base64\n");
}
}
delete [] md_value;
return scope.Close(outString);
return scope.Close(outString);
}
Hash () : ObjectWrap () {
@@ -2296,6 +2319,7 @@ void InitCrypto(Handle<Object> target) {
issuer_symbol = NODE_PSYMBOL("issuer");
valid_from_symbol = NODE_PSYMBOL("valid_from");
valid_to_symbol = NODE_PSYMBOL("valid_to");
fingerprint_symbol = NODE_PSYMBOL("fingerprint");
name_symbol = NODE_PSYMBOL("name");
version_symbol = NODE_PSYMBOL("version");
}

View File

@@ -39,7 +39,11 @@ class SecureContext : ObjectWrap {
}
~SecureContext() {
// Free up
if (pCtx) {
SSL_CTX_free(pCtx);
pCtx = NULL;
caStore = NULL;
}
}
private:

View File

@@ -31,6 +31,7 @@ using namespace v8;
ThrowException(Exception::TypeError(String::New("Bad argument")))
static Persistent<String> encoding_symbol;
static Persistent<String> errno_symbol;
static Persistent<String> buf_symbol;
// Buffer for readlink() and other misc callers; keep this scoped at
// file-level rather than method-level to avoid excess stack usage.
@@ -43,33 +44,31 @@ static int After(eio_req *req) {
ev_unref(EV_DEFAULT_UC);
int argc = 0;
Local<Value> argv[6]; // 6 is the maximum number of args
// there is always at least one argument. "error"
int argc = 1;
if (req->errorno != 0) {
argc = 1;
switch (req->type) {
case EIO_STAT:
case EIO_LSTAT:
case EIO_LINK:
case EIO_UNLINK:
case EIO_RMDIR:
case EIO_RENAME:
case EIO_READLINK:
case EIO_OPEN:
case EIO_CHMOD:
case EIO_CHOWN:
case EIO_MKDIR:
argv[0] = ErrnoException(req->errorno, NULL, "", static_cast<const char*>(req->ptr1));
break;
default:
argv[0] = ErrnoException(req->errorno);
// Allocate space for two args. We may only use one depending on the case.
// (Feel free to increase this if you need more)
Local<Value> argv[2];
// NOTE: This may be needed to be changed if something returns a -1
// for a success, which is possible.
if (req->result == -1) {
// If the request doesn't have a path parameter set.
if (!req->ptr1) {
argv[0] = ErrnoException(req->errorno);
} else {
argv[0] = ErrnoException(req->errorno, NULL, "", static_cast<const char*>(req->ptr1));
}
} else {
// Note: the error is always given the first argument of the callback.
// If there is no error then then the first argument is null.
// error value is empty or null for non-error.
argv[0] = Local<Value>::New(Null());
// All have at least two args now.
argc = 2;
switch (req->type) {
// These all have no data to pass.
case EIO_CLOSE:
case EIO_RENAME:
case EIO_UNLINK:
@@ -82,68 +81,59 @@ static int After(eio_req *req) {
case EIO_SYMLINK:
case EIO_CHMOD:
case EIO_CHOWN:
argc = 0;
// These, however, don't.
argc = 1;
break;
case EIO_OPEN:
case EIO_SENDFILE:
argc = 2;
argv[1] = Integer::New(req->result);
break;
case EIO_WRITE:
argc = 2;
argv[1] = Integer::New(req->result);
break;
case EIO_STAT:
case EIO_LSTAT:
case EIO_FSTAT:
{
struct stat *s = reinterpret_cast<struct stat*>(req->ptr2);
argc = 2;
argv[1] = BuildStatsObject(s);
{
struct stat *s = reinterpret_cast<struct stat*>(req->ptr2);
argv[1] = BuildStatsObject(s);
}
break;
}
case EIO_READLINK:
{
argc = 2;
argv[1] = String::New(static_cast<char*>(req->ptr2), req->result);
break;
}
case EIO_READ:
{
// Buffer interface
argv[1] = Integer::New(req->result);
argc = 2;
break;
}
case EIO_READDIR:
{
char *namebuf = static_cast<char*>(req->ptr2);
int nnames = req->result;
{
char *namebuf = static_cast<char*>(req->ptr2);
int nnames = req->result;
Local<Array> names = Array::New(nnames);
Local<Array> names = Array::New(nnames);
for (int i = 0; i < nnames; i++) {
Local<String> name = String::New(namebuf);
names->Set(Integer::New(i), name);
for (int i = 0; i < nnames; i++) {
Local<String> name = String::New(namebuf);
names->Set(Integer::New(i), name);
#ifndef NDEBUG
namebuf += strlen(namebuf);
assert(*namebuf == '\0');
namebuf += 1;
namebuf += strlen(namebuf);
assert(*namebuf == '\0');
namebuf += 1;
#else
namebuf += strlen(namebuf) + 1;
namebuf += strlen(namebuf) + 1;
#endif
}
}
argc = 2;
argv[1] = names;
argv[1] = names;
}
break;
}
default:
assert(0 && "Unhandled eio response");
@@ -647,6 +637,10 @@ static Handle<Value> Write(const Arguments& args) {
Local<Value> cb = args[5];
if (cb->IsFunction()) {
// Grab a reference to buffer so it isn't GCed
Local<Object> cb_obj = cb->ToObject();
cb_obj->Set(buf_symbol, buffer->handle_);
ASYNC_CALL(write, cb, fd, buf, len, pos)
} else {
ssize_t written = pos < 0 ? write(fd, buf, len) : pwrite(fd, buf, len, pos);
@@ -709,6 +703,11 @@ static Handle<Value> Read(const Arguments& args) {
cb = args[5];
if (cb->IsFunction()) {
// Grab a reference to buffer so it isn't GCed
// TODO: need test coverage
Local<Object> cb_obj = cb->ToObject();
cb_obj->Set(buf_symbol, buffer->handle_);
ASYNC_CALL(read, cb, fd, buf, len, pos);
} else {
// SYNC
@@ -799,6 +798,7 @@ void File::Initialize(Handle<Object> target) {
errno_symbol = NODE_PSYMBOL("errno");
encoding_symbol = NODE_PSYMBOL("node:encoding");
buf_symbol = NODE_PSYMBOL("__buf");
}
void InitFs(Handle<Object> target) {

View File

@@ -51,9 +51,7 @@ void IOWatcher::Callback(EV_P_ ev_io *w, int revents) {
argv[0] = Local<Value>::New(revents & EV_READ ? True() : False());
argv[1] = Local<Value>::New(revents & EV_WRITE ? True() : False());
io->Ref();
callback->Call(io->handle_, 2, argv);
io->Unref();
if (try_catch.HasCaught()) {
FatalException(try_catch);
@@ -135,6 +133,7 @@ Handle<Value> IOWatcher::Set(const Arguments& args) {
if (args[2]->IsTrue()) events |= EV_WRITE;
assert(!io->watcher_.active);
ev_io_set(&io->watcher_, fd, events);
return Undefined();

View File

@@ -372,34 +372,52 @@ static Handle<Value> Connect(const Arguments& args) {
return Undefined();
}
#define ADDRESS_TO_JS(info, address_storage) \
#define ADDRESS_TO_JS(info, address_storage, addrlen) \
do { \
char ip[INET6_ADDRSTRLEN]; \
int port; \
struct sockaddr_in *a4; \
struct sockaddr_in6 *a6; \
struct sockaddr_un *au; \
switch ((address_storage).ss_family) { \
case AF_INET6: \
a6 = (struct sockaddr_in6*)&(address_storage); \
inet_ntop(AF_INET6, &(a6->sin6_addr), ip, INET6_ADDRSTRLEN); \
port = ntohs(a6->sin6_port); \
(info)->Set(address_symbol, String::New(ip)); \
(info)->Set(port_symbol, Integer::New(port)); \
break; \
case AF_INET: \
a4 = (struct sockaddr_in*)&(address_storage); \
inet_ntop(AF_INET, &(a4->sin_addr), ip, INET6_ADDRSTRLEN); \
port = ntohs(a4->sin_port); \
(info)->Set(address_symbol, String::New(ip)); \
(info)->Set(port_symbol, Integer::New(port)); \
break; \
case AF_UNIX: \
au = (struct sockaddr_un*)&(address_storage); \
(info)->Set(address_symbol, String::New(au->sun_path)); \
break; \
default: \
(info)->Set(address_symbol, String::Empty()); \
if (addrlen == 0) { \
(info)->Set(address_symbol, String::Empty()); \
} else { \
switch ((address_storage).ss_family) { \
case AF_INET6: \
a6 = (struct sockaddr_in6*)&(address_storage); \
inet_ntop(AF_INET6, &(a6->sin6_addr), ip, INET6_ADDRSTRLEN); \
port = ntohs(a6->sin6_port); \
(info)->Set(address_symbol, String::New(ip)); \
(info)->Set(port_symbol, Integer::New(port)); \
break; \
case AF_INET: \
a4 = (struct sockaddr_in*)&(address_storage); \
inet_ntop(AF_INET, &(a4->sin_addr), ip, INET6_ADDRSTRLEN); \
port = ntohs(a4->sin_port); \
(info)->Set(address_symbol, String::New(ip)); \
(info)->Set(port_symbol, Integer::New(port)); \
break; \
case AF_UNIX: \
/*
* Three types of addresses (see man 7 unix):
* * unnamed: sizeof(sa_family_t) (sun_path should not be used)
* * abstract (Linux extension): sizeof(struct sockaddr_un)
* * pathname: sizeof(sa_family_t) + strlen(sun_path) + 1
*/ \
au = (struct sockaddr_un*)&(address_storage); \
if (addrlen == sizeof(sa_family_t)) { \
(info)->Set(address_symbol, String::Empty()); \
} else if (addrlen == sizeof(struct sockaddr_un)) { \
/* first byte is '\0' and all remaining bytes are name;
* it is not NUL-terminated and may contain embedded NULs */ \
(info)->Set(address_symbol, String::New(au->sun_path + 1, sizeof(au->sun_path - 1))); \
} else { \
(info)->Set(address_symbol, String::New(au->sun_path)); \
} \
break; \
default: \
(info)->Set(address_symbol, String::Empty()); \
} \
} \
} while (0)
@@ -420,7 +438,7 @@ static Handle<Value> GetSockName(const Arguments& args) {
Local<Object> info = Object::New();
ADDRESS_TO_JS(info, address_storage);
ADDRESS_TO_JS(info, address_storage, len);
return scope.Close(info);
}
@@ -442,7 +460,7 @@ static Handle<Value> GetPeerName(const Arguments& args) {
Local<Object> info = Object::New();
ADDRESS_TO_JS(info, address_storage);
ADDRESS_TO_JS(info, address_storage, len);
return scope.Close(info);
}
@@ -484,6 +502,7 @@ static Handle<Value> Accept(const Arguments& args) {
if (peer_fd < 0) {
if (errno == EAGAIN) return scope.Close(Null());
if (errno == ECONNABORTED) return scope.Close(Null());
return ThrowException(ErrnoException(errno, "accept"));
}
@@ -497,7 +516,7 @@ static Handle<Value> Accept(const Arguments& args) {
peer_info->Set(fd_symbol, Integer::New(peer_fd));
ADDRESS_TO_JS(peer_info, address_storage);
ADDRESS_TO_JS(peer_info, address_storage, len);
return scope.Close(peer_info);
}
@@ -614,7 +633,7 @@ static Handle<Value> RecvFrom(const Arguments& args) {
info->Set(size_symbol, Integer::New(bytes_read));
ADDRESS_TO_JS(info, address_storage);
ADDRESS_TO_JS(info, address_storage, addrlen);
return scope.Close(info);
}

View File

@@ -12,38 +12,39 @@ class ObjectWrap {
refs_ = 0;
}
virtual ~ObjectWrap ( ) {
if (!handle_.IsEmpty()) {
assert(handle_.IsNearDeath());
handle_.ClearWeak();
handle_->SetInternalField(0, v8::Undefined());
handle_.Dispose();
handle_.Clear();
}
}
template <class T>
static inline T* Unwrap (v8::Handle<v8::Object> handle)
{
static inline T* Unwrap (v8::Handle<v8::Object> handle) {
assert(!handle.IsEmpty());
assert(handle->InternalFieldCount() > 0);
return static_cast<T*>(v8::Handle<v8::External>::Cast(
handle->GetInternalField(0))->Value());
return static_cast<T*>(handle->GetPointerFromInternalField(0));
}
v8::Persistent<v8::Object> handle_; // ro
protected:
inline void Wrap (v8::Handle<v8::Object> handle)
{
inline void Wrap (v8::Handle<v8::Object> handle) {
assert(handle_.IsEmpty());
assert(handle->InternalFieldCount() > 0);
handle_ = v8::Persistent<v8::Object>::New(handle);
handle_->SetInternalField(0, v8::External::New(this));
handle_->SetPointerInInternalField(0, this);
MakeWeak();
}
inline void MakeWeak (void)
{
inline void MakeWeak (void) {
handle_.MakeWeak(this, WeakCallback);
}
@@ -73,15 +74,17 @@ class ObjectWrap {
if (--refs_ == 0) { MakeWeak(); }
}
int refs_; // ro
private:
static void WeakCallback (v8::Persistent<v8::Value> value, void *data)
{
static void WeakCallback (v8::Persistent<v8::Value> value, void *data) {
ObjectWrap *obj = static_cast<ObjectWrap*>(data);
assert(value == obj->handle_);
assert(!obj->refs_);
if (value.IsNearDeath()) delete obj;
assert(value.IsNearDeath());
delete obj;
}
};

View File

@@ -212,6 +212,7 @@ template <node::Script::EvalInputFlags iFlag,
for (i = 0; i < keys->Length(); i++) {
Handle<String> key = keys->Get(Integer::New(i))->ToString();
Handle<Value> value = sandbox->Get(key);
if (value == sandbox) { value = context->Global(); }
context->Global()->Set(key, value);
}
}
@@ -264,6 +265,7 @@ template <node::Script::EvalInputFlags iFlag,
for (i = 0; i < keys->Length(); i++) {
Handle<String> key = keys->Get(Integer::New(i))->ToString();
Handle<Value> value = context->Global()->Get(key);
if (value == context->Global()) { value = sandbox; }
sandbox->Set(key, value);
}
}

View File

@@ -104,6 +104,18 @@ static Handle<Value> GetRows (const Arguments& args) {
return scope.Close(Integer::NewFromUnsigned(ws.ws_row));
}
static Handle<Value> IsATTY (const Arguments& args) {
HandleScope scope;
int fd = args[0]->IntegerValue();
int r = isatty(fd);
return scope.Close(r ? True() : False());
}
/* STDERR IS ALWAY SYNC ALWAYS UTF8 */
static Handle<Value>
WriteError (const Arguments& args)
@@ -219,6 +231,7 @@ void Stdio::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "setRawMode", SetRawMode);
NODE_SET_METHOD(target, "getColumns", GetColumns);
NODE_SET_METHOD(target, "getRows", GetRows);
NODE_SET_METHOD(target, "isatty", IsATTY);
struct sigaction sa = {0};
sa.sa_handler = HandleSIGCONT;

View File

@@ -6,7 +6,7 @@
#define NODE_MAJOR_VERSION 0
#define NODE_MINOR_VERSION 2
#define NODE_PATCH_VERSION 0
#define NODE_PATCH_VERSION 6
#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)

View File

@@ -47,11 +47,26 @@ int OS::GetMemory(size_t *rss, size_t *vsize) {
int itmp;
char ctmp;
size_t page_size = getpagesize();
char *cbuf;
bool foundExeEnd;
/* PID */
if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* Exec file */
if (fscanf (f, "%s ", buf) == 0) goto error; /* coverity[secure_coding] */
cbuf = buf;
foundExeEnd = false;
if (fscanf (f, "%c", cbuf++) == 0) goto error; // (
while (1) {
if (fscanf(f, "%c", cbuf) == 0) goto error;
if (*cbuf == ')') {
foundExeEnd = true;
} else if (foundExeEnd && *cbuf == ' ') {
*cbuf = 0;
break;
}
cbuf++;
}
/* State */
if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */
/* Parent process */

View File

@@ -30,7 +30,7 @@ int OS::GetMemory(size_t *rss, size_t *vsize) {
int OS::GetExecutablePath(char *buffer, size_t* size) {
*size = 0;
return 0;
return -1;
}
} // namespace node

View File

@@ -3,6 +3,7 @@ var path = require("path");
exports.testDir = path.dirname(__filename);
exports.fixturesDir = path.join(exports.testDir, "fixtures");
exports.libDir = path.join(exports.testDir, "../lib");
exports.tmpDir = path.join(exports.testDir, "tmp");
exports.PORT = 12346;
exports.assert = require('assert');

View File

@@ -12,7 +12,12 @@ var client = net.createConnection(443, "www.microsoft.com");
var caPem = fs.readFileSync(common.fixturesDir+"/msca.pem");
//var caPem = fs.readFileSync("ca.pem");
var credentials = crypto.createCredentials({ca:caPem});
try{
var credentials = crypto.createCredentials({ca:caPem});
} catch (e) {
console.log("Not compiled with OPENSSL support.");
process.exit();
}
client.setEncoding("UTF8");
client.addListener("connect", function () {

View File

@@ -9,7 +9,12 @@ var crypto=require('crypto');
var keyPem = fs.readFileSync(common.fixturesDir + "/cert.pem");
var certPem = fs.readFileSync(common.fixturesDir + "/cert.pem");
var credentials = crypto.createCredentials({key:keyPem, cert:certPem});
try{
var credentials = crypto.createCredentials({key:keyPem, cert:certPem});
} catch (e) {
console.log("Not compiled with OPENSSL support.");
process.exit();
}
var i = 0;
var server = net.createServer(function (connection) {
connection.setSecure(credentials);

View File

@@ -1,6 +1,4 @@
foo = "foo";
global.bar = "bar";
exports.fooBar = function () {
return {foo: global.foo, bar: bar};
};
exports.fooBar = {foo: global.foo, bar:bar};

View File

@@ -1,13 +0,0 @@
foo1 = "foo1";
global.bar1 = "bar1";
var sub2 = require('./sub2');
exports.subGlobalKeys = function () {
return sub2.globalKeys();
};
exports.subAllFooBars = function () {
return sub2.allFooBars();
};

View File

@@ -1,18 +0,0 @@
foo2 = "foo2";
global.bar2 = "bar2";
exports.globalKeys = function () {
return Object.getOwnPropertyNames(global);
};
exports.allFooBars = function () {
return {
foo0: global.foo0,
bar0: bar0,
foo1: global.foo1,
bar1: bar1,
foo2: global.foo2,
bar2: bar2
};
};

1
test/fixtures/semicolon.js vendored Normal file
View File

@@ -0,0 +1 @@
;

View File

@@ -35,10 +35,10 @@ FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)")
FILES_PATTERN = re.compile(r"//\s+Files:(.*)")
class SimpleTestCase(test.TestCase):
class InternetTestCase(test.TestCase):
def __init__(self, path, file, mode, context, config):
super(SimpleTestCase, self).__init__(context, path)
super(InternetTestCase, self).__init__(context, path)
self.file = file
self.config = config
self.mode = mode
@@ -68,10 +68,10 @@ class SimpleTestCase(test.TestCase):
return open(self.file).read()
class SimpleTestConfiguration(test.TestConfiguration):
class InternetTestConfiguration(test.TestConfiguration):
def __init__(self, context, root):
super(SimpleTestConfiguration, self).__init__(context, root)
super(InternetTestConfiguration, self).__init__(context, root)
def Ls(self, path):
def SelectTest(name):
@@ -91,7 +91,7 @@ class SimpleTestConfiguration(test.TestConfiguration):
for test in all_tests:
if self.Contains(path, test):
file_path = join(self.root, reduce(join, test[1:], "") + ".js")
result.append(SimpleTestCase(test, file_path, mode, self.context, self))
result.append(InternetTestCase(test, file_path, mode, self.context, self))
return result
def GetBuildRequirements(self):
@@ -105,4 +105,4 @@ class SimpleTestConfiguration(test.TestConfiguration):
def GetConfiguration(context, root):
return SimpleTestConfiguration(context, root)
return InternetTestConfiguration(context, root)

View File

@@ -35,7 +35,7 @@ FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)")
class MessageTestCase(test.TestCase):
def __init__(self, path, file, expected, mode, context, config):
super(MessageTestCase, self).__init__(context, path)
super(MessageTestCase, self).__init__(context, path, mode)
self.file = file
self.expected = expected
self.config = config
@@ -105,10 +105,7 @@ class MessageTestConfiguration(test.TestConfiguration):
return []
def ListTests(self, current_path, path, mode):
mjsunit = [current_path + [t] for t in self.Ls(self.root)]
#regress = [current_path + ['regress', t] for t in self.Ls(join(self.root, 'regress'))]
#bugs = [current_path + ['bugs', t] for t in self.Ls(join(self.root, 'bugs'))]
all_tests = mjsunit #+ regress + bugs
all_tests = [current_path + [t] for t in self.Ls(self.root)]
result = []
for test in all_tests:
if self.Contains(path, test):

View File

@@ -1,9 +1,8 @@
before
*.js:*
*cript.runIn*Context(*);
*^
node.js:*
throw e;
^
ReferenceError: foo is not defined
at evalmachine.<anonymous>:*
at *test/message/undefined_reference_in_new_context.js:*
@@ -11,4 +10,6 @@ ReferenceError: foo is not defined
at Module._loadScriptSync (node.js:*)
at Module.loadSync (node.js:*)
at Object.runMain (node.js:*)
at Array.<anonymous> (node.js:*)
at EventEmitter._tickCallback (node.js:*)
at node.js:*

View File

@@ -74,7 +74,14 @@ setInterval(function(param1, param2){
clearInterval(this);
}, 1000, "param1", "param2");
// setInterval(cb, 0) should be called multiple times.
count4 = 0;
interval4 = setInterval(function () {
if (++count4 > 10) clearInterval(interval4);
}, 0);
process.addListener("exit", function () {
assert.equal(true, setTimeout_called);
assert.equal(3, interval_count);
assert.equal(11, count4);
});

View File

@@ -27,6 +27,10 @@
import test
import os
import shutil
from shutil import rmtree
from os import mkdir
from glob import glob
from os.path import join, dirname, exists
import re
@@ -35,14 +39,33 @@ FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)")
FILES_PATTERN = re.compile(r"//\s+Files:(.*)")
class SimpleTestCase(test.TestCase):
class PummelTestCase(test.TestCase):
def __init__(self, path, file, mode, context, config):
super(SimpleTestCase, self).__init__(context, path)
super(PummelTestCase, self).__init__(context, path, mode)
self.file = file
self.config = config
self.mode = mode
self.tmpdir = join(dirname(self.config.root), 'tmp')
def AfterRun(self, result):
# delete the whole tmp dir
try:
rmtree(self.tmpdir)
except:
pass
# make it again.
mkdir(self.tmpdir)
def BeforeRun(self):
# delete the whole tmp dir
try:
rmtree(self.tmpdir)
except:
pass
# make it again.
mkdir(self.tmpdir)
def GetLabel(self):
return "%s %s" % (self.mode, self.GetName())
@@ -68,10 +91,10 @@ class SimpleTestCase(test.TestCase):
return open(self.file).read()
class SimpleTestConfiguration(test.TestConfiguration):
class PummelTestConfiguration(test.TestConfiguration):
def __init__(self, context, root):
super(SimpleTestConfiguration, self).__init__(context, root)
super(PummelTestConfiguration, self).__init__(context, root)
def Ls(self, path):
def SelectTest(name):
@@ -79,19 +102,12 @@ class SimpleTestConfiguration(test.TestConfiguration):
return [f[:-3] for f in os.listdir(path) if SelectTest(f)]
def ListTests(self, current_path, path, mode):
simple = [current_path + [t] for t in self.Ls(self.root)]
#simple = [current_path + ['simple', t] for t in self.Ls(join(self.root, 'simple'))]
#pummel = [current_path + ['pummel', t] for t in self.Ls(join(self.root, 'pummel'))]
#internet = [current_path + ['internet', t] for t in self.Ls(join(self.root, 'internet'))]
#regress = [current_path + ['regress', t] for t in self.Ls(join(self.root, 'regress'))]
#bugs = [current_path + ['bugs', t] for t in self.Ls(join(self.root, 'bugs'))]
#tools = [current_path + ['tools', t] for t in self.Ls(join(self.root, 'tools'))]
all_tests = simple # + regress + bugs + tools
all_tests = [current_path + [t] for t in self.Ls(join(self.root))]
result = []
for test in all_tests:
if self.Contains(path, test):
file_path = join(self.root, reduce(join, test[1:], "") + ".js")
result.append(SimpleTestCase(test, file_path, mode, self.context, self))
result.append(PummelTestCase(test, file_path, mode, self.context, self))
return result
def GetBuildRequirements(self):
@@ -105,4 +121,4 @@ class SimpleTestConfiguration(test.TestConfiguration):
def GetConfiguration(context, root):
return SimpleTestConfiguration(context, root)
return PummelTestConfiguration(context, root)

View File

@@ -4,13 +4,15 @@ assert = require("assert");
var Buffer = require('buffer').Buffer;
var b = new Buffer(1024);
var b = Buffer(1024); // safe constructor
console.log("b.length == " + b.length);
assert.strictEqual(1024, b.length);
b[0] = -1;
assert.equal(b[0], 255);
for (var i = 0; i < 1024; i++) {
assert.ok(b[i] >= 0);
b[i] = i % 256;
}
@@ -258,6 +260,25 @@ bytesWritten = b.write(expected, 0, 'base64');
assert.equal(quote, b.toString('ascii', 0, quote.length));
assert.equal(quote.length, bytesWritten);
// check that the base64 decoder ignores whitespace
var expectedWhite = expected.slice(0, 60) + " \n" +
expected.slice(60, 120) + " \n" +
expected.slice(120, 180) + " \n" +
expected.slice(180, 240) + " \n" +
expected.slice(240, 300) + "\n" +
expected.slice(300, 360) + "\n";
b = new Buffer(1024);
bytesWritten = b.write(expectedWhite, 0, 'base64');
assert.equal(quote.length, bytesWritten);
assert.equal(quote, b.toString('ascii', 0, quote.length));
// check that the base64 decoder on the constructor works
// even in the presence of whitespace.
b = new Buffer(expectedWhite, 'base64');
assert.equal(quote.length, b.length);
assert.equal(quote, b.toString('ascii', 0, quote.length));
assert.equal(new Buffer('', 'base64').toString(), '');
assert.equal(new Buffer('K', 'base64').toString(), '');

View File

@@ -0,0 +1,71 @@
var assert = require('assert'),
util = require('util'),
spawn = require('child_process').spawn;
// We're trying to reproduce:
// $ echo "hello\nnode\nand\nworld" | grep o | sed s/o/a/
var
echo = spawn('echo', ['hello\nnode\nand\nworld\n']),
grep = spawn('grep', ['o']),
sed = spawn('sed', ['s/o/O/']);
/*
* grep and sed hang if the spawn function leaks file descriptors to child
* processes.
* This happens when calling pipe(2) and then forgetting to set the
* FD_CLOEXEC flag on the resulting file descriptors.
*
* This test checks child processes exit, meaning they don't hang like
* explained above.
*/
// pipe echo | grep
echo.stdout.on('data', function (data) {
if (!grep.stdin.write(data)) {
echo.stdout.pause();
}
});
grep.stdin.on('drain', function (data) {
echo.stdout.resume();
});
// propagate end from echo to grep
echo.stdout.on('end', function (code) {
grep.stdin.end();
});
// pipe grep | sed
grep.stdout.on('data', function (data) {
if (!sed.stdin.write(data)) {
grep.stdout.pause();
}
});
sed.stdin.on('drain', function (data) {
grep.stdout.resume();
});
// propagate end from grep to sed
grep.stdout.on('end', function (code) {
sed.stdin.end();
});
var result = '';
// print sed's output
sed.stdout.on('data', function (data) {
result += data.toString('utf8', 0, data.length);
util.print(data);
});
sed.stdout.on('end', function (code) {
assert.equal(result, 'hellO\nnOde\nwOrld\n');
});

View File

@@ -0,0 +1,17 @@
common = require("../common");
assert = common.assert;
var stdout_write = global.process.stdout.write;
var strings = [];
global.process.stdout.write = function(string) {
strings.push(string);
};
console.log('foo');
console.log('foo', 'bar');
console.log('%s %s', 'foo', 'bar', 'hop');
global.process.stdout.write = stdout_write;
assert.equal('foo\n', strings.shift());
assert.equal('foo bar\n', strings.shift());
assert.equal('foo bar hop\n', strings.shift());

View File

@@ -17,7 +17,12 @@ var caPem = fs.readFileSync(common.fixturesDir+"/test_ca.pem", 'ascii');
var certPem = fs.readFileSync(common.fixturesDir+"/test_cert.pem", 'ascii');
var keyPem = fs.readFileSync(common.fixturesDir+"/test_key.pem", 'ascii');
var credentials = crypto.createCredentials({key:keyPem, cert:certPem, ca:caPem});
try{
var credentials = crypto.createCredentials({key:keyPem, cert:certPem, ca:caPem});
} catch (e) {
console.log("Not compiled with OPENSSL support.");
process.exit();
}
// Test HMAC
//var h1 = (new crypto.Hmac).init("sha1", "Node").update("some data").update("to hmac").digest("hex");

View File

@@ -1,56 +1,48 @@
common = require("../common");
assert = common.assert
var Buffer = require("buffer").Buffer,
fs = require("fs"),
dgram = require("dgram"), server, client,
server_path = "/tmp/dgram_server_sock",
client_path = "/tmp/dgram_client_sock",
message_to_send = new Buffer("A message to send"),
timer;
fs = require("fs");
dgram = require("dgram");
serverPath = "/tmp/dgram_server_sock";
clientPath = "/tmp/dgram_client_sock";
msgToSend = new Buffer("A message to send");
server = dgram.createSocket("unix_dgram");
server.on("message", function (msg, rinfo) {
console.log("server got: " + msg + " from " + rinfo.address);
assert.strictEqual(rinfo.address, client_path);
assert.strictEqual(msg.toString(), message_to_send.toString());
assert.strictEqual(rinfo.address, clientPath);
assert.strictEqual(msg.toString(), msgToSend.toString());
server.send(msg, 0, msg.length, rinfo.address);
});
server.on("listening", function () {
console.log("server is listening");
client = dgram.createSocket("unix_dgram");
client.on("message", function (msg, rinfo) {
console.log("client got: " + msg + " from " + rinfo.address);
assert.strictEqual(rinfo.address, server_path);
assert.strictEqual(msg.toString(), message_to_send.toString());
assert.strictEqual(rinfo.address, serverPath);
assert.strictEqual(msg.toString(), msgToSend.toString());
client.close();
server.close();
});
client.on("listening", function () {
console.log("client is listening");
client.send(message_to_send, 0, message_to_send.length, server_path, function (err, bytes) {
client.send(msgToSend, 0, msgToSend.length, serverPath, function (err, bytes) {
if (err) {
console.log("Caught error in client send.");
throw err;
}
console.log("client wrote " + bytes + " bytes.");
assert.strictEqual(bytes, message_to_send.length);
assert.strictEqual(bytes, msgToSend.length);
});
});
client.on("close", function () {
if (server.fd === null) {
clearTimeout(timer);
}
});
client.bind(client_path);
});
server.on("close", function () {
if (client.fd === null) {
clearTimeout(timer);
}
});
server.bind(server_path);
timer = setTimeout(function () {
throw new Error("Timeout");
}, 200);
client.bind(clientPath);
});
server.bind(serverPath);

View File

@@ -0,0 +1,27 @@
common = require("../common");
assert = common.assert
var events = require('events');
var e = new events.EventEmitter(),
num_args_emited = [];
e.on("numArgs", function() {
var numArgs = arguments.length;
console.log("numArgs: " + numArgs);
num_args_emited.push(numArgs);
});
console.log("start");
e.emit("numArgs");
e.emit("numArgs", null);
e.emit("numArgs", null, null);
e.emit("numArgs", null, null, null);
e.emit("numArgs", null, null, null, null);
e.emit("numArgs", null, null, null, null, null);
process.addListener("exit", function () {
assert.deepEqual([0, 1, 2, 3, 4, 5], num_args_emited);
});

View File

@@ -0,0 +1,21 @@
common = require("../common");
assert = common.assert
process.on('uncaughtException', function (err) {
console.log('Caught exception: ' + err);
});
var timeoutFired = false;
setTimeout(function () {
console.log('This will still run.');
timeoutFired = true;
}, 500);
process.on('exit', function() {
assert.ok(timeoutFired);
});
// Intentionally cause an exception, but don't catch it.
nonexistentFunc();
console.log('This will not run.');

View File

@@ -35,16 +35,53 @@ exec("ls /DOES_NOT_EXIST", function (err, stdout, stderr) {
}
});
exec("sleep 10", { timeout: 50 }, function (err, stdout, stderr) {
var sleeperStart = new Date();
exec("sleep 3", { timeout: 50 }, function (err, stdout, stderr) {
var diff = (new Date()) - sleeperStart;
console.log("sleep 3 with timeout 50 took %d ms", diff);
assert.ok(diff < 500);
assert.ok(err);
assert.ok(err.killed);
assert.equal(err.signal, 'SIGKILL');
assert.equal(err.signal, 'SIGTERM');
});
var startSleep3 = new Date();
var killMeTwice = exec("sleep 3", { timeout: 1000 }, killMeTwiceCallback);
process.nextTick(function(){
console.log("kill pid %d", killMeTwice.pid);
// make sure there is no race condition in starting the process
// the PID SHOULD exist directly following the exec() call.
assert.equal('number', typeof killMeTwice._internal.pid);
// Kill the process
killMeTwice.kill();
});
function killMeTwiceCallback(err, stdout, stderr) {
var diff = (new Date()) - startSleep3;
// We should have already killed this process. Assert that
// the timeout still works and that we are getting the proper callback
// parameters.
assert.ok(err);
assert.ok(err.killed);
assert.equal(err.signal, 'SIGTERM');
// the timeout should still be in effect
console.log("'sleep 3' was already killed. Took %d ms", diff);
assert.ok(diff < 1500);
}
exec('python -c "print 200000*\'C\'"', { maxBuffer: 1000 }, function (err, stdout, stderr) {
assert.ok(err);
assert.ok(err.killed);
assert.equal(err.signal, 'SIGKILL');
assert.equal(err.signal, 'SIGTERM');
});
process.addListener("exit", function () {

View File

@@ -4,7 +4,7 @@ assert = common.assert
var
path = require('path'),
fs = require('fs'),
fn = path.join(common.fixturesDir, "write.txt"),
fn = path.join(common.tmpDir, "write.txt"),
file = fs.createWriteStream(fn),
EXPECTED = '012345678910',

View File

@@ -15,7 +15,7 @@ callbacks = { open: 0, end: 0, close: 0, destroy: 0 };
paused = false;
file = fs.createReadStream(fn);
file = fs.ReadStream(fn);
file.addListener('open', function(fd) {
file.length = 0;
@@ -60,9 +60,8 @@ file2.destroy(function(err) {
callbacks.destroy++;
});
var file3 = fs.createReadStream(fn);
var file3 = fs.createReadStream(fn, {encoding: 'utf8'});
file3.length = 0;
file3.setEncoding('utf8');
file3.addListener('data', function(data) {
assert.equal("string", typeof(data));
file3.length += data.length;
@@ -88,11 +87,36 @@ process.addListener('exit', function() {
assert.equal(10000, file3.length);
});
var file4 = fs.createReadStream(rangeFile, {start: 1, end: 2});
var file4 = fs.createReadStream(rangeFile, {bufferSize: 1, start: 1, end: 2});
var contentRead = '';
file4.addListener('data', function(data) {
contentRead += data.toString('utf-8');
contentRead += data.toString('utf-8');
});
file4.addListener('end', function(data) {
assert.equal(contentRead, 'yz');
assert.equal(contentRead, 'yz');
});
try {
fs.createReadStream(rangeFile, {start: 10, end: 2});
assert.fail('Creating a ReadStream with incorrect range limits must throw.');
} catch(e) {
assert.equal(e.message, 'start must be <= end');
}
try {
fs.createReadStream(rangeFile, {start: 2});
assert.fail('Creating a ReadStream with a only one range limits must throw.');
} catch(e) {
assert.equal(e.message, 'Both start and end are needed for range streaming.');
}
var stream = fs.createReadStream(rangeFile, { start: 0, end: 0 });
stream.data = '';
stream.on('data', function(chunk){
stream.data += chunk;
});
stream.on('end', function(){
assert.equal('x', stream.data);
});

View File

@@ -9,3 +9,7 @@ var
fs.readFile(fn, function(err, data) {
assert.ok(data);
});
fs.readFile(fn, 'utf8', function(err, data) {
assert.strictEqual('', data);
});

View File

@@ -2,16 +2,16 @@ common = require("../common");
assert = common.assert
var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;
var async_completed = 0, async_expected = 0, unlink = [];
function asynctest(testBlock, args, callback, assertBlock) {
async_expected++;
testBlock.apply(testBlock, args.concat([function(err){
testBlock.apply(testBlock, args.concat(function(err){
var ignoreError = false;
if (assertBlock) {
try {
ignoreError = assertBlock.apply(assertBlock,
Array.prototype.slice.call(arguments));
ignoreError = assertBlock.apply(assertBlock, arguments);
}
catch (e) {
err = e;
@@ -19,11 +19,11 @@ function asynctest(testBlock, args, callback, assertBlock) {
}
async_completed++;
callback(ignoreError ? null : err);
}]));
}));
}
function bashRealpath(path, callback) {
common.exec("cd '"+path.replace("'","\\'")+"' && pwd -P",function
exec("cd '"+path.replace("'","\\'")+"' && pwd -P",function
(err, o) {
callback(err, o.trim());
});
@@ -32,6 +32,7 @@ function bashRealpath(path, callback) {
// sub-tests:
function test_simple_relative_symlink(callback) {
console.log("test_simple_relative_symlink");
var entry = common.fixturesDir+'/cycles/symlink',
expected = common.fixturesDir+'/cycles/root.js';
[
@@ -51,6 +52,7 @@ function test_simple_relative_symlink(callback) {
}
function test_simple_absolute_symlink(callback) {
console.log("test_simple_absolute_symlink");
bashRealpath(common.fixturesDir, function(err, fixturesAbsDir) {
if (err) return callback(err);
var entry = fixturesAbsDir+'/cycles/symlink',
@@ -73,6 +75,7 @@ function test_simple_absolute_symlink(callback) {
}
function test_deep_relative_file_symlink(callback) {
console.log("test_deep_relative_file_symlink");
var expected = path.join(common.fixturesDir, 'cycles', 'root.js');
var linkData1 = "../../cycles/root.js";
var linkPath1 = path.join(common.fixturesDir, "nested-index", 'one', 'symlink1.js');
@@ -94,6 +97,7 @@ function test_deep_relative_file_symlink(callback) {
}
function test_deep_relative_dir_symlink(callback) {
console.log("test_deep_relative_dir_symlink");
var expected = path.join(common.fixturesDir, 'cycles', 'folder');
var linkData1b = "../../cycles/folder";
var linkPath1b = path.join(common.fixturesDir, "nested-index", 'one', 'symlink1-dir');
@@ -116,6 +120,7 @@ function test_deep_relative_dir_symlink(callback) {
}
function test_cyclic_link_protection(callback) {
console.log("test_cyclic_link_protection");
var entry = common.fixturesDir+'/cycles/realpath-3a';
[
[entry, '../cycles/realpath-3b'],
@@ -133,7 +138,24 @@ function test_cyclic_link_protection(callback) {
});
}
function test_cyclic_link_overprotection (callback) {
console.log("test_cyclic_link_overprotection");
var cycles = common.fixturesDir+'/cycles';
var expected = fs.realpathSync(cycles);
var folder = cycles+'/folder';
var link = folder+'/cycles';
var testPath = cycles;
for (var i = 0; i < 10; i ++) testPath += '/folder/cycles';
try {fs.unlinkSync(link)} catch (ex) {}
fs.symlinkSync(cycles, link);
assert.equal(fs.realpathSync(testPath), expected);
asynctest(fs.realpath, [testPath], callback, function (er, res) {
assert.equal(res, expected);
});
}
function test_relative_input_cwd(callback) {
console.log("test_relative_input_cwd");
var p = common.fixturesDir.lastIndexOf('/');
var entrydir = common.fixturesDir.substr(0, p);
var entry = common.fixturesDir.substr(p+1)+'/cycles/realpath-3a';
@@ -161,6 +183,7 @@ function test_relative_input_cwd(callback) {
}
function test_deep_symlink_mix(callback) {
console.log("test_deep_symlink_mix");
// todo: check to see that common.fixturesDir is not rooted in the
// same directory as our test symlink.
// obtain our current realpath using bash (so we can test ourselves)
@@ -209,6 +232,7 @@ function test_deep_symlink_mix(callback) {
}
function test_non_symlinks(callback) {
console.log("test_non_symlinks");
bashRealpath(common.fixturesDir, function(err, fixturesAbsDir) {
if (err) return callback(err);
var p = fixturesAbsDir.lastIndexOf('/');
@@ -227,6 +251,71 @@ function test_non_symlinks(callback) {
});
}
var upone = path.join(process.cwd(), "..");
function test_escape_cwd (cb) {
console.log("test_escape_cwd");
asynctest(fs.realpath, [".."], cb, function(er, uponeActual){
assert.equal(upone, uponeActual,
"realpath('..') expected: "+upone+" actual:"+uponeActual);
})
}
var uponeActual = fs.realpathSync("..");
assert.equal(upone, uponeActual,
"realpathSync('..') expected: "+upone+" actual:"+uponeActual);
// absolute symlinks with children.
// .
// `-- a/
// |-- b/
// | `-- c/
// | `-- x.txt
// `-- link -> /tmp/node-test-realpath-abs-kids/a/b/
// realpath(root+'/a/link/c/x.txt') ==> root+'/a/b/c/x.txt'
function test_abs_with_kids (cb) {
console.log("test_abs_with_kids");
bashRealpath(common.fixturesDir, function(err, fixturesAbsDir) {
var root = fixturesAbsDir+'/node-test-realpath-abs-kids';
function cleanup () {
;['/a/b/c/x.txt'
, '/a/link'
].forEach(function (file) {
try {fs.unlinkSync(root+file)} catch (ex) {}
});
;['/a/b/c'
, '/a/b'
, '/a'
, ''
].forEach(function (folder) {
try {fs.rmdirSync(root+folder)} catch (ex) {}
});
}
function setup () {
cleanup()
;[''
, '/a'
, '/a/b'
, '/a/b/c'
].forEach(function (folder) {
console.log("mkdir "+root+folder)
fs.mkdirSync(root+folder, 0700);
});
fs.writeFileSync(root+'/a/b/c/x.txt', 'foo');
fs.symlinkSync(root+'/a/b', root+'/a/link');
}
setup();
var linkPath = root+'/a/link/c/x.txt';
var expectPath = root+'/a/b/c/x.txt';
var actual = fs.realpathSync(linkPath);
// console.log({link:linkPath,expect:expectPath,actual:actual},'sync');
assert.equal(actual, expectPath);
asynctest(fs.realpath, [linkPath], cb, function (er, actual) {
// console.log({link:linkPath,expect:expectPath,actual:actual},'async');
assert.equal(actual, expectPath);
cleanup();
});
})
}
// ----------------------------------------------------------------------------
var tests = [
@@ -235,9 +324,12 @@ var tests = [
test_deep_relative_file_symlink,
test_deep_relative_dir_symlink,
test_cyclic_link_protection,
test_cyclic_link_overprotection,
test_relative_input_cwd,
test_deep_symlink_mix,
test_non_symlinks,
test_escape_cwd,
test_abs_with_kids
];
var numtests = tests.length;
function runNextTest(err) {
@@ -248,6 +340,15 @@ function runNextTest(err) {
}
runNextTest();
assert.equal('/', fs.realpathSync('/'));
fs.realpath('/', function (err, result) {
assert.equal(null, err);
assert.equal('/', result);
});
process.addListener("exit", function () {
unlink.forEach(function(path){ try {fs.unlinkSync(path);}catch(e){} });
assert.equal(async_completed, async_expected);

View File

@@ -0,0 +1,50 @@
var common = require('../common');
var fs = require("fs");
var assert = require("assert");
var join = require('path').join;
var filename = join(common.tmpDir, 'out.txt');
try {
fs.unlinkSync(filename);
} catch (e) {
// might not exist, that's okay.
}
var fd = fs.openSync(filename, "w");
var line = "aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n";
var N = 10240, complete = 0;
for (var i = 0; i < N; i ++) {
// Create a new buffer for each write. Before the write is actually
// executed by the thread pool, the buffer will be collected.
var buffer = new Buffer(line);
fs.write(fd, buffer, 0, buffer.length, null, function (er, written) {
complete++;
if (complete === N) {
fs.closeSync(fd);
var s = fs.createReadStream(filename);
s.on("data", testBuffer);
}
});
}
var bytesChecked = 0;
function testBuffer (b) {
for (var i = 0; i < b.length; i++) {
bytesChecked++;
if (b[i] !== 'a'.charCodeAt(0) && b[i] !== '\n'.charCodeAt(0)) {
throw new Error("invalid char "+i+","+b[i]);
}
}
}
process.on('exit', function () {
// Probably some of the writes are going to overlap, so we can't assume
// that we get (N * line.length). Let's just make sure we've checked a
// few...
assert.ok(bytesChecked > 1000);
});

View File

@@ -1,13 +1,12 @@
common = require("../common");
assert = common.assert
var assert = common.assert;
var path = require('path');
var fs = require('fs');
var completed = 0;
// test creating and reading symbolic link
var linkData = "../../cycles/root.js";
var linkPath = path.join(common.fixturesDir, "nested-index", 'one', 'symlink1.js');
try {fs.unlinkSync(linkPath);}catch(e){}
var linkData = path.join(common.fixturesDir, "/cycles/root.js");
var linkPath = path.join(common.tmpDir, 'symlink1.js');
fs.symlink(linkData, linkPath, function(err){
if (err) throw err;
console.log('symlink done');
@@ -21,8 +20,7 @@ fs.symlink(linkData, linkPath, function(err){
// test creating and reading hard link
var srcPath = path.join(common.fixturesDir, "cycles", 'root.js');
var dstPath = path.join(common.fixturesDir, "nested-index", 'one', 'link1.js');
try {fs.unlinkSync(dstPath);}catch(e){}
var dstPath = path.join(common.tmpDir, 'link1.js');
fs.link(srcPath, dstPath, function(err){
if (err) throw err;
console.log('hard link done');
@@ -33,8 +31,6 @@ fs.link(srcPath, dstPath, function(err){
});
process.addListener("exit", function () {
try {fs.unlinkSync(linkPath);}catch(e){}
try {fs.unlinkSync(dstPath);}catch(e){}
assert.equal(completed, 2);
});

View File

@@ -3,7 +3,7 @@ assert = common.assert
var path = require('path'),
Buffer = require('buffer').Buffer,
fs = require('fs'),
filename = path.join(common.fixturesDir, 'write.txt'),
filename = path.join(common.tmpDir, 'write.txt'),
expected = new Buffer('hello'),
openCalled = 0,
writeCalled = 0;

View File

@@ -0,0 +1,30 @@
var common = require('../common');
var join = require('path').join;
var util = require('util');
var fs = require('fs');
var data = [
'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcH',
'Bw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/',
'2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e',
'Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAQABADASIAAhEBAxEB/8QA',
'HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF',
'BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK',
'FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1',
'dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG',
'x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB',
'AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC',
'AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom',
'JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE',
'hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU',
'1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDhfBUFl/wk',
'OmPqKJJZw3aiZFBw4z93jnkkc9u9dj8XLfSI/EBt7DTo7ea2Ox5YXVo5FC7g',
'Tjq24nJPXNVtO0KATRvNHCIg3zoWJWQHqp+o4pun+EtJ0zxBq8mnLJa2d1L5',
'0NvnKRjJBUE5PAx3NYxxUY0pRtvYHSc5Ka2X9d7H/9k='];
data = data.join('\n');
var buf = new Buffer(data, 'base64');
fs.writeFileSync(join(common.tmpDir, 'test.jpg'), buf);
util.log('Done!');

View File

@@ -0,0 +1,28 @@
common = require("../common");
assert = common.assert
var path = require('path'),
fs = require('fs');
var file = path.join(common.tmpDir, "write.txt");
(function() {
var stream = fs.WriteStream(file),
_fs_close = fs.close;
fs.close = function(fd) {
assert.ok(fd, "fs.close must not be called without an undefined fd.")
fs.close = _fs_close;
}
stream.destroy();
})();
(function() {
var stream = fs.createWriteStream(file);
stream.addListener('drain', function () {
assert.fail('"drain" event must not be emitted before stream.write() has been called at least once.')
});
stream.destroy();
})();

View File

@@ -3,7 +3,7 @@ assert = common.assert
path = require('path'),
Buffer = require('buffer').Buffer
fs = require('fs')
fn = path.join(common.fixturesDir, 'write.txt');
fn = path.join(common.tmpDir, 'write.txt');
foo = 'foo'

View File

@@ -3,7 +3,7 @@ assert = common.assert
var path = require('path');
var Buffer = require('buffer').Buffer;
var fs = require('fs');
var fn = path.join(common.fixturesDir, "write.txt");
var fn = path.join(common.tmpDir, "write.txt");
var expected = "ümlaut.";
var found;
@@ -11,7 +11,7 @@ fs.open(fn, 'w', 0644, function (err, fd) {
if (err) throw err;
console.log('open done');
fs.write(fd, '', 0, 'utf8', function(err, written) {
assert.fail('zero length write should not go through to callback');
assert.equal(0, written);
});
fs.write(fd, expected, 0, "utf8", function (err, written) {
console.log('write done');

View File

@@ -1,21 +0,0 @@
common = require("../common");
assert = common.assert
foo0 = "foo0";
global.bar0 = "bar0";
var module = require("../fixtures/global/sub1"),
keys = module.subGlobalKeys();
var fooBarKeys = keys.filter(
function (x) { return x.match(/^foo/) || x.match(/^bar/); }
);
fooBarKeys.sort();
assert.equal("bar0,bar1,bar2,foo0,foo1,foo2", fooBarKeys.join(), "global keys not as expected: "+JSON.stringify(keys));
var fooBars = module.subAllFooBars();
assert.equal("foo0", fooBars.foo0, "x from base level not visible in deeper levels.");
assert.equal("bar0", fooBars.bar0, "global.x from base level not visible in deeper levels.");
assert.equal("foo1", fooBars.foo1, "x from medium level not visible in deeper levels.");
assert.equal("bar1", fooBars.bar1, "global.x from medium level not visible in deeper levels.");

View File

@@ -0,0 +1,32 @@
var assert = require('assert');
var knownGlobals = [ setTimeout
, setInterval
, clearTimeout
, clearInterval
, console
, Buffer
, process
, global
, __module
, include
, puts
, print
, p
];
for (var x in global) {
var found = false;
for (var y in knownGlobals) {
if (global[x] === knownGlobals[y]) {
found = true;
break;
}
}
if (!found) {
console.error("Unknown global: %s", x);
assert.ok(false);
}
}

View File

@@ -9,7 +9,7 @@ assert.equal("foo", global.baseFoo, "x -> global.x in base level not working");
assert.equal("bar", baseBar, "global.x -> x in base level not working");
var module = require("../fixtures/global/plain"),
fooBar = module.fooBar();
fooBar = module.fooBar;
assert.equal("foo", fooBar.foo, "x -> global.x in sub level not working");

View File

@@ -0,0 +1,43 @@
var common = require('../common');
var http = require('http');
var assert = require('assert');
// first 204 or 304 works, subsequent anything fails
var codes = [ 204, 200 ];
// Methods don't really matter, but we put in something realistic.
var methods = ['DELETE', 'DELETE'];
var server = http.createServer(function (req, res) {
var code = codes.shift();
assert.equal('number', typeof code);
assert.ok(code > 0);
console.error('writing %d response', code);
res.writeHead(code, {});
res.end();
});
var client = http.createClient(common.PORT);
function nextRequest() {
var method = methods.shift();
console.error("writing request: %s", method);
var request = client.request(method, '/');
request.on('response', function (response) {
response.on('end', function() {
if (methods.length == 0) {
console.error("close server");
server.close();
} else {
// throws error:
nextRequest();
// works just fine:
//process.nextTick(nextRequest);
}
});
});
request.end();
}
server.listen(common.PORT, nextRequest);

View File

@@ -0,0 +1,65 @@
var common = require('../common');
var assert = require('assert');
var http = require('http');
var sys = require('sys');
var bufferSize = 5 * 1024 * 1024;
var measuredSize = 0;
var buffer = Buffer(bufferSize);
for (var i = 0; i < buffer.length; i++) {
buffer[i] = i % 256;
}
var web = http.Server(function (req, res) {
web.close();
console.log(req.headers);
var i = 0;
req.on('data', function (d) {
process.stdout.write(",");
measuredSize += d.length;
for (var j = 0; j < d.length; j++) {
assert.equal(buffer[i], d[j]);
i++;
}
});
req.on('end', function () {
res.writeHead(200);
res.write("thanks");
res.end();
console.log("response with 'thanks'");
});
req.connection.on('error', function (e) {
console.log("http server-side error: " + e.message);
process.exit(1);
});
});
web.listen(common.PORT, function () {
console.log("Making request");
var client = http.createClient(common.PORT);
var req = client.request('GET', '/', { 'content-length': buffer.length });
req.end(buffer);
req.on('response', function (res) {
console.log('Got response');
res.setEncoding('utf8');
res.on('data', function (string) {
assert.equal("thanks", string);
gotThanks = true;
});
});
});
process.on('exit', function () {
assert.equal(bufferSize, measuredSize);
});

View File

@@ -0,0 +1,35 @@
var common = require("../common");
var assert = require('assert');
var http = require('http');
var net = require('net');
// Create a TCP server
var srv = net.createServer(function(c) {
c.write('bad http - should trigger parse error\r\n');
console.log("connection");
c.addListener('end', function() { c.end(); });
});
var parseError = false;
srv.listen(common.PORT, '127.0.0.1', function () {
var hc = http.createClient(common.PORT, '127.0.0.1');
hc.request('GET', '/').end();
hc.on('error', function (e) {
console.log("got error from client");
srv.close();
assert.ok(e.message.indexOf("Parse Error") >= 0);
parseError = true;
});
});
process.addListener('exit', function() {
assert.ok(parseError);
});

View File

@@ -0,0 +1,55 @@
// http://groups.google.com/group/nodejs/browse_thread/thread/f66cd3c960406919
var common = require('../common');
var assert = require('assert');
var http = require('http'),
cp = require('child_process');
var filename = require('path').join(common.tmpDir || '/tmp', 'big');
var count = 0;
function maybeMakeRequest () {
if (++count < 2) return;
console.log("making curl request");
cp.exec('curl http://127.0.0.1:' + common.PORT + '/ | shasum',
function (err, stdout, stderr) {
if (err) throw err;
assert.equal('8c206a1a87599f532ce68675536f0b1546900d7a', stdout.slice(0, 40));
console.log("got the correct response");
server.close();
});
}
cp.exec('dd if=/dev/zero of=' + filename + ' bs=1024 count=10240',
function (err, stdout, stderr) {
if (err) throw err;
maybeMakeRequest();
});
var server = http.createServer(function (req, res) {
res.writeHead(200);
// Create the subprocess
var cat = cp.spawn('cat', [filename]);
// Stream the data through to the response as binary chunks
cat.stdout.on('data', function (data) {
res.write(data);
});
// End the response on exit (and log errors)
cat.on('exit', function (code) {
if (code !== 0) {
console.error('subprocess exited with code ' + code);
exit(1);
}
res.end();
});
});
server.listen(common.PORT, maybeMakeRequest);
console.log('Server running at http://localhost:8080');

View File

@@ -0,0 +1,49 @@
common = require("../common");
assert = common.assert
assert = require("assert");
http = require("http");
sys = require("sys");
body = "hello world\n";
headers = {'connection':'keep-alive'}
server = http.createServer(function (req, res) {
res.writeHead(200, {"Content-Length": body.length, "Connection":"close"});
res.write(body);
res.end();
});
connectCount = 0;
server.listen(common.PORT, function () {
var client = http.createClient(common.PORT);
client.addListener("connect", function () {
common.error("CONNECTED")
connectCount++;
})
var request = client.request("GET", "/", headers);
request.end();
request.addListener('response', function (response) {
common.error('response start');
response.addListener("end", function () {
common.error('response end');
var req = client.request("GET", "/", headers);
req.addListener('response', function (response) {
response.addListener("end", function () {
client.end();
server.close();
})
})
req.end();
});
});
});
process.addListener('exit', function () {
assert.equal(2, connectCount);
});

View File

@@ -15,7 +15,7 @@ var buffer = new Buffer(1024);
var request = "GET /hello HTTP/1.1\r\n\r\n";
buffer.asciiWrite(request, 0, request.length);
buffer.write(request, 0, 'ascii');
var callbacks = 0;
@@ -39,7 +39,7 @@ parser.onURL = function (b, off, len) {
parser.onPath = function (b, off, length) {
console.log("path [" + off + ", " + length + "]");
var path = b.asciiSlice(off, off+length);
var path = b.toString('ascii', off, off+length);
console.log("path = '" + path + "'");
assert.equal('/hello', path);
callbacks++;

View File

@@ -27,7 +27,13 @@ var caPem = fs.readFileSync(common.fixturesDir+"/test_ca.pem", 'ascii');
var certPem = fs.readFileSync(common.fixturesDir+"/test_cert.pem", 'ascii');
var keyPem = fs.readFileSync(common.fixturesDir+"/test_key.pem", 'ascii');
var credentials = crypto.createCredentials({key:keyPem, cert:certPem, ca:caPem});
try{
var credentials = crypto.createCredentials({key:keyPem, cert:certPem, ca:caPem});
} catch (e) {
console.log("Not compiled with OPENSSL support.");
process.exit();
}
var https_server = http.createServer(function (req, res) {
res.id = request_number;
@@ -41,7 +47,8 @@ var https_server = http.createServer(function (req, res) {
+ '"issuer":"/C=UK/ST=Acknack Ltd/L=Rhys Jones/O=node.js'
+ '/OU=Test TLS Certificate/CN=localhost","valid_from":'
+ '"Nov 11 09:52:22 2009 GMT","valid_to":'
+ '"Nov 6 09:52:22 2029 GMT"}');
+ '"Nov 6 09:52:22 2029 GMT",'
+ '"fingerprint":"2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF"}');
if (req.id == 0) {
assert.equal("GET", req.method);
@@ -92,7 +99,8 @@ https_server.addListener("listening", function() {
+ '"issuer":"/C=UK/ST=Acknack Ltd/L=Rhys Jones/O=node.js'
+ '/OU=Test TLS Certificate/CN=localhost","valid_from":'
+ '"Nov 11 09:52:22 2009 GMT","valid_to":'
+ '"Nov 6 09:52:22 2029 GMT"}');
+ '"Nov 6 09:52:22 2029 GMT",'
+ '"fingerprint":"2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF"}');
c.write( "GET /hello?hello=world&foo=b==ar HTTP/1.1\r\n\r\n" );
requests_sent += 1;
});

View File

@@ -8,69 +8,49 @@ assert = common.assert
var http = require('http');
var net = require('net');
// Parse a string of data, returning an object if headers are complete, and
// undefined otherwise
var parseHeaders = function(data) {
var m = data.search(/\r\n\r\n/);
if (!m) {
return;
}
var o = {};
data.substring(0, m.index).split('\r\n').forEach(function(h) {
var foo = h.split(':');
if (foo.length < 2) {
return;
}
o[foo[0].trim().toLowerCase()] = foo[1].trim().toLowerCase();
});
return o;
};
// Create a TCP server
var srv = net.createServer(function(c) {
var data = '';
c.addListener('data', function(d) {
data += d.toString('utf8');
// We found the end of the headers; make sure that we have an 'upgrade'
// header and send back a response
var headers = parseHeaders(data);
if (!headers) {
return;
}
assert.ok('upgrade' in headers);
c.write('HTTP/1.1 101\r\n');
c.write('hello: world\r\n');
c.write('connection: upgrade\r\n');
c.write('upgrade: ' + headers.upgrade + '\r\n');
c.write('upgrade: websocket\r\n');
c.write('\r\n');
c.write('nurtzo');
});
c.addListener('end', function() {
c.end();
});
});
srv.listen(common.PORT, '127.0.0.1');
var gotUpgrade = false;
var hc = http.createClient(common.PORT, '127.0.0.1');
hc.addListener('upgrade', function(req, socket, upgradeHead) {
// XXX: This test isn't fantastic, as it assumes that the entire response
// from the server will arrive in a single data callback
assert.equal(upgradeHead, 'nurtzo');
socket.end();
srv.close();
srv.listen(common.PORT, '127.0.0.1', function () {
gotUpgrade = true;
var hc = http.createClient(common.PORT, '127.0.0.1');
hc.addListener('upgrade', function(res, socket, upgradeHead) {
// XXX: This test isn't fantastic, as it assumes that the entire response
// from the server will arrive in a single data callback
assert.equal(upgradeHead, 'nurtzo');
console.log(res.headers);
var expectedHeaders = { "hello": "world"
, "connection": "upgrade"
, "upgrade": "websocket"
};
assert.deepEqual(expectedHeaders, res.headers);
socket.end();
srv.close();
gotUpgrade = true;
});
hc.request('GET', '/').end();
});
hc.request('/', {
'Connection' : 'Upgrade',
'Upgrade' : 'WebSocket'
}).end();
process.addListener('exit', function() {
assert.ok(gotUpgrade);

View File

@@ -0,0 +1,68 @@
var common = require("../common");
var assert = common.assert
var http = require('http'),
CRLF = '\r\n';
var server = http.createServer();
server.on('upgrade', function(req, socket, head) {
socket.write('HTTP/1.1 101 Ok' + CRLF +
'Connection: Upgrade' + CRLF +
'Upgrade: Test' + CRLF + CRLF + 'head');
socket.on('end', function () {
socket.end();
});
});
server.listen(common.PORT, function () {
var client = http.createClient(common.PORT);
function upgradeRequest(fn) {
var request = client.request('GET', '/', {
'Connection': 'Upgrade',
'Upgrade': 'Test'
});
var wasUpgrade = false;
function onUpgrade(res, socket, head) {
wasUpgrade = true;
client.removeListener('upgrade', onUpgrade);
socket.end();
}
client.on('upgrade', onUpgrade);
function onEnd() {
client.removeListener('end', onEnd);
if (!wasUpgrade) {
throw new Error('hasn\'t received upgrade event');
} else {
fn && process.nextTick(fn);
}
}
client.on('end', onEnd);
request.write('head');
}
successCount = 0;
upgradeRequest(function() {
successCount++;
upgradeRequest(function() {
successCount++;
// Test pass
console.log('Pass!');
client.end();
client.destroy();
server.close();
});
});
});
process.on('exit', function () {
assert.equal(2, successCount);
});

Some files were not shown because too many files have changed in this diff Show More