-
Notifications
You must be signed in to change notification settings - Fork 29.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TCP socket yield by http 'upgrade' event stops emitting data #3321
Comments
On further investigation it turns out the
This is from running the script on Node v0.12. Running on v4.0 gives the same result except it stops after the I wonder if there's something about their implementation that means they work on all versions from 0.6 up to 0.12 and iojs-3.3.1 but not on 4.0. |
(Although the difference between the HTTP and TCP servers on the same Node version with the same client and the same |
Some more info: applying this patch makes it work: diff --git a/lib/websocket/streams.js b/lib/websocket/streams.js
index 96ab31f..298e858 100644
--- a/lib/websocket/streams.js
+++ b/lib/websocket/streams.js
@@ -76,9 +76,10 @@ IO.prototype.resume = function() {
// When we receive input from a socket, send it to the parser and tell the
// source whether to back off.
IO.prototype.write = function(chunk) {
+ var result = !this._paused;
if (!this.writable) return false;
this._driver.parse(chunk);
- return !this._paused;
+ return result;
};
// The IO end() method will be called when the socket piping into it emits
@@ -125,10 +126,11 @@ Messages.prototype.resume = function() {
// When we receive messages from the user, send them to the formatter and tell
// the source whether to back off.
Messages.prototype.write = function(message) {
+ var result = !this._paused;
if (!this.writable) return false;
if (typeof message === 'string') this._driver.text(message);
else this._driver.binary(message);
- return !this._paused;
+ return result;
};
// The Messages end() method will be called when a stream piping into it emits It turns out that when This is surprising because every time |
Cc @nodejs/streams |
A couple of things:
|
@mscdex How do I try it out with |
@mscdex I've managed to reproduce the issue without using any external modules: https://gist.github.com/jcoglan/a1b1be5b82b2122a1df5 This script implements a simple protocol where messages consist of a 4-byte length header followed by the message content. The HTTP server accepts an Upgrade request and then echoes this protocol back to the client. The client opens a socket and writes an Upgrade request, then sends a sequence of random fixed-size messages to the server. On my machine, setting |
@mscdex The fix for this script is the same as in my websocket example: changing Encode.prototype.write = function(chunk) {
if (!this.writable) return false;
var paused = this._paused;
var lengthHeader = new Buffer(4);
lengthHeader.writeUInt32BE(chunk.length, 0);
this.emit('data', lengthHeader);
this.emit('data', chunk);
return !paused;
}; makes the script loop at any message size. |
@jcoglan Here's generally what I usually do to test master (without git):
|
I've now added a TCP server that shares the protocol streams from the HTTP example for comparison. The TCP example loops at any message size while the HTTP one gets stuck. |
I'm also curious, why are you implementing streams from scratch instead of inheriting from |
@mscdex partly for historical reasons -- the Faye project that this is extracted from dates back to Node 0.1. I don't know what usage of pre-0.10 versions is like these days among my users but I've not had a strong reason to move to new streams so far. Also, WebSocket can send both UTF-8 text and binary messages over the same connection. websocket-driver emits these as strings and buffers respectively and determines whether to send a text or binary message based on whether Also, a WebSocket stream is a sequence of discrete messages. The It might be I've not sufficiently read or understood the docs so if I've got anything wrong I'd like to know :) |
@jcoglan FWIW streams2 streams can also be object streams (meaning you could write objects, Buffers, strings, whatever you want), not just binary data streams. I would guess/hope that most people are at least on node v0.10 by now. Even if there are still users on v0.8 (or older), |
There are other issues with |
The thing is I don't find the new classes any easier to use than the old ones and in my use case they have a lot of surprises like those I've mentioned that I need to work around. I any case, if only for my own understanding I'd like to know why my code as it stands does not work so I can make an informed choice about how to fix it, or know if it's a bug in Node. |
@jcoglan an object mode stream would probably be the more idiomatic way to do what you want to do as it treats each chunk as an opaque entity vs regular streams which treat them as an arbitrary sequence of bytes. |
I finally got node master to compile and my example doesn't work on node commit |
I've found using |
cc @indutny ^ That's your StreamBase change to http |
I would say if it is working now - it was most likely fixed. |
I maintain the websocket-driver module. This module implements the WebSocket protocol but does not contain any of its own I/O logic. The user hooks up the driver to an I/O stream of their choosing.
The following script sets up two servers that should be functionally identical, one using the
http.Server
upgrade
event, and one using anet
server. Each server runs the following actions:socket
anddriver.io
Following the servers is a function
testServer()
to test each server using a client. This function makes a TCP connection to a port, attaches a driver to that connection. When the connection is established, a random message of a certain size is sent. When the message is received, the client closes the connection. And when the connection becomes closed, we print the code and reason.The script as given sents a message of 16,379 bytes to the HTTP server on port 4007. Here's what I get when I run this program:
The final buffer beginning with
88
is a WebSocket closing frame, all previous buffers are part of the random message.If I switch the port to 4008, I get:
So both servers work as expected. However, if I go back to port 4007 and increase the message size to 16,380:
In this case, the HTTP receives the whole data message and echoes it back to the client, but it never receives the closing frame beginning
88
from the client. Given the client is the same in all experiments, and the TCP server works fine with larger messages, this seems to be an issue with the socket yielded by the HTTP module.It doesn't seem to be a case that the stream simply stops emitting data after 16,379 bytes because you can send it much larger payloads, and it will receive them in multiple chunks and echo them:
I'm sorry I can't make the test case any more simple, I'm not sure what to investigate next.
This problem exists in versions 4.0.0 and 4.1.2, but not in any earlier releases as far as I can tell.
The text was updated successfully, but these errors were encountered: