Tutorial: HPACK API¶
+In this tutorial, we describe basic use of HPACK API in nghttp2 +library. We briefly describe APIs for deflating and inflating header +fields. The example of using these APIs are presented as complete +source code deflate.c.
+Deflating (encoding) headers¶
+First we need to initialize nghttp2_hd_deflater object using +nghttp2_hd_deflate_new() function:
+int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr,
+ size_t deflate_hd_table_bufsize_max);
+
This function allocates nghttp2_hd_deflater object and +initializes it and assigns its pointer to *deflater_ptr passed by +parameter. The deflate_hd_table_bufsize_max is the upper bound of +header table size the deflater will use. This will limit the memory +usage in deflater object for dynamic header table. If you doubt, just +specify 4096 here, which is the default upper bound of dynamic header +table buffer size.
+To encode header fields, nghttp2_hd_deflate_hd() function:
+ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
+ uint8_t *buf, size_t buflen,
+ const nghttp2_nv *nva, size_t nvlen);
+
The deflater is the deflater object initialized by +nghttp2_hd_deflate_new() function described above. The buf is a +pointer to buffer to store encoded byte string. The buflen is +capacity of buf. The nva is a pointer to nghttp2_nv, +which is an array of header fields to deflate. The nvlen is the +number of header fields which nva contains.
+It is important to initialize and assign all members of +nghttp2_nv. If a header field should not be inserted in +dynamic header table for a security reason, set +NGHTTP2_NV_FLAG_NO_INDEX flag in nghttp2_nv.flags.
+nghttp2_hd_deflate_hd() processes all headers given in nva. The +nva must include all request or response header fields to be sent in +one HEADERS (or optionally following (multiple) CONTINUATION +frame(s)). The buf must have enough space to store the encoded +result. Otherwise, the function will fail. To estimate the upper +bound of encoded result, use nghttp2_hd_deflate_bound() function:
+size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater,
+ const nghttp2_nv *nva, size_t nvlen);
+
Pass this function with the same paramters deflater, nva and +nvlen which will be passed to nghttp2_hd_deflate_hd().
+The subsequent call of nghttp2_hd_deflate_hd() will use current +encoder state and perform differential encoding which is the +fundamental compression gain for HPACK.
+Once nghttp2_hd_deflate_hd() fails, it cannot be undone and its +further call with the same deflater object shall fail. So it is very +important to use nghttp2_hd_deflate_bound() to know the required +size of buffer.
+To delete nghttp2_hd_deflater object, use nghttp2_hd_deflate_del() +function.
+Note
+Generally, the order of header fields passed to +nghttp2_hd_deflate_hd() function is not preserved. It is known +that the relative ordering of header fields which do not share the +same name is insignificant. But some header fields sharing the +same name require the explicit ordering. To preserve this +ordering, those header values are concatenated into single header +field value using NULL (0x00) as delimiter. This is transparent to +HPACK API. Therefore, the application should examine the inflated +header values and split into multiple header field values by NULL.
+Inflating (decoding) headers¶
+We use nghttp2_hd_inflater object to inflate compressed header +data. To initialize the object, use nghttp2_hd_inflate_new():
+int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr);
+
To inflate header data, use nghttp2_hd_inflate_hd() function:
+ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater,
+ nghttp2_nv *nv_out, int *inflate_flags,
+ uint8_t *in, size_t inlen, int in_final);
+
The inflater is the inflater object initialized above. The nv_out +is a pointer to nghttp2_nv to store the result. The in is a +pointer to input data and inlen is its length. The caller is not +required to specify whole deflated header data to in at once. It +can call this function multiple times for portion of the data in +streaming way. If in_final is nonzero, it tells the function that +the passed data is the final sequence of deflated header data. The +inflate_flags is output parameter and successful call of this +function stores a set of flags in it. It will be described later.
+This function returns when each header field is inflated. When this +happens, the function sets NGHTTP2_HD_INFLATE_EMIT flag to +inflate_flag parameter and header field is stored in nv_out. The +return value indicates the number of data read from in to processed +so far. It may be less than inlen. The caller should call the +function repeatedly until all data are processed by adjusting in and +inlen with the processed bytes.
+If in_final is nonzero and all given data was processed, the +function sets NGHTTP2_HD_INFLATE_FINAL flag to +inflate_flag. If the caller sees this flag set, call +nghttp2_hd_inflate_end_headers() function.
+If in_final is zero and NGHTTP2_HD_INFLATE_EMIT flag is not +set, it indicates that all given data was processed. The caller is +required to pass subsequent data.
+It is important to note that the function may produce one or more +header fields even if inlen is 0 when in_final is nonzero, due to +differential encoding.
+The example use of nghttp2_hd_inflate_hd() is shown in +inflate_header_block() function in deflate.c.
+To delete nghttp2_hd_inflater object, use nghttp2_hd_inflate_del() +function.
+deflate.c¶
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2014 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include <nghttp2/nghttp2.h>
+
+#define MAKE_NV(K, V) \
+ { (uint8_t*)K, (uint8_t*)V, sizeof(K) - 1, sizeof(V) - 1, \
+ NGHTTP2_NV_FLAG_NONE }
+
+static void deflate(nghttp2_hd_deflater *deflater,
+ nghttp2_hd_inflater *inflater,
+ const nghttp2_nv * const nva, size_t nvlen);
+
+static int inflate_header_block(nghttp2_hd_inflater *inflater,
+ uint8_t *in, size_t inlen, int final);
+
+int main(int argc, char **argv)
+{
+ int rv;
+ nghttp2_hd_deflater *deflater;
+ nghttp2_hd_inflater *inflater;
+ /* Define 1st header set. This is looks like a HTTP request. */
+ nghttp2_nv nva1[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":authority", "example.org"),
+ MAKE_NV(":path", "/"),
+ MAKE_NV("user-agent", "libnghttp2"),
+ MAKE_NV("accept-encoding", "gzip, deflate")
+ };
+ /* Define 2nd header set */
+ nghttp2_nv nva2[] = {
+ MAKE_NV(":scheme", "https"),
+ MAKE_NV(":authority", "example.org"),
+ MAKE_NV(":path", "/stylesheet/style.css"),
+ MAKE_NV("user-agent", "libnghttp2"),
+ MAKE_NV("accept-encoding", "gzip, deflate"),
+ MAKE_NV("referer", "https://example.org")
+ };
+
+ rv = nghttp2_hd_deflate_new(&deflater, 4096);
+
+ if(rv != 0) {
+ fprintf(stderr, "nghttp2_hd_deflate_init failed with error: %s\n",
+ nghttp2_strerror(rv));
+ exit(EXIT_FAILURE);
+ }
+
+ rv = nghttp2_hd_inflate_new(&inflater);
+
+ if(rv != 0) {
+ fprintf(stderr, "nghttp2_hd_inflate_init failed with error: %s\n",
+ nghttp2_strerror(rv));
+ exit(EXIT_FAILURE);
+ }
+
+ /* Encode and decode 1st header set */
+ deflate(deflater, inflater, nva1, sizeof(nva1) / sizeof(nva1[0]));
+
+ /* Encode and decode 2nd header set, using differential encoding
+ using state after encoding 1st header set. */
+ deflate(deflater, inflater, nva2, sizeof(nva2) / sizeof(nva2[0]));
+
+ nghttp2_hd_inflate_del(inflater);
+ nghttp2_hd_deflate_del(deflater);
+
+ return 0;
+}
+
+static void deflate(nghttp2_hd_deflater *deflater,
+ nghttp2_hd_inflater *inflater,
+ const nghttp2_nv * const nva, size_t nvlen)
+{
+ ssize_t rv;
+ uint8_t *buf;
+ size_t buflen;
+ size_t outlen;
+ size_t i;
+ size_t sum;
+
+ sum = 0;
+
+ for(i = 0; i < nvlen; ++i) {
+ sum += nva[i].namelen + nva[i].valuelen;
+ }
+
+ printf("Input (%zu byte(s)):\n\n", sum);
+
+ for(i = 0; i < nvlen; ++i) {
+ fwrite(nva[i].name, nva[i].namelen, 1, stdout);
+ printf(": ");
+ fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
+ printf("\n");
+ }
+
+ buflen = nghttp2_hd_deflate_bound(deflater, nva, nvlen);
+ buf = malloc(buflen);
+
+ rv = nghttp2_hd_deflate_hd(deflater, buf, buflen, nva, nvlen);
+
+ if(rv < 0) {
+ fprintf(stderr, "nghttp2_hd_deflate_hd() failed with error: %s\n",
+ nghttp2_strerror((int)rv));
+
+ free(buf);
+
+ exit(EXIT_FAILURE);
+ }
+
+ outlen = rv;
+
+ printf("\nDeflate (%zu byte(s), ratio %.02f):\n\n",
+ outlen, (double)outlen / sum);
+
+ for(i = 0; i < outlen; ++i) {
+ if((i & 0x0fu) == 0) {
+ printf("%08zX: ", i);
+ }
+
+ printf("%02X ", buf[i]);
+
+ if(((i + 1) & 0x0fu) == 0) {
+ printf("\n");
+ }
+ }
+
+ printf("\n\nInflate:\n\n");
+
+ /* We pass 1 to final parameter, because buf contains whole deflated
+ header data. */
+ rv = inflate_header_block(inflater, buf, outlen, 1);
+
+ if(rv != 0) {
+ free(buf);
+
+ exit(EXIT_FAILURE);
+ }
+
+ printf("\n-----------------------------------------------------------"
+ "--------------------\n");
+
+ free(buf);
+}
+
+int inflate_header_block(nghttp2_hd_inflater *inflater,
+ uint8_t *in, size_t inlen, int final)
+{
+ ssize_t rv;
+
+ for(;;) {
+ nghttp2_nv nv;
+ int inflate_flags = 0;
+ size_t proclen;
+
+ rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags,
+ in, inlen, final);
+
+ if(rv < 0) {
+ fprintf(stderr, "inflate failed with error code %zd", rv);
+ return -1;
+ }
+
+ proclen = rv;
+
+ in += proclen;
+ inlen -= proclen;
+
+ if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
+ fwrite(nv.name, nv.namelen, 1, stderr);
+ fprintf(stderr, ": ");
+ fwrite(nv.value, nv.valuelen, 1, stderr);
+ fprintf(stderr, "\n");
+ }
+
+ if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
+ nghttp2_hd_inflate_end_headers(inflater);
+ break;
+ }
+
+ if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 &&
+ inlen == 0) {
+ break;
+ }
+ }
+
+ return 0;
+}
+