Repository navigation
Expand file tree
/
Copy pathhpack_encoder.hpp
More file actions
105 lines (97 loc) · 5.06 KB
/
Copy pathhpack_encoder.hpp
File metadata and controls
105 lines (97 loc) · 5.06 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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/*
This file is part of libhttpserver
Copyright (C) 2011-2026 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the file
LICENSE in the distribution; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#if !defined(HTTPSERVER_COMPILATION)
#error "hpack_encoder.hpp is internal to libhttpserver"
#endif
#ifndef SRC_HTTPSERVER_DETAIL_HPACK_ENCODER_HPP_
#define SRC_HTTPSERVER_DETAIL_HPACK_ENCODER_HPP_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <new>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <utility>
#include <httpserver/detail/hpack_field_section.hpp>
namespace httpserver::detail {
// Encode at the final transmission-order boundary. A successful block must be
// sent without dropping/reordering it; otherwise close the connection. All
// operations require the connection's serialized execution boundary. Input
// octets must remain owned outside this encoder for the entire call.
class hpack_encoder {
public:
explicit hpack_encoder(server::resource_budget budget) : table_(std::move(budget)) {}
bool usable() const noexcept { return usable_; }
const hpack_dynamic_table& table() const noexcept { return table_; }
std::size_t peer_maximum() const noexcept { return peer_maximum_; }
hpack_status set_peer_maximum(std::uint64_t maximum) noexcept;
hpack_status choose_capacity(std::uint64_t capacity) noexcept;
hpack_bytes_result encode(std::span<const hpack_field_view> fields, hpack_section_limits limits, bool huffman = false);
hpack_bytes_result encode_section(std::span<const hpack_field> fields, hpack_section_limits limits, bool huffman = false);
// Semantic fields do not retain sensitivity. Policy is always explicit;
// use encode_section when relaying decoded never-indexed occurrences.
hpack_bytes_result encode_fields(const http::fields& fields, hpack_indexing policy, hpack_section_limits limits, bool huffman = false);
private:
static bool valid_indexing(hpack_indexing indexing) noexcept;
template<typename Range, typename View>
hpack_bytes_result encode_sequence(const Range& fields, View view, hpack_section_limits limits, bool huffman) {
// Validate every policy before emitting pending updates or changing tables.
for (const auto& field : fields) {
if (!valid_indexing(view(field).indexing)) return {{hpack_state::invalid_argument}, {}};
}
if (!usable_) return {{hpack_state::malformed}, {}};
try {
return process(fields, view, limits, huffman);
} catch (const std::bad_alloc&) {
return fail({hpack_state::limit_exceeded});
} catch (const std::length_error&) {
return fail({hpack_state::limit_exceeded});
}
}
template<typename Range, typename View>
hpack_bytes_result process(const Range& fields, View view, hpack_section_limits limits, bool huffman) {
if (fields.size() > limits.max_fields) return fail({hpack_state::limit_exceeded});
std::string wire;
auto status = updates(wire, limits.max_compressed_bytes);
if (!status.ok()) return fail(status);
auto remaining = limits.max_expanded_bytes;
for (const auto& occurrence : fields) {
const auto field = view(occurrence);
if (!hpack_admit_field(field.name.size(), field.value.size(), remaining)) return fail({hpack_state::limit_exceeded});
status = write_field(field, huffman, wire, limits.max_compressed_bytes);
if (!status.ok()) return fail(status);
}
pending_minimum_.reset();
const auto size = wire.size();
return {{}, std::move(wire), size};
}
hpack_status write_string(std::string_view value, bool huffman, std::string& wire, std::size_t limit);
hpack_status write_field(hpack_field_view field, bool huffman, std::string& wire, std::size_t limit);
hpack_status write_literal(hpack_field_view field, bool huffman, std::string& wire, std::size_t limit);
hpack_bytes_result fail(hpack_status status) noexcept;
static hpack_status append(hpack_bytes_result bytes, std::string& wire);
hpack_status updates(std::string& wire, std::size_t limit);
hpack_dynamic_table table_;
std::size_t peer_maximum_ = 4096;
std::optional<std::size_t> pending_minimum_;
bool usable_ = true;
};
} // namespace httpserver::detail
#endif // SRC_HTTPSERVER_DETAIL_HPACK_ENCODER_HPP_