diff --git a/ChangeLog b/ChangeLog
index a0ca933870..46318dd4dc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,38 @@
-2010.01.20, Version 0.1.26
+2010.02.03, Version 0.1.27
+
+ * Implemented __dirname (Felix Geisendörfer)
+
+ * Downcase process.ARGV, process.ENV, GLOBAL
+ (now process.argv, process.env, global)
+
+ * Bug Fix: Late promise promise callbacks firing
+ (Felix Geisendörfer, Jonas Pfenniger)
+
+ * Make assert.AssertionError instance of Error
+
+ * Removed inline require call for querystring
+ (self@cloudhead.net)
+
+ * Add support for MX, TXT, and SRV records in DNS module.
+ (Blaine Cook)
+
+ * Bugfix: HTTP client automatically reconnecting
+
+ * Adding OS X .dmg build scripts. (Standa Opichal)
+
+ * Bugfix: ObjectWrap memory leak
+
+ * Bugfix: Multipart handle Content-Type headers with charset
+ (Felix Geisendörfer)
+
+ * Upgrade http-parser to fix header overflow attack.
+
+ * Upgrade V8 to 2.1.0
+
+ * Various other bug fixes, performance improvements.
+
+
+2010.01.20, Version 0.1.26, da00413196e432247346d9e587f8c78ce5ceb087
* Bugfix, HTTP eof causing crash (Ben Williamson)
diff --git a/deps/coupling/coupling.c b/deps/coupling/coupling.c
index 57eca1c8ce..f6b03f38eb 100644
--- a/deps/coupling/coupling.c
+++ b/deps/coupling/coupling.c
@@ -143,98 +143,176 @@ ring_buffer_push (ring_buffer *ring, int fd)
return r;
}
+/* PULL PUMP
+ *
+ * This is used to read data from a blocking file descriptor and pump it into
+ * a non-blocking pipe (or other non-blocking fd). The algorithm is this:
+ *
+ * while (true) {
+ * read(STDIN_FILENO) // blocking
+ *
+ * while (!ring.empty) {
+ * write(pipe) // non-blocking
+ * select(pipe, writable)
+ * }
+ * }
+ *
+ */
static void
-pump (int is_pull, int pullfd, int pushfd)
+pull_pump (int pullfd, int pushfd)
{
int r;
ring_buffer ring;
- fd_set readfds, writefds, exceptfds;
+
+ fd_set writefds, exceptfds;
+ FD_ZERO(&exceptfds);
+ FD_ZERO(&writefds);
+ FD_SET(pushfd, &exceptfds);
+ FD_SET(pushfd, &writefds);
ring_buffer_init(&ring);
- int maxfd;
+ while (pullfd >= 0) {
+ /* Blocking read from STDIN_FILENO */
+ r = ring_buffer_pull(&ring, pullfd);
- while (pushfd >= 0 && (pullfd >= 0 || !ring_buffer_empty_p(&ring))) {
- FD_ZERO(&exceptfds);
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
-
- maxfd = -1;
-
- if (is_pull) {
- if (!ring_buffer_empty_p(&ring)) {
- maxfd = pushfd;
- FD_SET(pushfd, &exceptfds);
- FD_SET(pushfd, &writefds);
- }
- } else {
- if (pullfd >= 0) {
- if (!ring_buffer_filled_p(&ring)) {
- maxfd = pullfd;
- FD_SET(pullfd, &exceptfds);
- FD_SET(pullfd, &readfds);
+ if (r == 0) {
+ /* eof */
+ close(pullfd);
+ pullfd = -1;
+ } else if (r < 0 && errno != EINTR && errno != EAGAIN) {
+ /* error */
+ perror("pull_pump read()");
+ close(pullfd);
+ pullfd = -1;
+ }
+
+ /* Push all of the data in the ring buffer out. */
+ while (!ring_buffer_empty_p(&ring)) {
+ /* non-blocking write() to the pipe */
+ r = ring_buffer_push(&ring, pushfd);
+
+ if (r < 0 && errno != EAGAIN && errno != EINTR) {
+ if (errno == EPIPE) {
+ /* This happens if someone closes the other end of the pipe. This
+ * is a normal forced close of STDIN. Hopefully there wasn't data
+ * in the ring buffer. Just close both ends and exit.
+ */
+ close(pushfd);
+ close(pullfd);
+ pushfd = pullfd = -1;
+ } else {
+ perror("pull_pump write()");
+ close(pushfd);
+ close(pullfd);
}
+ return;
}
- }
- if (maxfd >= 0) {
- r = select(maxfd+1, &readfds, &writefds, &exceptfds, NULL);
+ /* Select for writablity on the pipe end.
+ * Very rarely will this stick.
+ */
+ r = select(pushfd+1, NULL, &writefds, &exceptfds, NULL);
- if (r < 0 || (pullfd >= 0 && FD_ISSET(pushfd, &exceptfds))) {
+ if (r < 0 || FD_ISSET(pushfd, &exceptfds)) {
close(pushfd);
close(pullfd);
pushfd = pullfd = -1;
return;
}
}
+ }
+ assert(pullfd < 0);
+ assert(ring_buffer_empty_p(&ring));
+ close(pushfd);
+}
+
+/* PUSH PUMP
+ *
+ * This is used to push data out to a blocking file descriptor. It pulls
+ * data from a non-blocking pipe (pullfd) and pushes to STDOUT_FILENO
+ * (pushfd).
+ * When the pipe is closed, then the rest of the data is pushed out and then
+ * STDOUT_FILENO is closed.
+ *
+ * The algorithm looks roughly like this:
+ *
+ * while (true) {
+ * r = read(pipe) // nonblocking
+ *
+ * while (!ring.empty) {
+ * write(STDOUT_FILENO) // blocking
+ * }
+ *
+ * select(pipe, readable);
+ * }
+ */
+static void
+push_pump (int pullfd, int pushfd)
+{
+ int r;
+ ring_buffer ring;
+
+ fd_set readfds, exceptfds;
+ FD_ZERO(&exceptfds);
+ FD_ZERO(&readfds);
+ FD_SET(pullfd, &exceptfds);
+ FD_SET(pullfd, &readfds);
+
+ ring_buffer_init(&ring);
+
+ /* The pipe is open or there is data left to be pushed out
+ * NOTE: if pushfd (STDOUT_FILENO) ever errors out, then we just exit the
+ * loop.
+ */
+ while (pullfd >= 0 || !ring_buffer_empty_p(&ring)) {
- if (pullfd >= 0 && FD_ISSET(pullfd, &exceptfds)) {
+ /* Pull from the non-blocking pipe */
+ r = ring_buffer_pull(&ring, pullfd);
+
+ if (r == 0) {
+ /* eof */
+ close(pullfd);
+ pullfd = -1;
+ } else if (r < 0 && errno != EINTR && errno != EAGAIN) {
+ perror("push_pump read()");
close(pullfd);
pullfd = -1;
+ return;
}
- if (pullfd >= 0 && (is_pull || FD_ISSET(pullfd, &readfds))) {
- r = ring_buffer_pull(&ring, pullfd);
- if (r == 0) {
- /* eof */
- close(pullfd);
- pullfd = -1;
+ /* Push everything out to STDOUT */
+ while (!ring_buffer_empty_p(&ring)) {
+ /* Blocking write() to pushfd (STDOUT_FILENO) */
+ r = ring_buffer_push(&ring, pushfd);
+
+ /* If there was a problem, just exit the entire function */
- } else if (r < 0) {
- if (errno != EINTR && errno != EAGAIN) goto error;
+ if (r < 0 && errno != EINTR) {
+ close(pushfd);
+ close(pullfd);
+ pushfd = pullfd = -1;
+ return;
}
}
+
+ if (pullfd >= 0) {
+ /* select for readability on the pullfd */
+ r = select(pullfd+1, &readfds, NULL, &exceptfds, NULL);
- if (!is_pull || FD_ISSET(pushfd, &writefds)) {
- r = ring_buffer_push(&ring, pushfd);
- if (r < 0) {
- switch (errno) {
- case EINTR:
- case EAGAIN:
- continue;
-
- case EPIPE:
- /* TODO catch SIGPIPE? */
- close(pushfd);
- close(pullfd);
- pushfd = pullfd = -1;
- return;
-
- default:
- goto error;
- }
+ if (r < 0 || FD_ISSET(pullfd, &exceptfds)) {
+ close(pushfd);
+ close(pullfd);
+ pushfd = pullfd = -1;
+ return;
}
}
}
-
+ /* If we got here then we got eof on pullfd and pushed all the data out.
+ * so now just close pushfd */
+ assert(pullfd < 0);
+ assert(ring_buffer_empty_p(&ring));
close(pushfd);
- close(pullfd);
- return;
-
-error:
- close(pushfd);
- close(pullfd);
- perror("(coupling) pump");
}
static inline int
@@ -262,7 +340,11 @@ pump_thread (void *data)
{
struct coupling *c = (struct coupling*)data;
- pump(c->is_pull, c->pullfd, c->pushfd);
+ if (c->is_pull) {
+ pull_pump(c->pullfd, c->pushfd);
+ } else {
+ push_pump(c->pullfd, c->pushfd);
+ }
return NULL;
}
diff --git a/doc/api.txt b/doc/api.txt
index 27cc1f14b6..12e61b02a6 100644
--- a/doc/api.txt
+++ b/doc/api.txt
@@ -1,7 +1,7 @@
NODE(1)
=======
Ryan Dahl
-Version, 0.1.26, 2010.01.20
+Version, 0.1.27, 2010.02.03
== NAME
@@ -48,7 +48,7 @@ execution.
=== Global Objects
-+GLOBAL+ ::
++global+ ::
The global namespace object.
+process+ ::
@@ -100,10 +100,10 @@ more information.
signal names such as SIGINT, SIGUSR1, etc.
|=========================================================
-+process.ARGV+ ::
++process.argv+ ::
An array containing the command line arguments.
-+process.ENV+ ::
++process.env+ ::
An object containing the user environment. See environ(7).
+process.pid+ ::
@@ -565,7 +565,7 @@ Node provides a tridirectional +popen(3)+ facility through the class
+"error"+ callbacks will no longer be made.
|=========================================================
-+process.createChildProcess(command, args=[], env=ENV)+::
++process.createChildProcess(command, args=[], env=process.env)+::
Launches a new process with the given +command+, command line arguments, and
environmental variables. For example:
+
@@ -1459,14 +1459,14 @@ resolution.addCallback(function (addresses, ttl, cname) {
reversing.addCallback( function (domains, ttl, cname) {
sys.puts("reverse for " + a + ": " + JSON.stringify(domains));
});
- reversing.addErrback( function (code, msg) {
- sys.puts("reverse for " + a + " failed: " + msg);
+ reversing.addErrback( function (e) {
+ puts("reverse for " + a + " failed: " + e.message);
});
}
});
-resolution.addErrback(function (code, msg) {
- sys.puts("error: " + msg);
+resolution.addErrback(function (e) {
+ puts("error: " + e.message);
});
-------------------------------------------------------------------------
@@ -1482,8 +1482,9 @@ This function returns a promise.
canonical name for the query.
The type of each item in +addresses+ is determined by the record type, and
described in the documentation for the corresponding lookup methods below.
-- on error: returns +code, msg+. +code+ is one of the error codes listed
- below and +msg+ is a string describing the error in English.
+- on error: Returns an instanceof Error object, where the "errno" field is one
+ of the error codes listed below and the "message" field is a string
+ describing the error in English.
+dns.resolve4(domain)+::
@@ -1521,8 +1522,9 @@ Reverse resolves an ip address to an array of domain names.
- on success: returns +domains, ttl, cname+. +ttl+ (time-to-live) is an integer
specifying the number of seconds this result is valid for. +cname+ is the
canonical name for the query. +domains+ is an array of domains.
-- on error: returns +code, msg+. +code+ is one of the error codes listed
- below and +msg+ is a string describing the error in English.
+- on error: Returns an instanceof Error object, where the "errno" field is one
+ of the error codes listed below and the "message" field is a string
+ describing the error in English.
Each DNS query can return an error code.
diff --git a/doc/index.html b/doc/index.html
index fe8800a18b..de7efdd7e5 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -97,9 +97,9 @@ server.listen(7000, "localhost");
git repo