test_send_headers.c revision e2c49070
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * test_send_headers.c -- Test what happens when lsquic_stream_send_headers()
4 * is called.
5 */
6
7#include <assert.h>
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/queue.h>
13#include <sys/types.h>
14#include <fcntl.h>
15#include <limits.h>
16#ifndef WIN32
17#include <unistd.h>
18#else
19#include <getopt.h>
20#endif
21
22#include "lsquic.h"
23
24#include "lsquic_packet_common.h"
25#include "lsquic_packet_ietf.h"
26#include "lsquic_alarmset.h"
27#include "lsquic_packet_in.h"
28#include "lsquic_conn_flow.h"
29#include "lsquic_rtt.h"
30#include "lsquic_sfcw.h"
31#include "lsquic_varint.h"
32#include "lsquic_hq.h"
33#include "lsquic_hash.h"
34#include "lsquic_stream.h"
35#include "lsquic_types.h"
36#include "lsquic_malo.h"
37#include "lsquic_mm.h"
38#include "lsquic_conn_public.h"
39#include "lsquic_logger.h"
40#include "lsquic_parse.h"
41#include "lsquic_conn.h"
42#include "lsquic_engine_public.h"
43#include "lsquic_cubic.h"
44#include "lsquic_pacer.h"
45#include "lsquic_senhist.h"
46#include "lsquic_bw_sampler.h"
47#include "lsquic_minmax.h"
48#include "lsquic_bbr.h"
49#include "lsquic_adaptive_cc.h"
50#include "lsquic_send_ctl.h"
51#include "lsquic_ver_neg.h"
52#include "lsquic_packet_out.h"
53#include "lsquic_enc_sess.h"
54#include "lsqpack.h"
55#include "lsquic_frab_list.h"
56#include "lsquic_http1x_if.h"
57#include "lsquic_qdec_hdl.h"
58#include "lsquic_qenc_hdl.h"
59#include "lsquic_varint.h"
60#include "lsquic_hq.h"
61#include "lsquic_data_in_if.h"
62#include "lsquic_headers.h"
63#include "lsquic_push_promise.h"
64
65static int s_call_wantwrite_in_ctor;
66static int s_wantwrite_arg;
67static int s_onwrite_called;
68
69static lsquic_stream_ctx_t *
70on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
71{
72    if (s_call_wantwrite_in_ctor)
73        lsquic_stream_wantwrite(stream, s_wantwrite_arg);
74    return NULL;
75}
76
77
78static void
79on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
80{
81}
82
83
84static void
85on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *h)
86{
87    s_onwrite_called = 1;
88    lsquic_stream_wantwrite(stream, 0);
89}
90
91
92static struct reset_call_ctx {
93    struct lsquic_stream    *stream;
94    int                      how;
95} s_onreset_called = { NULL, -1, };
96
97
98static void
99on_reset (lsquic_stream_t *stream, lsquic_stream_ctx_t *h, int how)
100{
101    s_onreset_called = (struct reset_call_ctx) { stream, how, };
102}
103
104
105const struct lsquic_stream_if stream_if = {
106    .on_new_stream          = on_new_stream,
107    .on_write               = on_write,
108    .on_close               = on_close,
109    .on_reset               = on_reset,
110};
111
112
113enum buf_packet_type
114lsquic_send_ctl_determine_bpt (struct lsquic_send_ctl *ctl,
115                                        const struct lsquic_stream *stream)
116{
117    return BPT_HIGHEST_PRIO;
118}
119
120
121/* This function is only here to avoid crash in the test: */
122void
123lsquic_engine_add_conn_to_tickable (struct lsquic_engine_public *enpub,
124                                    lsquic_conn_t *conn)
125{
126}
127
128
129struct test_objs {
130    struct lsquic_engine_public eng_pub;
131    struct lsquic_conn        lconn;
132    struct lsquic_conn_public conn_pub;
133    struct lsquic_send_ctl    send_ctl;
134    struct lsquic_alarmset    alset;
135    void                     *stream_if_ctx;
136    struct ver_neg            ver_neg;
137    const struct lsquic_stream_if *
138                              stream_if;
139    unsigned                  initial_stream_window;
140    enum stream_ctor_flags    ctor_flags;
141    struct qpack_enc_hdl      qeh;
142    struct qpack_dec_hdl      qdh;
143};
144
145
146static int
147unit_test_doesnt_write_ack (struct lsquic_conn *lconn)
148{
149    return 0;
150}
151
152
153static struct network_path network_path;
154
155static struct network_path *
156get_network_path (struct lsquic_conn *lconn, const struct sockaddr *sa)
157{
158    return &network_path;
159}
160
161static void
162abort_error (struct lsquic_conn *lconn, int is_app,
163                                unsigned error_code, const char *fmt, ...)
164{
165}
166
167static const struct conn_iface our_conn_if =
168{
169    .ci_can_write_ack = unit_test_doesnt_write_ack,
170    .ci_get_path      = get_network_path,
171    .ci_abort_error   = abort_error,
172};
173
174
175static struct http1x_ctor_ctx ctor_ctx = { .is_server = 0, };
176
177#if LSQUIC_CONN_STATS
178static struct conn_stats s_conn_stats;
179#endif
180
181static void
182init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window,
183        unsigned initial_stream_window, enum stream_ctor_flags addl_ctor_flags)
184{
185    int s;
186    memset(tobjs, 0, sizeof(*tobjs));
187    LSCONN_INITIALIZE(&tobjs->lconn);
188    tobjs->lconn.cn_pf = select_pf_by_ver(LSQVER_ID27);
189    tobjs->lconn.cn_version = LSQVER_ID27;
190    tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1;
191    network_path.np_pack_size = IQUIC_MAX_IPv4_PACKET_SZ;
192    tobjs->lconn.cn_if = &our_conn_if;
193    lsquic_mm_init(&tobjs->eng_pub.enp_mm);
194    TAILQ_INIT(&tobjs->conn_pub.sending_streams);
195    TAILQ_INIT(&tobjs->conn_pub.read_streams);
196    TAILQ_INIT(&tobjs->conn_pub.write_streams);
197    TAILQ_INIT(&tobjs->conn_pub.service_streams);
198    lsquic_cfcw_init(&tobjs->conn_pub.cfcw, &tobjs->conn_pub,
199                                                    initial_conn_window);
200    lsquic_conn_cap_init(&tobjs->conn_pub.conn_cap, initial_conn_window);
201    lsquic_alarmset_init(&tobjs->alset, 0);
202    tobjs->conn_pub.mm = &tobjs->eng_pub.enp_mm;
203    tobjs->conn_pub.lconn = &tobjs->lconn;
204    tobjs->conn_pub.enpub = &tobjs->eng_pub;
205    tobjs->conn_pub.send_ctl = &tobjs->send_ctl;
206    tobjs->conn_pub.packet_out_malo =
207                        lsquic_malo_create(sizeof(struct lsquic_packet_out));
208    tobjs->conn_pub.path = &network_path;
209#if LSQUIC_CONN_STATS
210    tobjs->conn_pub.conn_stats = &s_conn_stats;
211#endif
212    tobjs->initial_stream_window = initial_stream_window;
213    lsquic_send_ctl_init(&tobjs->send_ctl, &tobjs->alset, &tobjs->eng_pub,
214        &tobjs->ver_neg, &tobjs->conn_pub, 0);
215    tobjs->stream_if = &stream_if;
216    tobjs->stream_if_ctx = NULL;
217    tobjs->ctor_flags = SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH|SCF_HTTP
218                      |addl_ctor_flags;
219    if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
220    {
221        lsquic_qeh_init(&tobjs->qeh, &tobjs->lconn);
222        s = lsquic_qeh_settings(&tobjs->qeh, 0, 0, 0, 0);
223        assert(0 == s);
224        tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh;
225        tobjs->conn_pub.enpub->enp_hsi_if  = lsquic_http1x_if;
226        tobjs->conn_pub.enpub->enp_hsi_ctx = &ctor_ctx;
227        s = lsquic_qdh_init(&tobjs->qdh, &tobjs->lconn, 0,
228                                    tobjs->conn_pub.enpub, 0, 0);
229        tobjs->conn_pub.u.ietf.qdh = &tobjs->qdh;
230        assert(0 == s);
231    }
232}
233
234
235static void
236deinit_test_objs (struct test_objs *tobjs)
237{
238    assert(!lsquic_malo_first(tobjs->eng_pub.enp_mm.malo.stream_frame));
239    lsquic_send_ctl_cleanup(&tobjs->send_ctl);
240    lsquic_malo_destroy(tobjs->conn_pub.packet_out_malo);
241    lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm);
242    if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS)
243    {
244        lsquic_qeh_cleanup(&tobjs->qeh);
245        lsquic_qdh_cleanup(&tobjs->qdh);
246    }
247}
248
249
250static struct lsquic_stream *
251new_stream (struct test_objs *tobjs, unsigned stream_id, uint64_t send_off)
252{
253    return lsquic_stream_new(stream_id, &tobjs->conn_pub, tobjs->stream_if,
254        tobjs->stream_if_ctx, tobjs->initial_stream_window, send_off,
255        tobjs->ctor_flags);
256}
257
258static struct test_vals {
259    /* What lsquic_qeh_write_headers() returns or sets */
260    enum qwh_status     status;
261    size_t              prefix_sz;
262    size_t              headers_sz;
263    uint64_t            completion_offset;
264} test_vals;
265
266
267enum qwh_status
268lsquic_qeh_write_headers (struct qpack_enc_hdl *qeh,
269    lsquic_stream_id_t stream_id, unsigned seqno,
270    const struct lsquic_http_headers *headers, unsigned char *buf,
271    size_t *prefix_sz, size_t *headers_sz, uint64_t *completion_offset,
272    enum lsqpack_enc_header_flags *hflags)
273{
274    memset(buf - *prefix_sz, 0xC5, *prefix_sz + *headers_sz);
275    *prefix_sz = test_vals.prefix_sz;
276    *headers_sz = test_vals.headers_sz;
277    *completion_offset = test_vals.completion_offset;
278    if (hflags)
279        *hflags = 0;
280    return test_vals.status;
281}
282
283
284static uint64_t s_enc_off;
285
286uint64_t
287lsquic_qeh_enc_off (struct qpack_enc_hdl *qeh)
288{
289    return s_enc_off;
290}
291
292
293static void
294test_flushes_and_closes (void)
295{
296    struct test_objs tobjs;
297    struct lsquic_stream *stream;
298    ssize_t nw;
299    int s;
300    struct uncompressed_headers *uh;
301    void *hset;
302
303    /* For our tests purposes, we treat headers as an opaque object */
304    struct lsquic_http_headers *headers = (void *) 1;
305
306    init_test_objs(&tobjs, 0x1000, 0x1000, SCF_IETF);
307
308    stream = new_stream(&tobjs, 0, 0x1000);
309    test_vals.status = QWH_FULL;
310    test_vals.prefix_sz = 2;
311    test_vals.headers_sz = 40;
312    test_vals.completion_offset = 0;
313    s = lsquic_stream_send_headers(stream, headers, 0);
314    assert(0 == s);
315    assert(stream->sm_n_buffered == test_vals.prefix_sz + test_vals.headers_sz);
316    assert(0 == stream->sm_hblock_sz);
317    lsquic_stream_destroy(stream);
318
319    stream = new_stream(&tobjs, 4, 0x1000);
320    test_vals.status = QWH_PARTIAL;
321    test_vals.prefix_sz = 2;
322    test_vals.headers_sz = 40;
323    test_vals.completion_offset = 10;
324    s = lsquic_stream_send_headers(stream, headers, 0);
325    assert(0 == s);
326    assert(stream->sm_hblock_sz == test_vals.prefix_sz + test_vals.headers_sz);
327    assert(0 == stream->sm_n_buffered);
328    nw = lsquic_stream_write(stream, "hello", 5);
329    assert(0 == nw);
330    s = lsquic_stream_flush(stream);
331    assert(s == 0);
332    lsquic_stream_destroy(stream);
333
334    /* Mock server side stream cycle */
335    stream = new_stream(&tobjs, 8, 0x1000);
336    uh = calloc(1, sizeof(*uh));
337    *uh = (struct uncompressed_headers) {
338        .uh_stream_id   = stream->id,
339        .uh_weight      = 127,
340        .uh_hset        = (void *) 12345,
341    };
342    s = lsquic_stream_uh_in(stream, uh);
343    assert(s == 0);
344    hset = lsquic_stream_get_hset(stream);
345    assert(hset == (void *) 12345);
346    s = lsquic_stream_shutdown(stream, 0);
347    assert(0 == s);
348    test_vals.status = QWH_PARTIAL;
349    test_vals.prefix_sz = 2;
350    test_vals.headers_sz = 40;
351    test_vals.completion_offset = 10;
352    assert(!(stream->sm_qflags & SMQF_WANT_WRITE)); /* Begin with them off */
353    s = lsquic_stream_send_headers(stream, headers, 0);
354    assert(0 == s);
355    assert(stream->sm_hblock_sz == test_vals.prefix_sz + test_vals.headers_sz);
356    assert(0 == stream->sm_n_buffered);
357    assert(stream->sm_qflags & SMQF_WANT_WRITE);    /* Want write is now set */
358    nw = lsquic_stream_write(stream, "hello", 5);
359    assert(0 == nw);
360    s = lsquic_stream_flush(stream);
361    assert(s == 0);
362    s = lsquic_stream_close(stream);
363    assert(s == 0);
364    /* OK, we did not read FIN, expect these flags: */
365    assert((stream->sm_qflags & (SMQF_SEND_STOP_SENDING|SMQF_WAIT_FIN_OFF)) == (SMQF_SEND_STOP_SENDING|SMQF_WAIT_FIN_OFF));
366    lsquic_stream_ss_frame_sent(stream);
367    assert(stream->sm_hblock_sz == test_vals.prefix_sz + test_vals.headers_sz);
368    assert(0 == stream->sm_n_buffered);
369    assert(stream->sm_qflags & SMQF_WANT_WRITE);    /* Still set */
370    s_enc_off = 10;   /* Encoder is done writing */
371    lsquic_stream_dispatch_write_events(stream);
372    assert(stream->sm_qflags & SMQF_CALL_ONCLOSE);
373    lsquic_stream_acked(stream, QUIC_FRAME_STREAM);
374    lsquic_stream_call_on_close(stream);
375    assert(!(stream->sm_qflags & SMQF_FREE_STREAM));    /* Not yet */
376    s_onreset_called = (struct reset_call_ctx) { NULL, -1, };
377    lsquic_stream_rst_in(stream, 0, 0);
378    assert(s_onreset_called.stream == NULL);
379    assert(s_onreset_called.how == -1);
380    assert(!(stream->sm_qflags & (SMQF_SEND_STOP_SENDING|SMQF_WAIT_FIN_OFF)));
381    assert(stream->sm_qflags & SMQF_FREE_STREAM);
382    lsquic_stream_destroy(stream);
383
384    deinit_test_objs(&tobjs);
385}
386
387
388static void
389test_headers_wantwrite_restoration (const int want_write)
390{
391    struct test_objs tobjs;
392    struct lsquic_stream *stream;
393    ssize_t nw;
394    int s;
395    struct uncompressed_headers *uh;
396    void *hset;
397
398    s_call_wantwrite_in_ctor = 1;
399    s_wantwrite_arg = want_write;
400
401    /* For our tests purposes, we treat headers as an opaque object */
402    struct lsquic_http_headers *headers = (void *) 1;
403
404    init_test_objs(&tobjs, 0x1000, 0x1000, SCF_IETF);
405
406    /* Mock server side stream cycle */
407
408    stream = new_stream(&tobjs, 4 * __LINE__, 0x1000);
409    uh = calloc(1, sizeof(*uh));
410    *uh = (struct uncompressed_headers) {
411        .uh_stream_id   = stream->id,
412        .uh_weight      = 127,
413        .uh_hset        = (void *) 12345,
414    };
415    s = lsquic_stream_uh_in(stream, uh);
416    assert(s == 0);
417    hset = lsquic_stream_get_hset(stream);
418    assert(hset == (void *) 12345);
419    stream->stream_flags |= STREAM_FIN_RECVD;   /* Pretend we received FIN */
420    s = lsquic_stream_shutdown(stream, 0);
421    assert(0 == s);
422    test_vals.status = QWH_PARTIAL;
423    test_vals.prefix_sz = 2;
424    test_vals.headers_sz = 40;
425    test_vals.completion_offset = 10;
426    assert(want_write == !!(stream->sm_qflags & SMQF_WANT_WRITE));
427    s = lsquic_stream_send_headers(stream, headers, 0);
428    assert(0 == s);
429    assert(stream->sm_hblock_sz == test_vals.prefix_sz + test_vals.headers_sz);
430    assert(0 == stream->sm_n_buffered);
431    assert(stream->sm_qflags & SMQF_WANT_WRITE);    /* Want write is now set */
432    nw = lsquic_stream_write(stream, "hello", 5);
433    assert(0 == nw);
434    s = lsquic_stream_flush(stream);
435    assert(s == 0);
436    s = lsquic_stream_close(stream);
437    assert(s == 0);
438    assert(stream->sm_hblock_sz == test_vals.prefix_sz + test_vals.headers_sz);
439    assert(0 == stream->sm_n_buffered);
440    assert(stream->sm_qflags & SMQF_WANT_WRITE);    /* Still set */
441    s_enc_off = 10;   /* Encoder is done writing */
442    lsquic_stream_dispatch_write_events(stream);
443    assert(stream->sm_qflags & SMQF_CALL_ONCLOSE);
444    lsquic_stream_acked(stream, QUIC_FRAME_STREAM);
445    lsquic_stream_call_on_close(stream);
446    assert(stream->sm_qflags & SMQF_FREE_STREAM);
447    lsquic_stream_destroy(stream);
448
449    stream = new_stream(&tobjs, 4 * __LINE__, 0x1000);
450    uh = calloc(1, sizeof(*uh));
451    *uh = (struct uncompressed_headers) {
452        .uh_stream_id   = stream->id,
453        .uh_weight      = 127,
454        .uh_hset        = (void *) 12345,
455    };
456    s = lsquic_stream_uh_in(stream, uh);
457    assert(s == 0);
458    hset = lsquic_stream_get_hset(stream);
459    assert(hset == (void *) 12345);
460    s = lsquic_stream_shutdown(stream, 0);
461    assert(0 == s);
462    test_vals.status = QWH_PARTIAL;
463    test_vals.prefix_sz = 2;
464    test_vals.headers_sz = 40;
465    test_vals.completion_offset = 10;
466    assert(want_write == !!(stream->sm_qflags & SMQF_WANT_WRITE));
467    s = lsquic_stream_send_headers(stream, headers, 0);
468    assert(0 == s);
469    assert(stream->sm_hblock_sz == test_vals.prefix_sz + test_vals.headers_sz);
470    assert(0 == stream->sm_n_buffered);
471    assert(stream->sm_qflags & SMQF_WANT_WRITE);    /* Want write is now set */
472    s_enc_off = 10;   /* Encoder is done writing */
473    lsquic_stream_dispatch_write_events(stream);
474    assert(0 == stream->sm_hblock_sz);  /* Wrote header */
475    assert(want_write == s_onwrite_called);
476    lsquic_stream_destroy(stream);
477
478    deinit_test_objs(&tobjs);
479    s_call_wantwrite_in_ctor = 0;
480    s_wantwrite_arg = 0;
481    s_onwrite_called = 0;
482}
483
484
485static void
486test_pp_wantwrite_restoration (const int want_write)
487{
488    struct test_objs tobjs;
489    struct lsquic_stream *stream;
490    int s;
491    struct uncompressed_headers *uh;
492    struct push_promise *promise;
493    void *hset;
494
495    s_call_wantwrite_in_ctor = 1;
496    s_wantwrite_arg = want_write;
497
498    init_test_objs(&tobjs, 0x1000, 0x1000, SCF_IETF);
499
500    /* Mock server side stream cycle */
501
502    stream = new_stream(&tobjs, 4 * __LINE__, 10);
503    uh = calloc(1, sizeof(*uh));
504    *uh = (struct uncompressed_headers) {
505        .uh_stream_id   = stream->id,
506        .uh_weight      = 127,
507        .uh_hset        = (void *) 12345,
508    };
509    s = lsquic_stream_uh_in(stream, uh);
510    assert(s == 0);
511    hset = lsquic_stream_get_hset(stream);
512    assert(hset == (void *) 12345);
513    s = lsquic_stream_shutdown(stream, 0);
514    assert(0 == s);
515    promise = calloc(1, sizeof(*promise) + 20);
516    promise->pp_id = 0;
517    promise->pp_content_len = 20;
518    assert(want_write == !!(stream->sm_qflags & SMQF_WANT_WRITE));
519    s = lsquic_stream_push_promise(stream, promise);
520    assert(s == 0);
521    assert((stream->stream_flags & (STREAM_NOPUSH|STREAM_PUSHING))
522                                        == (STREAM_NOPUSH|STREAM_PUSHING));
523    assert(stream->sm_qflags & SMQF_WANT_WRITE);    /* Want write is now set */
524    /* Dispatch: there should be no progress made */
525    lsquic_stream_dispatch_write_events(stream);
526    assert((stream->stream_flags & (STREAM_NOPUSH|STREAM_PUSHING))
527                                        == (STREAM_NOPUSH|STREAM_PUSHING));
528    assert(stream->sm_qflags & SMQF_WANT_WRITE);
529    assert(SLIST_FIRST(&stream->sm_promises)->pp_write_state != PPWS_DONE);
530    /* Now update window and dispatch again */
531    lsquic_stream_window_update(stream, 100);
532    lsquic_stream_dispatch_write_events(stream);
533    assert((stream->stream_flags & (STREAM_NOPUSH|STREAM_PUSHING))
534                                        == (STREAM_NOPUSH|STREAM_PUSHING));
535    assert(SLIST_FIRST(&stream->sm_promises)->pp_write_state == PPWS_DONE); /* Done! */
536    assert(want_write == s_onwrite_called); /* Restored: and on_write called */
537
538    lsquic_stream_destroy(stream);
539    deinit_test_objs(&tobjs);
540    s_call_wantwrite_in_ctor = 0;
541    s_wantwrite_arg = 0;
542    s_onwrite_called = 0;
543}
544
545
546/* Create a new stream frame.  Each stream frame has a real packet_in to
547 * back it up, just like in real code.  The contents of the packet do
548 * not matter.
549 */
550static stream_frame_t *
551new_frame_in_ext (struct test_objs *tobjs, size_t off, size_t sz, int fin,
552                                                            const void *data)
553{
554    lsquic_packet_in_t *packet_in;
555    stream_frame_t *frame;
556
557    assert(sz <= 1370);
558
559    packet_in = lsquic_mm_get_packet_in(&tobjs->eng_pub.enp_mm);
560    if (data)
561        packet_in->pi_data = (void *) data;
562    else
563    {
564        packet_in->pi_data = lsquic_mm_get_packet_in_buf(&tobjs->eng_pub.enp_mm, 1370);
565        packet_in->pi_flags |= PI_OWN_DATA;
566        memset(packet_in->pi_data, 'A', sz);
567    }
568    /* This is not how stream frame looks in the packet: we have no
569     * header.  In our test case it does not matter, as we only care
570     * about stream frame.
571     */
572    packet_in->pi_data_sz = sz;
573    packet_in->pi_refcnt = 1;
574
575    frame = lsquic_malo_get(tobjs->eng_pub.enp_mm.malo.stream_frame);
576    memset(frame, 0, sizeof(*frame));
577    frame->packet_in = packet_in;
578    frame->data_frame.df_offset = off;
579    frame->data_frame.df_size = sz;
580    frame->data_frame.df_data = &packet_in->pi_data[0];
581    frame->data_frame.df_fin  = fin;
582
583    return frame;
584}
585
586
587static stream_frame_t *
588new_frame_in (struct test_objs *tobjs, size_t off, size_t sz, int fin)
589{
590    return new_frame_in_ext(tobjs, off, sz, fin, NULL);
591}
592
593
594/* Test that reading from stream returns -1/EWOULDBLOCK if no headers are
595 * available.
596 */
597static void
598test_read_headers (int ietf, int use_hset)
599{
600    struct test_objs tobjs;
601    struct lsquic_stream *stream;
602    struct stream_frame *frame;
603    ssize_t nr;
604    int s;
605    void *hset;
606    unsigned char buf[1];
607
608    init_test_objs(&tobjs, 0x1000, 0x1000, ietf ? SCF_IETF : 0);
609
610    stream = new_stream(&tobjs, 0, 0x1000);
611    frame = new_frame_in(&tobjs, 0, 35, 1);
612    s = lsquic_stream_frame_in(stream, frame);
613    assert(s == 0);
614
615    if (use_hset)
616    {
617        hset = lsquic_stream_get_hset(stream);
618        assert(NULL == hset);
619    }
620    else
621    {
622        nr = lsquic_stream_read(stream, buf, sizeof(buf));
623        assert(-1 == nr);
624        /* In GQUIC mode, the error is that the headers are no available yet.
625         * In IETF mode, the error is that we hit EOF unexpectedly -- as headers
626         * are sent on the same stream in HEADERS frame.
627         */
628        if (!ietf)
629            assert(EWOULDBLOCK == errno);
630    }
631
632    lsquic_stream_destroy(stream);
633
634    deinit_test_objs(&tobjs);
635}
636
637
638static void
639test_read_headers_http1x (void)
640{
641    struct test_objs tobjs;
642    struct lsquic_stream *stream;
643    struct stream_frame *frame;
644    int s;
645    const unsigned char headers_frame[5] = {
646        0x01,   /* Headers frame */
647        0x03,   /* Frame length */
648        0x00,
649        0x00,
650        0xC0 | 25   /* :status 200 */,
651    };
652    ssize_t nr;
653    unsigned char buf[0x100];
654
655    init_test_objs(&tobjs, 0x1000, 0x1000, SCF_IETF);
656
657    stream = new_stream(&tobjs, 0, 0x1000);
658    frame = new_frame_in(&tobjs, 0, sizeof(headers_frame), 1);
659    memcpy((unsigned char *) frame->data_frame.df_data, headers_frame,
660                                                    sizeof(headers_frame));
661    s = lsquic_stream_frame_in(stream, frame);
662    assert(s == 0);
663
664    assert(stream->stream_flags & STREAM_FIN_REACHED);
665    s = lsquic_stream_readable(stream);
666
667    nr = lsquic_stream_read(stream, buf, sizeof(buf));
668    assert(nr > 0);
669    assert(nr == 19);
670    assert(0 == memcmp(buf, "HTTP/1.1 200 OK\r\n\r\n", nr));
671
672    lsquic_stream_destroy(stream);
673
674    deinit_test_objs(&tobjs);
675}
676
677
678int
679main (int argc, char **argv)
680{
681    int opt;
682
683    lsquic_global_init(LSQUIC_GLOBAL_SERVER);
684
685    while (-1 != (opt = getopt(argc, argv, "l:")))
686    {
687        switch (opt)
688        {
689        case 'l':
690            lsquic_log_to_fstream(stderr, 0);
691            lsquic_logger_lopt(optarg);
692            break;
693        default:
694            exit(1);
695        }
696    }
697
698    test_flushes_and_closes();
699    test_headers_wantwrite_restoration(0);
700    test_headers_wantwrite_restoration(1);
701    test_pp_wantwrite_restoration(0);
702    test_pp_wantwrite_restoration(1);
703    test_read_headers(0, 0);
704    test_read_headers(0, 1);
705    test_read_headers(1, 0);
706    test_read_headers(1, 1);
707    test_read_headers_http1x();
708
709    return 0;
710}
711