tutorial.rst revision 9a7f663e
1******** 2Tutorial 3******** 4 5Code for this tutorial is available on `GitHub <https://github.com/dtikhonov/lsquic-tutorial>`_. 6 7.. highlight:: c 8 9Introduction 10============ 11 12The LSQUIC library provides facilities for operating a QUIC (Google QUIC 13or IETF QUIC) server or client with optional HTTP (or HTTP/3) functionality. 14To do that, it specifies an application programming interface (API) and 15exposes several basic object types to operate upon: 16 17- engine; 18- connection; and 19- stream. 20 21Engine 22------ 23 24An engine manages connections, processes incoming packets, and schedules outgoing packets. It can be instantiated in either server or client mode. If your program needs to have both QUIC client and server functionality, instantiate two engines. (This is what we do in our LiteSpeed ADC server.) 25In addition, HTTP mode can be turned on for gQUIC and HTTP/3 support. 26 27Connection 28---------- 29 30A connection carries one or more streams, ensures reliable data delivery, and handles the protocol details. 31In client mode, a connection is created using a function call, which we will cover later in the tutorial. 32In server mode, by the time the user code gets a hold of the connection object, the handshake has already been completed successfully. This is not the case in client mode. 33 34Stream 35------ 36 37A connection can have several streams in parallel and many streams during its lifetime. 38Streams do not exist by themselves; they belong to a connection. Streams are bidirectional and usually correspond to a request/response exchange - depending on the application protocol. 39Application data is transmitted over streams. 40 41HTTP Mode 42--------- 43 44The HTTP support is included directly into LSQUIC. The library hides the interaction between the HTTP application layer and the QUIC transport layer and presents a simple, unified way of sending and receiving HTTP messages. (By "unified way," we mean between Google QUIC and HTTP/3). Behind the scenes, the library will compress and decompress HTTP headers, add and remove HTTP/3 stream framing, and operate the necessary control streams. 45 46In the following sections, we will describe how to: 47 48- initialize the library; 49- configure and instantiate an engine object; 50- send and receive packets; and 51- work with connections and streams. 52 53Include Files 54------------- 55 56In your source files, you need to include a single header, "lsquic.h". 57It pulls in an auxiliary file "lsquic_types.h". 58 59:: 60 61 #include "lsquic.h" 62 63Library Initialization 64====================== 65 66Before the first engine object is instantiated, the library must be 67initialized using :func:`lsquic_global_init()`: 68 69:: 70 71 if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) 72 { 73 exit(EXIT_FAILURE); 74 } 75 /* OK, do something useful */ 76 77This will initialize the crypto library, gQUIC server certificate cache, and, depending on the platform, monotonic timers. 78If you plan to instantiate engines only in a single mode, client or server, 79you can omit the appropriate flag. 80 81After all engines have been destroyed and the LSQUIC library is no longer 82going to be used, the global initialization can be undone: 83 84:: 85 86 lsquic_global_cleanup(); 87 exit(EXIT_SUCCESS); 88 89Engine Instantiation 90==================== 91 92Engine instantiation is performed by :func:`lsquic_engine_new()`: 93 94:: 95 96 /* Create an engine in server mode with HTTP behavior: */ 97 lsquic_engine_t *engine 98 = lsquic_engine_new(LSENG_SERVER|LSENG_HTTP, &engine_api); 99 100The engine mode is selected by using the :macro:`LSENG_SERVER` flag. 101If present, the engine will be in server mode; if not, the engine will 102be in client mode. If you need both server and client functionality 103in your program, instantiate two engines (or as many as you like). 104 105Using the :macro:`LSENG_HTTP` flag enables the HTTP behavior: The library 106hides the interaction between the HTTP application layer and the QUIC 107transport layer and presents a simple, unified (between Google QUIC and 108HTTP/3) way of sending and receiving HTTP messages. Behind the scenes, 109the library will compress and uncompress HTTP headers, add and remove 110HTTP/3 stream framing, and operate the necessary control streams. 111 112Engine Configuration 113-------------------- 114 115The second argument to :func:`lsquic_engine_new()` is a pointer to 116a struct of type :type:`lsquic_engine_api`. This structure lists 117several user-specified function pointers that the engine is to use 118to perform various functions. Mandatory among these are: 119 120- function to send packets out, :member:`lsquic_engine_api.ea_packets_out`; 121- functions linked to connection and stream events, 122 :member:`lsquic_engine_api.ea_stream_if`; 123- function to look up certificate to use, :member:`lsquic_engine_api.ea_lookup_cert` (in server mode); and 124- function to fetch SSL context, :member:`lsquic_engine_api.ea_get_ssl_ctx` (optional in client mode). 125 126The minimal structure for a client will look like this: 127 128:: 129 130 lsquic_engine_api engine_api = { 131 .ea_packets_out = send_packets_out, 132 .ea_packets_out_ctx = (void *) sockfd, /* For example */ 133 .ea_stream_if = &stream_callbacks, 134 .ea_stream_if_ctx = &some_context, 135 }; 136 137Engine Settings 138--------------- 139 140Engine settings can be changed by specifying 141:member:`lsquic_engine_api.ea_settings`. There are **many** parameters 142to tweak: supported QUIC versions, amount of memory dedicated to connections 143and streams, various timeout values, and so on. See 144:ref:`apiref-engine-settings` for full details. If ``ea_settings`` is set 145to ``NULL``, the engine will use the defaults, which should be OK. 146 147 148Receiving Packets 149================= 150 151UDP datagrams are passed to the engine using the :func:`lsquic_engine_packet_in()` function. This is the only way to do so. 152A pointer to the UDP payload is passed along with the size of the payload. 153Local and peer socket addresses are passed in as well. 154The void "peer ctx" pointer is associated with the peer address. It gets passed to the function that sends outgoing packets and to a few other callbacks. In a standard setup, this is most likely the socket file descriptor, but it could be pointing to something else. 155The ECN value is in the range of 0 through 3, as in RFC 3168. 156 157:: 158 159 /* 0: processed by real connection 160 * 1: handled 161 * -1: error: invalid arguments, malloc failure 162 */ 163 int 164 lsquic_engine_packet_in (lsquic_engine_t *, 165 const unsigned char *udp_payload, size_t sz, 166 const struct sockaddr *sa_local, 167 const struct sockaddr *sa_peer, 168 void *peer_ctx, int ecn); 169 170Why specify local address 171------------------------- 172 173The local address is necessary because it becomes the source address of the outgoing packets. This is important in a multihomed configuration, when packets arriving at a socket can have different destination addresses. Changes in local and peer addresses are also used to detect changes in paths, such as path migration during the classic "parking lot" scenario or NAT rebinding. When path change is detected, QUIC connection performs special steps to validate the new path. 174 175Sending Packets 176=============== 177 178The :member:`lsquic_engine_api.ea_packets_out` is the function that gets 179called when an engine instance has packets to send. It could look like 180this: 181 182:: 183 184 /* Return number of packets sent or -1 on error */ 185 static int 186 send_packets_out (void *ctx, const struct lsquic_out_spec *specs, 187 unsigned n_specs) 188 { 189 struct msghdr msg; 190 int sockfd; 191 unsigned n; 192 193 memset(&msg, 0, sizeof(msg)); 194 sockfd = (int) (uintptr_t) ctx; 195 196 for (n = 0; n < n_specs; ++n) 197 { 198 msg.msg_name = (void *) specs[n].dest_sa; 199 msg.msg_namelen = sizeof(struct sockaddr_in); 200 msg.msg_iov = specs[n].iov; 201 msg.msg_iovlen = specs[n].iovlen; 202 if (sendmsg(sockfd, &msg, 0) < 0) 203 break; 204 } 205 206 return (int) n; 207 } 208 209Note that the version above is very simple: it does not use local 210address and ECN value specified in :type:`lsquic_out_spec`. 211These can be set using ancillary data in a platform-dependent way. 212 213When an error occurs 214-------------------- 215 216When an error occurs, the value of ``errno`` is examined: 217 218- ``EAGAIN`` (or ``EWOULDBLOCK``) means that the packets could not be sent and to retry later. It is up to the caller to call :func:`lsquic_engine_send_unsent_packets()` when sending can resume. 219- ``EMSGSIZE`` means that a packet was too large. This occurs when lsquic send MTU probes. In that case, the engine will retry sending without the offending packet immediately. 220- Any other error causes the connection whose packet could not be sent to be terminated. 221 222Outgoing Packet Specification 223----------------------------- 224 225:: 226 227 struct lsquic_out_spec 228 { 229 struct iovec *iov; 230 size_t iovlen; 231 const struct sockaddr *local_sa; 232 const struct sockaddr *dest_sa; 233 void *peer_ctx; 234 int ecn; /* 0 - 3; see RFC 3168 */ 235 }; 236 237 238Each packet specification in the array given to the "packets out" function looks like this. In addition to the packet payload, specified via an iovec, the specification contains local and remote addresses, the peer context associated with the connection (which is just a file descriptor in tut.c), and ECN. 239The reason for using iovec in the specification is that a UDP datagram may contain several QUIC packets. QUIC packets with long headers, which are used during QUIC handshake, can be coalesced and lsquic tries to do that to reduce the number of datagrams needed to be sent. On the incoming side, :func:`lsquic_engine_packet_in()` takes care of splitting incoming UDP datagrams into individual packets. 240 241When to process connections 242=========================== 243 244Now that we covered how to initialize the library, instantiate an engine, and send and receive packets, it is time to see how to make the engine tick. "LSQUIC" has the concept of "tick," which is a way to describe a connection doing something productive. Other verbs could have been "kick," "prod," "poke," and so on, but we settled on "tick." 245 246There are several ways for a connection to do something productive. When a connection can do any of these things, it is "tickable:" 247 248- There are incoming packets to process 249- A user wants to read from a stream and there is data that can be read 250- A user wants to write to a stream and the stream is writeable 251- A stream has buffered packets generated when a user has written to stream outside of the regular callback mechanism. (This is allowed as an optimization: sometimes data becomes available and it's faster to just write to stream than to buffer it in the user code and wait for the "on write" callback.) 252- Internal QUIC protocol or LSQUIC maintenance actions need to be taken, such as sending out a control frame or recycling a stream. 253 254:: 255 256 /* Returns true if there are connections to be processed, in 257 * which case `diff' is set to microseconds from current time. 258 */ 259 int 260 lsquic_engine_earliest_adv_tick (lsquic_engine_t *, int *diff); 261 262There is a single function, 263:func:`lsquic_engine_earliest_adv_tick()`, that can tell the user whether and when there is at least one connection managed by an engine that needs to be ticked. "Adv" in the name of the function stands for "advisory," meaning that you do not have to process connections at that exact moment; it is simply recommended. If there is a connection to be ticked, the function will return a true value and ``diff`` will be set to a relative time to when the connection is to be ticked. This value may be negative, which means that the best time to tick the connection has passed. 264The engine keeps all connections in several data structures. It tracks each connection's timers and knows when it needs to fire. 265 266Example with libev 267------------------ 268 269:: 270 271 void 272 process_conns (struct tut *tut) 273 { 274 ev_tstamp timeout; 275 int diff; 276 ev_timer_stop(); 277 lsquic_engine_process_conns(engine); 278 if (lsquic_engine_earliest_adv_tick(engine, &diff) { 279 if (diff > 0) 280 timeout = (ev_tstamp) diff / 1000000; /* To seconds */ 281 else 282 timeout = 0.; 283 ev_timer_init(timeout) 284 ev_timer_start(); 285 } 286 } 287 288Here is a simple example that uses the libev library. First, we stop the timer and process connections. Then, we query the engine to tell us when the next advisory tick time is. Based on that, we calculate the timeout to reinitialize the timer with and start the timer. 289If ``diff`` is negative, we set timeout to zero. 290When the timer expires (not shown here), it simply calls this ``process_conns()`` again. 291 292Note that one could ignore the advisory tick time and simply process connections every few milliseconds and it will still work. This, however, will result in worse performance. 293 294Processing Connections 295---------------------- 296 297Recap: 298To process connections, call :func:`lsquic_engine_process_conns()`. 299This will call necessary callbacks to read from and write to streams 300and send packets out. Call `lsquic_engine_process_conns()` when advised 301by `lsquic_engine_earliest_adv_tick()`. 302 303Do not call `lsquic_engine_process_conns()` from inside callbacks, for 304this function is not reentrant. 305 306Another function that sends packets is 307:func:`lsquic_engine_send_unsent_packets()`. Call it if there was a 308previous failure to send out all packets 309 310Required Engine Callbacks 311========================= 312 313Now we continue to initialize our engine instance. We have covered the callback to send out packets. This is one of the required engine callbacks. 314Other required engine callbacks are a set of stream and connection callbacks that get called on various events in then connections and stream lifecycles and a callback to get the default TLS context. 315 316:: 317 318 struct lsquic_engine_api engine_api = { 319 /* --- 8< --- snip --- 8< --- */ 320 .ea_stream_if = &stream_callbacks, 321 .ea_stream_if_ctx = &some_context, 322 .ea_get_ssl_ctx = get_ssl_ctx, 323 }; 324 325 326Optional Callbacks 327------------------ 328 329Here we mention some optional callbacks. While they are not covered by 330this tutorial, it is good to know that they are available. 331 332- Looking up certificate and TLS context by SNI. 333- Callbacks to control memory allocation for outgoing packets. These are useful when sending packets using a custom library. For example, when all packets must be in contiguous memory. 334- Callbacks to observe connection ID lifecycle. These are useful in multi-process applications. 335- Callbacks that provide access to a shared-memory hash. This is also used in multi-process applications. 336- HTTP header set processing. These callbacks may be used in HTTP mode for HTTP/3 and Google QUIC. 337 338Please refer to :ref:`apiref-engine-settings` for details. 339 340Stream and connection callbacks 341=============================== 342 343Stream and connection callbacks are the way that the library communicates with user code. Some of these callbacks are mandatory; others are optional. 344They are all collected in :type:`lsquic_stream_if` ("if" here stands 345for "interface"). 346The mandatory callbacks include calls when connections and streams are created and destroyed and callbacks when streams can be read from or written to. 347The optional callbacks are used to observe some events in the connection lifecycle, such as being informed when handshake has succeeded (or failed) or when a goaway signal is received from peer. 348 349:: 350 351 struct lsquic_stream_if 352 { 353 /* Mandatory callbacks: */ 354 lsquic_conn_ctx_t *(*on_new_conn)(void *stream_if_ctx, 355 lsquic_conn_t *c); 356 void (*on_conn_closed)(lsquic_conn_t *c); 357 lsquic_stream_ctx_t * 358 (*on_new_stream)(void *stream_if_ctx, lsquic_stream_t *s); 359 void (*on_read) (lsquic_stream_t *s, lsquic_stream_ctx_t *h); 360 void (*on_write) (lsquic_stream_t *s, lsquic_stream_ctx_t *h); 361 void (*on_close) (lsquic_stream_t *s, lsquic_stream_ctx_t *h); 362 363 /* Optional callbacks: */ 364 void (*on_goaway_received)(lsquic_conn_t *c); 365 void (*on_hsk_done)(lsquic_conn_t *c, enum lsquic_hsk_status s); 366 void (*on_new_token)(lsquic_conn_t *c, const unsigned char *token, 367 void (*on_sess_resume_info)(lsquic_conn_t *c, const unsigned char *, size_t); 368 }; 369 370On new connection 371----------------- 372 373When a connection object is created, the "on new connection" callback is called. In server mode, the handshake is already known to have succeeded; in client mode, the connection object is created before the handshake is attempted. The client can tell when handshake succeeds or fails by relying on the optional "handshake is done" callback or the "on connection close" callback. 374 375:: 376 377 /* Return pointer to per-connection context. OK to return NULL. */ 378 static lsquic_conn_ctx_t * 379 my_on_new_conn (void *ea_stream_if_ctx, lsquic_conn_t *conn) 380 { 381 struct some_context *ctx = ea_stream_if_ctx; 382 struct my_conn_ctx *my_ctx = my_ctx_new(ctx); 383 if (ctx->is_client) 384 /* Need a stream to send request */ 385 lsquic_conn_make_stream(conn); 386 return (void *) my_ctx; 387 } 388 389In the made-up example above, a new per-connection context is allocated and returned. This context is then associated with the connection and can be retrieved using a dedicated function. Note that it is OK to return a ``NULL`` pointer. 390Note that in client mode, this is a good place to request that the connection make a new stream by calling :func:`lsquic_conn_make_stream()`. The connection will create a new stream when handshake succeeds. 391 392On new stream 393------------- 394 395QUIC allows either endpoint to create streams and send and receive data on them. There are unidirectional and bidirectional streams. Thus, there are four stream types. In our tutorial, however, we use the familiar paradigm of the client sending requests to the server using bidirectional stream. 396 397On the server, new streams are created when client requests arrive. On the client, streams are created when possible after the user code has requested stream creation by calling :func:`lsquic_conn_make_stream()`. 398 399:: 400 401 /* Return pointer to per-connection context. OK to return NULL. */ 402 static lsquic_stream_ctx_t * 403 my_on_new_stream (void *ea_stream_if_ctx, lsquic_stream_t *stream) { 404 struct some_context *ctx = ea_stream_if_ctx; 405 /* Associate some data with this stream: */ 406 struct my_stream_ctx *stream_ctx 407 = my_stream_ctx_new(ea_stream_if_ctx); 408 stream_ctx->stream = stream; 409 if (ctx->is_client) 410 lsquic_stream_wantwrite(stream, 1); 411 return (void *) stream_ctx; 412 } 413 414In a pattern similar to the "on new connection" callback, a per-stream context can be created at this time. The function returns this context and other stream callbacks - "on read," "on write," and "on close" - will be passed a pointer to it. As before, it is OK to return ``NULL``. 415You can register an interest in reading from or writing to the stream by using a "want read" or "want write" function. Alternatively, you can simply read or write; be prepared that this may fail and you have to try again in the "regular way." We talk about that next. 416 417On read 418------- 419 420When the "on read" callback is called, there is data to be read from stream, end-of-stream has been reached, or there is an error. 421 422:: 423 424 static void 425 my_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *h) { 426 struct my_stream_ctx *my_stream_ctx = (void *) h; 427 unsigned char buf[BUFSZ]; 428 429 ssize_t nr = lsquic_stream_read(stream, buf, sizeof(buf)); 430 /* Do something with the data.... */ 431 if (nr == 0) /* EOF */ { 432 lsquic_stream_shutdown(stream, 0); 433 lsquic_stream_wantwrite(stream, 1); /* Want to reply */ 434 } 435 } 436 437To read the data or to collect the error, call :func:`lsquic_stream_read`. If a negative value is returned, examine ``errno``. If it is not ``EWOULDBLOCK``, then an error has occurred, and you should close the stream. Here, an error means an application error, such as peer resetting the stream. A protocol error or an internal library error (such as memory allocation failure) lead to the connection being closed outright. 438To reiterate, the "on read" callback is called only when the user has registered interest in reading from the stream. 439 440On write 441-------- 442 443The "on write" callback is called when the stream can be written to. At this point, you should be able to write at least a byte to the stream. 444As with the "on read" callback, for this callback to be called, the user must have registered interest in writing to stream using :func:`lsquic_stream_wantwrite()`. 445 446 447:: 448 449 static void 450 my_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *h) { 451 struct my_stream_ctx *my_stream_ctx = (void *) h; 452 ssize_t nw = lsquic_stream_write(stream, 453 my_stream_ctx->resp, my_stream_ctx->resp_sz); 454 if (nw == my_stream_ctx->resp_sz) 455 lsquic_stream_close(stream); 456 } 457 458By default, "on read" and "on write" callbacks will be called in a loop as long as there is data to read or the stream can be written to. If you are done reading from or writing to stream, you should either shut down the appropriate end, close the stream, or unregister your interest. The library implements a circuit breaker to stop would-be infinite loops when no reading or writing progress is made. Both loop dispatch and the circuit breaker are configurable (see :member:`lsquic_engine_settings.es_progress_check` and :member:`lsquic_engine_settings.es_rw_once`). 459 460On stream close 461--------------- 462 463When reading and writing ends of the stream have been closed, the "on close" callback is called. After this function returns, pointers to the stream become invalid. (The library destroys the stream object when it deems proper.) 464This is a good place to perform necessary cleanup. 465 466:: 467 468 static void 469 my_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *h) { 470 lsquic_conn_t *conn = lsquic_stream_conn(stream); 471 struct my_conn_ctx *my_ctx = lsquic_conn_get_ctx(conn); 472 if (!has_more_reqs_to_send(my_ctx)) /* For example */ 473 lsquic_conn_close(conn); 474 free(h); 475 } 476 477In the made-up example above, we free the per-stream context allocated in the "on new stream" callback and we may close the connection. 478 479On connection close 480------------------- 481 482When either :func:`lsquic_conn_close()` has been called; or the peer has closed the connection; or an error has occurred, the "on connection close" callback is called. At this point, it is time to free the per-connection context, if any. 483 484:: 485 486 static void 487 my_on_conn_closed (lsquic_conn_t *conn) { 488 struct my_conn_ctx *my_ctx = lsquic_conn_get_ctx(conn); 489 struct some_context *ctx = my_ctx->some_context; 490 491 --ctx->n_conns; 492 if (0 == ctx->n_conn && (ctx->flags & CLOSING)) 493 exit_event_loop(ctx); 494 495 free(my_ctx); 496 } 497 498In the example above, you see the call to :func:`lsquic_conn_get_ctx()`. This returns the pointer returned by the "on new connection" callback. 499 500Using Streams 501============= 502 503To reduce buffering, most of the time bytes written to stream are written into packets directly. Bytes are buffered in the stream until a full packet can be created. Alternatively, one could flush the data by calling :func:`lsquic_stream_flush`. 504It is impossible to write more data than the congestion window. This prevents excessive buffering inside the library. 505Inside the "on read" and "on write" callbacks, reading and writing should succeed. The exception is error collection inside the "on read" callback. 506Outside of the callbacks, be ready to handle errors. For reading, it is -1 with ``EWOULDBLOCK`` errno. For writing, it is the return value of 0. 507 508More stream functions 509--------------------- 510 511Here are a few more useful stream functions. 512 513:: 514 515 /* Flush any buffered data. This triggers packetizing even a single 516 * byte into a separate frame. 517 */ 518 int 519 lsquic_stream_flush (lsquic_stream_t *); 520 521 /* Possible values for how are 0, 1, and 2. See shutdown(2). */ 522 int 523 lsquic_stream_shutdown (lsquic_stream_t *, int how); 524 525 int 526 lsquic_stream_close (lsquic_stream_t *); 527 528As mentioned before, calling :func:`lsquic_stream_flush()` will cause the stream to packetize the buffered data. Note that it may not happen immediately, as there may be higher-priority writes pending or there may not be sufficient congestion window to do so. Calling "flush" only schedules writing to packets. 529 530:func:`lsquic_stream_shutdown()` and :func:`lsquic_stream_close()` mimic the interface of the "shutdown" and "close" socket functions. After both read and write ends of a stream are closed, the "on stream close" callback will soon be called. 531 532Stream return values 533-------------------- 534 535The stream read and write functions are modeled on the standard UNIX read and write functions, including the use of the ``errno``. The most important of these error codes are ``EWOULDBLOCK`` and ``ECONNRESET`` because you may encounter these even if you structure your code correctly. Other errors typically occur when the user code does something unexpected. 536 537Return value of 0 is different for reads and writes. For reads, it means that EOF has been reached and you need to stop reading from the stream. For writes, it means that you should try writing later. 538 539If writing to stream returns an error, it may mean an internal error. If the error is not recoverable, the library will abort the connection; if it is recoverable (the only recoverable error is failure to allocate memory), attempting to write later may succeed. 540 541Scatter/gather stream functions 542------------------------------- 543 544There is the scatter/gather way to read from and write to stream and the interface is similar to the usual "readv" and "writev" functions. All return values and error codes are the same as in the stream read and write functions we have just discussed. Those are actually just wrappers around the scatter/gather versions. 545 546:: 547 548 ssize_t 549 lsquic_stream_readv (lsquic_stream_t *, const struct iovec *, 550 int iovcnt); 551 ssize_t 552 lsquic_stream_writev (lsquic_stream_t *, const struct iovec *, 553 int count); 554 555Read using a callback 556--------------------- 557 558The scatter/gather functions themselves are also wrappers. LSQUIC provides stream functions that skip intermediate buffering. They are used for zero-copy stream processing. 559 560:: 561 562 ssize_t 563 lsquic_stream_readf (lsquic_stream_t *, 564 size_t (*readf)(void *ctx, const unsigned char *, size_t len, int fin), 565 void *ctx); 566 567 568The second argument to :func:`lsquic_stream_readf()` is a callback that 569returns the number of bytes processed. The callback is passed: 570 571- Pointer to user-supplied context; 572- Pointer to the data; 573- Data size (can be zero); and 574- Indicator whether the FIN follows the data. 575 576If callback returns 0 or value smaller than `len`, reading stops. 577 578Read with callback: Example 1 579----------------------------- 580 581Here is the first example of reading from stream using a callback. Now the process of reading from stream 582is split into two functions. 583 584:: 585 586 static void 587 tut_client_on_read_v1 (lsquic_stream_t *stream, lsquic_stream_ctx_t *h) 588 { 589 struct tut *tut = (struct tut *) h; 590 size_t nread = lsquic_stream_readf(stream, tut_client_readf_v1, NULL); 591 if (nread == 0) 592 { 593 LOG("read to end-of-stream: close and read from stdin again"); 594 lsquic_stream_shutdown(stream, 0); 595 ev_io_start(tut->tut_loop, &tut->tut_u.c.stdin_w); 596 } 597 /* ... */ 598 } 599 600Here, we see the :func:`lsquic_stream_readf()` call. The return value is the same as the other read functions. 601Because in this example there is no extra information to pass to the callback (we simply print data to stdout), 602the third argument is NULL. 603 604:: 605 606 static size_t 607 tut_client_readf_v1 (void *ctx, const unsigned char *data, 608 size_t len, int fin) 609 { 610 if (len) 611 { 612 fwrite(data, 1, len, stdout); 613 fflush(stdout); 614 } 615 return len; 616 } 617 618Here is the callback itself. You can see it is very simple. If there is data to be processed, 619it is printed to stdout. 620 621Note that the data size (``len`` above) can be anything. It is not limited by UDP datagram size. This is because when incoming STREAM frames pass some fragmentation threshold, LSQUIC begins to copy incoming STREAM data to a data structure that is impervious to stream fragmentation attacks. Thus, it is possible for the callback to pass a pointer to data that is over 3KB in size. The implementation may change, so again, no guarantees. 622When the fourth argument, ``fin``, is true, this indicates that the incoming data ends after ``len`` bytes have been read. 623 624Read with callback: Example 2: Use FIN 625-------------------------------------- 626 627The FIN indicator passed to the callback gives us yet another way to detect end-of-stream. 628The previous version checked the return value of :func:`lsquic_stream_readf()` to check for EOS. 629Instead, we can use ``fin`` in the callback. 630 631The second zero-copy read example is a little more efficient as it saves us 632an extra call to ``tut_client_on_read_v2``. 633Here, we package pointers to the tut struct and stream into a special struct and pass it to 634``lsquic_stream_readf()``. 635 636:: 637 638 struct client_read_v2_ctx { struct tut *tut; lsquic_stream_t *stream; }; 639 640 static void 641 tut_client_on_read_v2 (lsquic_stream_t *stream, 642 lsquic_stream_ctx_t *h) 643 { 644 struct tut *tut = (struct tut *) h; 645 struct client_read_v2_ctx v2ctx = { tut, stream, }; 646 ssize_t nread = lsquic_stream_readf(stream, tut_client_readf_v2, 647 &v2ctx); 648 if (nread < 0) 649 /* ERROR */ 650 } 651 652Now the callback becomes more complicated, as we moved the logic to stop reading from stream into it. We need pointer to both stream and user context when "fin" is true. In that case, we call :func:`lsquic_stream_shutdown()` and begin reading from stdin again to grab the next line of input. 653 654:: 655 656 static size_t 657 tut_client_readf_v2 (void *ctx, const unsigned char *data, 658 size_t len, int fin) 659 { 660 struct client_read_v2_ctx *v2ctx = ctx; 661 if (len) 662 fwrite(data, 1, len, stdout); 663 if (fin) 664 { 665 fflush(stdout); 666 LOG("read to end-of-stream: close and read from stdin again"); 667 lsquic_stream_shutdown(v2ctx->stream, 0); 668 ev_io_start(v2ctx->tut->tut_loop, &v2ctx->tut->tut_u.c.stdin_w); 669 } 670 return len; 671 } 672 673Writing to stream: Example 1 674---------------------------- 675 676Now let's consider writing to stream. 677 678:: 679 680 static void 681 tut_server_on_write_v0 (lsquic_stream_t *stream, lsquic_stream_ctx_t *h) 682 { 683 struct tut_server_stream_ctx *const tssc = (void *) h; 684 ssize_t nw = lsquic_stream_write(stream, 685 tssc->tssc_buf + tssc->tssc_off, tssc->tssc_sz - tssc->tssc_off); 686 if (nw > 0) 687 { 688 tssc->tssc_off += nw; 689 if (tssc->tssc_off == tssc->tssc_sz) 690 lsquic_stream_close(stream); 691 /* ... */ 692 } 693 694Here, we call :func:`lsquic_stream_write()` directly. If writing succeeds and we reached the 695end of the buffer we wanted to write, we close the stream. 696 697Write using callbacks 698--------------------- 699 700To write using a callback, we need to use :func:`lsquic_stream_writef()`. 701 702:: 703 704 struct lsquic_reader { 705 /* Return number of bytes written to buf */ 706 size_t (*lsqr_read) (void *lsqr_ctx, void *buf, size_t count); 707 /* Return number of bytes remaining in the reader. */ 708 size_t (*lsqr_size) (void *lsqr_ctx); 709 void *lsqr_ctx; 710 }; 711 712 /* Return umber of bytes written or -1 on error. */ 713 ssize_t 714 lsquic_stream_writef (lsquic_stream_t *, struct lsquic_reader *); 715 716We must specify not only the function that will perform the copy, but also the function that will return the number of bytes remaining. This is useful in situations where the size of the data source may change. For example, an underlying file may change size. 717The :member:`lsquic_reader.lsqr_read` callback will be called in a loop until stream can write no more or until :member:`lsquic_reader.lsqr_size` returns zero. 718The return value of ``lsquic_stream_writef`` is the same as :func:`lsquic_stream_write()` and :func:`lsquic_stream_writev()`, which are just wrappers around the "writef" version. 719 720Writing to stream: Example 2 721---------------------------- 722 723Here is the second version of the "on write" callback. It uses :func:`lsquic_stream_writef()`. 724 725:: 726 727 static void 728 tut_server_on_write_v1 (lsquic_stream_t *stream, lsquic_stream_ctx_t *h) 729 { 730 struct tut_server_stream_ctx *const tssc = (void *) h; 731 struct lsquic_reader reader = { tssc_read, tssc_size, tssc, }; 732 ssize_t nw = lsquic_stream_writef(stream, &reader); 733 if (nw > 0 && tssc->tssc_off == tssc->tssc_sz) 734 lsquic_stream_close(stream); 735 /* ... */ 736 } 737 738 739The reader struct is initialized with pointers to read and size functions and this struct is passed 740to the "writef" function. 741 742:: 743 744 static size_t 745 tssc_size (void *ctx) 746 { 747 struct tut_server_stream_ctx *tssc = ctx; 748 return tssc->tssc_sz - tssc->tssc_off; 749 } 750 751 752The size callback simply returns the number of bytes left. 753 754:: 755 756 static size_t 757 tssc_read (void *ctx, void *buf, size_t count) 758 { 759 struct tut_server_stream_ctx *tssc = ctx; 760 761 if (count > tssc->tssc_sz - tssc->tssc_off) 762 count = tssc->tssc_sz - tssc->tssc_off; 763 memcpy(buf, tssc->tssc_buf + tssc->tssc_off, count); 764 tssc->tssc_off += count; 765 return count; 766 } 767 768 769The read callback (so called because you *read* data from the source) writes no more than ``count`` bytes 770to memory location pointed by ``buf`` and returns the number of bytes copied. 771In our case, ``count`` is never larger than the number of bytes still left to write. 772This is because the caller - the LSQUIC library - gets the value of ``count`` from the ``lsqr_size()`` callback. When reading from a file descriptor, on the other hand, this can very well happen that you don't have as much data to write as you thought you had. 773 774Client: making connection 775========================= 776 777We now switch our attention to making a QUIC connection. The function :func:`lsquic_engine_connect()` does that. This function has twelve arguments. (These arguments have accreted over time.) 778 779:: 780 781 lsquic_conn_t * 782 lsquic_engine_connect (lsquic_engine_t *, 783 enum lsquic_version, /* Set to N_LSQVER for default */ 784 const struct sockaddr *local_sa, 785 const struct sockaddr *peer_sa, 786 void *peer_ctx, 787 lsquic_conn_ctx_t *conn_ctx, 788 const char *hostname, /* Used for SNI */ 789 unsigned short base_plpmtu, /* 0 means default */ 790 const unsigned char *sess_resume, size_t sess_resume_len, 791 const unsigned char *token, size_t token_sz); 792 793- The first argument is the pointer to the engine instance. 794- The second argument is the QUIC version to use. 795- The third and fourth arguments specify local and destination addresses, respectively. 796- The fifth argument is the so-called "peer context." 797- The sixth argument is the connection context. This is used if you need to pass a pointer to the "on new connection" callback. This context is overwritten by the return value of the "on new connection" callback. 798- The argument "hostname," which is the seventh argument, is used for SNI. This argument is optional, just as the rest of the arguments that follow. 799- The eighth argument is the initial maximum size of the UDP payload. This will be the base PLPMTU if DPLPMTUD is enabled. Specifying zero, or default, is the safe way to go: lsquic will pick a good starting value. 800- The next two arguments allow one to specify a session resumption information to establish a connection faster. In the case of IETF QUIC, this is the TLS Session Ticket. To get this ticket, specify the :member:`lsquic_stream_if.on_sess_resume_info` callback. 801- The last pair of arguments is for specifying a token to try to prevent a potential stateless retry from the server. The token is learned in a previous session. See the optional callback :member:`lsquic_stream_if.on_new_token`. 802 803:: 804 805 tut.tut_u.c.conn = lsquic_engine_connect( 806 tut.tut_engine, N_LSQVER, 807 (struct sockaddr *) &tut.tut_local_sas, &addr.sa, 808 (void *) (uintptr_t) tut.tut_sock_fd, /* Peer ctx */ 809 NULL, NULL, 0, NULL, 0, NULL, 0); 810 if (!tut.tut_u.c.conn) 811 { 812 LOG("cannot create connection"); 813 exit(EXIT_FAILURE); 814 } 815 tut_process_conns(&tut); 816 817Here is an example from a tutorial program. The connect call is a lot less intimidating in real life, as half the arguments are set to zero. 818We pass a pointer to the engine instance, N_LSQVER to let the engine pick the version to use and the two socket addresses. 819The peer context is simply the socket file descriptor cast to a pointer. 820This is what is passed to the "send packets out" callback. 821 822Specifying QUIC version 823======================= 824 825QUIC versions in LSQUIC are gathered in an enum, :type:`lsquic_version`, and have an arbitrary value. 826 827:: 828 829 enum lsquic_version { 830 LSQVER_043, LSQVER_046, LSQVER_050, /* Google QUIC */ 831 LSQVER_ID27, LSQVER_ID28, LSQVER_ID29, /* IETF QUIC */ 832 /* ...some special entries skipped */ 833 N_LSQVER /* <====================== Special value */ 834 }; 835 836The special value "N_LSQVER" is used to let the engine pick the QUIC version. 837It picks the latest non-experimental version, so in this case it picks ID-29. 838(Experimental from the point of view of the library.) 839 840Because version enum values are small -- and that is by design -- a list of 841versions can be passed around as bitmasks. 842 843:: 844 845 /* This allows list of versions to be specified as bitmask: */ 846 es_versions = (1 << LSQVER_ID28) | (1 << LSQVER_ID29); 847 848This is done, for example, when 849specifying list of versions to enable in engine settings using :member:`lsquic_engine_api.ea_versions`. 850There are a couple of more places in the API where this technique is used. 851 852Server callbacks 853================ 854 855The server requires SSL callbacks to be present. The basic required callback is :member:`lsquic_engine_api.ea_get_ssl_ctx`. It is used to get a pointer to an initialized ``SSL_CTX``. 856 857:: 858 859 typedef struct ssl_ctx_st * (*lsquic_lookup_cert_f)( 860 void *lsquic_cert_lookup_ctx, const struct sockaddr *local, 861 const char *sni); 862 863 struct lsquic_engine_api { 864 lsquic_lookup_cert_f ea_lookup_cert; 865 void *ea_cert_lu_ctx; 866 struct ssl_ctx_st * (*ea_get_ssl_ctx)(void *peer_ctx, 867 const struct sockaddr *local); 868 /* (Other members of the struct are not shown) */ 869 }; 870 871In case SNI is used, LSQUIC will call :member:`lsquic_engine_api.ea_lookup_cert`. 872For example, SNI is required in HTTP/3. 873In `our web server`_, each virtual host has its own SSL context. Note that besides the SNI string, the callback is also given the local socket address. This makes it possible to implement a flexible lookup mechanism. 874 875Engine settings 876=============== 877 878Besides the engine API struct passed to the engine constructor, there is also an engine settings struct, :type:`lsquic_engine_settings`. :member:`lsquic_engine_api.ea_settings` in the engine API struct 879can be pointed to a custom settings struct. By default, this pointer is ``NULL``. 880In that case, the engine uses default settings. 881 882There are many settings, controlling everything from flow control windows to the number of times an "on read" callback can be called in a loop before it is deemed an infinite loop and the circuit breaker is tripped. To make changing default settings values easier, the library provides functions to initialize the settings struct to defaults and then to check these values for sanity. 883 884Settings helper functions 885------------------------- 886 887:: 888 889 /* Initialize `settings' to default values */ 890 void 891 lsquic_engine_init_settings (struct lsquic_engine_settings *, 892 /* Bitmask of LSENG_SERVER and LSENG_HTTP */ 893 unsigned lsquic_engine_flags); 894 895 /* Check settings for errors, return 0 on success, -1 on failure. */ 896 int 897 lsquic_engine_check_settings (const struct lsquic_engine_settings *, 898 unsigned lsquic_engine_flags, 899 /* Optional, can be NULL: */ 900 char *err_buf, size_t err_buf_sz); 901 902The first function is :func:`lsquic_engine_init_settings()`, which does just that. 903The second argument is a bitmask to specify whether the engine is in server mode 904and whether HTTP mode is turned on. These should be the same flags as those 905passed to the engine constructor. 906 907Once you have initialized the settings struct in this manner, change the setting 908or settings you want and then call :func:`lsquic_engine_check_settings()`. The 909first two arguments are the same as in the initializer. The third and fourth 910argument are used to pass a pointer to a buffer into which a human-readable error 911string can be placed. 912 913The checker function does only the basic sanity checks. If you really set out 914to misconfigure LSQUIC, you can. On the bright side, each setting is clearly 915documented (see :ref:`apiref-engine-settings`). Most settings are standalone; 916when there is interplay between them, it is also documented. 917Test before deploying! 918 919Settings example 920---------------- 921 922The example is adapted from a tutorial program. Here, command-line options 923are processed and appropriate options is set. The first time the ``-o`` 924flag is encountered, the settings struct is initialized. Then the argument 925is parsed to see which setting to alter. 926 927:: 928 929 while (/* getopt */) 930 { 931 case 'o': /* For example: -o version=h3-27 -o cc_algo=2 */ 932 if (!settings_initialized) { 933 lsquic_engine_init_settings(&settings, 934 cert_file || key_file ? LSENG_SERVER : 0); 935 settings_initialized = 1; 936 } 937 /* ... */ 938 else if (0 == strncmp(optarg, "cc_algo=", val - optarg)) 939 settings.es_cc_algo = atoi(val); 940 /* ... */ 941 } 942 943 /* Check settings */ 944 if (0 != lsquic_engine_check_settings(&settings, 945 tut.tut_flags & TUT_SERVER ? LSENG_SERVER : 0, 946 errbuf, sizeof(errbuf))) 947 { 948 LOG("invalid settings: %s", errbuf); 949 exit(EXIT_FAILURE); 950 } 951 952 /* ... */ 953 eapi.ea_settings = &settings; 954 955After option processing is completed, the settings are checked. The error 956buffer is used to log a configuration error. 957 958Finally, the settings struct is pointed to by the engine API struct before 959the engine constructor is called. 960 961Logging 962======= 963 964LSQUIC provides a simple logging interface using a single callback function. 965By default, no messages are logged. This can be changed by calling :func:`lsquic_logger_init()`. 966This will set a library-wide logger callback function. 967 968:: 969 970 void lsquic_logger_init(const struct lsquic_logger_if *, 971 void *logger_ctx, enum lsquic_logger_timestamp_style); 972 973 struct lsquic_logger_if { 974 int (*log_buf)(void *logger_ctx, const char *buf, size_t len); 975 }; 976 977 enum lsquic_logger_timestamp_style { LLTS_NONE, LLTS_HHMMSSMS, 978 LLTS_YYYYMMDD_HHMMSSMS, LLTS_CHROMELIKE, LLTS_HHMMSSUS, 979 LLTS_YYYYMMDD_HHMMSSUS, N_LLTS }; 980 981You can instruct the library to generate a timestamp and include it as part of the message. 982Several timestamp formats are available. Some display microseconds, some do not; some 983display the date, some do not. One of the most useful formats is "chromelike," 984which matches the somewhat weird timestamp format used by Chromium. This makes it easy to 985compare the two logs side by side. 986 987There are eight log levels in LSQUIC: debug, info, notice, warning, error, alert, emerg, 988and crit. 989These correspond to the usual log levels. (For example, see ``syslog(3)``). Of these, only five are used: debug, info, notice, warning, and error. Usually, warning and error messages are printed when there is a bug in the library or something very unusual has occurred. Memory allocation failures might elicit a warning as well, to give the operator a heads up. 990 991LSQUIC possesses about 40 logging modules. Each module usually corresponds to a single piece 992of functionality in the library. The exception is the "event" module, which logs events of note in many modules. 993There are two functions to manipulate which log messages will be generated. 994 995:: 996 997 /* Set log level for all modules */ 998 int 999 lsquic_set_log_level (const char *log_level); 1000 1001 /* Set log level per module "event=debug" */ 1002 int 1003 lsquic_logger_lopt (const char *optarg); 1004 1005The first is :func:`lsquic_set_log_level()`. It sets the same log level for each module. 1006The second is :func:`lsquic_logger_lopt()`. This function takes a comma-separated list of name-value pairs. For example, "event=debug." 1007 1008Logging Example 1009--------------- 1010 1011The following example is adapted from a tutorial program. In the program, log messages 1012are written to a file handle. By default, this is the standard error. One can change 1013that by using the "-f" command-line option and specify the log file. 1014 1015:: 1016 1017 static int 1018 tut_log_buf (void *ctx, const char *buf, size_t len) { 1019 FILE *out = ctx; 1020 fwrite(buf, 1, len, out); 1021 fflush(out); 1022 return 0; 1023 } 1024 static const struct lsquic_logger_if logger_if = { tut_log_buf, }; 1025 1026 lsquic_logger_init(&logger_if, s_log_fh, LLTS_HHMMSSUS); 1027 1028 1029``tut_log_buf()`` returns 0, but the truth is that the return value is ignored. 1030There is just nothing for the library to do when the user-supplied log function fails! 1031 1032:: 1033 1034 case 'l': /* e.g. -l event=debug,cubic=info */ 1035 if (0 != lsquic_logger_lopt(optarg)) { 1036 fprintf(stderr, "error processing -l option\n"); 1037 exit(EXIT_FAILURE); 1038 } 1039 break; 1040 case 'L': /* e.g. -L debug */ 1041 if (0 != lsquic_set_log_level(optarg)) { 1042 fprintf(stderr, "error processing -L option\n"); 1043 exit(EXIT_FAILURE); 1044 } 1045 break; 1046 1047Here you can see how we use ``-l`` and ``-L`` command-line options to call one of 1048the two log level functions. These functions can fail if the incorrect log level 1049or module name is passed. Both log level and module name are treated in case-insensitive manner. 1050 1051Sample log messages 1052------------------- 1053 1054When log messages are turned on, you may see something like this in your log file (timestamps and 1055log levels are elided for brevity): 1056 1057.. code-block:: text 1058 1059 [QUIC:B508E8AA234E0421] event: generated STREAM frame: stream 0, offset: 0, size: 3, fin: 1 1060 [QUIC:B508E8AA234E0421-0] stream: flushed to or past required offset 3 1061 [QUIC:B508E8AA234E0421] event: sent packet 13, type Short, crypto: forw-secure, size 32, frame types: STREAM, ecn: 0, spin: 0; kp: 0, path: 0, flags: 9470472 1062 [QUIC:B508E8AA234E0421] event: packet in: 15, type: Short, size: 44; ecn: 0, spin: 0; path: 0 1063 [QUIC:B508E8AA234E0421] rechist: received 15 1064 [QUIC:B508E8AA234E0421] event: ACK frame in: [13-9] 1065 [QUIC:B508E8AA234E0421] conn: about to process QUIC_FRAME_STREAM frame 1066 [QUIC:B508E8AA234E0421] event: STREAM frame in: stream 0; offset 0; size 3; fin: 1 1067 [QUIC:B508E8AA234E0421-0] stream: received stream frame, offset 0x0, len 3; fin: 1 1068 [QUIC:B508E8AA234E0421-0] di: FIN set at 3 1069 1070Here we see the connection ID, ``B508E8AA234E0421``, and logging for modules "event", "stream", "rechist" 1071(that stands for "receive history"), "conn", and "di" (the "data in" module). When the connection ID is 1072followed by a dash and that number, the number is the stream ID. Note that stream ID is logged not just 1073for the stream, but for some other modules as well. 1074 1075Key logging and Wireshark 1076========================= 1077 1078`Wireshark`_ supports IETF QUIC. The developers have been very good at keeping up with latest versions. 1079You will need version 3.3 of Wireshark to support Internet-Draft 29. Support for HTTP/3 is in progress. 1080 1081To export TLS secrets, use BoringSSL's ``SSL_CTX_set_keylog_callback()``. 1082Use `lsquic_ssl_to_conn()` to get the connection associated 1083with the SSL object. 1084 1085Key logging example 1086------------------- 1087 1088:: 1089 1090 static void * 1091 keylog_open_file (const SSL *ssl) 1092 { 1093 const lsquic_conn_t *conn; 1094 const lsquic_cid_t *cid; 1095 FILE *fh; 1096 int sz; 1097 unsigned i; 1098 char id_str[MAX_CID_LEN * 2 + 1]; 1099 char path[PATH_MAX]; 1100 static const char b2c[16] = "0123456789ABCDEF"; 1101 1102 conn = lsquic_ssl_to_conn(ssl); 1103 cid = lsquic_conn_id(conn); 1104 for (i = 0; i < cid->len; ++i) 1105 { 1106 id_str[i * 2 + 0] = b2c[ cid->idbuf[i] >> 4 ]; 1107 id_str[i * 2 + 1] = b2c[ cid->idbuf[i] & 0xF ]; 1108 } 1109 id_str[i * 2] = '\0'; 1110 sz = snprintf(path, sizeof(path), "/secret_dir/%s.keys", id_str); 1111 if ((size_t) sz >= sizeof(path)) 1112 { 1113 LOG("WARN: %s: file too long", __func__); 1114 return NULL; 1115 } 1116 fh = fopen(path, "ab"); 1117 if (!fh) 1118 LOG("WARN: could not open %s for appending: %s", path, strerror(errno)); 1119 return fh; 1120 } 1121 1122 static void 1123 keylog_log_line (const SSL *ssl, const char *line) 1124 { 1125 file = keylog_open_file(ssl); 1126 if (file) 1127 { 1128 fputs(line, file); 1129 fputs("\n", file); 1130 fclose(file); 1131 } 1132 } 1133 1134 /* ... */ 1135 1136 SSL_CTX_set_keylog_callback(ssl, keylog_log_line); 1137 1138The most involved part of this is opening the necessary file, creating it if necessary. 1139The connection can be used to generate a filename based on the connection ID. 1140We see that the line logger simply writes the passed C string to the filehandle and appends a newline. 1141 1142Wireshark screenshot 1143-------------------- 1144 1145After jumping through those hoops, our reward is a decoded QUIC trace in Wireshark! 1146 1147.. image:: wireshark-screenshot.png 1148 1149Here, we highlighted the STREAM frame payload. 1150Other frames in view are ACK and TIMESTAMP frames. 1151In the top panel with the packet list, you can see that frames are listed after the packet number. 1152Another interesting item is the DCID. This stands for "Destination Connection ID," and you can 1153see that there are two different values there. This is because the two peers of the QUIC connection 1154place different connection IDs in the packets! 1155 1156Connection IDs 1157============== 1158 1159A QUIC connection has two sets of connection IDs: source connection IDs and destination connection IDs. The source connection IDs set is what the peer uses to place in QUIC packets; the destination connection IDs is what this endpoint uses to include in the packets it sends to the peer. One's source CIDs is the other's destination CIDs and vice versa. 1160What interesting is that either side of the QUIC connection may change the DCID. Use CIDs with care. 1161 1162:: 1163 1164 #define MAX_CID_LEN 20 1165 1166 typedef struct lsquic_cid 1167 { 1168 uint_fast8_t len; 1169 union { 1170 uint8_t buf[MAX_CID_LEN]; 1171 uint64_t id; 1172 } u_cid; 1173 #define idbuf u_cid.buf 1174 } lsquic_cid_t; 1175 1176 #define LSQUIC_CIDS_EQ(a, b) ((a)->len == 8 ? \ 1177 (b)->len == 8 && (a)->u_cid.id == (b)->u_cid.id : \ 1178 (a)->len == (b)->len && 0 == memcmp((a)->idbuf, (b)->idbuf, (a)->len)) 1179 1180The LSQUIC representation of a CID is the struct above. The CID can be up to 20 bytes in length. 1181By default, LSQUIC uses 8-byte CIDs to speed up comparisons. 1182 1183Get this-and-that API 1184===================== 1185 1186Here are a few functions to get different LSQUIC objects from other objects. 1187 1188:: 1189 1190 const lsquic_cid_t * 1191 lsquic_conn_id (const lsquic_conn_t *); 1192 1193 lsquic_conn_t * 1194 lsquic_stream_conn (const lsquic_stream_t *); 1195 1196 lsquic_engine_t * 1197 lsquic_conn_get_engine (lsquic_conn_t *); 1198 1199 int lsquic_conn_get_sockaddr (lsquic_conn_t *, 1200 const struct sockaddr **local, const struct sockaddr **peer); 1201 1202The CID returned by :func:`lsquic_conn_id()` is that used for logging: server and client should return the same CID. As noted earlier, you should not rely on this value to identify a connection! 1203You can get a pointer to the connection from a stream and a pointer to the engine from a connection. 1204Calling :func:`lsquic_conn_get_sockaddr()` will point ``local`` and ``peer`` to the socket addressess of the current path. QUIC supports multiple paths during migration, but access to those paths has not been exposed via an API yet. This may change when or if QUIC adds true multipath support. 1205 1206.. _`our web server`: https://www.litespeedtech.com/products 1207.. _`Wireshark`: https://www.wireshark.org/ 1208