/* Copyright (c) 2017 - 2026 LiteSpeed Technologies Inc.  See LICENSE. */
/*
 * lsquic_frame_reader.c -- Read HTTP frames from stream
 */

#ifndef WIN32
#include <arpa/inet.h>
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>

#include "lshpack.h"
#include "lsquic.h"
#include "lsquic_mm.h"
#include "lsquic_frame_common.h"
#include "lsquic_frame_reader.h"
#include "lsquic_http1x_if.h"
#include "lsquic_headers.h"
#include "lsquic_ev_log.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"

#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_READER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(lsquic_stream_conn(\
                                                            fr->fr_stream))
#include "lsquic_logger.h"


/* headers_state is used by HEADERS, PUSH_PROMISE, and CONTINUATION frames */
struct headers_state
{
    enum http_frame_type
                    frame_type;
    unsigned        nread;  /* Not counting pesw, only payload and padding */

    /* Values parsed out from pesw buffer: */
    uint32_t        oth_stream_id;  /* For HEADERS: ID of stream we depend on;
                                     * for PUSH_PROMISE: promised stream ID.
                                     */
    unsigned short  weight;         /* HEADERS only */
    signed char     exclusive;      /* HEADERS only */
    unsigned char   pad_length;

    unsigned char   pseh;

    /* PESW: Pad length, Exclusive, Stream Dependency, Weight.  This is at
     * most six bytes for HEADERS frame (RFC 7540, page 33) and five bytes
     * for PUSH_PROMISE frame (Ibid, p. 40).
     */
    unsigned char   pesw_size;
    unsigned char   pesw_nread;
    unsigned char   pesw[6];
};


struct settings_state
{   /* RFC 7540, Section 6.5.1 */
    unsigned char   nread;
    unsigned char   set_buf[2 + 4]; /* We'll read one setting at a time */
};


struct priority_state
{   /* RFC 7540, Section 6.3 */
    unsigned char   nread;
    union {
        unsigned char           prio_buf[sizeof(struct http_prio_frame)];
        struct http_prio_frame  prio_frame;
    }               u;
};


struct skip_state
{
    uint32_t    n_skipped;
};


struct reader_state
{
    unsigned                    nh_read;    /* Number of bytes of header read */
    struct http_frame_header    header;
    enum {
        READER_SKIP,
        READER_HEADERS,
        READER_PUSH_PROMISE,
        READER_CONTIN,
        READER_SETTINGS,
        READER_PRIORITY,
    }                           reader_type;
    unsigned                    payload_length;
    union {
        struct headers_state    headers_state;
        struct skip_state       skip_state;
        struct settings_state   settings_state;
        struct priority_state   priority_state;
    }                           by_type;
};


struct lsquic_frame_reader
{
    struct lsquic_mm                *fr_mm;
    struct lshpack_dec              *fr_hdec;
    struct lsquic_stream            *fr_stream;
    fr_stream_read_f                 fr_read;
    const struct frame_reader_callbacks
                                    *fr_callbacks;
    void                            *fr_cb_ctx;
    const struct lsquic_hset_if     *fr_hsi_if;
    void                            *fr_hsi_ctx;
    struct http1x_ctor_ctx           fr_h1x_ctor_ctx;
    /* The the header block is shared between HEADERS, PUSH_PROMISE, and
     * CONTINUATION frames.  It gets added to as block fragments come in.
     */
    unsigned char                   *fr_header_block;
#if LSQUIC_CONN_STATS
    struct conn_stats               *fr_conn_stats;
#endif
    unsigned                         fr_header_block_sz;
    unsigned                         fr_max_headers_sz; /* 0 means no limit */
    enum frame_reader_flags          fr_flags;
    /* Keep some information about previous frame to catch framing errors.
     */
    uint32_t                         fr_prev_stream_id;
    enum http_frame_header_flags     fr_prev_hfh_flags:8;
    enum http_frame_type             fr_prev_frame_type:8;
    struct reader_state              fr_state;
};


#define reset_state(fr) do {                                                \
    LSQ_DEBUG("reset state");                                                \
    (fr)->fr_state.nh_read = 0;                                             \
} while (0)


