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