1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_grpc_private/hpack.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include <array>
18*61c4878aSAndroid Build Coastguard Worker
19*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_bytes/byte_builder.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_string/string_builder.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_string/util.h"
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard Worker namespace pw::grpc {
28*61c4878aSAndroid Build Coastguard Worker
29*61c4878aSAndroid Build Coastguard Worker namespace {
30*61c4878aSAndroid Build Coastguard Worker #include "hpack.autogen.inc"
31*61c4878aSAndroid Build Coastguard Worker }
32*61c4878aSAndroid Build Coastguard Worker
33*61c4878aSAndroid Build Coastguard Worker // RFC 7541 §5.1
HpackIntegerDecode(ConstByteSpan & input,int bits_in_first_byte)34*61c4878aSAndroid Build Coastguard Worker Result<int> HpackIntegerDecode(ConstByteSpan& input, int bits_in_first_byte) {
35*61c4878aSAndroid Build Coastguard Worker if (input.empty()) {
36*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
37*61c4878aSAndroid Build Coastguard Worker }
38*61c4878aSAndroid Build Coastguard Worker
39*61c4878aSAndroid Build Coastguard Worker const int n = bits_in_first_byte;
40*61c4878aSAndroid Build Coastguard Worker int i = static_cast<int>(input[0]) & ((1 << n) - 1);
41*61c4878aSAndroid Build Coastguard Worker input = input.subspan(1);
42*61c4878aSAndroid Build Coastguard Worker
43*61c4878aSAndroid Build Coastguard Worker if (i < ((1 << n) - 1)) {
44*61c4878aSAndroid Build Coastguard Worker return i;
45*61c4878aSAndroid Build Coastguard Worker }
46*61c4878aSAndroid Build Coastguard Worker
47*61c4878aSAndroid Build Coastguard Worker int m = 0;
48*61c4878aSAndroid Build Coastguard Worker while (true) {
49*61c4878aSAndroid Build Coastguard Worker if (input.empty()) {
50*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
51*61c4878aSAndroid Build Coastguard Worker }
52*61c4878aSAndroid Build Coastguard Worker int b = static_cast<int>(input[0]);
53*61c4878aSAndroid Build Coastguard Worker input = input.subspan(1);
54*61c4878aSAndroid Build Coastguard Worker i += (b & 127) << m;
55*61c4878aSAndroid Build Coastguard Worker m += 7;
56*61c4878aSAndroid Build Coastguard Worker if ((b & 128) == 0) {
57*61c4878aSAndroid Build Coastguard Worker return i;
58*61c4878aSAndroid Build Coastguard Worker }
59*61c4878aSAndroid Build Coastguard Worker if (m >= 31) {
60*61c4878aSAndroid Build Coastguard Worker // Shift overflowed.
61*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
62*61c4878aSAndroid Build Coastguard Worker }
63*61c4878aSAndroid Build Coastguard Worker }
64*61c4878aSAndroid Build Coastguard Worker }
65*61c4878aSAndroid Build Coastguard Worker
66*61c4878aSAndroid Build Coastguard Worker // RFC 7541 §5.2
HpackStringDecode(ConstByteSpan & input)67*61c4878aSAndroid Build Coastguard Worker Result<InlineString<kHpackMaxStringSize>> HpackStringDecode(
68*61c4878aSAndroid Build Coastguard Worker ConstByteSpan& input) {
69*61c4878aSAndroid Build Coastguard Worker if (input.empty()) {
70*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
71*61c4878aSAndroid Build Coastguard Worker }
72*61c4878aSAndroid Build Coastguard Worker
73*61c4878aSAndroid Build Coastguard Worker int first = static_cast<int>(input[0]);
74*61c4878aSAndroid Build Coastguard Worker bool is_huffman = (first & 0x80) != 0;
75*61c4878aSAndroid Build Coastguard Worker
76*61c4878aSAndroid Build Coastguard Worker PW_TRY_ASSIGN(size_t length, HpackIntegerDecode(input, 7));
77*61c4878aSAndroid Build Coastguard Worker if (length > input.size()) {
78*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
79*61c4878aSAndroid Build Coastguard Worker }
80*61c4878aSAndroid Build Coastguard Worker if (length > kHpackMaxStringSize) {
81*61c4878aSAndroid Build Coastguard Worker return Status::OutOfRange();
82*61c4878aSAndroid Build Coastguard Worker }
83*61c4878aSAndroid Build Coastguard Worker
84*61c4878aSAndroid Build Coastguard Worker auto value = input.subspan(0, length);
85*61c4878aSAndroid Build Coastguard Worker input = input.subspan(length);
86*61c4878aSAndroid Build Coastguard Worker if (is_huffman) {
87*61c4878aSAndroid Build Coastguard Worker return HpackHuffmanDecode(value);
88*61c4878aSAndroid Build Coastguard Worker }
89*61c4878aSAndroid Build Coastguard Worker return InlineString<kHpackMaxStringSize>(
90*61c4878aSAndroid Build Coastguard Worker reinterpret_cast<const char*>(value.data()), value.size());
91*61c4878aSAndroid Build Coastguard Worker }
92*61c4878aSAndroid Build Coastguard Worker
HpackHuffmanDecode(ConstByteSpan input)93*61c4878aSAndroid Build Coastguard Worker Result<InlineString<kHpackMaxStringSize>> HpackHuffmanDecode(
94*61c4878aSAndroid Build Coastguard Worker ConstByteSpan input) {
95*61c4878aSAndroid Build Coastguard Worker StringBuffer<kHpackMaxStringSize> buffer;
96*61c4878aSAndroid Build Coastguard Worker int table_index = 0;
97*61c4878aSAndroid Build Coastguard Worker
98*61c4878aSAndroid Build Coastguard Worker // See definition of kHuffmanDecoderTable in hpack.autogen.h.
99*61c4878aSAndroid Build Coastguard Worker for (std::byte byte : input) {
100*61c4878aSAndroid Build Coastguard Worker for (int k = 7; k >= 0; k--) {
101*61c4878aSAndroid Build Coastguard Worker auto bit = int(byte >> k) & 0x1;
102*61c4878aSAndroid Build Coastguard Worker auto cmd = kHuffmanDecoderTable[table_index][bit];
103*61c4878aSAndroid Build Coastguard Worker if ((cmd & 0b1000'0000) == 0) {
104*61c4878aSAndroid Build Coastguard Worker table_index = cmd;
105*61c4878aSAndroid Build Coastguard Worker } else if (cmd == 0b1111'1110 || cmd == 0b1111'1111) {
106*61c4878aSAndroid Build Coastguard Worker // Error: unprintable character or the decoder entered an invalid state.
107*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
108*61c4878aSAndroid Build Coastguard Worker } else {
109*61c4878aSAndroid Build Coastguard Worker if (buffer.size() == buffer.max_size()) {
110*61c4878aSAndroid Build Coastguard Worker return Status::OutOfRange();
111*61c4878aSAndroid Build Coastguard Worker }
112*61c4878aSAndroid Build Coastguard Worker buffer.push_back(32 + (cmd & 0b0111'1111));
113*61c4878aSAndroid Build Coastguard Worker table_index = 0;
114*61c4878aSAndroid Build Coastguard Worker }
115*61c4878aSAndroid Build Coastguard Worker }
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker
118*61c4878aSAndroid Build Coastguard Worker return InlineString<kHpackMaxStringSize>(buffer.view());
119*61c4878aSAndroid Build Coastguard Worker }
120*61c4878aSAndroid Build Coastguard Worker
121*61c4878aSAndroid Build Coastguard Worker // RFC 7541 §6
HpackParseRequestHeaders(ConstByteSpan input)122*61c4878aSAndroid Build Coastguard Worker Result<InlineString<kHpackMaxStringSize>> HpackParseRequestHeaders(
123*61c4878aSAndroid Build Coastguard Worker ConstByteSpan input) {
124*61c4878aSAndroid Build Coastguard Worker while (!input.empty()) {
125*61c4878aSAndroid Build Coastguard Worker int first = static_cast<int>(input[0]);
126*61c4878aSAndroid Build Coastguard Worker
127*61c4878aSAndroid Build Coastguard Worker // RFC 7541 §6.1
128*61c4878aSAndroid Build Coastguard Worker if ((first & 0b1000'0000) != 0) {
129*61c4878aSAndroid Build Coastguard Worker PW_TRY_ASSIGN(int index, HpackIntegerDecode(input, 7));
130*61c4878aSAndroid Build Coastguard Worker // RFC 7541 Appendix A: these are the only static table entries for :path.
131*61c4878aSAndroid Build Coastguard Worker if (index == 4) {
132*61c4878aSAndroid Build Coastguard Worker return "/";
133*61c4878aSAndroid Build Coastguard Worker }
134*61c4878aSAndroid Build Coastguard Worker if (index == 5) {
135*61c4878aSAndroid Build Coastguard Worker return "/index.html";
136*61c4878aSAndroid Build Coastguard Worker }
137*61c4878aSAndroid Build Coastguard Worker continue;
138*61c4878aSAndroid Build Coastguard Worker }
139*61c4878aSAndroid Build Coastguard Worker
140*61c4878aSAndroid Build Coastguard Worker // RFC 7541 §6.3: dynamic table size update
141*61c4878aSAndroid Build Coastguard Worker if ((first & 0b1110'0000) == 0b0010'0000) {
142*61c4878aSAndroid Build Coastguard Worker // Ignore: we don't use the dynamic table.
143*61c4878aSAndroid Build Coastguard Worker PW_TRY(HpackIntegerDecode(input, 5));
144*61c4878aSAndroid Build Coastguard Worker continue;
145*61c4878aSAndroid Build Coastguard Worker }
146*61c4878aSAndroid Build Coastguard Worker
147*61c4878aSAndroid Build Coastguard Worker // RFC 7541 §6.2
148*61c4878aSAndroid Build Coastguard Worker int index;
149*61c4878aSAndroid Build Coastguard Worker if ((first & 0b1100'0000) == 0b0100'0000) {
150*61c4878aSAndroid Build Coastguard Worker PW_TRY_ASSIGN(index, HpackIntegerDecode(input, 6));
151*61c4878aSAndroid Build Coastguard Worker } else {
152*61c4878aSAndroid Build Coastguard Worker PW_CHECK((first & 0b1111'0000) == 0b0000'0000 ||
153*61c4878aSAndroid Build Coastguard Worker (first & 0b1111'0000) == 0b0001'0000);
154*61c4878aSAndroid Build Coastguard Worker PW_TRY_ASSIGN(index, HpackIntegerDecode(input, 4));
155*61c4878aSAndroid Build Coastguard Worker }
156*61c4878aSAndroid Build Coastguard Worker
157*61c4878aSAndroid Build Coastguard Worker // Check if the name is ":path".
158*61c4878aSAndroid Build Coastguard Worker bool is_path;
159*61c4878aSAndroid Build Coastguard Worker if (index == 0) {
160*61c4878aSAndroid Build Coastguard Worker PW_TRY_ASSIGN(auto name, HpackStringDecode(input));
161*61c4878aSAndroid Build Coastguard Worker is_path = (name == ":path");
162*61c4878aSAndroid Build Coastguard Worker } else {
163*61c4878aSAndroid Build Coastguard Worker // RFC 7541 Appendix A: these are the only static table entries for :path.
164*61c4878aSAndroid Build Coastguard Worker is_path = (index == 4 || index == 5);
165*61c4878aSAndroid Build Coastguard Worker }
166*61c4878aSAndroid Build Coastguard Worker
167*61c4878aSAndroid Build Coastguard Worker // Always extract the value to advance the `input` span.
168*61c4878aSAndroid Build Coastguard Worker PW_TRY_ASSIGN(auto value, HpackStringDecode(input));
169*61c4878aSAndroid Build Coastguard Worker if (is_path) {
170*61c4878aSAndroid Build Coastguard Worker return value;
171*61c4878aSAndroid Build Coastguard Worker }
172*61c4878aSAndroid Build Coastguard Worker }
173*61c4878aSAndroid Build Coastguard Worker
174*61c4878aSAndroid Build Coastguard Worker return Status::NotFound();
175*61c4878aSAndroid Build Coastguard Worker }
176*61c4878aSAndroid Build Coastguard Worker
ResponseHeadersPayload()177*61c4878aSAndroid Build Coastguard Worker ConstByteSpan ResponseHeadersPayload() {
178*61c4878aSAndroid Build Coastguard Worker return as_bytes(span{kResponseHeaderFields});
179*61c4878aSAndroid Build Coastguard Worker }
180*61c4878aSAndroid Build Coastguard Worker
ResponseTrailersPayload(Status response_code)181*61c4878aSAndroid Build Coastguard Worker ConstByteSpan ResponseTrailersPayload(Status response_code) {
182*61c4878aSAndroid Build Coastguard Worker PW_CHECK_UINT_LT(response_code.code(), kResponseTrailerFields.size());
183*61c4878aSAndroid Build Coastguard Worker auto* payload = &kResponseTrailerFields[response_code.code()];
184*61c4878aSAndroid Build Coastguard Worker return as_bytes(span{payload->bytes}.subspan(0, payload->size));
185*61c4878aSAndroid Build Coastguard Worker }
186*61c4878aSAndroid Build Coastguard Worker
187*61c4878aSAndroid Build Coastguard Worker } // namespace pw::grpc
188