static uint32_t
fr_get_stream_id (const struct lsquic_frame_reader *fr)
{
    uint32_t stream_id;
    assert(fr->fr_state.nh_read >= sizeof(fr->fr_state.header));
    memcpy(&stream_id, fr->fr_state.header.hfh_stream_id, sizeof(stream_id));
    stream_id = ntohl(stream_id);
    return stream_id;
}


static const char *
hft_to_string (enum http_frame_type hft)
{
    static const char *const map[] = {
        [HTTP_FRAME_DATA]           =  "HTTP_FRAME_DATA",
        [HTTP_FRAME_HEADERS]        =  "HTTP_FRAME_HEADERS",
        [HTTP_FRAME_PRIORITY]       =  "HTTP_FRAME_PRIORITY",
        [HTTP_FRAME_RST_STREAM]     =  "HTTP_FRAME_RST_STREAM",
        [HTTP_FRAME_SETTINGS]       =  "HTTP_FRAME_SETTINGS",
        [HTTP_FRAME_PUSH_PROMISE]   =  "HTTP_FRAME_PUSH_PROMISE",
        [HTTP_FRAME_PING]           =  "HTTP_FRAME_PING",
        [HTTP_FRAME_GOAWAY]         =  "HTTP_FRAME_GOAWAY",
        [HTTP_FRAME_WINDOW_UPDATE]  =  "HTTP_FRAME_WINDOW_UPDATE",
        [HTTP_FRAME_CONTINUATION]   =  "HTTP_FRAME_CONTINUATION",
    };
    if (hft < N_HTTP_FRAME_TYPES)
        return map[hft];
    else
        return "<unknown>";
}


struct lsquic_frame_reader *
lsquic_frame_reader_new (enum frame_reader_flags flags,
                    unsigned max_headers_sz,
                    struct lsquic_mm *mm,
                    struct lsquic_stream *stream, fr_stream_read_f read,
                    struct lshpack_dec *hdec,
                    const struct frame_reader_callbacks *cb,
                    void *frame_reader_cb_ctx,
#if LSQUIC_CONN_STATS
                    struct conn_stats *conn_stats,
#endif
                    const struct lsquic_hset_if *hsi_if, void *hsi_ctx)
{
    struct lsquic_frame_reader *fr = calloc(1, sizeof(*fr));
    if (!fr)
        return NULL;
    fr->fr_mm             = mm;
    fr->fr_hdec           = hdec;
    fr->fr_flags          = flags;
    fr->fr_stream         = stream;
    fr->fr_read           = read;
    fr->fr_callbacks      = cb;
    fr->fr_cb_ctx         = frame_reader_cb_ctx;
    fr->fr_header_block   = NULL;
    fr->fr_max_headers_sz = max_headers_sz;
    fr->fr_hsi_if         = hsi_if;
    if (hsi_if == lsquic_http1x_if)
    {
        fr->fr_h1x_ctor_ctx = (struct http1x_ctor_ctx) {
            .conn           = lsquic_stream_conn(stream),
            .max_headers_sz = fr->fr_max_headers_sz,
            .is_server      = fr->fr_flags & FRF_SERVER,
        };
        fr->fr_hsi_ctx = &fr->fr_h1x_ctor_ctx;
    }
    else
        fr->fr_hsi_ctx = hsi_ctx;
    reset_state(fr);
#if LSQUIC_CONN_STATS
    fr->fr_conn_stats = conn_stats;
#endif
    return fr;
}


void
lsquic_frame_reader_destroy (struct lsquic_frame_reader *fr)
{
    free(fr->fr_header_block);
    free(fr);
}


#define RETURN_ERROR(nread) do {                                        \
    assert(nread <= 0);                                                 \
    if (0 == nread)                                                     \
    {                                                                   \
        LSQ_INFO("%s: unexpected EOF", __func__);                        \
        return -1;                                                      \
    }                                                                   \
    else                                                                \
    {                                                                   \
        LSQ_WARN("%s: error reading from stream: %s", __func__,          \
            strerror(errno));                                           \
        return -1;                                                      \
    }                                                                   \
} while (0)


