lsquic_headers_stream.c revision fb3e20e0
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * HEADERS stream logic
4 */
5
6#include <assert.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <stdarg.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/queue.h>
13#ifdef MSVC
14#include <vc_compat.h>
15#endif
16
17#include "lsquic.h"
18#include "lsquic_types.h"
19#include "lsquic_int_types.h"
20#include "lsquic_frame_common.h"
21#include "lsquic_frame_reader.h"
22#include "lsquic_frame_writer.h"
23#include "lsquic_mm.h"
24#include "lsquic_engine_public.h"
25#include "lshpack.h"
26
27#include "lsquic_headers_stream.h"
28
29#define MAX_HEADERS_SIZE (64 * 1024)
30#define MAX_HEADER_TABLE_SIZE (512 * 1024)
31
32#define LSQUIC_LOGGER_MODULE LSQLM_HEADERS
33#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
34                                        lsquic_stream_conn(hs->hs_stream))
35#include "lsquic_logger.h"
36
37static const struct frame_reader_callbacks *frame_callbacks_ptr;
38
39struct headers_stream
40{
41    struct lsquic_stream               *hs_stream;
42    struct lsquic_frame_reader         *hs_fr;
43    struct lsquic_frame_writer         *hs_fw;
44    const struct headers_stream_callbacks
45                                       *hs_callbacks;
46    void                               *hs_cb_ctx;
47    struct lshpack_enc                  hs_henc;
48    struct lshpack_dec                  hs_hdec;
49    enum {
50            HS_IS_SERVER    = (1 << 0),
51            HS_HENC_INITED  = (1 << 1),
52    }                                   hs_flags;
53    struct lsquic_engine_public        *hs_enpub;
54#if LSQUIC_CONN_STATS
55    struct conn_stats                  *hs_conn_stats;
56#endif
57};
58
59
60int
61lsquic_headers_stream_send_settings (struct headers_stream *hs,
62        const struct lsquic_http2_setting *settings, unsigned n_settings)
63{
64    if (0 == lsquic_frame_writer_write_settings(hs->hs_fw, settings,
65                                                            n_settings))
66    {
67        lsquic_stream_wantwrite(hs->hs_stream,
68            lsquic_frame_writer_have_leftovers(hs->hs_fw));
69        return 0;
70    }
71    else
72    {
73        LSQ_WARN("Could not write settings to stream: %s",
74                                                        strerror(errno));
75        return -1;
76    }
77}
78
79
80static lsquic_stream_ctx_t *
81headers_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
82{
83    struct headers_stream *hs = stream_if_ctx;
84
85    lshpack_dec_init(&hs->hs_hdec);
86    if (0 != lshpack_enc_init(&hs->hs_henc))
87    {
88        LSQ_WARN("could not initialize HPACK encoder: %s", strerror(errno));
89        return NULL;
90    }
91    (void) lshpack_enc_use_hist(&hs->hs_henc, 1);
92    hs->hs_flags |= HS_HENC_INITED;
93    hs->hs_stream = stream;
94    LSQ_DEBUG("stream created");
95    hs->hs_fr = lsquic_frame_reader_new((hs->hs_flags & HS_IS_SERVER) ? FRF_SERVER : 0,
96                                MAX_HEADERS_SIZE, &hs->hs_enpub->enp_mm,
97                                stream, lsquic_stream_read, &hs->hs_hdec,
98                                frame_callbacks_ptr, hs,
99#if LSQUIC_CONN_STATS
100                        hs->hs_conn_stats,
101#endif
102                        hs->hs_enpub->enp_hsi_if, hs->hs_enpub->enp_hsi_ctx);
103    if (!hs->hs_fr)
104    {
105        LSQ_WARN("could not create frame reader: %s", strerror(errno));
106        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
107        return NULL;
108    }
109    hs->hs_fw = lsquic_frame_writer_new(&hs->hs_enpub->enp_mm, stream, 0,
110            &hs->hs_henc, lsquic_stream_writef,
111#if LSQUIC_CONN_STATS
112            hs->hs_conn_stats,
113#endif
114            (hs->hs_flags & HS_IS_SERVER));
115    if (!hs->hs_fw)
116    {
117        LSQ_WARN("could not create frame writer: %s", strerror(errno));
118        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
119        return NULL;
120    }
121    lsquic_stream_wantread(stream, 1);
122    return (lsquic_stream_ctx_t *) hs;
123}
124
125
126static void
127headers_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
128{
129    struct headers_stream *hs = (struct headers_stream *) ctx;
130    if (0 != lsquic_frame_reader_read(hs->hs_fr))
131    {
132        LSQ_ERROR("frame reader failed");
133        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
134    }
135}
136
137
138static void
139headers_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
140{
141    struct headers_stream *hs = (struct headers_stream *) ctx;
142    assert(lsquic_frame_writer_have_leftovers(hs->hs_fw));
143    int s = lsquic_frame_writer_flush(hs->hs_fw);
144    if (0 == s)
145    {
146        LSQ_DEBUG("flushed");
147        lsquic_stream_wantwrite(stream,
148            lsquic_frame_writer_have_leftovers(hs->hs_fw));
149    }
150    else
151    {
152        LSQ_WARN("Error writing to stream: %s", strerror(errno));
153        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
154    }
155}
156
157
158static void
159headers_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
160{
161    struct headers_stream *hs = (struct headers_stream *) ctx;
162    LSQ_DEBUG("stream closed");
163}
164
165
166int
167lsquic_headers_stream_send_headers (struct headers_stream *hs,
168    lsquic_stream_id_t stream_id, const struct lsquic_http_headers *headers,
169    int eos, unsigned weight)
170{
171    LSQ_DEBUG("received compressed headers to send");
172    int s;
173    s = lsquic_frame_writer_write_headers(hs->hs_fw, stream_id, headers, eos,
174                                                                        weight);
175    if (0 == s)
176    {
177        lsquic_stream_wantwrite(hs->hs_stream,
178            lsquic_frame_writer_have_leftovers(hs->hs_fw));
179    }
180    else
181        LSQ_INFO("Error writing headers: %s", strerror(errno));
182    return s;
183}
184
185
186int
187lsquic_headers_stream_send_priority (struct headers_stream *hs,
188        lsquic_stream_id_t stream_id, int exclusive,
189        lsquic_stream_id_t dep_stream_id, unsigned weight)
190{
191    LSQ_DEBUG("received priority to send");
192    int s;
193    if (stream_id == dep_stream_id)
194    {
195        LSQ_INFO("stream cannot depend on itself"); /* RFC 7540, Sec. 5.3.1. */
196        return -1;
197    }
198    s = lsquic_frame_writer_write_priority(hs->hs_fw, stream_id, exclusive,
199                                                        dep_stream_id, weight);
200    if (0 == s)
201    {
202        lsquic_stream_wantwrite(hs->hs_stream,
203            lsquic_frame_writer_have_leftovers(hs->hs_fw));
204    }
205    else
206        LSQ_INFO("Error writing priority frame: %s", strerror(errno));
207    return s;
208}
209
210
211struct headers_stream *
212lsquic_headers_stream_new (int is_server, struct lsquic_engine_public *enpub,
213                           const struct headers_stream_callbacks *callbacks,
214#if LSQUIC_CONN_STATS
215                           struct conn_stats *conn_stats,
216#endif
217                           void *cb_ctx)
218{
219    struct headers_stream *hs = calloc(1, sizeof(*hs));
220    if (!hs)
221        return NULL;
222    hs->hs_callbacks = callbacks;
223    hs->hs_cb_ctx    = cb_ctx;
224    if (is_server)
225        hs->hs_flags = HS_IS_SERVER;
226    else
227        hs->hs_flags = 0;
228    hs->hs_enpub     = enpub;
229#if LSQUIC_CONN_STATS
230    hs->hs_conn_stats= conn_stats;
231#endif
232    return hs;
233}
234
235
236void
237lsquic_headers_stream_destroy (struct headers_stream *hs)
238{
239    if (hs->hs_fr)
240        lsquic_frame_reader_destroy(hs->hs_fr);
241    if (hs->hs_fw)
242        lsquic_frame_writer_destroy(hs->hs_fw);
243    if (hs->hs_flags & HS_HENC_INITED)
244        lshpack_enc_cleanup(&hs->hs_henc);
245    lshpack_dec_cleanup(&hs->hs_hdec);
246    free(hs);
247}
248
249
250static const struct lsquic_stream_if headers_stream_if =
251{
252    .on_new_stream = headers_on_new_stream,
253    .on_read       = headers_on_read,
254    .on_write      = headers_on_write,
255    .on_close      = headers_on_close,
256};
257
258
259const struct lsquic_stream_if *const lsquic_headers_stream_if =
260                                                &headers_stream_if;
261
262
263static void
264headers_on_incoming_headers (void *ctx, struct uncompressed_headers *uh)
265{
266    struct headers_stream *hs = ctx;
267    hs->hs_callbacks->hsc_on_headers(hs->hs_cb_ctx, uh);
268}
269
270
271static void
272headers_on_push_promise (void *ctx, struct uncompressed_headers *uh)
273{
274    struct headers_stream *hs = ctx;
275    hs->hs_callbacks->hsc_on_push_promise(hs->hs_cb_ctx, uh);
276}
277
278
279static void
280headers_on_priority (void *ctx, lsquic_stream_id_t stream_id, int exclusive,
281                     lsquic_stream_id_t dep_stream_id, unsigned weight)
282{
283    struct headers_stream *hs = ctx;
284    hs->hs_callbacks->hsc_on_priority(hs->hs_cb_ctx, stream_id, exclusive,
285                                                    dep_stream_id, weight);
286}
287
288
289static void
290headers_on_error (void *ctx, lsquic_stream_id_t stream_id,
291                                            enum frame_reader_error err)
292{
293    struct headers_stream *hs = ctx;
294    switch (err)
295    {
296    case FR_ERR_BAD_HEADER:
297    case FR_ERR_DECOMPRESS:
298    case FR_ERR_SELF_DEP_STREAM:
299        LSQ_INFO("error %u is a stream error (stream %"PRIu64")", err,
300                                                                    stream_id);
301        hs->hs_callbacks->hsc_on_stream_error(hs->hs_cb_ctx, stream_id);
302        break;
303    case FR_ERR_INVALID_FRAME_SIZE:
304    case FR_ERR_NONZERO_STREAM_ID:
305    case FR_ERR_UNEXPECTED_PUSH:
306    case FR_ERR_ZERO_STREAM_ID:
307    case FR_ERR_EXPECTED_CONTIN:
308    case FR_ERR_OTHER_ERROR:
309        LSQ_INFO("error %u is a connection error (stream %"PRIu64")", err,
310                                                                    stream_id);
311        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
312        break;
313    }
314}
315
316
317static void
318headers_on_settings (void *ctx, uint16_t setting_id, uint32_t setting_value)
319{
320    struct headers_stream *hs = ctx;
321    switch (setting_id)
322    {
323    case SETTINGS_HEADER_TABLE_SIZE:
324        if (setting_value > MAX_HEADER_TABLE_SIZE)
325        {
326            LSQ_INFO("tried to update table size to %u, which is larger than "
327                "allowed maximum of %u bytes", setting_value,
328                MAX_HEADER_TABLE_SIZE);
329            hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
330        }
331        else
332        {
333            LSQ_INFO("update hpack table size to %u", setting_value);
334            lshpack_enc_set_max_capacity(&hs->hs_henc, setting_value);
335        }
336        break;
337    case SETTINGS_MAX_HEADER_LIST_SIZE:
338        LSQ_INFO("set max header list size to %u", setting_value);
339        lsquic_frame_writer_max_header_list_size(hs->hs_fw, setting_value);
340        break;
341    case SETTINGS_ENABLE_PUSH:
342        LSQ_INFO("got setting enable_push: %u", setting_value);
343        if (hs->hs_flags & HS_IS_SERVER)
344        {
345            if (setting_value <= 1)
346                hs->hs_callbacks->hsc_on_enable_push(hs->hs_cb_ctx,
347                                                            setting_value);
348            else
349            {
350                LSQ_INFO("invalid value of enable_push");
351                hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
352            }
353        }
354        else
355        {
356            LSQ_INFO("it is an error to receive enable_push setting in "
357                     "client mode");
358            hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
359        }
360        break;
361    case SETTINGS_MAX_CONCURRENT_STREAMS:
362    case SETTINGS_INITIAL_WINDOW_SIZE:
363    case SETTINGS_MAX_FRAME_SIZE:
364        /* [draft-ietf-quic-http-00], Section 3 */
365        LSQ_INFO("Specifying setting 0x%X is a QUIC error", setting_id);
366        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
367        break;
368    default:
369        LSQ_INFO("Ignoring unknown setting 0x%X; value 0x%X", setting_id,
370                                                                setting_value);
371        break;
372    }
373}
374
375
376int
377lsquic_headers_stream_push_promise (struct headers_stream *hs,
378        lsquic_stream_id_t stream_id64, lsquic_stream_id_t promised_stream_id64,
379        const struct lsquic_http_headers *headers)
380{
381    uint32_t stream_id = stream_id64;
382    uint32_t promised_stream_id = promised_stream_id64;
383    int s;
384    LSQ_DEBUG("promising stream %u in response to stream %u",
385                                            promised_stream_id, stream_id);
386    s = lsquic_frame_writer_write_promise(hs->hs_fw, stream_id,
387                                                promised_stream_id, headers);
388    if (0 == s)
389    {
390        lsquic_stream_wantwrite(hs->hs_stream,
391            lsquic_frame_writer_have_leftovers(hs->hs_fw));
392    }
393    else
394        LSQ_INFO("Error writing push promise: %s", strerror(errno));
395    return s;
396}
397
398
399size_t
400lsquic_headers_stream_mem_used (const struct headers_stream *hs)
401{
402    size_t size;
403
404    size = sizeof(*hs);
405    size += lsquic_frame_reader_mem_used(hs->hs_fr);
406    size += lsquic_frame_writer_mem_used(hs->hs_fw);
407    /* XXX: does not cover HPACK encoder and HPACK decoder */
408
409    return size;
410}
411
412
413struct lsquic_stream *
414lsquic_headers_stream_get_stream (const struct headers_stream *hs)
415{
416    return hs->hs_stream;
417}
418
419
420static const struct frame_reader_callbacks frame_callbacks = {
421    .frc_on_headers      = headers_on_incoming_headers,
422    .frc_on_push_promise = headers_on_push_promise,
423    .frc_on_error        = headers_on_error,
424    .frc_on_settings     = headers_on_settings,
425    .frc_on_priority     = headers_on_priority,
426};
427
428static const struct frame_reader_callbacks *frame_callbacks_ptr = &frame_callbacks;
429
430