1=pod 2 3=begin comment 4 5NB: Changes to the source code samples in this file should also be reflected in 6demos/guide/quic-multi-stream.c 7 8=end comment 9 10=head1 NAME 11 12ossl-guide-quic-multi-stream 13- OpenSSL Guide: Writing a simple multi-stream QUIC client 14 15=head1 INTRODUCTION 16 17This page will introduce some important concepts required to write a simple 18QUIC multi-stream application. It assumes a basic understanding of QUIC and how 19it is used in OpenSSL. See L<ossl-guide-quic-introduction(7)> and 20L<ossl-guide-quic-client-block(7)>. 21 22=head1 QUIC STREAMS 23 24In a QUIC multi-stream application we separate out the concepts of a QUIC 25"connection" and a QUIC "stream". A connection object represents the overarching 26details of the connection between a client and a server including all its 27negotiated and configured parameters. We use the B<SSL> object for that in an 28OpenSSL application (known as the connection B<SSL> object). It is created by an 29application calling L<SSL_new(3)>. 30 31Separately a connection can have zero or more streams associated with it 32(although a connection with zero streams is probably not very useful, so 33normally you would have at least one). A stream is used to send and receive 34data between the two peers. Each stream is also represented by an B<SSL> 35object. A stream is logically independent of all the other streams associated 36with the same connection. Data sent on a stream is guaranteed to be delivered 37in the order that it was sent within that stream. The same is not true across 38streams, e.g. if an application sends data on stream 1 first and then sends some 39more data on stream 2 second, then the remote peer may receive the data sent on 40stream 2 before it receives the data sent on stream 1. 41 42Once the connection B<SSL> object has completed its handshake (i.e. 43L<SSL_connect(3)> has returned 1), stream B<SSL> objects are created by the 44application calling L<SSL_new_stream(3)> or L<SSL_accept_stream(3)> (see 45L</CREATING NEW STREAMS> below). 46 47The same threading rules apply to B<SSL> objects as for most OpenSSL objects 48(see L<ossl-guide-libraries-introduction(7)>). In particular most OpenSSL 49functions are thread safe, but the B<SSL> object is not. This means that you can 50use an B<SSL> object representing one stream at the same time as another thread 51is using a different B<SSL> object for a different stream on the same 52connection. But you cannot use the same B<SSL> object on two different threads 53at the same time (without additional application level locking). 54 55=head1 THE DEFAULT STREAM 56 57A connection B<SSL> object may also (optionally) be associated with a stream. 58This stream is known as the default stream. The default stream is automatically 59created and associated with the B<SSL> object when the application calls 60L<SSL_read_ex(3)>, L<SSL_read(3)>, L<SSL_write_ex(3)> or L<SSL_write(3)> and 61passes the connection B<SSL> object as a parameter. 62 63If a client application calls L<SSL_write_ex(3)> or L<SSL_write(3)> first then 64(by default) the default stream will be a client-initiated bi-directional 65stream. If a client application calls L<SSL_read_ex(3)> or L<SSL_read(3)> 66first then the first stream initiated by the server will be used as the default 67stream (whether it is bi-directional or uni-directional). 68 69This behaviour can be controlled via the default stream mode. See 70L<SSL_set_default_stream_mode(3)> for further details. 71 72It is recommended that new multi-stream applications should not use a default 73stream at all and instead should use a separate stream B<SSL> object for each 74stream that is used. This requires calling L<SSL_set_default_stream_mode(3)> 75and setting the mode to B<SSL_DEFAULT_STREAM_MODE_NONE>. 76 77=head1 CREATING NEW STREAMS 78 79An endpoint can create a new stream by calling L<SSL_new_stream(3)>. This 80creates a locally initiated stream. In order to do so you must pass the QUIC 81connection B<SSL> object as a parameter. You can also specify whether you want a 82bi-directional or a uni-directional stream. 83 84The function returns a new QUIC stream B<SSL> object for sending and receiving 85data on that stream. 86 87The peer may also initiate streams. An application can use the function 88L<SSL_get_accept_stream_queue_len(3)> to determine the number of streams that 89the peer has initiated that are waiting for the application to handle. An 90application can call L<SSL_accept_stream(3)> to create a new B<SSL> object for 91a remotely initiated stream. If the peer has not initiated any then this call 92will block until one is available if the connection object is in blocking mode 93(see L<SSL_set_blocking_mode(3)>). 94 95When using a default stream OpenSSL will prevent new streams from being 96accepted. To override this behaviour you must call 97L<SSL_set_incoming_stream_policy(3)> to set the policy to 98B<SSL_INCOMING_STREAM_POLICY_ACCEPT>. See the man page for further details. This 99is not relevant if the default stream has been disabled as described in 100L</THE DEFAULT STREAM> above. 101 102Any stream may be bi-directional or uni-directional. If it is uni-directional 103then the initiator can write to it but not read from it, and vice-versa for the 104peer. You can determine what type of stream an B<SSL> object represents by 105calling L<SSL_get_stream_type(3)>. See the man page for further details. 106 107=head1 USING A STREAM TO SEND AND RECEIVE DATA 108 109Once you have a stream B<SSL> object (which includes the connection B<SSL> 110object if a default stream is in use) then you can send and receive data over it 111using the L<SSL_write_ex(3)>, L<SSL_write(3)>, L<SSL_read_ex(3)> or 112L<SSL_read(3)> functions. See the man pages for further details. 113 114In the event of one of these functions not returning a success code then 115you should call L<SSL_get_error(3)> to find out further details about the error. 116In blocking mode this will either be a fatal error (e.g. B<SSL_ERROR_SYSCALL> 117or B<SSL_ERROR_SSL>), or it will be B<SSL_ERROR_ZERO_RETURN> which can occur 118when attempting to read data from a stream and the peer has indicated that the 119stream is concluded (i.e. "FIN" has been signalled on the stream). This means 120that the peer will send no more data on that stream. Note that the 121interpretation of B<SSL_ERROR_ZERO_RETURN> is slightly different for a QUIC 122application compared to a TLS application. In TLS it occurs when the connection 123has been shutdown by the peer. In QUIC this only tells you that the current 124stream has been concluded by the peer. It tells you nothing about the underlying 125connection. If the peer has concluded the stream then no more data will be 126received on it, however an application can still send data to the peer until 127the send side of the stream has also been concluded. This can happen by the 128application calling L<SSL_stream_conclude(3)>. It is an error to attempt to 129send more data on a stream after L<SSL_stream_conclude(3)> has been called. 130 131It is also possible to abandon a stream abnormally by calling 132L<SSL_stream_reset(3)>. 133 134Once a stream object is no longer needed it should be freed via a call to 135L<SSL_free(3)>. An application should not call L<SSL_shutdown(3)> on it since 136this is only meaningful for connection level B<SSL> objects. Freeing the stream 137will automatically signal STOP_SENDING to the peer. 138 139=head1 STREAMS AND CONNECTIONS 140 141Given a stream object it is possible to get the B<SSL> object corresponding to 142the connection via a call to L<SSL_get0_connection(3)>. Multi-threaded 143restrictions apply so care should be taken when using the returned connection 144object. Specifically, if you are handling each of your stream objects in a 145different thread and call L<SSL_get0_connection(3)> from within that thread then 146you must be careful to not to call any function that uses the connection object 147at the same time as one of the other threads is also using that connection 148object (with the exception of L<SSL_accept_stream(3)> and 149L<SSL_get_accept_stream_queue_len(3)> which are thread-safe). 150 151A stream object does not inherit all its settings and values from its parent 152B<SSL> connection object. Therefore certain function calls that are relevant to 153the connection as a whole will not work on a stream. For example the function 154L<SSL_get_certificate(3)> can be used to obtain a handle on the peer certificate 155when called with a connection B<SSL> object. When called with a stream B<SSL> 156object it will return NULL. 157 158=head1 SIMPLE MULTI-STREAM QUIC CLIENT EXAMPLE 159 160This section will present various source code samples demonstrating how to write 161a simple multi-stream QUIC client application which connects to a server, send 162some HTTP/1.0 requests to it, and read back the responses. Note that HTTP/1.0 163over QUIC is non-standard and will not be supported by real world servers. This 164is for demonstration purposes only. 165 166We will build on the example code for the simple blocking QUIC client that is 167covered on the L<ossl-guide-quic-client-block(7)> page and we assume that you 168are familiar with it. We will only describe the differences between the simple 169blocking QUIC client and the multi-stream QUIC client. Although the example code 170uses blocking B<SSL> objects, you can equally use nonblocking B<SSL> objects. 171See L<ossl-guide-quic-client-non-block(7)> for more information about writing a 172nonblocking QUIC client. 173 174The complete source code for this example multi-stream QUIC client is available 175in the C<demos/guide> directory of the OpenSSL source distribution in the file 176C<quic-multi-stream.c>. It is also available online at 177L<https://github.com/openssl/openssl/blob/master/demos/guide/quic-multi-stream.c>. 178 179=head2 Disabling the default stream 180 181As discussed above in L</THE DEFAULT STREAM> we will follow the recommendation 182to disable the default stream for our multi-stream client. To do this we call 183the L<SSL_set_default_stream_mode(3)> function and pass in our connection B<SSL> 184object and the value B<SSL_DEFAULT_STREAM_MODE_NONE>. 185 186 /* 187 * We will use multiple streams so we will disable the default stream mode. 188 * This is not a requirement for using multiple streams but is recommended. 189 */ 190 if (!SSL_set_default_stream_mode(ssl, SSL_DEFAULT_STREAM_MODE_NONE)) { 191 printf("Failed to disable the default stream mode\n"); 192 goto end; 193 } 194 195=head2 Creating the request streams 196 197For the purposes of this example we will create two different streams to send 198two different HTTP requests to the server. For the purposes of demonstration the 199first of these will be a bi-directional stream and the second one will be a 200uni-directional one: 201 202 /* 203 * We create two new client initiated streams. The first will be 204 * bi-directional, and the second will be uni-directional. 205 */ 206 stream1 = SSL_new_stream(ssl, 0); 207 stream2 = SSL_new_stream(ssl, SSL_STREAM_FLAG_UNI); 208 if (stream1 == NULL || stream2 == NULL) { 209 printf("Failed to create streams\n"); 210 goto end; 211 } 212 213=head2 Writing data to the streams 214 215Once the streams are successfully created we can start writing data to them. In 216this example we will be sending a different HTTP request on each stream. To 217avoid repeating too much code we write a simple helper function to send an HTTP 218request to a stream: 219 220 int write_a_request(SSL *stream, const char *request_start, 221 const char *hostname) 222 { 223 const char *request_end = "\r\n\r\n"; 224 size_t written; 225 226 if (!SSL_write_ex(stream, request_start, strlen(request_start), &written)) 227 return 0; 228 if (!SSL_write_ex(stream, hostname, strlen(hostname), &written)) 229 return 0; 230 if (!SSL_write_ex(stream, request_end, strlen(request_end), &written)) 231 return 0; 232 233 return 1; 234 } 235 236We assume the strings B<request1_start> and B<request2_start> hold the 237appropriate HTTP requests. We can then call our helper function above to send 238the requests on the two streams. For the sake of simplicity this example does 239this sequentially, writing to B<stream1> first and, when this is successful, 240writing to B<stream2> second. Remember that our client is blocking so these 241calls will only return once they have been successfully completed. A real 242application would not need to do these writes sequentially or in any particular 243order. For example we could start two threads (one for each stream) and write 244the requests to each stream simultaneously. 245 246 /* Write an HTTP GET request on each of our streams to the peer */ 247 if (!write_a_request(stream1, request1_start, hostname)) { 248 printf("Failed to write HTTP request on stream 1\n"); 249 goto end; 250 } 251 252 if (!write_a_request(stream2, request2_start, hostname)) { 253 printf("Failed to write HTTP request on stream 2\n"); 254 goto end; 255 } 256 257=head2 Reading data from a stream 258 259In this example B<stream1> is a bi-directional stream so, once we have sent the 260request on it, we can attempt to read the response from the server back. Here 261we just repeatedly call L<SSL_read_ex(3)> until that function fails (indicating 262either that there has been a problem, or that the peer has signalled the stream 263as concluded). 264 265 printf("Stream 1 data:\n"); 266 /* 267 * Get up to sizeof(buf) bytes of the response from stream 1 (which is a 268 * bidirectional stream). We keep reading until the server closes the 269 * connection. 270 */ 271 while (SSL_read_ex(stream1, buf, sizeof(buf), &readbytes)) { 272 /* 273 * OpenSSL does not guarantee that the returned data is a string or 274 * that it is NUL terminated so we use fwrite() to write the exact 275 * number of bytes that we read. The data could be non-printable or 276 * have NUL characters in the middle of it. For this simple example 277 * we're going to print it to stdout anyway. 278 */ 279 fwrite(buf, 1, readbytes, stdout); 280 } 281 /* In case the response didn't finish with a newline we add one now */ 282 printf("\n"); 283 284In a blocking application like this one calls to L<SSL_read_ex(3)> will either 285succeed immediately returning data that is already available, or they will block 286waiting for more data to become available and return it when it is, or they will 287fail with a 0 response code. 288 289Once we exit the while loop above we know that the last call to 290L<SSL_read_ex(3)> gave a 0 response code so we call the L<SSL_get_error(3)> 291function to find out more details. Since this is a blocking application this 292will either return B<SSL_ERROR_SYSCALL> or B<SSL_ERROR_SSL> indicating a 293fundamental problem, or it will return B<SSL_ERROR_ZERO_RETURN> indicating that 294the stream is concluded and there will be no more data available to read from 295it. Care must be taken to distinguish between an error at the stream level (i.e. 296a stream reset) and an error at the connection level (i.e. a connection closed). 297The L<SSL_get_stream_read_state(3)> function can be used to distinguish between 298these different cases. 299 300 /* 301 * Check whether we finished the while loop above normally or as the 302 * result of an error. The 0 argument to SSL_get_error() is the return 303 * code we received from the SSL_read_ex() call. It must be 0 in order 304 * to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN. In 305 * QUIC terms this means that the peer has sent FIN on the stream to 306 * indicate that no further data will be sent. 307 */ 308 switch (SSL_get_error(stream1, 0)) { 309 case SSL_ERROR_ZERO_RETURN: 310 /* Normal completion of the stream */ 311 break; 312 313 case SSL_ERROR_SSL: 314 /* 315 * Some stream fatal error occurred. This could be because of a stream 316 * reset - or some failure occurred on the underlying connection. 317 */ 318 switch (SSL_get_stream_read_state(stream1)) { 319 case SSL_STREAM_STATE_RESET_REMOTE: 320 printf("Stream reset occurred\n"); 321 /* The stream has been reset but the connection is still healthy. */ 322 break; 323 324 case SSL_STREAM_STATE_CONN_CLOSED: 325 printf("Connection closed\n"); 326 /* Connection is already closed. Skip SSL_shutdown() */ 327 goto end; 328 329 default: 330 printf("Unknown stream failure\n"); 331 break; 332 } 333 break; 334 335 default: 336 /* Some other unexpected error occurred */ 337 printf ("Failed reading remaining data\n"); 338 break; 339 } 340 341=head2 Accepting an incoming stream 342 343Our B<stream2> object that we created above was a uni-directional stream so it 344cannot be used to receive data from the server. In this hypothetical example 345we assume that the server initiates a new stream to send us back the data that 346we requested. To do that we call L<SSL_accept_stream(3)>. Since this is a 347blocking application this will wait indefinitely until the new stream has 348arrived and is available for us to accept. In the event of an error it will 349return B<NULL>. 350 351 /* 352 * In our hypothetical HTTP/1.0 over QUIC protocol that we are using we 353 * assume that the server will respond with a server initiated stream 354 * containing the data requested in our uni-directional stream. This doesn't 355 * really make sense to do in a real protocol, but its just for 356 * demonstration purposes. 357 * 358 * We're using blocking mode so this will block until a stream becomes 359 * available. We could override this behaviour if we wanted to by setting 360 * the SSL_ACCEPT_STREAM_NO_BLOCK flag in the second argument below. 361 */ 362 stream3 = SSL_accept_stream(ssl, 0); 363 if (stream3 == NULL) { 364 printf("Failed to accept a new stream\n"); 365 goto end; 366 } 367 368We can now read data from the stream in the same way that we did for B<stream1> 369above. We won't repeat that here. 370 371=head2 Cleaning up the streams 372 373Once we have finished using our streams we can simply free them by calling 374L<SSL_free(3)>. Optionally we could call L<SSL_stream_conclude(3)> on them if 375we want to indicate to the peer that we won't be sending them any more data, but 376we don't do that in this example because we assume that the HTTP application 377protocol supplies sufficient information for the peer to know when we have 378finished sending request data. 379 380We should not call L<SSL_shutdown(3)> or L<SSL_shutdown_ex(3)> on the stream 381objects since those calls should not be used for streams. 382 383 SSL_free(stream1); 384 SSL_free(stream2); 385 SSL_free(stream3); 386 387=head1 SEE ALSO 388 389L<ossl-guide-introduction(7)>, L<ossl-guide-libraries-introduction(7)>, 390L<ossl-guide-libssl-introduction(7)> L<ossl-guide-quic-introduction(7)>, 391L<ossl-guide-quic-client-block(7)> 392 393=head1 COPYRIGHT 394 395Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. 396 397Licensed under the Apache License 2.0 (the "License"). You may not use 398this file except in compliance with the License. You can obtain a copy 399in the file LICENSE in the source distribution or at 400L<https://www.openssl.org/source/license.html>. 401 402=cut 403