static int
prepare_for_payload (struct lsquic_frame_reader *fr)
{
    uint32_t stream_id;
    unsigned char *header_block;

    /* RFC 7540, Section 4.1: Ignore R bit: */
    fr->fr_state.header.hfh_stream_id[0] &= ~0x80;

    fr->fr_state.payload_length = hfh_get_length(&fr->fr_state.header);

    stream_id = fr_get_stream_id(fr);

    if (fr->fr_state.header.hfh_type != HTTP_FRAME_CONTINUATION &&
        (fr->fr_flags & FRF_HAVE_PREV) &&
        (fr->fr_prev_frame_type == HTTP_FRAME_HEADERS      ||
         fr->fr_prev_frame_type == HTTP_FRAME_PUSH_PROMISE ||
         fr->fr_prev_frame_type == HTTP_FRAME_CONTINUATION    ) &&
        0 == (fr->fr_prev_hfh_flags & HFHF_END_HEADERS))
    {
        LSQ_INFO("Framing error: expected CONTINUATION frame, got %u",
                                            fr->fr_state.header.hfh_type);
        fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                FR_ERR_EXPECTED_CONTIN);
        return -1;
    }

    switch (fr->fr_state.header.hfh_type)
    {
    case HTTP_FRAME_HEADERS:
        if (fr->fr_max_headers_sz &&
            fr->fr_state.payload_length > fr->fr_max_headers_sz)
            goto headers_too_large;
        fr->fr_state.by_type.headers_state.frame_type = HTTP_FRAME_HEADERS;
        fr->fr_state.by_type.headers_state.nread = 0;
        fr->fr_state.by_type.headers_state.pesw_nread = 0;
        fr->fr_state.by_type.headers_state.pseh = 0;
        if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
            fr->fr_state.by_type.headers_state.pesw_size = 1;
        else
        {
            fr->fr_state.by_type.headers_state.pad_length = 0;
            fr->fr_state.by_type.headers_state.pesw_size = 0;
        }
        if (fr->fr_state.header.hfh_flags & HFHF_PRIORITY)
            fr->fr_state.by_type.headers_state.pesw_size += 5;
        else
        {
            fr->fr_state.by_type.headers_state.exclusive     = -1;
            fr->fr_state.by_type.headers_state.oth_stream_id = 0;
            fr->fr_state.by_type.headers_state.weight        = 0;
        }
        LSQ_DEBUG("pesw size: %u; payload length: %u; flags: 0x%X",
            fr->fr_state.by_type.headers_state.pesw_size,
            fr->fr_state.payload_length, fr->fr_state.header.hfh_flags);
        if (fr->fr_state.by_type.headers_state.pesw_size >
                                        fr->fr_state.payload_length)
        {
            LSQ_INFO("Invalid headers frame: payload length too small");
            errno = EBADMSG;
            return -1;
        }
        fr->fr_state.reader_type = READER_HEADERS;
        break;
    case HTTP_FRAME_PUSH_PROMISE:
        if (fr->fr_flags & FRF_SERVER)
        {
            LSQ_INFO("clients should not push promised");
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                    FR_ERR_UNEXPECTED_PUSH);
            return -1;
        }
        if (fr->fr_max_headers_sz &&
            fr->fr_state.payload_length > fr->fr_max_headers_sz)
            goto headers_too_large;
        fr->fr_state.by_type.headers_state.frame_type = HTTP_FRAME_PUSH_PROMISE;
        fr->fr_state.by_type.headers_state.nread = 0;
        fr->fr_state.by_type.headers_state.pesw_nread = 0;
        fr->fr_state.by_type.headers_state.pseh = 0;
        if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
            fr->fr_state.by_type.headers_state.pesw_size = 5;
        else
        {
            fr->fr_state.by_type.headers_state.pad_length = 0;
            fr->fr_state.by_type.headers_state.pesw_size = 4;
        }
        LSQ_DEBUG("pesw size: %u; payload length: %u; flags: 0x%X",
            fr->fr_state.by_type.headers_state.pesw_size,
            fr->fr_state.payload_length, fr->fr_state.header.hfh_flags);
        if (fr->fr_state.by_type.headers_state.pesw_size >
                                        fr->fr_state.payload_length)
        {
            LSQ_INFO("Invalid headers frame: payload length too small");
            errno = EBADMSG;
            return -1;
        }
        fr->fr_state.reader_type = READER_PUSH_PROMISE;
        break;
    case HTTP_FRAME_CONTINUATION:
        if (0 == (fr->fr_flags & FRF_HAVE_PREV))
        {
            LSQ_INFO("Framing error: unexpected CONTINUATION");
            return -1;
        }
        if (!(fr->fr_prev_frame_type == HTTP_FRAME_HEADERS      ||
              fr->fr_prev_frame_type == HTTP_FRAME_PUSH_PROMISE ||
              fr->fr_prev_frame_type == HTTP_FRAME_CONTINUATION))
        {
            LSQ_INFO("Framing error: unexpected CONTINUATION");
            return -1;
        }
        if (fr->fr_prev_hfh_flags & HFHF_END_HEADERS)
        {
            LSQ_INFO("Framing error: unexpected CONTINUATION");
            return -1;
        }
        if (stream_id != fr->fr_prev_stream_id)
        {
            LSQ_INFO("Framing error: CONTINUATION does not have matching "
                "stream ID");
            return -1;
        }
        if (fr->fr_state.reader_type == READER_SKIP)
            goto continue_skipping;
        fr->fr_header_block_sz += fr->fr_state.payload_length;
        if (fr->fr_max_headers_sz &&
            fr->fr_header_block_sz > fr->fr_max_headers_sz)
        {
            free(fr->fr_header_block);
            fr->fr_header_block = NULL;
            goto headers_too_large;
        }
        header_block = realloc(fr->fr_header_block, fr->fr_header_block_sz);
        if (!header_block)
        {
            LSQ_WARN("cannot allocate %u bytes for header block",
                                                    fr->fr_header_block_sz);
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                        FR_ERR_OTHER_ERROR);
            return -1;
        }
        fr->fr_header_block = header_block;
        fr->fr_state.by_type.headers_state.nread = 0;
        fr->fr_state.reader_type = READER_CONTIN;
        break;
    case HTTP_FRAME_SETTINGS:
        if (0 == fr->fr_state.payload_length ||
            0 != fr->fr_state.payload_length % 6)
        {
            LSQ_INFO("Framing error: %u is not a valid SETTINGS length",
                fr->fr_state.payload_length);
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                    FR_ERR_INVALID_FRAME_SIZE);
            return -1;
        }
        if (stream_id)
        {   /* RFC 7540, Section 6.5 */
            LSQ_INFO("Error: SETTINGS frame should not have stream ID set");
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                    FR_ERR_NONZERO_STREAM_ID);
            return -1;
        }
        fr->fr_state.by_type.settings_state.nread = 0;
        fr->fr_state.reader_type = READER_SETTINGS;
        break;
    case HTTP_FRAME_PRIORITY:
        if (fr->fr_state.payload_length != sizeof(struct http_prio_frame))
        {
            LSQ_INFO("Framing error: %u is not a valid PRIORITY length",
                fr->fr_state.payload_length);
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                    FR_ERR_INVALID_FRAME_SIZE);
            return -1;
        }
        if (!stream_id)
        {   /* RFC 7540, Section 6.3 */
            LSQ_INFO("Error: PRIORITY frame must have stream ID set");
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                    FR_ERR_ZERO_STREAM_ID);
            return -1;
        }
        fr->fr_state.by_type.settings_state.nread = 0;
        fr->fr_state.reader_type = READER_PRIORITY;
        break;
    headers_too_large:
        LSQ_INFO("headers are too large (%u bytes), skipping",
            fr->fr_state.payload_length);
        fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                FR_ERR_BAD_HEADER);
        /* fallthru */
    continue_skipping:
    default:
        fr->fr_state.by_type.skip_state.n_skipped = 0;
        fr->fr_state.reader_type = READER_SKIP;
        break;
    }

    fr->fr_flags |= FRF_HAVE_PREV;
    fr->fr_prev_frame_type = fr->fr_state.header.hfh_type;
    fr->fr_prev_hfh_flags  = fr->fr_state.header.hfh_flags;
    fr->fr_prev_stream_id  = stream_id;

    return 0;
}


