Home | History | Annotate | Line # | Download | only in ddd
      1 Report on the Conclusions of the QUIC DDD Process
      2 =================================================
      3 
      4 The [QUIC Demo-Driven Design process](README.md) was undertaken to meet the OMC
      5 requirement to develop a QUIC API that required only minimal changes to existing
      6 applications to be able to adapt their code to use QUIC. The demo-driven design
      7 process developed a set of representative demos modelling a variety of common
      8 OpenSSL usage patterns based on analysis of a broad spectrum of open source
      9 software projects using OpenSSL.
     10 
     11 As part of this process, a set of proposed diffs were produced. These proposed
     12 diffs were the expected changes which would be needed to the baseline demos to
     13 support QUIC based on theoretical analysis of the minimum requirements to be
     14 able to support QUIC. This analysis concluded that the changes needed to
     15 applications could be kept very small in many circumstances, with only minimal
     16 diff sizes to the baseline demos.
     17 
     18 Following the development of QUIC MVP, these demos have been revisited and the
     19 correspondence of our actual final API and usage patterns with the planned diffs
     20 have been reviewed.
     21 
     22 This document discusses the planned changes and the actual changes for each demo
     23 and draws conclusions on the level of disparity.
     24 
     25 Since tracking a set of diffs separately is unwieldy, both the planned and
     26 unplanned changes have been folded into the original baseline demo files guarded
     27 with `#ifdef USE_QUIC`. Viewing these files therefore is informative to
     28 application writers as it provides a clear view of what is different when using
     29 QUIC. (The originally planned changes, and the final changes, are added in
     30 separate, clearly-labelled commits; to view the originally planned changes only,
     31 view the commit history for a given demo file.)
     32 
     33 ddd-01-conn-blocking
     34 --------------------
     35 
     36 This demo exists to demonstrate the simplest possible usage of OpenSSL, whether
     37 with TLS or QUIC.
     38 
     39 ### Originally planned changes
     40 
     41 The originally planned change to enable applications for QUIC amounted to just a
     42 single line:
     43 
     44 ```diff
     45 +    ctx = SSL_CTX_new(QUIC_client_method());
     46 -    ctx = SSL_CTX_new(TLS_client_method());
     47 ```
     48 
     49 ### Actual changes
     50 
     51 The following additional changes needed to be made:
     52 
     53 - `QUIC_client_method` was renamed to `OSSL_QUIC_client_method` for namespacing
     54   reasons.
     55 
     56 - A call to `SSL_set_alpn_protos` to configure ALPN was added. This is necessary
     57   because QUIC mandates the use of ALPN, and this was not noted during the
     58   DDD process.
     59 
     60 ddd-02-conn-nonblocking
     61 -----------------------
     62 
     63 This demo exists to demonstrate simple non-blocking usage. As with
     64 ddd-01-conn-blocking, the name resolution process is managed by `BIO_s_connect`.
     65 
     66 It also arbitrarily adds a `BIO_f_buffer` pushed onto the BIO stack
     67 as this is a common application usage pattern.
     68 
     69 ### Originally planned changes
     70 
     71 The originally planned changes to enable applications for QUIC amounted to:
     72 
     73 - Change of method (as for ddd-01-conn-blocking);
     74 
     75 - Use of a `BIO_f_dgram_buffer` BIO method instead of a `BIO_f_buffer`;
     76 
     77 - Use of a `BIO_get_poll_fd` function to get the FD to poll rather than
     78   `BIO_get_fd`;
     79 
     80 - A change to how the `POLLIN`/`POLLOUT`/`POLLERR` flags to pass to poll(2)
     81   need to be determined.
     82 
     83 - Additional functions in application code to determine event handling
     84   timeouts related to QUIC (`get_conn_pump_timeout`) and to pump
     85   the QUIC event loop (`pump`).
     86 
     87 - Timeout computation code which involves merging and comparing different
     88   timeouts and calling `pump` as needed, based on deadlines reported
     89   by libssl.
     90 
     91 Note that some of these changes are unnecessary when using the thread assisted
     92 mode (see the variant ddd-02-conn-nonblocking-threads below).
     93 
     94 ### Actual changes
     95 
     96 The following additional changes needed to be made:
     97 
     98 - Change of method name (as for ddd-01-conn-blocking);
     99 
    100 - Use of ALPN (as for ddd-01-conn-blocking);
    101 
    102 - The strategy for how to expose pollable OS resource handles
    103   to applications to determine I/O readiness has changed substantially since the
    104   original DDD process. As such, applications now use `BIO_get_rpoll_descriptor`
    105   and `BIO_get_wpoll_descriptor` to determine I/O readiness, rather than the
    106   originally hypothesised `SSL_get_poll_fd`.
    107 
    108 - The strategy for how to determine when to poll for `POLLIN`, when to
    109   poll for `POLLOUT`, etc. has changed since the original DDD process.
    110   This information is now exposed via `SSL_net_read_desired` and
    111   `SSL_net_write_desired`.
    112 
    113 - The API to expose the event handling deadline for the QUIC engine
    114   has evolved since the original DDD process. The new API
    115   `SSL_get_event_timeout` is used, rather than the originally hypothesised
    116   `BIO_get_timeout`/`SSL_get_timeout`.
    117 
    118 - The API to perform QUIC event processing has been renamed to be
    119   more descriptive. It is now called `SSL_handle_events` rather than
    120   the originally hypothesised `BIO_pump`/`SSL_pump`.
    121 
    122 The following changes were foreseen to be necessary, but turned out to actually
    123 not be necessary:
    124 
    125 - The need to change code which pushes a `BIO_f_buffer()` after an SSL BIO
    126   was foreseen as use of buffering on the network side is unworkable with
    127   QUIC. This turned out not to be necessary since we can just reject the
    128   BIO_push() call. The buffer should still be freed eventually when the
    129   SSL BIO is freed. The buffer is not used and is unnecessary, so it is
    130   still desirable for applications to remove this code.
    131 
    132 ddd-02-conn-nonblocking-threads
    133 -------------------------------
    134 
    135 This is a variant of the ddd-02-conn-nonblocking demo. The base is the same, but
    136 the changes made are different. The use of thread-assisted mode, in which an
    137 internal assist thread is used to perform QUIC event handling, enables an
    138 application to make fewer changes than are needed in the ddd-02-conn-nonblocking
    139 demo.
    140 
    141 ### Originally planned changes
    142 
    143 The originally planned changes to enable applications for QUIC amounted to:
    144 
    145 - Change of method, this time using method `QUIC_client_thread_method` rather
    146   than `QUIC_client_method`;
    147 
    148 - Use of a `BIO_get_poll_fd` function to get the FD to poll rather than
    149   `BIO_get_fd`;
    150 
    151 - A change to how the `POLLIN`/`POLLOUT`/`POLLERR` flags to pass to poll(2)
    152   need to be determined.
    153 
    154   Note that this is a substantially smaller list of changes than for
    155   ddd-02-conn-nonblocking.
    156 
    157 ### Actual changes
    158 
    159 The following additional changes needed to be made:
    160 
    161 - Change of method name (`QUIC_client_thread_method` was renamed to
    162   `OSSL_QUIC_client_thread_method` for namespacing reasons);
    163 
    164 - Use of ALPN (as for ddd-01-conn-blocking);
    165 
    166 - Use of `BIO_get_rpoll_descriptor` rather than `BIO_get_poll_fd` (as for
    167   ddd-02-conn-nonblocking).
    168 
    169 - Use of `SSL_net_read_desired` and `SSL_net_write_desired` (as for
    170   ddd-02-conn-nonblocking).
    171 
    172 ddd-03-fd-blocking
    173 ------------------
    174 
    175 This demo is similar to ddd-01-conn-blocking but uses a file descriptor passed
    176 directly by the application rather than BIO_s_connect.
    177 
    178 ### Originally planned changes
    179 
    180 - Change of method (as for ddd-01-conn-blocking);
    181 
    182 - The arguments to the `socket(2)` call are changed from `(AF_INET, SOCK_STREAM,
    183   IPPROTO_TCP)` to `(AF_INET, SOCK_DGRAM, IPPROTO_UDP)`.
    184 
    185 ### Actual changes
    186 
    187 The following additional changes needed to be made:
    188 
    189 - Change of method name (as for ddd-01-conn-blocking);
    190 
    191 - Use of ALPN (as for ddd-01-conn-blocking).
    192 
    193 ddd-04-fd-nonblocking
    194 ---------------------
    195 
    196 This demo is similar to ddd-01-conn-nonblocking but uses a file descriptor
    197 passed directly by the application rather than BIO_s_connect.
    198 
    199 ### Originally planned changes
    200 
    201 - Change of method (as for ddd-01-conn-blocking);
    202 
    203 - The arguments to the `socket(2)` call are changed from `(AF_INET, SOCK_STREAM,
    204   IPPROTO_TCP)` to `(AF_INET, SOCK_DGRAM, IPPROTO_UDP)`;
    205 
    206 - A change to how the `POLLIN`/`POLLOUT`/`POLLERR` flags to pass to poll(2)
    207   need to be determined.
    208 
    209 - Additional functions in application code to determine event handling
    210   timeouts related to QUIC (`get_conn_pump_timeout`) and to pump
    211   the QUIC event loop (`pump`).
    212 
    213 - Timeout computation code which involves merging and comparing different
    214   timeouts and calling `pump` as needed, based on deadlines reported
    215   by libssl.
    216 
    217 ### Actual changes
    218 
    219 The following additional changes needed to be made:
    220 
    221 - Change of method name (as for ddd-01-conn-blocking);
    222 
    223 - Use of ALPN (as for ddd-01-conn-blocking);
    224 
    225 - `SSL_get_timeout` replaced with `SSL_get_event_timeout` (as for
    226   ddd-02-conn-nonblocking);
    227 
    228 - `SSL_pump` renamed to `SSL_handle_events` (as for ddd-02-conn-nonblocking);
    229 
    230 - The strategy for how to determine when to poll for `POLLIN`, when to
    231   poll for `POLLOUT`, etc. has changed since the original DDD process.
    232   This information is now exposed via `SSL_net_read_desired` and
    233   `SSL_net_write_desired` (as for ddd-02-conn-nonblocking).
    234 
    235 ddd-05-mem-nonblocking
    236 ----------------------
    237 
    238 This demo is more elaborate. It uses memory buffers created and managed by an
    239 application as an intermediary between libssl and the network, which is a common
    240 usage pattern for applications. Managing this pattern for QUIC is more elaborate
    241 since datagram semantics on the network channel need to be maintained.
    242 
    243 ### Originally planned changes
    244 
    245 - Change of method (as for ddd-01-conn-blocking);
    246 
    247 - Call to `BIO_new_bio_pair` is changed to `BIO_new_dgram_pair`, which
    248   provides a bidirectional memory buffer BIO with datagram semantics.
    249 
    250 - A change to how the `POLLIN`/`POLLOUT`/`POLLERR` flags to pass to poll(2)
    251   need to be determined.
    252 
    253 - Potential changes to buffer sizes used by applications to buffer
    254   datagrams, if those buffers are smaller than 1472 bytes.
    255 
    256 - The arguments to the `socket(2)` call are changed from `(AF_INET, SOCK_STREAM,
    257   IPPROTO_TCP)` to `(AF_INET, SOCK_DGRAM, IPPROTO_UDP)`;
    258 
    259 ### Actual changes
    260 
    261 The following additional changes needed to be made:
    262 
    263 - Change of method name (as for ddd-01-conn-blocking);
    264 
    265 - Use of ALPN (as for ddd-01-conn-blocking);
    266 
    267 - The API to construct a `BIO_s_dgram_pair` ended up being named
    268   `BIO_new_bio_dgram_pair` rather than `BIO_new_dgram_pair`;
    269 
    270 - Use of `SSL_net_read_desired` and `SSL_net_write_desired` (as for
    271   ddd-02-conn-nonblocking).
    272 
    273 ddd-06-mem-uv
    274 -------------
    275 
    276 This demo is the most elaborate of the set. It uses a real-world asynchronous
    277 I/O reactor, namely libuv (the engine used by Node.js). In doing so it seeks to
    278 demonstrate and prove the viability of our API design with a real-world
    279 asynchronous I/O system. It operates wholly in non-blocking mode and uses memory
    280 buffers on either side of the QUIC stack to feed data to and from the
    281 application and the network.
    282 
    283 ### Originally planned changes
    284 
    285 - Change of method (as for ddd-01-conn-blocking);
    286 
    287 - Various changes to use of libuv needed to switch to using UDP;
    288 
    289 - Additional use of libuv to configure a timer event;
    290 
    291 - Call to `BIO_new_bio_pair` is changed to `BIO_new_dgram_pair`
    292   (as for ddd-05-mem-nonblocking);
    293 
    294 - Some reordering of code required by the design of libuv.
    295 
    296 ### Actual changes
    297 
    298 The following additional changes needed to be made:
    299 
    300 - Change of method name (as for ddd-01-conn-blocking);
    301 
    302 - Use of ALPN (as for ddd-01-conn-blocking);
    303 
    304 - `BIO_new_dgram_pair` renamed to `BIO_new_bio_dgram_pair` (as for
    305   ddd-05-mem-nonblocking);
    306 
    307 - `SSL_get_timeout` replaced with `SSL_get_event_timeout` (as for
    308   ddd-02-conn-nonblocking);
    309 
    310 - `SSL_pump` renamed to `SSL_handle_events` (as for ddd-02-conn-nonblocking);
    311 
    312 - Fixes to use of libuv based on a corrected understanding
    313   of its operation, and changes that necessarily ensue.
    314 
    315 Conclusions
    316 -----------
    317 
    318 The DDD process has successfully delivered on the objective of delivering a QUIC
    319 API which can be used with only minimal API changes. The additional changes on
    320 top of those originally planned which were required to successfully execute the
    321 demos using QUIC were highly limited in scope and mostly constituted only minor
    322 changes. The sum total of the changes required for each demo (both planned and
    323 additional), as denoted in each DDD demo file under `#ifdef USE_QUIC` guards,
    324 are both minimal and limited in scope.
    325 
    326 Minimal and limited are distinct criteria. If inexorable technical
    327 requirements dictate, an enormous set of changes to an application could be
    328 considered minimal. The changes required to representative applications, as
    329 demonstrated by the DDD demos, are not merely minimal but also limited.
    330 
    331 For example, while the extent of these necessary changes varies by the
    332 sophistication of each demo and the kind of application usage pattern it
    333 represents, some demos in particular demonstrate exceptionally small changesets;
    334 for example, ddd-01-conn-blocking and ddd-02-conn-nonblocking-threads, with
    335 ddd-01-conn-blocking literally being enabled by a single line change assuming
    336 ALPN is already configured.
    337 
    338 This report concludes the DDD process for the single-stream QUIC client API
    339 design process, which sought to validate our API design and API ease of use for
    340 existing applications seeking to adopt QUIC.
    341