| #include <haproxy/qpack-enc.h> |
| |
| #include <haproxy/buf.h> |
| #include <haproxy/intops.h> |
| |
| /* Returns the byte size required to encode <i> as a <prefix_size>-prefix |
| * integer. |
| */ |
| static size_t qpack_get_prefix_int_size(int i, int prefix_size) |
| { |
| int n = (1 << prefix_size) - 1; |
| if (i < n) { |
| return 1; |
| } |
| else { |
| size_t result = 0; |
| while (i) { |
| ++result; |
| i >>= 7; |
| } |
| return 1 + result; |
| } |
| } |
| |
| /* Encode the integer <i> in the buffer <out> in a <prefix_size>-bit prefix |
| * integer. The caller must ensure there is enough size in the buffer. The |
| * prefix is OR-ed with <before_prefix> byte. |
| * |
| * Returns 0 if success else non-zero. |
| */ |
| static int qpack_encode_prefix_integer(struct buffer *out, int i, int prefix_size, unsigned char before_prefix) |
| { |
| BUG_ON(!prefix_size); |
| |
| if (i < (1 << prefix_size) - 1) { |
| if (b_data(out) < 1) |
| return 1; |
| |
| b_putchr(out, before_prefix | i); |
| } |
| else { |
| if (b_data(out) < 2) |
| return 1; |
| |
| b_putchr(out, before_prefix | ((1 << prefix_size) - 1)); |
| b_putchr(out, i - ((1 << prefix_size) - 1)); |
| } |
| |
| return 0; |
| } |
| |
| /* Returns 0 on success else non-zero. */ |
| int qpack_encode_int_status(struct buffer *out, unsigned int status) |
| { |
| int status_size, idx = 0; |
| |
| switch (status) { |
| case 103: idx = 24; break; |
| case 200: idx = 25; break; |
| case 304: idx = 26; break; |
| case 404: idx = 27; break; |
| case 503: idx = 28; break; |
| case 100: idx = 63; break; |
| case 204: idx = 64; break; |
| case 206: idx = 65; break; |
| case 302: idx = 66; break; |
| case 400: idx = 67; break; |
| case 403: idx = 68; break; |
| case 421: idx = 69; break; |
| case 425: idx = 70; break; |
| case 500: idx = 71; break; |
| |
| default: |
| BUG_ON(1); |
| break; |
| } |
| |
| status_size = qpack_get_prefix_int_size(idx, 6); |
| if (b_room(out) < status_size) |
| return 1; |
| |
| //b_quic_enc_int(out, idx); |
| qpack_encode_prefix_integer(out, idx, 6, 0xc0); |
| return 0; |
| } |
| |
| /* Returns 0 on success else non-zero. */ |
| int qpack_encode_field_section_line(struct buffer *out) |
| { |
| if (b_room(out) < 2) |
| return 1; |
| |
| char qpack_field_section[] = { |
| '\x00', /* required insert count */ |
| '\x00', /* S + delta base */ |
| }; |
| b_putblk(out, qpack_field_section, 2); |
| |
| return 0; |
| } |
| |
| #define QPACK_LFL_WLN_BIT 0x20 // Literal field line with literal name |
| |
| /* Encode a header in literal field line with literal name. |
| * Returns 0 on success else non-zero. |
| */ |
| int qpack_encode_header(struct buffer *out, const struct ist n, const struct ist v) |
| { |
| int i; |
| size_t sz = qpack_get_prefix_int_size(n.len, 3) + n.len + |
| qpack_get_prefix_int_size(v.len, 7) + v.len; |
| |
| if (sz > b_room(out)) |
| return 1; |
| |
| /* literal field line with literal name |
| * | 0 | 0 | 1 | N | H | . | . | . | |
| * N :(allow an intermediary to add the header in a dynamic table) |
| * H: huffman encoded |
| * name len |
| */ |
| qpack_encode_prefix_integer(out, n.len, 3, QPACK_LFL_WLN_BIT); |
| /* name */ |
| for (i = 0; i < n.len; ++i) |
| b_putchr(out, n.ptr[i]); |
| |
| /* | 0 | . | . | . | . | . | . | . | |
| * value len |
| */ |
| qpack_encode_prefix_integer(out, v.len, 7, 0x00); |
| /* value */ |
| for (i = 0; i < v.len; ++i) |
| b_putchr(out, v.ptr[i]); |
| |
| return 0; |
| } |