blob: 006f1f1a9749e7d2a5e7de611691e956e68605cb [file] [log] [blame]
Amaury Denoyelle4652a592021-08-24 15:50:32 +02001#include <haproxy/qpack-enc.h>
Amaury Denoyellee0930fc2021-08-24 16:17:38 +02002
3#include <haproxy/buf.h>
4#include <haproxy/intops.h>
5
6/* Returns the byte size required to encode <i> as a <prefix_size>-prefix
7 * integer.
8 */
9static size_t qpack_get_prefix_int_size(int i, int prefix_size)
10{
11 int n = (1 << prefix_size) - 1;
12 if (i < n) {
13 return 1;
14 }
15 else {
16 size_t result = 0;
17 while (i) {
18 ++result;
19 i >>= 7;
20 }
21 return 1 + result;
22 }
23}
24
25/* Encode the integer <i> in the buffer <out> in a <prefix_size>-bit prefix
26 * integer. The caller must ensure there is enough size in the buffer. The
27 * prefix is OR-ed with <before_prefix> byte.
28 *
29 * Returns 0 if success else non-zero.
30 */
Amaury Denoyelle11f5a792022-05-30 14:24:40 +020031static int qpack_encode_prefix_integer(struct buffer *out, int i,
32 int prefix_size,
33 unsigned char before_prefix)
Amaury Denoyellee0930fc2021-08-24 16:17:38 +020034{
Amaury Denoyelle11f5a792022-05-30 14:24:40 +020035 const int mod = (1 << prefix_size) - 1;
36 BUG_ON_HOT(!prefix_size);
Amaury Denoyellee0930fc2021-08-24 16:17:38 +020037
Amaury Denoyelle11f5a792022-05-30 14:24:40 +020038 if (i < mod) {
Amaury Denoyelle5f6de8d2022-05-30 11:58:06 +020039 if (b_room(out) < 1)
Amaury Denoyellee0930fc2021-08-24 16:17:38 +020040 return 1;
41
42 b_putchr(out, before_prefix | i);
43 }
44 else {
Amaury Denoyelle11f5a792022-05-30 14:24:40 +020045 int to_encode = i - mod;
46 const size_t sz = to_encode / mod;
47
48 if (b_room(out) < sz)
Amaury Denoyellee0930fc2021-08-24 16:17:38 +020049 return 1;
50
Amaury Denoyelle11f5a792022-05-30 14:24:40 +020051 b_putchr(out, before_prefix | mod);
52 while (1) {
53 if (to_encode > 0x7f) {
54 b_putchr(out, 0x80 | (to_encode & 0x7f));
55 to_encode >>= 7;
56 }
57 else {
58 b_putchr(out, to_encode & 0x7f);
59 break;
60 }
61 }
Amaury Denoyellee0930fc2021-08-24 16:17:38 +020062 }
63
64 return 0;
65}
66
67/* Returns 0 on success else non-zero. */
68int qpack_encode_int_status(struct buffer *out, unsigned int status)
69{
70 int status_size, idx = 0;
71
Amaury Denoyellef54c14a2024-01-29 13:45:48 +010072 if (status < 100 || status > 999)
Amaury Denoyellefccffe02021-09-30 14:47:32 +020073 return 1;
74
Amaury Denoyellee0930fc2021-08-24 16:17:38 +020075 switch (status) {
76 case 103: idx = 24; break;
77 case 200: idx = 25; break;
78 case 304: idx = 26; break;
79 case 404: idx = 27; break;
80 case 503: idx = 28; break;
81 case 100: idx = 63; break;
82 case 204: idx = 64; break;
83 case 206: idx = 65; break;
84 case 302: idx = 66; break;
85 case 400: idx = 67; break;
86 case 403: idx = 68; break;
87 case 421: idx = 69; break;
88 case 425: idx = 70; break;
89 case 500: idx = 71; break;
90
Amaury Denoyelle3a590c72021-09-30 14:47:55 +020091 /* status code not in QPACK static table, idx is null. */
92 default: break;
Amaury Denoyellee0930fc2021-08-24 16:17:38 +020093 }
94
Amaury Denoyelle3a590c72021-09-30 14:47:55 +020095 if (idx) {
96 /* status code present in QPACK static table
97 * -> indexed field line
98 */
99 status_size = qpack_get_prefix_int_size(idx, 6);
100 if (b_room(out) < status_size)
101 return 1;
102
103 qpack_encode_prefix_integer(out, idx, 6, 0xc0);
104 }
105 else {
106 /* status code not present in QPACK static table
107 * -> literal field line with name reference
108 */
109 char a, b, c;
110 a = '0' + status / 100;
111 status -= (status / 100 * 100);
112 b = '0' + status / 10;
113 status -= (status / 10 * 10);
114 c = '0' + status;
115
116 /* field name */
117 if (qpack_encode_prefix_integer(out, 24, 4, 0x50))
118 return 1;
119
120 /* field value length */
121 if (qpack_encode_prefix_integer(out, 3, 7, 0x00))
122 return 1;
123
124 if (b_room(out) < 3)
125 return 1;
126
127 b_putchr(out, a);
128 b_putchr(out, b);
129 b_putchr(out, c);
130 }
Amaury Denoyellee0930fc2021-08-24 16:17:38 +0200131
Amaury Denoyellee0930fc2021-08-24 16:17:38 +0200132 return 0;
133}
134
135/* Returns 0 on success else non-zero. */
136int qpack_encode_field_section_line(struct buffer *out)
137{
Amaury Denoyellee0930fc2021-08-24 16:17:38 +0200138 char qpack_field_section[] = {
139 '\x00', /* required insert count */
140 '\x00', /* S + delta base */
141 };
Amaury Denoyelle3cae4042021-11-08 08:57:18 +0100142
143 if (b_room(out) < 2)
144 return 1;
145
Amaury Denoyellee0930fc2021-08-24 16:17:38 +0200146 b_putblk(out, qpack_field_section, 2);
147
148 return 0;
149}
150
151#define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name
152
153/* Encode a header in literal field line with literal name.
154 * Returns 0 on success else non-zero.
155 */
156int qpack_encode_header(struct buffer *out, const struct ist n, const struct ist v)
157{
158 int i;
159 size_t sz = qpack_get_prefix_int_size(n.len, 3) + n.len +
160 qpack_get_prefix_int_size(v.len, 7) + v.len;
161
162 if (sz > b_room(out))
163 return 1;
164
165 /* literal field line with literal name
166 * | 0 | 0 | 1 | N | H | . | . | . |
167 * N :(allow an intermediary to add the header in a dynamic table)
168 * H: huffman encoded
169 * name len
170 */
171 qpack_encode_prefix_integer(out, n.len, 3, QPACK_LFL_WLN_BIT);
172 /* name */
173 for (i = 0; i < n.len; ++i)
174 b_putchr(out, n.ptr[i]);
175
176 /* | 0 | . | . | . | . | . | . | . |
177 * value len
178 */
179 qpack_encode_prefix_integer(out, v.len, 7, 0x00);
180 /* value */
181 for (i = 0; i < v.len; ++i)
182 b_putchr(out, v.ptr[i]);
183
184 return 0;
185}