mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
e8bd737db4
The API is simplified: do not expose the user code to several queues. A "connection queue" is now an internal concept. The user processes connections using the single function lsquic_engine_process_conns(). When this function is called, only those connections are processed that need to be processed. A connection needs to be processed when: 1. New incoming packets have been fed to the connection. 2. User wants to read from a stream that is readable. 3. User wants to write to a stream that is writeable. 4. There are buffered packets that can be sent out. (This means that the user wrote to a stream outside of the lsquic library callback.) 5. A control frame (such as BLOCKED) needs to be sent out. 6. A stream needs to be serviced or delayed stream needs to be created. 7. An alarm rings. 8. Pacer timer expires. To achieve this, the library places the connections into two priority queues (min heaps): 1. Tickable Queue; and 2. Advisory Tick Time queue (ATTQ). Each time lsquic_engine_process_conns() is called, the Tickable Queue is emptied. After the connections have been ticked, they are queried again: if a connection is not being closed, it is placed either in the Tickable Queue if it is ready to be ticked again or it is placed in the Advisory Tick Time Queue. It is assumed that a connection always has at least one timer set (the idle alarm). The connections in the Tickable Queue are arranged in the least recently ticked order. This lets connections that have been quiet longer to get their packets scheduled first. This change means that the library no longer needs to be ticked periodically. The user code can query the library when is the next tick event and schedule it exactly. When connections are processed, only the tickable connections are processed, not *all* the connections. When there are no tick events, it means that no timer event is necessary -- only the file descriptor READ event is active. The following are improvements and simplifications that have been triggered: - Queue of connections with incoming packets is gone. - "Pending Read/Write Events" Queue is gone (along with its history and progress checks). This queue has become the Tickable Queue. - The connection hash no longer needs to track the connection insertion order.
228 lines
7.8 KiB
Text
228 lines
7.8 KiB
Text
# Copyright (c) 2017 - 2018 LiteSpeed Technologies Inc. See LICENSE.
|
|
LSQUIC APIs
|
|
===========
|
|
|
|
LSQUIC exposes the following object types to the user:
|
|
|
|
- Engine Settings (struct lsquic_engine_settings)
|
|
- Stream Interface (struct lsquic_stream_if)
|
|
- Engine API (struct lsquic_engine_api)
|
|
- Engine
|
|
- Connection
|
|
- Stream
|
|
|
|
The first three -- engine settings, engine APIs, and stream interface --
|
|
are used to instantiate the engine. After engine is instantiated, the
|
|
user code need only concern itself with engine, connections, and streams.
|
|
|
|
|
|
Engine Settings
|
|
---------------
|
|
|
|
Engine settings is the struct lsquic_engine_settings. It contains various
|
|
QUIC settings and LSQUIC parameters. The usual way to use it is to initialize
|
|
it to default values using lsquic_engine_init_settings(), modify any values
|
|
if necessary, and pass it as parameter to lsquic_engine_new().
|
|
|
|
QUIC settings are specified by the following members:
|
|
|
|
lsquic_engine_settings QUIC
|
|
member parameter
|
|
---------------------- ---------
|
|
es_cfcw CFCW
|
|
es_sfcw SFCW
|
|
es_max_streams_in MIDS
|
|
es_ua UAID
|
|
es_versions VER
|
|
es_idle_conn_to ICSL
|
|
es_silent_close SCLS
|
|
es_support_srej COPT/SREJ
|
|
es_support_nstp COPT/NSTP
|
|
es_support_tcid0 TCID
|
|
|
|
The following parameters affect run-time behavior:
|
|
|
|
es_rw_once Important: affects event dispatch
|
|
es_handshake_to
|
|
es_support_push
|
|
es_pace_packets
|
|
|
|
Other noteworthy settings:
|
|
|
|
es_max_header_list_size
|
|
es_progress_check
|
|
|
|
To be sure your settings are good (in other words, passing this struct won't
|
|
trip up the engine constructor), use lsquic_engine_check_settings().
|
|
|
|
|
|
Stream Interface
|
|
----------------
|
|
|
|
The stream interface, lsquic_stream_if, specifies callbacks LSQUIC engine
|
|
will call for connections and streams.
|
|
|
|
The following callbacks should be specified for connection:
|
|
|
|
on_new_conn This is called when connection is created.
|
|
|
|
on_goaway_received This function is called when we receive GOAWAY
|
|
frame from peer. This callback is optional.
|
|
|
|
on_conn_closed Connection is closed: all streams have been
|
|
destroyed.
|
|
|
|
The streams have four callbacks:
|
|
|
|
on_new_stream Stream has been created.
|
|
|
|
on_read Stream can be read from (see Events).
|
|
|
|
on_write Stream can be written to (see Events).
|
|
|
|
on_close Stream has been closed.
|
|
|
|
For both connections and streams, the "on new" callback return value can
|
|
be use to specify user-supplied data. This data pointer is optional and
|
|
can be NULL. It can also refer to the same data for the connection and
|
|
its streams. "on close" callbacks should be used to free user-supplied
|
|
data.
|
|
|
|
|
|
Engine API
|
|
----------
|
|
|
|
The engine API, struct lsquic_engine_api, is a combination structure to
|
|
make calling lsquic_engine_new() manageable. It holds references to
|
|
struct lsquic_engine_settings and struct lsquic_stream_if, as well as:
|
|
|
|
- Interface for sending outgoing packets, ea_packets_out
|
|
- Interface for allocating memory for outgoing packet buffers
|
|
(optional).
|
|
|
|
ea_packets_out is a pointer to a function of type lsquic_packets_out_f.
|
|
The engine calls this function when it is appropriate to send out packets
|
|
for one or more connections, which it gives to the function in a batch.
|
|
This batch is an array of struct lsquic_out_spec.
|
|
|
|
|
|
Engine
|
|
------
|
|
|
|
The engine is instantiated using lsquic_engine_new(). The first parameter
|
|
is a list flags and the second parameter is the reference to the engine
|
|
api. The engine settings are specified, they are copied; changing
|
|
the setting after the engine has been created will not affect engine's
|
|
behavior. If the settings are not specified, the engine will use default
|
|
settings created by lsquic_engine_init_settings().
|
|
|
|
Once the engine is instantiated, there are four main ways to use it to
|
|
drive QUIC connections:
|
|
|
|
1. Create a connection using lsquic_engine_connect().
|
|
2. Feed it incoming packets using lsquic_engine_packet_in() function.
|
|
3. Process connections using one of the connection queue functions
|
|
(see Connection Queues).
|
|
4. Accept outgoing packets for sending (and send them!) using
|
|
ea_packets_out callback.
|
|
|
|
|
|
Connection Management
|
|
---------------------
|
|
|
|
A connections needs to be processed once in a while. It needs to be
|
|
processed when one of the following is true:
|
|
|
|
- There are incoming packets;
|
|
- A stream is both readable by the user code and the user code wants
|
|
to read from it;
|
|
- A stream is both writeable by the user code and the user code wants
|
|
to write to it;
|
|
- User has written to stream outside of on_write() callbacks (that is
|
|
allowed) and now there are packets ready to be sent;
|
|
- A timer (pacer, retransmission, idle, etc) has expired;
|
|
- A control frame needs to be sent out;
|
|
- A stream needs to be serviced or created.
|
|
|
|
Each of these use cases is handled by a single function:
|
|
|
|
lsquic_engine_process_conns()
|
|
|
|
The connections to which the conditions above apply are processed (or
|
|
"ticked") in the least recently ticked order. After calling this function,
|
|
you can see when is the next time a connection needs to be processed using
|
|
|
|
lsquic_engine_earliest_adv_tick()
|
|
|
|
Based on this value, next event can be scheduled (in the event loop of
|
|
your choice).
|
|
|
|
Connection
|
|
----------
|
|
|
|
A connection is created using lsquic_engine_connect(). When on_new_conn()
|
|
is called, the client code should call lsquic_conn_make_stream() one or
|
|
more times. One new stream will be created for each one of those calls.
|
|
|
|
Several auxiliary functions are available:
|
|
|
|
- lsquic_conn_id()
|
|
- lsquic_conn_going_away()
|
|
- lsquic_conn_get_peer_ctx()
|
|
- lsquic_conn_get_stream_by_id()
|
|
- lsquic_conn_get_ctx()
|
|
|
|
|
|
Stream
|
|
------
|
|
|
|
LSQUIC stream hides QUIC and HTTP/2 framing complexities from the user.
|
|
What it presents is a way to send HTTP headers and, optionally, body to
|
|
peer. On read side, the user gets what looks like HTTP/1.1 stream.
|
|
|
|
Expected usage for client is to express the desire to write to stream
|
|
using lsquic_stream_wantwrite() call. Once on_write() is called:
|
|
|
|
1. Write headers using lsquic_stream_send_headers()
|
|
2. Optionally write payload body using of of lsquic_stream_write(),
|
|
lsquic_stream_writev(), or lsquic_stream_writef().
|
|
|
|
That done, shutdown write side using lsquic_stream_shutdown(), unregister
|
|
for write events and register for read events using lsquic_stream_wantread().
|
|
|
|
Read and parse HTTP/1.1 stream from on_read() callback until end-of-stream
|
|
or an error is encountered.
|
|
|
|
Then unregister the read event and shutdown the read side. The stream will
|
|
be closed after that at some point and on_close() callback will be called,
|
|
at which point resources can be freed. (Internally, the stream object is
|
|
not destroyed until either all the packets carrying its data are ACKed or
|
|
the connection is destroyed).
|
|
|
|
on_read() and on_write() callbacks are dispatched differently based on the
|
|
value of es_rw_once:
|
|
|
|
If es_rw_once is false, then the callbacks are dispatched in a loop until
|
|
the user unregisters the event or the stream becomes unreadable (or
|
|
unwriteable).
|
|
|
|
If es_rw_once is true, on_read() and on_write() are called once "per tick".
|
|
It is the up to the user to read and write enough data.
|
|
|
|
|
|
Events
|
|
------
|
|
|
|
Stream events are persistent: once call lsquic_stream_wantwrite() or
|
|
lsquic_stream_wantread(), the event stays active until turned off.
|
|
|
|
Note that when an error is encountered (such as a stream reset), the
|
|
stream becomes readable and writeable: this allows user code to collect
|
|
the error.
|
|
|
|
|
|
Versions
|
|
--------
|
|
|
|
QUIC version are listed in enum lsquic_version. To specify a list of
|
|
versions, they are usually placed in a bitmask, e.g. es_versions.
|