I could easily reproduce this in Mac: load a http3 page, put laptop to hibernation, turn it back on.
The underlying problem was that the quic connection was closed because of timeout and necko has tried to add a new stream: it reads headers(GetHeadersString) and calls TryActivating that fails failed (line).
The transaction has written some data so the session calls ReadSegment again to see if the transaction will write more data:
- Http3Stream is still in PREPARING_HEADERS state and calls GetHeadersString again here (because mActivatingFailed is false)
- GetHeadersString will read some data because it is looking for the end-of-headers determinator but it can not find it in the request body (note the headers are already read in the first call).
- here ReadSegment return NS_OK and also countRead is > 0 so Http3Session calls ReadSegment again,
- and so no...
The easy fix was to not call GetHeadersString when mRequestHeadersDone==true.
I have decided to refactor Http3Stream states:
- PREPARING_HEADER - this state only read the headers from the transaction and switches to WAITING_TO_ACTIVATE state when it is done reading,
- WAITING_TO_ACTIVATE only calls OnReadSegment directly (not through transaction) because it may happen that transaction will not call OnReadSegment
- and the rest are the same
In normal case when reading headers from transaction is done TryActivating will be call immediately (PREPARING_HEADER falls through to WAITING_TO_ACTIVATE in OnReadSegment) in most cases the transaction will be immediately activated and Http3Stream will switch to the SENDING_BODY or SEND_DONE state . If TryActivating fails next time calling ReadSegment will only call OnReadSegment directly to try to activate the transaction again.
The 2 code paths:
- Call ReadSegment -> in OnReadSegment all headers are read and in in switch it fails through and calls TryActivating. The stream is immediately activated -> Http3Session will call ReadSegment immediately again to send request body
- Call ReadSegment -> in OnReadSegment all headers are read and in the switch-case it fails through and calls TryActivating. TryActivating fails. The stream is in WAITING_TO_ACTIVATTE state. When new streams can be activated again Http3Session calls ReadSegment for this stream again. In ReadSegment a fake OnReasSegment is called directly. If it succeeds it fails through to SENDING_BODY.