static int
read_http_frame_header (struct lsquic_frame_reader *fr)
{
    ssize_t nr;
    size_t ntoread;
    unsigned char *dst;

    ntoread = sizeof(fr->fr_state.header) - fr->fr_state.nh_read;
    dst = (unsigned char *) &fr->fr_state.header + fr->fr_state.nh_read;
    nr = fr->fr_read(fr->fr_stream, dst, ntoread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    fr->fr_state.nh_read += nr;
    if (fr->fr_state.nh_read == sizeof(fr->fr_state.header))
    {
        LSQ_DEBUG("read in frame %s", hft_to_string(fr->fr_state.header.hfh_type));
        return prepare_for_payload(fr);
    }
    else
        return 0;
}


static int
skip_payload (struct lsquic_frame_reader *fr)
{
    struct skip_state *ss = &fr->fr_state.by_type.skip_state;
    size_t ntoread = fr->fr_state.payload_length - ss->n_skipped;
    unsigned char buf[0x100];
    if (ntoread > sizeof(buf))
        ntoread = sizeof(buf);
    ssize_t nr = fr->fr_read(fr->fr_stream, buf, ntoread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    ss->n_skipped += nr;
    if (ss->n_skipped == fr->fr_state.payload_length)
        reset_state(fr);
    return 0;
}


static int
skip_headers_padding (struct lsquic_frame_reader *fr)
{
    unsigned char buf[0x100];
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    unsigned pay_and_pad_length = fr->fr_state.payload_length - hs->pesw_size;
    unsigned ntoread = pay_and_pad_length - hs->nread;
    assert(ntoread <= sizeof(buf));
    if (ntoread > sizeof(buf))
        ntoread = sizeof(buf);
    ssize_t nr = fr->fr_read(fr->fr_stream, buf, ntoread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    hs->nread += nr;
    if (hs->nread == pay_and_pad_length)
        reset_state(fr);
    return 0;
}


static struct lsquic_stream *
find_target_stream (const struct lsquic_frame_reader *fr)
{
    lsquic_stream_id_t stream_id;
    struct lsquic_conn *lconn;

    stream_id = fr_get_stream_id(fr);
    lconn = lsquic_stream_conn(fr->fr_stream);
    if (lconn->cn_if->ci_get_stream_by_id)
        return lconn->cn_if->ci_get_stream_by_id(lconn, stream_id);

    return NULL;
}


static void
skip_headers (struct lsquic_frame_reader *fr)
{
    const unsigned char *comp, *end;
    void *buf;
    int s;
    struct lsxpack_header xhdr;
    const size_t buf_len = 64 * 1024;

    buf = malloc(buf_len);
    if (!buf)
    {
        fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr),
                                                        FR_ERR_OTHER_ERROR);
        goto end;
    }

    comp = fr->fr_header_block;
    end = comp + fr->fr_header_block_sz;
    while (comp < end)
    {
        lsxpack_header_prepare_decode(&xhdr, buf, 0, buf_len);
        s = lshpack_dec_decode(fr->fr_hdec, &comp, end, &xhdr);
        if (s != 0)
        {
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr),
                                                            FR_ERR_OTHER_ERROR);
            break;
        }
    }

  end:
    if (buf)
        free(buf);
}


