Packages
hackney
2.0.1
4.7.2
4.7.1
4.7.0
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.5
4.4.3
4.4.2
4.4.1
4.4.0
4.3.0
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.3
4.0.2
4.0.1
4.0.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.3
3.0.2
3.0.1
3.0.0
retired
2.0.1
2.0.0
2.0.0-beta.1
1.25.0
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.0
1.13.0
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.0
1.7.1
1.7.0
1.6.6
retired
1.6.5
1.6.4
retired
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.6
1.0.5
1.0.2
1.0.1
0.15.2
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
Simple HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
c_src/lsquic/src/liblsquic/lsquic_hcsi_reader.c
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_int_types.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_hash.h"
#include "lsquic_conn.h"
#include "lsquic_hcsi_reader.h"
#define LSQUIC_LOGGER_MODULE LSQLM_HCSI_READER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(reader->hr_conn)
#include "lsquic_logger.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
void
lsquic_hcsi_reader_init (struct hcsi_reader *reader,
struct lsquic_conn *conn, const struct hcsi_callbacks *callbacks,
void *ctx)
{
memset(reader, 0, sizeof(*reader));
reader->hr_state = HR_READ_FRAME_BEGIN;
reader->hr_conn = conn;
reader->hr_cb = callbacks;
reader->hr_ctx = ctx;
LSQ_DEBUG("initialized");
}
int
lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf,
size_t bufsz)
{
const unsigned char *p = buf;
const unsigned char *const end = p + bufsz;
const unsigned char *orig_p;
uint64_t len;
int s;
continue_reading:
while (p < end)
{
switch (reader->hr_state)
{
case HR_READ_FRAME_BEGIN:
reader->hr_u.vint2_state.vr2s_state = 0;
reader->hr_state = HR_READ_FRAME_CONTINUE;
/* fall-through */
case HR_READ_FRAME_CONTINUE:
s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
if (s < 0)
break;
reader->hr_frame_type = reader->hr_u.vint2_state.vr2s_one;
reader->hr_frame_length = reader->hr_u.vint2_state.vr2s_two;
if (!(reader->hr_flag & HR_FLAG_RCVD_SETTING)
&& reader->hr_frame_type != HQFT_SETTINGS)
{
reader->hr_cb->on_frame_error(reader->hr_ctx,
HEC_MISSING_SETTINGS, reader->hr_frame_type);
return -1;
}
switch (reader->hr_frame_type)
{
case HQFT_SETTINGS:
reader->hr_flag |= HR_FLAG_RCVD_SETTING;
if (reader->hr_frame_length)
{
reader->hr_state = HR_READ_SETTING_BEGIN;
reader->hr_nread = 0;
}
else
{
reader->hr_cb->on_settings_frame(reader->hr_ctx);
reader->hr_state = HR_READ_FRAME_BEGIN;
}
break;
case HQFT_GOAWAY:
reader->hr_state = HR_READ_VARINT;
break;
case HQFT_CANCEL_PUSH:
reader->hr_state = HR_READ_VARINT;
break;
case HQFT_MAX_PUSH_ID:
reader->hr_state = HR_READ_VARINT;
break;
case HQFT_PRIORITY_UPDATE_PUSH:
case HQFT_PRIORITY_UPDATE_STREAM:
reader->hr_state = HR_READ_VARINT;
break;
case HQFT_DATA:
case HQFT_HEADERS:
case HQFT_PUSH_PROMISE:
reader->hr_cb->on_frame_error(reader->hr_ctx,
HEC_FRAME_UNEXPECTED, reader->hr_frame_type);
return -1;
default:
{
/* From [draft-ietf-quic-http-31] Section 7.2.8:
" Frame types of the format "0x1f * N + 0x21" for non-negative
" integer values of N are reserved to exercise the requirement
" that unknown types be ignored
*/
enum lsq_log_level L;
if (!(reader->hr_frame_type >= 0x21 &&
(reader->hr_frame_type - 0x21) % 0x1F == 0))
/* Non-grease: log with higher level: */
L = LSQ_LOG_INFO;
else
L = LSQ_LOG_DEBUG;
LSQ_LOG(L, "unknown frame type 0x%"PRIX64": will skip "
"%"PRIu64" bytes", reader->hr_frame_type,
reader->hr_frame_length);
reader->hr_state = HR_SKIPPING;
break;
}
}
break;
case HR_READ_VARINT:
reader->hr_u.vint_state.pos = 0;
reader->hr_state = HR_READ_VARINT_CONTINUE;
reader->hr_nread = 0;
/* fall-through */
case HR_READ_VARINT_CONTINUE:
orig_p = p;
s = lsquic_varint_read_nb(&p, end, &reader->hr_u.vint_state);
reader->hr_nread += p - orig_p;
if (0 == s)
{
switch (reader->hr_frame_type)
{
case HQFT_GOAWAY:
case HQFT_CANCEL_PUSH:
case HQFT_MAX_PUSH_ID:
if (reader->hr_nread != reader->hr_frame_length)
{
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
HEC_FRAME_ERROR,
"Frame length does not match actual payload length");
reader->hr_state = HR_ERROR;
return -1;
}
break;
}
switch (reader->hr_frame_type)
{
case HQFT_GOAWAY:
reader->hr_cb->on_goaway(reader->hr_ctx,
reader->hr_u.vint_state.val);
break;
case HQFT_CANCEL_PUSH:
reader->hr_cb->on_cancel_push(reader->hr_ctx,
reader->hr_u.vint_state.val);
break;
case HQFT_MAX_PUSH_ID:
reader->hr_cb->on_max_push_id(reader->hr_ctx,
reader->hr_u.vint_state.val);
break;
case HQFT_PRIORITY_UPDATE_PUSH:
case HQFT_PRIORITY_UPDATE_STREAM:
len = reader->hr_frame_length - reader->hr_nread;
if (len <= (uintptr_t) (end - p))
{
reader->hr_cb->on_priority_update(reader->hr_ctx,
reader->hr_frame_type, reader->hr_u.vint_state.val,
(char *) p, len);
p += len;
}
else if (len <= sizeof(reader->hr_u.prio_state.buf))
{
reader->hr_frame_length = len;
reader->hr_nread = 0;
reader->hr_state = HR_READ_PRIORITY_UPDATE;
goto continue_reading;
}
else
{
p += len;
/* 16 bytes is more than enough for a PRIORITY_UPDATE
* frame, anything larger than that is unreasonable.
*/
if (reader->hr_frame_length
> sizeof(reader->hr_u.prio_state.buf))
LSQ_INFO("skip PRIORITY_UPDATE frame that's too "
"long (%"PRIu64" bytes)", len);
}
break;
default:
assert(0);
}
reader->hr_state = HR_READ_FRAME_BEGIN;
break;
}
else
{
assert(p == end);
return 0;
}
case HR_SKIPPING:
len = MIN((uintptr_t) (end - p), reader->hr_frame_length);
p += len;
reader->hr_frame_length -= len;
if (0 == reader->hr_frame_length)
reader->hr_state = HR_READ_FRAME_BEGIN;
break;
case HR_READ_SETTING_BEGIN:
reader->hr_u.vint2_state.vr2s_state = 0;
reader->hr_state = HR_READ_SETTING_CONTINUE;
/* fall-through */
case HR_READ_SETTING_CONTINUE:
orig_p = p;
s = lsquic_varint_read_two(&p, end, &reader->hr_u.vint2_state);
reader->hr_nread += p - orig_p;
if (reader->hr_nread > reader->hr_frame_length)
{
reader->hr_conn->cn_if->ci_abort_error(reader->hr_conn, 1,
HEC_FRAME_ERROR, "SETTING frame contents too long");
reader->hr_state = HR_ERROR;
return -1;
}
if (s < 0)
break;
reader->hr_cb->on_setting(reader->hr_ctx,
reader->hr_u.vint2_state.vr2s_one,
reader->hr_u.vint2_state.vr2s_two);
if (reader->hr_nread >= reader->hr_frame_length)
{
reader->hr_state = HR_READ_FRAME_BEGIN;
reader->hr_cb->on_settings_frame(reader->hr_ctx);
}
else
reader->hr_state = HR_READ_SETTING_BEGIN;
break;
case HR_READ_PRIORITY_UPDATE:
len = MIN((uintptr_t) (end - p),
reader->hr_frame_length - reader->hr_nread);
memcpy(reader->hr_u.prio_state.buf + reader->hr_nread, p, len);
reader->hr_nread += len;
p += len;
if (reader->hr_frame_length == reader->hr_nread)
{
reader->hr_cb->on_priority_update(reader->hr_ctx,
reader->hr_frame_type, reader->hr_u.vint_state.val,
reader->hr_u.prio_state.buf, reader->hr_frame_length);
reader->hr_state = HR_READ_FRAME_BEGIN;
}
break;
default:
assert(0);
/* fall-through */
case HR_ERROR:
return -1;
}
}
return 0;
}