Repository navigation
Expand file tree
/
Copy pathhttp3_settings.cpp
More file actions
89 lines (89 loc) · 4.25 KB
/
Copy pathhttp3_settings.cpp
File metadata and controls
89 lines (89 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/* Copyright (C) 2026 Sebastiano Merlino; SPDX-License-Identifier: LGPL-2.1-or-later */
#include <algorithm>
#include <httpserver/detail/http3_connection.hpp>
namespace httpserver::detail {
namespace {
bool stage_setting(http3_peer_settings& staged, std::uint64_t key, std::uint64_t value) {
switch (key) {
case 1: staged.qpack_capacity = value; break;
case 6: staged.max_field_section = value; break;
case 7: staged.qpack_blocked = value; break;
case 8: staged.enable_connect_protocol = value == 1; return value <= 1;
default: break;
}
return true;
}
} // namespace
bool http3_connection::settings(std::span<const std::byte> payload, std::uint64_t id) {
http3_peer_settings staged;
std::array<std::uint64_t, 64> seen{};
std::size_t count = 0;
quic_cursor cursor(payload);
while (cursor.remaining()) {
const auto key = cursor.varint(); const auto value = cursor.varint();
if (cursor.code() != quic_codec_code::ok) {
fail(0x106, "Truncated SETTINGS pair", id); return false;
}
if (std::find(seen.begin(), seen.begin() + count, key) != seen.begin() + count || (key >= 2 && key <= 5)) {
fail(0x109, "Duplicate or HTTP/2 reserved setting", id); return false;
}
if (count == std::min(limits_.settings_identifiers, seen.size())) {
fail(0x107, "SETTINGS identifier limit", id); return false;
}
seen[count++] = key;
if (!stage_setting(staged, key, value)) {
fail(0x109, "Invalid ENABLE_CONNECT_PROTOCOL setting", id); return false;
}
}
if (!encoder_table_.initialize(data_.ancestors, static_cast<std::size_t>(std::min<std::uint64_t>(limits_.qpack_encoder_capacity.value_or(limits_.qpack_capacity), staged.qpack_capacity))).ok()) {
fail(0x107, "Encoder table budget exhausted", id); return false;
}
staged.received = true; peer_ = staged;
return true;
}
std::optional<http3_error> http3_connection::attach_local_stream(http3_role role, std::uint64_t id) {
if (error_) return error_;
if (role < http3_role::control || role > http3_role::qpack_decoder || id > k_quic_max_integer || (id & 3) != 3)
return fail(0x103, "Invalid local critical stream", id);
const auto slot = static_cast<std::size_t>(role) - static_cast<std::size_t>(http3_role::control);
if (local_ids_[slot] == id) return {};
if (local_ids_[slot] || std::find(local_ids_.begin(), local_ids_.end(), id) != local_ids_.end())
return fail(0x103, "Local critical stream initialized twice", id);
local_ids_[slot] = id;
return {};
}
std::span<const std::byte> http3_connection::local_prefix(http3_role role) const {
if (error_ || stopped_) return {};
if (role == http3_role::control) {
if (prefix_positions_[0] < control_prefix_.size()) return std::span(control_prefix_).subspan(prefix_positions_[0]);
if (!goaway_started_) return {};
const auto slot = goaway_positions_[0] < goaway_sizes_[0] ? 0U : 1U;
if (slot == 1 && !final_goaway_enabled_) return {};
return std::span(goaway_[slot]).first(goaway_sizes_[slot]).subspan(goaway_positions_[slot]);
}
if (role == http3_role::qpack_encoder) {
if (!prefix_positions_[1]) return encoder_prefix_;
return qpack_output_[0];
}
if (role == http3_role::qpack_decoder) {
if (!prefix_positions_[2]) return decoder_prefix_;
return qpack_output_[1];
}
return {};
}
bool http3_connection::advance_local_prefix(http3_role role, std::size_t count) {
if (role < http3_role::control || role > http3_role::qpack_decoder) return false;
const auto slot = static_cast<std::size_t>(role) - static_cast<std::size_t>(http3_role::control);
if (count > local_prefix(role).size()) return false;
if (slot == 0 && prefix_positions_[0] == control_prefix_.size() && goaway_started_) {
const auto stage = goaway_positions_[0] < goaway_sizes_[0] ? 0U : 1U;
goaway_positions_[stage] += count;
} else if (slot == 0 || prefix_positions_[slot] == 0) {
prefix_positions_[slot] += count;
} else {
auto& output = qpack_output_[slot - 1];
output.erase(output.begin(), output.begin() + count);
}
return true;
}
} // namespace httpserver::detail