static void
decode_and_pass_payload (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    const unsigned char *comp, *end;
    enum frame_reader_error err;
    int s;
    uint32_t stream_id32;
    struct uncompressed_headers *uh = NULL;
    void *hset = NULL;
    struct lsxpack_header *hdr = NULL;
    size_t req_space = 0;
    lsquic_stream_t *target_stream = NULL;

    if (!(fr->fr_flags & FRF_SERVER))
    {
        target_stream = find_target_stream(fr);
        /* If the response is for a stream that cannot be found, one of two
         * things is true: a) the stream has been closed or b) this is an
         * error.  If (a), we discard this header block.  We choose to do the
         * same for (b) instead of erroring out for the sake of simplicity.
         * There is no way to exploit this behavior.
         */
        if (!target_stream)
        {
            skip_headers(fr);
            return;
        }
    }
    hset = fr->fr_hsi_if->hsi_create_header_set(fr->fr_hsi_ctx, target_stream,
                            READER_PUSH_PROMISE == fr->fr_state.reader_type);
    if (!hset)
    {
        err = FR_ERR_OTHER_ERROR;
        goto stream_error;
    }

    comp = fr->fr_header_block;
    end = comp + fr->fr_header_block_sz;

    while (comp < end)
    {
  prepare:
        hdr = fr->fr_hsi_if->hsi_prepare_decode(hset, hdr, req_space);
        if (!hdr)
        {
            err = FR_ERR_OTHER_ERROR;
            goto stream_error;
        }
        s = lshpack_dec_decode(fr->fr_hdec, &comp, end, hdr);
        if (s == 0)
        {
            s = fr->fr_hsi_if->hsi_process_header(hset, hdr);
            if (s == 0)
            {
#if LSQUIC_CONN_STATS
                fr->fr_conn_stats->in.headers_uncomp += hdr->name_len +
                                                        hdr->val_len;
#endif
                req_space = 0;
                hdr = NULL;
                continue;
            }
            else if (s > 0)
                err = FR_ERR_BAD_HEADER;
            else
                err = FR_ERR_OTHER_ERROR;
        }
        else if (s == LSHPACK_ERR_MORE_BUF)
        {
            req_space = hdr->val_len;
            goto prepare;
        }
        else
            err = FR_ERR_DECOMPRESS;
        goto stream_error;
    }
    assert(comp == end);

    s = fr->fr_hsi_if->hsi_process_header(hset, NULL);
    if (s != 0)
    {
        err = s < 0 ? FR_ERR_OTHER_ERROR : FR_ERR_BAD_HEADER;
        goto stream_error;
    }

    uh = calloc(1, sizeof(*uh));
    if (!uh)
    {
        err = FR_ERR_OTHER_ERROR;
        goto stream_error;
    }

    memcpy(&stream_id32, fr->fr_state.header.hfh_stream_id,
                                                sizeof(stream_id32));
    uh->uh_stream_id     = ntohl(stream_id32);
    uh->uh_oth_stream_id = hs->oth_stream_id;
    if (HTTP_FRAME_HEADERS == fr->fr_state.by_type.headers_state.frame_type)
    {
        uh->uh_weight    = hs->weight;
        uh->uh_exclusive = hs->exclusive;
        uh->uh_flags     = 0;
    }
    else
    {
        assert(HTTP_FRAME_PUSH_PROMISE ==
                                fr->fr_state.by_type.headers_state.frame_type);
        uh->uh_weight    = 0;   /* Zero unused value */
        uh->uh_exclusive = 0;   /* Zero unused value */
        uh->uh_flags     = UH_PP;
    }
    if (fr->fr_state.header.hfh_flags & HFHF_END_STREAM)
        uh->uh_flags    |= UH_FIN;
    if (fr->fr_hsi_if == lsquic_http1x_if)
        uh->uh_flags    |= UH_H1H;
    uh->uh_hset = hset;

    EV_LOG_HTTP_HEADERS_IN(LSQUIC_LOG_CONN_ID, fr->fr_flags & FRF_SERVER, uh);
    if (HTTP_FRAME_HEADERS == fr->fr_state.by_type.headers_state.frame_type)
        fr->fr_callbacks->frc_on_headers(fr->fr_cb_ctx, uh);
    else
        fr->fr_callbacks->frc_on_push_promise(fr->fr_cb_ctx, uh);
#if LSQUIC_CONN_STATS
    fr->fr_conn_stats->in.headers_comp += fr->fr_header_block_sz;
#endif

    return;

  stream_error:
    LSQ_INFO("%s: stream error %u", __func__, err);
    if (hset)
        fr->fr_hsi_if->hsi_discard_header_set(hset);
    fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, fr_get_stream_id(fr), err);
}


