|
6 | 6 | * LICENSE file in the root directory of this source tree. |
7 | 7 | */ |
8 | 8 |
|
| 9 | +/** |
| 10 | + * This comment block documents the implementation details of WtStreamManager |
| 11 | + * (i.e. the how). |
| 12 | + * |
| 13 | + * WriteHandle & ReadHandle (concrete implementations of |
| 14 | + * WebTransport::WriteHandle and WebTransport::ReadHandle respsectively, defined |
| 15 | + * below) are anonymous and strictly scoped to this TU. This requires |
| 16 | + * downcasting in all apis that receive a pointer to those pure virtual classes. |
| 17 | + * To prevent leaking implementation details by marking methods in |
| 18 | + * WtStreamManager public just for internal use by WriteHandle & ReadHandle, we |
| 19 | + * utilize a friend struct Accessor (also anonymously defined and strictly |
| 20 | + * scoped to this TU). |
| 21 | + * |
| 22 | + * First thing to note is that we always allocate both a WriteHandle and |
| 23 | + * ReadHandle regardless of the underlying properities of the stream (e.g. if |
| 24 | + * it's a unidirectional stream). This makes things extremely easier to reason |
| 25 | + * about, as there's a single map from [id]->[bidi_handle]. The public api in |
| 26 | + * WtStreamManager is extremely restrictive, and we do not hand out a pointer to |
| 27 | + * an invalid handle. For example, if a client WtStreamManager attempts to |
| 28 | + * retrieve a EgressHandle for a server-initiated unidirectional stream (e.g. |
| 29 | + * id=0x03), the state for such a handle exists but we return nullptr – it's |
| 30 | + * effectively invisible to the user. |
| 31 | + * |
| 32 | + * A note about flow control: |
| 33 | + * – Ingress flow control is strict, as a peer exceeding the advertised max |
| 34 | + * offset is a connection- or stream-level flow control. |
| 35 | + * |
| 36 | + * – Egress flow control is not strict, as an application can enqueue too much |
| 37 | + * data for any number of reasons (large write, ignoring backpressure, etc.). |
| 38 | + * We buffer this data in WriteHandle and is subsequently dequeued by the |
| 39 | + * transport whenever the stream is writable (hence the need for |
| 40 | + * WtEgressContainer to manage this small complexity). Applications should |
| 41 | + * respect backpressure signalled by returning FcState::BLOCKED from |
| 42 | + * WriteHandle::writeStreamData |
| 43 | + * |
| 44 | + * A note about stream states: |
| 45 | + * – An egress handle transitions from [HandleState::Open] -> |
| 46 | + * [HandleState::Closed] in three cases: fin is dequeued from WriteHandle via |
| 47 | + * ::dequeue, the application resets the stream (i.e. |
| 48 | + * WebTransport::WriteHandle::resetStream), or the transport receives a |
| 49 | + * stop_sending and WtStreamManager::onStopSending is invoked. |
| 50 | + * |
| 51 | + * – An ingress handle transitions from [HandleState::Open] -> |
| 52 | + * [HandleState::Closed] in three cases: fin is read via ::readStreamData(), |
| 53 | + * the application is no longer interested in ingress (i.e. |
| 54 | + * WebTransport::ReadHandle::stopSending), or the transport receives a |
| 55 | + * reset_stream and WtStreamManager::onResetStream is invoked. |
| 56 | + * |
| 57 | + * – An invalid handle (e.g. client egress handle for a |
| 58 | + * server-initiated unidirectional stream) starts in the HandleState::Closed. |
| 59 | + * |
| 60 | + * – Once both the ingress & egress handles have reached the HandleState::Closed |
| 61 | + * state, we deallocate the state. Since we always allocate both a ReadHandle |
| 62 | + * and WriteHandle, they're both unconditionally linked together for |
| 63 | + * simplicity. |
| 64 | + */ |
| 65 | + |
9 | 66 | #include <proxygen/lib/http/coro/util/WtStreamManager.h> |
10 | 67 |
|
11 | 68 | #include <folly/logging/xlog.h> |
|
0 commit comments