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

Unclear stream close semantics #439

Closed
mikkelfj opened this issue Apr 16, 2017 · 10 comments
Closed

Unclear stream close semantics #439

mikkelfj opened this issue Apr 16, 2017 · 10 comments
Labels
-transport design An issue that affects the design of the protocol; resolution requires consensus. has-consensus An issue that the Chairs have determined has consensus, by canvassing the mailing list.

Comments

@mikkelfj
Copy link
Contributor

According to life of a stream, a stream can transition from "half closed local" to "closed" by sending a "RST" or by receiving "FIN" or "RST" from peer. However, sending a "RST" may race ahead of any in-flight packets send to the peer. In fact this is likely because the stream will transmit according to a fair schedule while RST will likely be prioritised. The RST will tear down state on both ends preventing the peer from receiving and the local endpont from retransmitting (depending on implementation).

In addition to the above there is also a user aspect. QUIC does not define an API, but what does it mean to close a stream? Do you close the write stream or do you close both read and write stream? This can encoded as either sending "FIN" or "FIN+RST" but has the race condition mentioned. When cleaning up after a stream initiated locally, it appears you need to always eventually send a RST even if the peer has no interest in writing. Application protocol could agree to not require a RST, but that seems a bit roundabout.

Perhaps it is saner to handle read and write streams entirely separately.
#175

The issue may also be related to:
#61

@martinthomson
Copy link
Member

This is related to #175, but not #61. The current requirement is that the FIN or RST is also acknowledged. That means that you DON'T have to send a RST, you simply retransmit the FIN until you get an acknowledgment.

You are right to observe that the consequences of the FIN are not obvious. If both FIN and RST are acknowledged and you don't have a timestamp for the receipt (and processing) of those packets, then you don't know which had effect first: was the whole stream delivered or not?

FWIW, I have been very careful to use close to mean FIN, and reset to mean RST. A RST is reserved for error conditions (including aborts) and will not guarantee any delivery of any data. A FIN would be used under normal conditions and would result in data being delivered. Sending FIN would likely correspond to closing the write stream in a bi-directional stream API. RST corresponds to killing the read-write pair.

Not sure what to do with this issue; it's the sort of thing that might have been better sent as a question to the mailing list.

@mikkelfj
Copy link
Contributor Author

My concern was related to how you can close the READ stream from the receiving end. It is correct that FIN ACK can be used to avoid the race condition by delaying a RST, but you still need to issue a RST unless you are willing to commit state until peer sends a FIN. In other words, there is no simple way to close a read stream.

@martinthomson
Copy link
Member

You might be interested in #171.

@mikkelfj
Copy link
Contributor Author

Thanks. Yes that seems to address the issue, though it still appear to me semantics could be cleaned up a bit. So I am closing this one.

@mikkelfj
Copy link
Contributor Author

There are still unclear issues:
For example, a server may send 1000 images with client gradually sending STREAM_LIMIT updates so it never receives more than 20 images at time. The server is not interested in data from the client at all, so it never updates STREAM_LIMIT towards the client.

However, now there are 1000 open streams. Even though the server has long forgotten about those streams, the client can now start sending random data on any of those server initiated streams. The server may choose consider this an error, but this is not easy because it might actually want to receive data on some recent streams anyway, say a progress stream interleaved among the images. So now, which streams are indeed closed and which are not? The server may send DISINTEREST frames on every stream used to send pictures, but that is just needless trafic and state. The application protocol may decide what should happen with each stream. But there is no simple way for the transport layer to enforce when it is reasonable to accept and buffer data on streams and when not. All of this goes away if we have uni-directional streams, or at least that seems to be case superficially.

@mikkelfj mikkelfj reopened this Apr 18, 2017
@martinthomson
Copy link
Member

For this situation to exist, the server would have to be using server push AND the client would have to send data on those streams, which is not permitted, so it would be detected as an attack/error pretty quickly.

Otherwise, it's the server that sends STREAM_LIMIT updates.

@mikkelfj
Copy link
Contributor Author

Isn't that assuming HTTP2, which is just one application level use case? The HTTP application can reject this, but another higher level protocol that is not HTTP2 will need to revisit those concerns.

A related concern is that is is non-trivial to remember which streams are only implicitly opened by higher ID and which are closed. With uni-directional streams and clean close semantics, the reader only needs to remember the actively open streams and update the STREAM_LIMIT as appropriate.

I'm not interested in placing application level semantics in transport, just to have a clean separation of concerns so the transport can take action based on simple interactions with the application layer.

@mnot mnot added the design An issue that affects the design of the protocol; resolution requires consensus. label Apr 19, 2017
@martinthomson
Copy link
Member

No, I'm just using HTTP for context. The discussion in #263 covers this in more detail. The other side can only send on streams up to your stream limit, plus any streams you opened.

I think that it's easy enough to remember which streams are opened. A peer can only open so many and the rules for that are clear. I agree that unidirectional streams is easier, but the rules are clear.

@mikkelfj
Copy link
Contributor Author

mikkelfj commented Apr 19, 2017

OK, so an application can choose to initiate a stream as write only or as read/write even if this concept does not exist at the protocol level. The transport layer can then consider it an error if receiving data on a write only stream.

@martinthomson
Copy link
Member

I no longer know what this issue is. I recommend creating a new issue with a clear description of a specific issue. I'm closing this for now.

If you have questions, or you aren't sure about the issue, please ask the quic@ietf.org mailing list before opening issue.

@mnot mnot added the has-consensus An issue that the Chairs have determined has consensus, by canvassing the mailing list. label Sep 26, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
-transport design An issue that affects the design of the protocol; resolution requires consensus. has-consensus An issue that the Chairs have determined has consensus, by canvassing the mailing list.
Projects
None yet
Development

No branches or pull requests

3 participants