mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Add tutorial.rst -- forgotten in the previous commit
This commit is contained in:
parent
afe3d36359
commit
aedecb458e
1 changed files with 221 additions and 0 deletions
221
docs/tutorial.rst
Normal file
221
docs/tutorial.rst
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
********
|
||||||
|
Tutorial
|
||||||
|
********
|
||||||
|
|
||||||
|
.. highlight:: c
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
The LSQUIC library provides facilities for operating a QUIC (Google QUIC
|
||||||
|
or IETF QUIC) server or client with optional HTTP (or HTTP/3) functionality.
|
||||||
|
To do that, it specifies an application programming interface (API) and
|
||||||
|
exposes several basic object types to operate upon:
|
||||||
|
|
||||||
|
- engine;
|
||||||
|
- connection; and
|
||||||
|
- stream.
|
||||||
|
|
||||||
|
An engine manages connections, processes incoming packets, and schedules
|
||||||
|
outgoing packets. An engine operates in one of two modes: client or server.
|
||||||
|
|
||||||
|
The LSQUIC library does not use sockets to receive and send packets; that is
|
||||||
|
handled by the user-supplied callbacks. The library also does not mandate
|
||||||
|
the use of any particular event loop. Instead, it has functions to help the
|
||||||
|
user schedule events. (Thus, using an event loop is not even strictly
|
||||||
|
necessary.) The various callbacks and settings are supplied to the engine
|
||||||
|
constructor.
|
||||||
|
|
||||||
|
A connection carries one or more streams, ensures reliable data delivery,
|
||||||
|
and handles the protocol details.
|
||||||
|
|
||||||
|
A stream usually corresponds to a request/response pair: a client sends
|
||||||
|
its request over a single stream and a server sends its response back
|
||||||
|
using the same stream. This is the Google QUIC and HTTP/3 use case.
|
||||||
|
Nevertheless, the library does not limit one to this scenario. Any
|
||||||
|
application protocol can be implemented using LSQUIC -- as long as it
|
||||||
|
can be implemented using the QUIC transport protocol. The library provides
|
||||||
|
hooks for stream events: when a stream is created or closed, when it has
|
||||||
|
data to read or when it can be written to, and so on.
|
||||||
|
|
||||||
|
In the following sections, we will describe how to:
|
||||||
|
|
||||||
|
- initialize the library;
|
||||||
|
- configure and instantiate an engine object;
|
||||||
|
- send and receive packets; and
|
||||||
|
- work with connections and streams.
|
||||||
|
|
||||||
|
Include Files
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A single include file, :file:`lsquic.h`, contains all the necessary
|
||||||
|
LSQUIC declarations:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
#include <lsquic.h>
|
||||||
|
|
||||||
|
Library Initialization
|
||||||
|
======================
|
||||||
|
|
||||||
|
Before the first engine object is instantiate, the library must be
|
||||||
|
initialized using :func:`lsquic_global_init()`:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER))
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
/* OK, do something useful */
|
||||||
|
|
||||||
|
If you plan to instantiate engines only in a single mode, client or server,
|
||||||
|
you can omit the appropriate flag.
|
||||||
|
|
||||||
|
After all engines have been destroyed and the LSQUIC library is no longer
|
||||||
|
going to be used, the global initialization can be undone:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
lsquic_global_cleanup();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
Engine Instantiation
|
||||||
|
====================
|
||||||
|
|
||||||
|
Engine instantiation is performed by :func:`lsquic_engine_new()`:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
/* Create an engine in server mode with HTTP behavior: */
|
||||||
|
lsquic_engine_t *engine
|
||||||
|
= lsquic_engine_new(LSENG_SERVER|LSENG_HTTP, &engine_api);
|
||||||
|
|
||||||
|
The engine mode is selected by using the ``LSENG_SERVER`` flag. If
|
||||||
|
present, the engine will be in server mode; if not, the engine will
|
||||||
|
be in client mode.
|
||||||
|
|
||||||
|
Using the ``LSENG_HTTP`` flag enables the HTTP behavior: The library
|
||||||
|
hides the interaction between the HTTP application layer and the QUIC
|
||||||
|
transport layer and presents a simple, unified (between Google QUIC and
|
||||||
|
HTTP/3) way of sending and receiving HTTP messages. Behind the scenes,
|
||||||
|
the library will compress and uncompress HTTP headers, add and remove
|
||||||
|
HTTP/3 stream framing, and operate the necessary control streams.
|
||||||
|
|
||||||
|
Engine Configuration
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The second argument to :func:`lsquic_engine_new()` is a pointer to
|
||||||
|
a struct of type :type:`lsquic_engine_api`. This structure lists
|
||||||
|
several user-specified function pointers that the engine is to use
|
||||||
|
to perform various functions. Mandatory among these are:
|
||||||
|
|
||||||
|
- function to set packets out, :member:`lsquic_engine_api.ea_packets_out`;
|
||||||
|
- functions linked to connection and stream events,
|
||||||
|
:member:`lsquic_engine_api.ea_stream_if`;
|
||||||
|
- function to look up certificate to use, :member:`lsquic_engine_api.ea_lookup_cert` (in server mode); and
|
||||||
|
- function to fetch SSL context, :member:`lsquic_engine_api.ea_get_ssl_ctx` (in server mode).
|
||||||
|
|
||||||
|
The minimal structure for a client will look like this:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
lsquic_engine_api engine_api = {
|
||||||
|
.ea_packets_out = send_packets_out,
|
||||||
|
.ea_packets_out_ctx = (void *) sockfd, /* For example */
|
||||||
|
.ea_stream_if = &stream_callbacks,
|
||||||
|
.ea_stream_if_ctx = &some_context,
|
||||||
|
};
|
||||||
|
|
||||||
|
Engine Settings
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Engine settings can be changed by specifying
|
||||||
|
:member:`lsquic_engine_api.ea_settings`. There are **many** parameters
|
||||||
|
to tweak: supported QUIC versions, amount of memory dedicated to connections
|
||||||
|
and streams, various timeout values, and so on. See
|
||||||
|
:ref:`apiref-engine-settings` for full details. If ``ea_settings`` is set
|
||||||
|
to ``NULL``, the engine will use the defaults, which should be OK.
|
||||||
|
|
||||||
|
Sending Packets
|
||||||
|
===============
|
||||||
|
|
||||||
|
The :member:`lsquic_engine_api.ea_packets_out` is the function that gets
|
||||||
|
called when an engine instance has packets to send. It could look like
|
||||||
|
this:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
/* Return number of packets sent or -1 on error */
|
||||||
|
static int
|
||||||
|
send_packets_out (void *ctx, const struct lsquic_out_spec *specs,
|
||||||
|
unsigned n_specs)
|
||||||
|
{
|
||||||
|
struct msghdr msg;
|
||||||
|
int sockfd;
|
||||||
|
unsigned n;
|
||||||
|
|
||||||
|
memset(&msg, 0, sizeof(msg));
|
||||||
|
sockfd = (int) (uintptr_t) ctx;
|
||||||
|
|
||||||
|
for (n = 0; n < n_specs; ++n)
|
||||||
|
{
|
||||||
|
msg.msg_name = (void *) specs[n].dest_sa;
|
||||||
|
msg.msg_namelen = sizeof(struct sockaddr_in);
|
||||||
|
msg.msg_iov = specs[n].iov;
|
||||||
|
msg.msg_iovlen = specs[n].iovlen;
|
||||||
|
if (sendmsg(sockfd, &msg, 0) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) n;
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that the version above is very simple. :type:`lsquic_out_spec`
|
||||||
|
also specifies local address as well as ECN value. These are set
|
||||||
|
using ancillary data in a platform-dependent way.
|
||||||
|
|
||||||
|
Receiving Packets
|
||||||
|
=================
|
||||||
|
|
||||||
|
The user reads packets and provides them to an engine instance using
|
||||||
|
:func:`lsquic_engine_packet_in()`.
|
||||||
|
|
||||||
|
*TODO*
|
||||||
|
|
||||||
|
Running Connections
|
||||||
|
===================
|
||||||
|
|
||||||
|
A connection 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,
|
||||||
|
:func:`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
|
||||||
|
:func:`lsquic_engine_earliest_adv_tick()`.
|
||||||
|
|
||||||
|
Based on this value, next event can be scheduled (in the event loop of
|
||||||
|
your choice).
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
|
||||||
|
Stream Reading and Writing
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Reading from (or writing to) a stream is best down when that stream is
|
||||||
|
readable (or writeable). To register an interest in an event,
|
Loading…
Reference in a new issue