static int
read_headers_block_fragment (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    ssize_t nr;
    unsigned payload_length = fr->fr_state.payload_length - hs->pesw_size -
                                                                hs->pad_length;
    if (!fr->fr_header_block)
    {
        fr->fr_header_block_sz = payload_length;
        fr->fr_header_block = malloc(payload_length);
        if (!fr->fr_header_block)
            return -1;
    }
    nr = fr->fr_read(fr->fr_stream, fr->fr_header_block + hs->nread,
                                            fr->fr_header_block_sz - hs->nread);
    if (nr <= 0)
    {
        free(fr->fr_header_block);
        fr->fr_header_block = NULL;
        RETURN_ERROR(nr);
    }
    hs->nread += nr;
    if (hs->nread == payload_length &&
                (fr->fr_state.header.hfh_flags & HFHF_END_HEADERS))
    {
        decode_and_pass_payload(fr);
        free(fr->fr_header_block);
        fr->fr_header_block = NULL;
    }

    return 0;
}


static int
read_headers_block_fragment_and_padding (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    unsigned payload_length = fr->fr_state.payload_length - hs->pesw_size;
    int rv;
    if (hs->nread < payload_length - hs->pad_length)
        rv = read_headers_block_fragment(fr);
    else if (payload_length)
        rv = skip_headers_padding(fr);
    else
    {   /* Edge case where PESW takes up the whole frame */
        fr->fr_header_block_sz = 0;
        fr->fr_header_block    = NULL;
        rv = 0;
    }
    if (0 == rv && hs->nread == payload_length)
        reset_state(fr);
    return rv;
}


static int
read_headers_pesw (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    ssize_t nr = fr->fr_read(fr->fr_stream, hs->pesw + hs->pesw_nread,
                                            hs->pesw_size - hs->pesw_nread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    hs->pesw_nread += nr;
    if (hs->pesw_nread == hs->pesw_size)
    {
        unsigned char *p = hs->pesw;
        if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
            hs->pad_length = *p++;
        if (fr->fr_state.header.hfh_flags & HFHF_PRIORITY)
        {
            hs->exclusive = p[0] >> 7;
            p[0] &= ~0x80;  /* Note that we are modifying pesw buffer. */
            memcpy(&hs->oth_stream_id, p, sizeof(hs->oth_stream_id));
            hs->oth_stream_id = ntohl(hs->oth_stream_id);
            p += 4;
            hs->weight = 1 + *p++;
        }
        assert(p - hs->pesw == hs->pesw_size);

        if (hs->pesw_size + hs->pad_length > fr->fr_state.payload_length)
        {
            LSQ_INFO("Invalid headers frame: pesw length and padding length "
                    "are larger than the payload length");
            errno = EBADMSG;
            return -1;
        }
    }
    return 0;
}


static int
read_headers (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    if (hs->pesw_nread < hs->pesw_size)
        return read_headers_pesw(fr);
    else
        return read_headers_block_fragment_and_padding(fr);
}


static int
read_push_promise_pesw (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    ssize_t nr = fr->fr_read(fr->fr_stream, hs->pesw + hs->pesw_nread,
                                            hs->pesw_size - hs->pesw_nread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    hs->pesw_nread += nr;
    if (hs->pesw_nread == hs->pesw_size)
    {
        unsigned char *p = hs->pesw;
        if (fr->fr_state.header.hfh_flags & HFHF_PADDED)
            hs->pad_length = *p++;
        p[0] &= ~0x80;  /* Clear reserved bit.  Note: modifying pesw buffer. */
        memcpy(&hs->oth_stream_id, p, sizeof(hs->oth_stream_id));
        hs->oth_stream_id = ntohl(hs->oth_stream_id);
        p += 4;
        assert(p - hs->pesw == hs->pesw_size);
        if (hs->pesw_size + hs->pad_length > fr->fr_state.payload_length)
        {
            LSQ_INFO("Invalid PUSH_PROMISE frame: pesw length and padding length "
                    "are larger than the payload length");
            errno = EBADMSG;
            return -1;
        }
    }
    return 0;
}


static int
read_push_promise (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    if (hs->pesw_nread < hs->pesw_size)
        return read_push_promise_pesw(fr);
    else
        return read_headers_block_fragment_and_padding(fr);
}


static int
read_contin (struct lsquic_frame_reader *fr)
{
    struct headers_state *hs = &fr->fr_state.by_type.headers_state;
    unsigned ntoread;
    ssize_t nr;

    ntoread = fr->fr_state.payload_length - hs->nread;
    nr = fr->fr_read(fr->fr_stream,
                     fr->fr_header_block + fr->fr_header_block_sz - ntoread,
                     ntoread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    hs->nread += nr;
    if (hs->nread == fr->fr_state.payload_length)
    {
        if (fr->fr_state.header.hfh_flags & HFHF_END_HEADERS)
        {
            decode_and_pass_payload(fr);
            free(fr->fr_header_block);
            fr->fr_header_block = NULL;
        }
        reset_state(fr);
    }

    return 0;
}


static int
read_settings (struct lsquic_frame_reader *fr)
{
    struct settings_state *ss = &fr->fr_state.by_type.settings_state;
    unsigned ntoread;
    ssize_t nr;
    uint32_t setting_value;
    uint16_t setting_id;

    ntoread = sizeof(ss->set_buf) - ss->nread;
    nr = fr->fr_read(fr->fr_stream, ss->set_buf + ss->nread, ntoread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    ss->nread += nr;
    if (ss->nread == sizeof(ss->set_buf))
    {
        memcpy(&setting_id,    ss->set_buf, 2);
        memcpy(&setting_value, ss->set_buf + 2, 4);
        setting_id    = ntohs(setting_id);
        setting_value = ntohl(setting_value);
        EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "read HTTP SETTING %s=%"PRIu32,
                        lsquic_http_setting_id2str(setting_id), setting_value);
        fr->fr_callbacks->frc_on_settings(fr->fr_cb_ctx, setting_id,
                                                        setting_value);

        fr->fr_state.payload_length -= sizeof(ss->set_buf);
        if (0 == fr->fr_state.payload_length)
            reset_state(fr);
        else
            ss->nread = 0;
    }
    return 0;
}


static int
read_priority (struct lsquic_frame_reader *fr)
{
    struct priority_state *ps = &fr->fr_state.by_type.priority_state;
    unsigned ntoread;
    ssize_t nr;
    uint32_t stream_id, dep_stream_id;
    int exclusive;

    ntoread = sizeof(ps->u.prio_buf) - ps->nread;
    nr = fr->fr_read(fr->fr_stream, ps->u.prio_buf + ps->nread, ntoread);
    if (nr <= 0)
        RETURN_ERROR(nr);
    ps->nread += nr;
    if (ps->nread == sizeof(ps->u.prio_buf))
    {
        memcpy(&dep_stream_id, ps->u.prio_frame.hpf_stream_id, 4);
        dep_stream_id = ntohl(dep_stream_id);
        exclusive = dep_stream_id >> 31;
        dep_stream_id &= ~(1UL << 31);
        stream_id = fr_get_stream_id(fr);
        if (stream_id == dep_stream_id)
            fr->fr_callbacks->frc_on_error(fr->fr_cb_ctx, stream_id,
                                                    FR_ERR_SELF_DEP_STREAM);
        else
        {
            EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "read PRIORITY frame; "
                "stream: %"PRIu32", dep stream %"PRIu32", exclusive: %d, "
                "weight: %u", stream_id, dep_stream_id, exclusive,
                ps->u.prio_frame.hpf_weight + 1);
            fr->fr_callbacks->frc_on_priority(fr->fr_cb_ctx, stream_id,
                    exclusive, dep_stream_id, ps->u.prio_frame.hpf_weight + 1);
        }
        reset_state(fr);
    }
    return 0;
}


static int
read_payload (struct lsquic_frame_reader *fr)
{
    switch (fr->fr_state.reader_type)
    {
    case READER_HEADERS:
        return read_headers(fr);
    case READER_PUSH_PROMISE:
        return read_push_promise(fr);
    case READER_CONTIN:
        return read_contin(fr);
    case READER_SETTINGS:
        return read_settings(fr);
    case READER_PRIORITY:
        return read_priority(fr);
    default:
        assert(READER_SKIP == fr->fr_state.reader_type);
        return skip_payload(fr);
    }
}


int
lsquic_frame_reader_read (struct lsquic_frame_reader *fr)
{
    if (fr->fr_state.nh_read < sizeof(fr->fr_state.header))
        return read_http_frame_header(fr);
    else
        return read_payload(fr);
}


size_t
lsquic_frame_reader_mem_used (const struct lsquic_frame_reader *fr)
{
    size_t size;
    size = sizeof(*fr);
    if (fr->fr_header_block)
        size += fr->fr_header_block_sz;
    return size;
}
