-
Notifications
You must be signed in to change notification settings - Fork 1
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
Create tests for QUICStreams
#10
Comments
When it comes to streams, connections and servers. we have two ways of ending them.
On the stream level this means we either wait for them to end and destroy themselves. Or force them to end with errors being sent through the readable and writable stream. On the connection level, if we're draining then we wait for the streams to end on their own while rejecting any new streams. Or we force all the existing streams to end then end itself. On the server level it's pretty similar. If we're draining we need to reject new connections and wait for existing connections to end. Or force all existing connections to destroy. All 3 levels of this have a similar enough usage that we need a standard way of handling it. How would we standardise this? Case 2 is pretty easy, if we're forcing things to end then we just need to propagate destruction down the layers. Case 1 takes a little more work. We need to support a draining state where we're waiting for all existing usage to end while rejecting any new usage. I'm using the work 'usage' generically here, usage can be existing or creating new connections, or the same for streams. To support this we need 3 things.
Another question is, do we have these things separate or do we encapsulate all this behaviour within the |
It's possible for a stream to be closed or rejected. This would be used when rejecting new streams during a draining state. Currently I don't think this is supported within To end a stream we can use The In any case, when calling |
I can see that we try to send a 0-length buffer when creating a new stream. So far as I can tell this message is not actually sent. In fact, as far as I can tell, no 0-length stream messages are sent unless its a finish frame for the stream. I'll dig into this some more. But so far, to actually open a stream I have to send a message. |
I thought this was being done. Do note that something has to actually "flush" the stream data... and correspond to sending a UDP message on the UDP socket. You might want to look into the looping problem first to get an understanding how the UDP socket is actually sending data while looping from any data that's still in the quiche buffer. |
The quiche send should be called after stream sends and recvs. I should check if that's being done. |
To have the stream shutdowns be more reactive on the receiving side, we need to check if the sending or receiving stream has closed. this matters for the sending stream since it can close at any time, but we currently only know this when writing to it. Ideally the |
I think I understand the stream state machines better now. One of the problems I was running into was that the SO what was happening, internally in The main change is, we can't finish destroying the Here are some quick notes about what needs to be done in each case of a stream ending. Note that this is from the perspective of a uni-direction stream, there are two of these in opposite directions for a bi direction stream.
|
This is mostly solved now. But annoyingly, the stream doesn't show up in the So unless I want to poll all active streams just to know if they're finished, I should have the stream check for a finished state after every time we process a recv on the connection. |
Just need to check for stream state change after connection receives a packet. Ideally we only check finishing streams. * Related #10 [ci skip]
I've dealt with most of the problems now. Just need to finish up the tests. Just to note one thing I need to look into. The time between destroying a connection and it cleaning up is taking longer than expected. I need to look into this. |
There's a problem where the readableStream is polling On top of this, since 0-length messages are coalesced and ignored, if we want the stream to exist on both ends when the client creates it, we need to send at least 1 byte. So a stream will need to ignore the 1st sent byte. I also want the ability to reject a new stream. In that case we want stream creation to fail. To facilitate this, I think stream creation should send and recv 1 byte before completing. If a stream is rejected or any errors happen, the stream creation can throw in that case. |
It looks like the quic spec doesn't require this. Just get rid of the 0 byte buffer concept.
1 May 2023 17:38:24 Brian Botha ***@***.***>:
…
There's a problem where the readableStream is polling *streamRecv()* before any stream messages have been sent. So technically the stream doesn't exist. So I'm getting an *InvalidStreamState(0)* error.
On top of this, since 0-length messages are coalesced and ignored, if we want the stream to exist on both ends when the client creates it, we need to send at least 1 byte. So a stream will need to ignore the 1st sent byte.
I also want the ability to reject a new stream. In that case we want stream creation to fail. To facilitate this, I think stream creation should send and recv 1 byte before completing. If a stream is rejected or any errors happen, the stream creation can throw in that case.
—
Reply to this email directly, view it on GitHub[#10 (comment)], or unsubscribe[https://github.com/notifications/unsubscribe-auth/AAE4OHJ4SUCUTRMKBGCI7JDXD5R65ANCNFSM6AAAAAAXGL3ZE4].
You are receiving this because you commented.[Tracking image][https://github.com/notifications/beacon/AAE4OHJRUWMUYZJA4GEARNDXD5R65A5CNFSM6AAAAAAXGL3ZE6WGG33NNVSW45C7OR4XAZNMJFZXG5LFINXW23LFNZ2KUY3PNVWWK3TUL5UWJTS3FF3MA.gif]
|
I can do that. In that case it means streams are not created until application sends data through it. So when we create a new stream, it will exist in a nascent state until data is sent through it. This is important since we can't do any stream operations until the underlying stream state has been created. So far as I can tell, stream state isn't created until data on that stream is sent or received. Until then most interaction with the underlying stream will result in an Ah, so local state is created if we 'send' a 0-length buffer for that stream. But no data is sent for that. So state is created locally but no state is created remotely in this case. |
So we should send a 0 length buffer then? |
Yes, just so we can create local state for the stream. |
All the tests are complete now and seem to be working. some notes about the streams.
I need to do some clean up on the streams code and then I can resolve this issue. |
How is 2. possible?
How would the remote side know to close a stream if the first message hasn't been sent? |
This ability to have bidirectional streams is going to be crucial for any interactive RPC comms where back and forth needs to occur. But if we can get away by nesting RPC calls that would be good. |
It can't that's why rejection would happen on the first message as apposed to |
Should handle more edge cases now, a lot of odd interactions and edge cases was revealed by the tests. * Fixes #10 [ci skip]
Specification
We're missing tests for streams, these need to be created. We should use fast-check to generate random data for streams. There are a few things we need to test.
Additional context
Tasks
The text was updated successfully, but these errors were encountered: