lsquic_headers_stream.c revision 7d09751d
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    lshpack_dec_init(&hs->hs_hdec);
85    if (0 != lshpack_enc_init(&hs->hs_henc))
86    {
87        LSQ_WARN("could not initialize HPACK encoder: %s", strerror(errno));
88        return NULL;
89    }
90    (void) lshpack_enc_use_hist(&hs->hs_henc, 1);
91    hs->hs_flags |= HS_HENC_INITED;
92    hs->hs_stream = stream;
93    LSQ_DEBUG("stream created");
94    hs->hs_fr = lsquic_frame_reader_new((hs->hs_flags & HS_IS_SERVER) ? FRF_SERVER : 0,
95                                MAX_HEADERS_SIZE, &hs->hs_enpub->enp_mm,
96                                stream, lsquic_stream_read, &hs->hs_hdec,
97                                frame_callbacks_ptr, hs,
98#if LSQUIC_CONN_STATS
99                        hs->hs_conn_stats,
100#endif
101                        hs->hs_enpub->enp_hsi_if, hs->hs_enpub->enp_hsi_ctx);
102    if (!hs->hs_fr)
103    {
104        LSQ_WARN("could not create frame reader: %s", strerror(errno));
105        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
106        return NULL;
107    }
108    hs->hs_fw = lsquic_frame_writer_new(&hs->hs_enpub->enp_mm, stream, 0,
109            &hs->hs_henc, lsquic_stream_writef,
110#if LSQUIC_CONN_STATS
111            hs->hs_conn_stats,
112#endif
113            (hs->hs_flags & HS_IS_SERVER));
114    if (!hs->hs_fw)
115    {
116        LSQ_WARN("could not create frame writer: %s", strerror(errno));
117        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
118        return NULL;
119    }
120    lsquic_stream_wantread(stream, 1);
121    return (lsquic_stream_ctx_t *) hs;
122}
123
124
125static void
126headers_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
127{
128    struct headers_stream *hs = (struct headers_stream *) ctx;
129    if (0 != lsquic_frame_reader_read(hs->hs_fr))
130    {
131        LSQ_ERROR("frame reader failed");
132        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
133    }
134}
135
136
137static void
138headers_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
139{
140    struct headers_stream *hs = (struct headers_stream *) ctx;
141    assert(lsquic_frame_writer_have_leftovers(hs->hs_fw));
142    int s = lsquic_frame_writer_flush(hs->hs_fw);
143    if (0 == s)
144    {
145        LSQ_DEBUG("flushed");
146        lsquic_stream_wantwrite(stream,
147            lsquic_frame_writer_have_leftovers(hs->hs_fw));
148    }
149    else
150    {
151        LSQ_WARN("Error writing to stream: %s", strerror(errno));
152        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
153    }
154}
155
156
157static void
158headers_on_close (lsquic_stream_t *stream, struct lsquic_stream_ctx *ctx)
159{
160    struct headers_stream *hs = (struct headers_stream *) ctx;
161    LSQ_DEBUG("stream closed");
162}
163
164
165int
166lsquic_headers_stream_send_headers (struct headers_stream *hs,
167    lsquic_stream_id_t stream_id, const struct lsquic_http_headers *headers,
168    int eos, unsigned weight)
169{
170    LSQ_DEBUG("received compressed headers to send");
171    int s;
172    s = lsquic_frame_writer_write_headers(hs->hs_fw, stream_id, headers, eos,
173                                                                        weight);
174    if (0 == s)
175    {
176        lsquic_stream_wantwrite(hs->hs_stream,
177            lsquic_frame_writer_have_leftovers(hs->hs_fw));
178    }
179    else
180        LSQ_INFO("Error writing headers: %s", strerror(errno));
181    return s;
182}
183
184
185int
186lsquic_headers_stream_send_priority (struct headers_stream *hs,
187        lsquic_stream_id_t stream_id, int exclusive,
188        lsquic_stream_id_t dep_stream_id, unsigned weight)
189{
190    LSQ_DEBUG("received priority to send");
191    int s;
192    if (stream_id == dep_stream_id)
193    {
194        LSQ_INFO("stream cannot depend on itself"); /* RFC 7540, Sec. 5.3.1. */
195        return -1;
196    }
197    s = lsquic_frame_writer_write_priority(hs->hs_fw, stream_id, exclusive,
198                                                        dep_stream_id, weight);
199    if (0 == s)
200    {
201        lsquic_stream_wantwrite(hs->hs_stream,
202            lsquic_frame_writer_have_leftovers(hs->hs_fw));
203    }
204    else
205        LSQ_INFO("Error writing priority frame: %s", strerror(errno));
206    return s;
207}
208
209
210struct headers_stream *
211lsquic_headers_stream_new (int is_server, struct lsquic_engine_public *enpub,
212                           const struct headers_stream_callbacks *callbacks,
213#if LSQUIC_CONN_STATS
214                           struct conn_stats *conn_stats,
215#endif
216                           void *cb_ctx)
217{
218    struct headers_stream *hs = calloc(1, sizeof(*hs));
219    if (!hs)
220        return NULL;
221    hs->hs_callbacks = callbacks;
222    hs->hs_cb_ctx    = cb_ctx;
223    if (is_server)
224        hs->hs_flags = HS_IS_SERVER;
225    else
226        hs->hs_flags = 0;
227    hs->hs_enpub     = enpub;
228#if LSQUIC_CONN_STATS
229    hs->hs_conn_stats= conn_stats;
230#endif
231    return hs;
232}
233
234
235void
236lsquic_headers_stream_destroy (struct headers_stream *hs)
237{
238    if (hs->hs_fr)
239        lsquic_frame_reader_destroy(hs->hs_fr);
240    if (hs->hs_fw)
241        lsquic_frame_writer_destroy(hs->hs_fw);
242    if (hs->hs_flags & HS_HENC_INITED)
243        lshpack_enc_cleanup(&hs->hs_henc);
244    lshpack_dec_cleanup(&hs->hs_hdec);
245    free(hs);
246}
247
248
249static const struct lsquic_stream_if headers_stream_if =
250{
251    .on_new_stream = headers_on_new_stream,
252    .on_read       = headers_on_read,
253    .on_write      = headers_on_write,
254    .on_close      = headers_on_close,
255};
256
257
258const struct lsquic_stream_if *const lsquic_headers_stream_if =
259                                                &headers_stream_if;
260
261
262static void
263headers_on_incoming_headers (void *ctx, struct uncompressed_headers *uh)
264{
265    struct headers_stream *hs = ctx;
266    hs->hs_callbacks->hsc_on_headers(hs->hs_cb_ctx, uh);
267}
268
269
270static void
271headers_on_push_promise (void *ctx, struct uncompressed_headers *uh)
272{
273    struct headers_stream *hs = ctx;
274    hs->hs_callbacks->hsc_on_push_promise(hs->hs_cb_ctx, uh);
275}
276
277
278static void
279headers_on_priority (void *ctx, lsquic_stream_id_t stream_id, int exclusive,
280                     lsquic_stream_id_t dep_stream_id, unsigned weight)
281{
282    struct headers_stream *hs = ctx;
283    hs->hs_callbacks->hsc_on_priority(hs->hs_cb_ctx, stream_id, exclusive,
284                                                    dep_stream_id, weight);
285}
286
287
288static void
289headers_on_error (void *ctx, lsquic_stream_id_t stream_id,
290                                            enum frame_reader_error err)
291{
292    struct headers_stream *hs = ctx;
293    switch (err)
294    {
295    case FR_ERR_DUPLICATE_PSEH:
296    case FR_ERR_INCOMPL_REQ_PSEH:
297    case FR_ERR_UNNEC_REQ_PSEH:
298    case FR_ERR_BAD_REQ_HEADER:
299    case FR_ERR_INCOMPL_RESP_PSEH:
300    case FR_ERR_UNNEC_RESP_PSEH:
301    case FR_ERR_UNKNOWN_PSEH:
302    case FR_ERR_UPPERCASE_HEADER:
303    case FR_ERR_MISPLACED_PSEH:
304    case FR_ERR_MISSING_PSEH:
305    case FR_ERR_DECOMPRESS:
306    case FR_ERR_HEADERS_TOO_LARGE:
307    case FR_ERR_SELF_DEP_STREAM:
308        LSQ_INFO("error %u is a stream error (stream %"PRIu64")", err,
309                                                                    stream_id);
310        hs->hs_callbacks->hsc_on_stream_error(hs->hs_cb_ctx, stream_id);
311        break;
312    case FR_ERR_INVALID_FRAME_SIZE:
313    case FR_ERR_NONZERO_STREAM_ID:
314    case FR_ERR_UNEXPECTED_PUSH:
315    case FR_ERR_ZERO_STREAM_ID:
316    case FR_ERR_NOMEM:
317    case FR_ERR_EXPECTED_CONTIN:
318        LSQ_INFO("error %u is a connection error (stream %"PRIu64")", err,
319                                                                    stream_id);
320        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
321        break;
322    }
323}
324
325
326static void
327headers_on_settings (void *ctx, uint16_t setting_id, uint32_t setting_value)
328{
329    struct headers_stream *hs = ctx;
330    switch (setting_id)
331    {
332    case SETTINGS_HEADER_TABLE_SIZE:
333        if (setting_value > MAX_HEADER_TABLE_SIZE)
334        {
335            LSQ_INFO("tried to update table size to %u, which is larger than "
336                "allowed maximum of %u bytes", setting_value,
337                MAX_HEADER_TABLE_SIZE);
338            hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
339        }
340        else
341        {
342            LSQ_INFO("update hpack table size to %u", setting_value);
343            lshpack_enc_set_max_capacity(&hs->hs_henc, setting_value);
344        }
345        break;
346    case SETTINGS_MAX_HEADER_LIST_SIZE:
347        LSQ_INFO("set max header list size to %u", setting_value);
348        lsquic_frame_writer_max_header_list_size(hs->hs_fw, setting_value);
349        break;
350    case SETTINGS_ENABLE_PUSH:
351        LSQ_INFO("got setting enable_push: %u", setting_value);
352        if (hs->hs_flags & HS_IS_SERVER)
353        {
354            if (setting_value <= 1)
355                hs->hs_callbacks->hsc_on_enable_push(hs->hs_cb_ctx,
356                                                            setting_value);
357            else
358            {
359                LSQ_INFO("invalid value of enable_push");
360                hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
361            }
362        }
363        else
364        {
365            LSQ_INFO("it is an error to receive enable_push setting in "
366                     "client mode");
367            hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
368        }
369        break;
370    case SETTINGS_MAX_CONCURRENT_STREAMS:
371    case SETTINGS_INITIAL_WINDOW_SIZE:
372    case SETTINGS_MAX_FRAME_SIZE:
373        /* [draft-ietf-quic-http-00], Section 3 */
374        LSQ_INFO("Specifying setting 0x%X is a QUIC error", setting_id);
375        hs->hs_callbacks->hsc_on_conn_error(hs->hs_cb_ctx);
376        break;
377    default:
378        LSQ_INFO("Ignoring unknown setting 0x%X; value 0x%X", setting_id,
379                                                                setting_value);
380        break;
381    }
382}
383
384
385int
386lsquic_headers_stream_push_promise (struct headers_stream *hs,
387        lsquic_stream_id_t stream_id64, lsquic_stream_id_t promised_stream_id64,
388        const struct iovec *path, const struct iovec *host,
389        const struct lsquic_http_headers *headers)
390{
391    uint32_t stream_id = stream_id64;
392    uint32_t promised_stream_id = promised_stream_id64;
393    int s;
394    LSQ_DEBUG("promising stream %u in response to stream %u",
395                                            promised_stream_id, stream_id);
396    s = lsquic_frame_writer_write_promise(hs->hs_fw, stream_id,
397                                    promised_stream_id, path, host, headers);
398    if (0 == s)
399    {
400        lsquic_stream_wantwrite(hs->hs_stream,
401            lsquic_frame_writer_have_leftovers(hs->hs_fw));
402    }
403    else
404        LSQ_INFO("Error writing push promise: %s", strerror(errno));
405    return s;
406}
407
408
409size_t
410lsquic_headers_stream_mem_used (const struct headers_stream *hs)
411{
412    size_t size;
413
414    size = sizeof(*hs);
415    size += lsquic_frame_reader_mem_used(hs->hs_fr);
416    size += lsquic_frame_writer_mem_used(hs->hs_fw);
417    /* XXX: does not cover HPACK encoder and HPACK decoder */
418
419    return size;
420}
421
422
423struct lsquic_stream *
424lsquic_headers_stream_get_stream (const struct headers_stream *hs)
425{
426    return hs->hs_stream;
427}
428
429
430static const struct frame_reader_callbacks frame_callbacks = {
431    .frc_on_headers      = headers_on_incoming_headers,
432    .frc_on_push_promise = headers_on_push_promise,
433    .frc_on_error        = headers_on_error,
434    .frc_on_settings     = headers_on_settings,
435    .frc_on_priority     = headers_on_priority,
436};
437
438static const struct frame_reader_callbacks *frame_callbacks_ptr = &frame_callbacks;
439
440