doc: Add HPACK API tutorial
This commit is contained in:
parent
bd06f2cec3
commit
8235bb136b
|
@ -37,3 +37,4 @@ doc/tutorial-client.rst
|
||||||
doc/tutorial-server.rst
|
doc/tutorial-server.rst
|
||||||
doc/nghttpx-howto.rst
|
doc/nghttpx-howto.rst
|
||||||
doc/h2load-howto.rst
|
doc/h2load-howto.rst
|
||||||
|
doc/tutorial-hpack.rst
|
||||||
|
|
|
@ -511,6 +511,7 @@ AC_CONFIG_FILES([
|
||||||
doc/package_README.rst
|
doc/package_README.rst
|
||||||
doc/tutorial-client.rst
|
doc/tutorial-client.rst
|
||||||
doc/tutorial-server.rst
|
doc/tutorial-server.rst
|
||||||
|
doc/tutorial-hpack.rst
|
||||||
doc/nghttpx-howto.rst
|
doc/nghttpx-howto.rst
|
||||||
doc/h2load-howto.rst
|
doc/h2load-howto.rst
|
||||||
doc/nghttp2.h.rst
|
doc/nghttp2.h.rst
|
||||||
|
|
|
@ -30,6 +30,7 @@ EXTRA_DIST = \
|
||||||
sources/index.rst \
|
sources/index.rst \
|
||||||
sources/tutorial-client.rst \
|
sources/tutorial-client.rst \
|
||||||
sources/tutorial-server.rst \
|
sources/tutorial-server.rst \
|
||||||
|
sources/tutorial-hpack.rst \
|
||||||
sources/nghttpx-howto.rst \
|
sources/nghttpx-howto.rst \
|
||||||
sources/h2load-howto.rst \
|
sources/h2load-howto.rst \
|
||||||
_themes/sphinx_rtd_theme/footer.html \
|
_themes/sphinx_rtd_theme/footer.html \
|
||||||
|
|
|
@ -19,6 +19,7 @@ Contents:
|
||||||
package_README
|
package_README
|
||||||
tutorial-client
|
tutorial-client
|
||||||
tutorial-server
|
tutorial-server
|
||||||
|
tutorial-hpack
|
||||||
nghttpx-howto
|
nghttpx-howto
|
||||||
h2load-howto
|
h2load-howto
|
||||||
apiref
|
apiref
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
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 :type:`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 :type:`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 :type:`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
|
||||||
|
:type:`nghttp2_nv`. If a header field should not be inserted in
|
||||||
|
dynamic header table for a security reason, set
|
||||||
|
:macro:`NGHTTP2_NV_FLAG_NO_INDEX` flag in :member:`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 :type:`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 :type:`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 :type:`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 :macro:`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 :macro:`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 :macro:`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 :type:`nghttp2_hd_inflater` object, use `nghttp2_hd_inflate_del()`
|
||||||
|
function.
|
|
@ -0,0 +1,6 @@
|
||||||
|
.. include:: @top_srcdir@/doc/sources/tutorial-hpack.rst
|
||||||
|
|
||||||
|
deflate.c
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. literalinclude:: @top_srcdir@/examples/deflate.c
|
|
@ -1,3 +1,4 @@
|
||||||
client
|
client
|
||||||
libevent-client
|
libevent-client
|
||||||
libevent-server
|
libevent-server
|
||||||
|
deflate
|
||||||
|
|
|
@ -38,7 +38,7 @@ LDADD = \
|
||||||
$(top_builddir)/lib/libnghttp2.la \
|
$(top_builddir)/lib/libnghttp2.la \
|
||||||
$(top_builddir)/third-party/libhttp-parser.la
|
$(top_builddir)/third-party/libhttp-parser.la
|
||||||
|
|
||||||
noinst_PROGRAMS = client libevent-client libevent-server
|
noinst_PROGRAMS = client libevent-client libevent-server deflate
|
||||||
|
|
||||||
client_SOURCES = client.c
|
client_SOURCES = client.c
|
||||||
|
|
||||||
|
@ -46,4 +46,6 @@ libevent_client_SOURCES = libevent-client.c
|
||||||
|
|
||||||
libevent_server_SOURCES = libevent-server.c
|
libevent_server_SOURCES = libevent-server.c
|
||||||
|
|
||||||
|
deflate_SOURCES = deflate.c
|
||||||
|
|
||||||
endif # ENABLE_EXAMPLES
|
endif # ENABLE_EXAMPLES
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
Loading…
Reference in New Issue