Problem: Since stream.pipe() is registering it's own error handlers on
the source and destination stream, it needs to replicate the
EventEmitter 'error' emitting semantics of throwing an error if there
are no other listeners. However, there was a off-by-one error because
the check for remaining listeners was done after cleanup() which means
the pipe's own listener was no longer included.
This would cause 'error' events on either the dest or the source to
throw if there was one other error listener, and while swallowing
the 'error' event if there was no other listener.
Solution: I added a test demonstrating the two issues and fixed the
problem by correcting the off-by-one error.
Fixes#1095.
This fixes a critical bug see in MJR's production. Very difficult to build a
test case. Sometimes HTTPS server gets sockets that are hanging in a
half-duplex state.
This commit does three things:
1. Uses an exposed counter rather than a hidden array for tracking dest
streams that may have multiple inputs. This allows for significantly
faster lookups, since the counter can be checked in constant time rather
than searching an array for the dest object. (A proper O(1) WeakMap
would be better, but that may have to wait for Harmony.)
2. Calls the 'end' event logic when there is an 'error' event on the
source object (and then throws if there are no other error listeners.)
This is important, because otherwise 'error' events would lead to
memory leaks.
3. Clean up the style a bit. Function Declarations are not allowed
within blocks in ES strict. Prefer Function Declarations to Function
Expressions, because hoisting allows for more expressive ordering of
logic.
Downside: It adds "_pipeCount" as part of the Stream API. It'll work
fine if the member is missing, but if anyone tries to use it for some
other purpose, it can mess things up.
When creating multiple .pipe()s to the same destination stream, the
first source to end would close the destination, breaking all remaining
pipes. This patch fixes the problem by keeping track of all open
pipes, so that we only call end on destinations that have no more
sources piping to them.
closes#929