Skip to content
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

Flow control may block non-flow-controlled frames #23

Open
nwgh opened this issue May 27, 2014 · 4 comments
Open

Flow control may block non-flow-controlled frames #23

nwgh opened this issue May 27, 2014 · 4 comments
Assignees

Comments

@nwgh
Copy link
Collaborator

nwgh commented May 27, 2014

For all frame types, lib/stream.js uses this._pushUpstream to submit the frame for sending, which in turn calls Flow.prototype.push. In Flow.prototype.push, we could conceivably get into a state where there are DATA frames queued (so this._queue.length !== 0), so moreNeeded will not get set to non-null, and the check on line 278 will evaluate to true, thus enqueuing the frame, regardless of what type of frame it is. We should check the frame type in Flow.prototype.push before checking moreNeeded at all, so we don't end up blocking non-flow-controlled frames (I think).

I can whip up a patch for this, but I wanted to submit the issue to make sure I'm not crazy before doing the work :)

@molnarg
Copy link
Owner

molnarg commented Jul 22, 2014

Hm, what we lose with this is the guarantee that frames are sent (and received) in the order they are written in the stream. I looked this up in the spec and it says:

The order in which frames are sent on a stream is significant. Recipients process frames in the order they are received. In particular, the order of HEADERS, and DATA frames is semantically significant.

So I don't think we can optimize this.

@nwgh
Copy link
Collaborator Author

nwgh commented Jul 22, 2014

I'm not so sure about that. It may not be as simple as I was originally thinking, but I think it's doable. When I was looking at this, I wasn't thinking about HEADERS or DATA particularly, this came up when I was implementing ALTSVC. Here are some things that we would need to guard against:

  • Sending any DATA before we've sent the full header block (HEADERS|PUSH_PROMISE CONTINUATION*) (because duh)
  • Reordering frames within a header block or a sequence of DATA frames (because duh)
  • Sending a SETTINGS that updates the compression context out-of-order with a header block (since the header block would depend on the previous max header table size)
  • Sending a RST_STREAM if we have any data waiting (since it's possible that the other endpoint may want to do something with the data that we've sent, such as saving a partial download for a later range request)

The only other one that could get a little weird is sending a PRIORITY, though if we're sending DATA on an particular stream, we're probably not the one that should be sending PRIORITY frames for that stream, anyway. (Obviously we wouldn't want to send PRIORITY before HEADERS.)

I don't see any issues sending, for example, ALTSVC on a stream that's blocked on flow control. I can even see a reason to actually do so - if you believe the client isn't going to be opening its window any time soon but that it's not malicious (not an incredibly likely scenario, but still...), you may want to try to redirect it to get a new connection with a new flow control window.

@molnarg
Copy link
Owner

molnarg commented Jul 24, 2014

Yeah, it's doable in certain scenarios. I'm OK with handling the cases we know are safe specially, but I'm afraid of using very complicated logic to do this. How about implementing a _pushUpstreamWithoutFlowControl() method on the Stream class as an alternative to _pushUpstream() which could be used in cases when we know this can not cause trouble (like ALTSVC)?

It would look like this:

Stream.prototype._pushUpstreamWithoutFlowControl = function _pushUpstreamWithoutFlowControl(frame) {
  this.upstream._parentPush(frame); // Logs the frame and pushed directly into the output queue
  this._transition(true, frame);
};

@nwgh
Copy link
Collaborator Author

nwgh commented Jul 24, 2014

Yeah, I think that seems like a reasonable way to go about it. Once draft14 lands (which should be as soon as Martin has the spec revision ready, unless he surprises me with some last-minute changes), I'll work on adding this.

@nwgh nwgh self-assigned this Jul 24, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants