summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/.gitignore1
-rw-r--r--examples/CMakeLists.txt50
-rw-r--r--examples/Makefile.am46
-rw-r--r--examples/qpack.cc143
-rw-r--r--examples/qpack.h44
-rw-r--r--examples/qpack_decode.cc299
-rw-r--r--examples/qpack_decode.h93
-rw-r--r--examples/qpack_encode.cc221
-rw-r--r--examples/qpack_encode.h59
-rw-r--r--examples/template.h77
-rw-r--r--examples/util.cc27
-rw-r--r--examples/util.h64
12 files changed, 1124 insertions, 0 deletions
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..20a9df3
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1 @@
+/qpack
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..31ec1aa
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,50 @@
+# ngtcp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2017 ngtcp2 contributors
+#
+# 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.
+
+if(ENABLE_EXAMPLES)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/lib/includes
+ ${CMAKE_BINARY_DIR}/lib/includes
+ )
+
+ link_libraries(
+ nghttp3
+ )
+
+ set(qpack_SOURCES
+ qpack.cc
+ qpack_encode.cc
+ qpack_decode.cc
+ util.cc
+ )
+
+ add_executable(qpack ${qpack_SOURCES})
+ set_target_properties(qpack PROPERTIES
+ COMPILE_FLAGS "${WARNCXXFLAGS}"
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ )
+
+ # TODO prevent qpack example from being installed?
+endif()
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..8c90892
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,46 @@
+# ngtcp3
+#
+# Copyright (c) 2019 nghttp3 contributors
+# Copyright (c) 2017 ngtcp2 contributors
+#
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+if ENABLE_EXAMPLES
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS)
+AM_CXXFLAGS = $(WARNCXXFLAGS) $(DEBUGCFLAGS)
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/lib/includes \
+ -I$(top_builddir)/lib/includes \
+ @DEFS@
+AM_LDFLAGS = -no-install
+LDADD = $(top_builddir)/lib/libnghttp3.la
+
+noinst_PROGRAMS = qpack
+
+qpack_SOURCES = \
+ qpack.cc qpack.h \
+ qpack_encode.cc qpack_encode.h \
+ qpack_decode.cc qpack_decode.h \
+ template.h \
+ util.cc util.h
+
+endif # ENABLE_EXAMPLES
diff --git a/examples/qpack.cc b/examples/qpack.cc
new file mode 100644
index 0000000..1353ad9
--- /dev/null
+++ b/examples/qpack.cc
@@ -0,0 +1,143 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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 "qpack.h"
+
+#include <cstring>
+#include <iostream>
+#include <string>
+
+#include <getopt.h>
+
+#include "qpack_encode.h"
+#include "qpack_decode.h"
+
+namespace nghttp3 {
+
+Config config{};
+
+namespace {
+void print_usage() {
+ std::cerr << "Usage: qpack [OPTIONS] <COMMAND> <INFILE> <OUTFILE>"
+ << std::endl;
+}
+} // namespace
+
+namespace {
+void print_help() {
+ print_usage();
+
+ std::cerr << R"(
+ <COMMAND> "encode" or "decode"
+ <INFILE> Path to an input file
+ <OUTFILE> Path to an output file
+Options:
+ -h, --help Display this help and exit.
+ -m, --max-blocked=<N>
+ The maximum number of streams which are permitted to be blocked.
+ -s, --max-dtable-size=<N>
+ The maximum size of dynamic table.
+ -a, --immediate-ack
+ Turn on immediate acknowlegement.
+)";
+}
+} // namespace
+
+int main(int argc, char **argv) {
+ for (;;) {
+ static int flag = 0;
+ (void)flag;
+ constexpr static option long_opts[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {"max-blocked", required_argument, nullptr, 'm'},
+ {"max-dtable-size", required_argument, nullptr, 's'},
+ {"immediate-ack", no_argument, nullptr, 'a'},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ auto optidx = 0;
+ auto c = getopt_long(argc, argv, "hm:s:a", long_opts, &optidx);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 'h':
+ // --help
+ print_help();
+ exit(EXIT_SUCCESS);
+ case 'm': {
+ // --max-blocked
+ config.max_blocked = strtoul(optarg, nullptr, 10);
+ break;
+ }
+ case 's': {
+ // --max-dtable-size
+ config.max_dtable_size = strtoul(optarg, nullptr, 10);
+ break;
+ }
+ case 'a':
+ // --immediate-ack
+ config.immediate_ack = true;
+ break;
+ case '?':
+ print_usage();
+ exit(EXIT_FAILURE);
+ case 0:
+ break;
+ default:
+ break;
+ };
+ }
+
+ if (argc - optind < 3) {
+ std::cerr << "Too few arguments" << std::endl;
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ auto command = std::string_view(argv[optind++]);
+ auto infile = std::string_view(argv[optind++]);
+ auto outfile = std::string_view(argv[optind++]);
+
+ int rv;
+ if (command == "encode") {
+ rv = encode(outfile, infile);
+ } else if (command == "decode") {
+ rv = decode(outfile, infile);
+ } else {
+ std::cerr << "Unrecognized command: " << command << std::endl;
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if (rv != 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+} // namespace nghttp3
+
+int main(int argc, char **argv) { return nghttp3::main(argc, argv); }
diff --git a/examples/qpack.h b/examples/qpack.h
new file mode 100644
index 0000000..d0b0ce6
--- /dev/null
+++ b/examples/qpack.h
@@ -0,0 +1,44 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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.
+ */
+#ifndef QPACK_H
+#define QPACK_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+namespace nghttp3 {
+
+struct Config {
+ size_t max_blocked;
+ size_t max_dtable_size;
+ bool immediate_ack;
+};
+
+} // namespace nghttp3
+
+#endif // QPACK_H
diff --git a/examples/qpack_decode.cc b/examples/qpack_decode.cc
new file mode 100644
index 0000000..22e44c8
--- /dev/null
+++ b/examples/qpack_decode.cc
@@ -0,0 +1,299 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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 "qpack_decode.h"
+
+#include <arpa/inet.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <cassert>
+#include <cstring>
+#include <cerrno>
+#include <iostream>
+#include <fstream>
+
+#include "qpack.h"
+#include "template.h"
+#include "util.h"
+
+namespace nghttp3 {
+
+extern Config config;
+
+Request::Request(int64_t stream_id, const nghttp3_buf *buf)
+ : buf(*buf), stream_id(stream_id) {
+ auto mem = nghttp3_mem_default();
+ nghttp3_qpack_stream_context_new(&sctx, stream_id, mem);
+}
+
+Request::~Request() { nghttp3_qpack_stream_context_del(sctx); }
+
+Decoder::Decoder(size_t max_dtable_size, size_t max_blocked)
+ : mem_(nghttp3_mem_default()),
+ dec_(nullptr),
+ max_dtable_size_(max_dtable_size),
+ max_blocked_(max_blocked) {}
+
+Decoder::~Decoder() { nghttp3_qpack_decoder_del(dec_); }
+
+int Decoder::init() {
+ if (auto rv = nghttp3_qpack_decoder_new(&dec_, max_dtable_size_, max_blocked_,
+ mem_);
+ rv != 0) {
+ std::cerr << "nghttp3_qpack_decoder_new: " << nghttp3_strerror(rv)
+ << std::endl;
+ return -1;
+ }
+
+ if (auto rv =
+ nghttp3_qpack_decoder_set_max_dtable_capacity(dec_, max_dtable_size_);
+ rv != 0) {
+ std::cerr << "nghttp3_qpack_decoder_set_max_dtable_capacity: "
+ << nghttp3_strerror(rv) << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+int Decoder::read_encoder(nghttp3_buf *buf) {
+ auto nread =
+ nghttp3_qpack_decoder_read_encoder(dec_, buf->pos, nghttp3_buf_len(buf));
+ if (nread < 0) {
+ std::cerr << "nghttp3_qpack_decoder_read_encoder: "
+ << nghttp3_strerror(nread) << std::endl;
+ return -1;
+ }
+
+ assert(static_cast<size_t>(nread) == nghttp3_buf_len(buf));
+
+ return 0;
+}
+
+std::tuple<Headers, int> Decoder::read_request(nghttp3_buf *buf,
+ int64_t stream_id) {
+ auto req = std::make_shared<Request>(stream_id, buf);
+
+ auto [headers, rv] = read_request(*req);
+ if (rv == -1) {
+ return {Headers{}, -1};
+ }
+ if (rv == 1) {
+ if (blocked_reqs_.size() >= max_blocked_) {
+ std::cerr << "Too many blocked streams: max_blocked=" << max_blocked_
+ << std::endl;
+ return {Headers{}, -1};
+ }
+ blocked_reqs_.emplace(std::move(req));
+ return {Headers{}, 1};
+ }
+ return {headers, 0};
+}
+
+std::tuple<Headers, int> Decoder::read_request(Request &req) {
+ nghttp3_qpack_nv nv;
+ uint8_t flags;
+ Headers headers;
+
+ for (;;) {
+ auto nread = nghttp3_qpack_decoder_read_request(
+ dec_, req.sctx, &nv, &flags, req.buf.pos, nghttp3_buf_len(&req.buf), 1);
+ if (nread < 0) {
+ std::cerr << "nghttp3_qpack_decoder_read_request: "
+ << nghttp3_strerror(nread) << std::endl;
+ return {Headers{}, -1};
+ }
+
+ req.buf.pos += nread;
+
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) {
+ break;
+ }
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) {
+ return {Headers{}, 1};
+ }
+ if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) {
+ auto name = nghttp3_rcbuf_get_buf(nv.name);
+ auto value = nghttp3_rcbuf_get_buf(nv.value);
+ headers.emplace_back(std::string{name.base, name.base + name.len},
+ std::string{value.base, value.base + value.len});
+ nghttp3_rcbuf_decref(nv.name);
+ nghttp3_rcbuf_decref(nv.value);
+ }
+ }
+
+ return {headers, 0};
+}
+
+std::tuple<int64_t, Headers, int> Decoder::process_blocked() {
+ if (!blocked_reqs_.empty()) {
+ auto &top = blocked_reqs_.top();
+ if (nghttp3_qpack_stream_context_get_ricnt(top->sctx) >
+ nghttp3_qpack_decoder_get_icnt(dec_)) {
+ return {-1, {}, 0};
+ }
+
+ auto req = top;
+ blocked_reqs_.pop();
+
+ auto [headers, rv] = read_request(*req);
+ if (rv < 0) {
+ return {-1, {}, -1};
+ }
+ assert(rv == 0);
+
+ return {req->stream_id, headers, 0};
+ }
+ return {-1, {}, 0};
+}
+
+size_t Decoder::get_num_blocked() const { return blocked_reqs_.size(); }
+
+namespace {
+void write_header(
+ std::ostream &out,
+ const std::vector<std::pair<std::string, std::string>> &headers) {
+ for (auto &nv : headers) {
+ out.write(nv.first.c_str(), nv.first.size());
+ out.put('\t');
+ out.write(nv.second.c_str(), nv.second.size());
+ out.put('\n');
+ }
+ out.put('\n');
+}
+} // namespace
+
+int decode(const std::string_view &outfile, const std::string_view &infile) {
+ auto fd = open(infile.data(), O_RDONLY);
+ if (fd == -1) {
+ std::cerr << "Could not open " << infile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto fd_closer = defer(close, fd);
+
+ struct stat st;
+ if (fstat(fd, &st) == -1) {
+ std::cerr << "fstat: " << strerror(errno) << std::endl;
+ return -1;
+ }
+
+ auto out = std::ofstream(outfile.data(), std::ios::trunc | std::ios::binary);
+ if (!out) {
+ std::cerr << "Could not open file " << outfile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto in = reinterpret_cast<uint8_t *>(
+ mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0));
+ if (in == MAP_FAILED) {
+ std::cerr << "mmap: " << strerror(errno) << std::endl;
+ return -1;
+ }
+
+ auto unmapper = defer(munmap, in, st.st_size);
+
+ auto dec = Decoder(config.max_dtable_size, config.max_blocked);
+ if (auto rv = dec.init(); rv != 0) {
+ return rv;
+ }
+
+ for (auto p = in, end = in + st.st_size; p != end;) {
+ int64_t stream_id;
+ uint32_t size;
+
+ if (static_cast<size_t>(end - p) < sizeof(stream_id) + sizeof(size)) {
+ std::cerr << "Could not read stream ID and size" << std::endl;
+ return -1;
+ }
+
+ memcpy(&stream_id, p, sizeof(stream_id));
+ stream_id = nghttp3_ntohl64(stream_id);
+ p += sizeof(stream_id);
+
+ memcpy(&size, p, sizeof(size));
+ size = ntohl(size);
+ p += sizeof(size);
+
+ if ((size_t)(end - p) < size) {
+ std::cerr << "Insufficient input: require " << size << " but "
+ << (end - p) << " is available" << std::endl;
+ return -1;
+ }
+
+ nghttp3_buf buf;
+ buf.begin = buf.pos = p;
+ buf.end = buf.last = p + size;
+
+ p += size;
+
+ if (stream_id == 0) {
+ if (auto rv = dec.read_encoder(&buf); rv != 0) {
+ return rv;
+ }
+
+ for (;;) {
+ auto [stream_id, headers, rv] = dec.process_blocked();
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (stream_id == -1) {
+ break;
+ }
+
+ write_header(out, headers);
+ }
+
+ continue;
+ }
+
+ auto [headers, rv] = dec.read_request(&buf, stream_id);
+ if (rv == -1) {
+ return rv;
+ }
+ if (rv == 1) {
+ // Stream blocked
+ continue;
+ }
+
+ write_header(out, headers);
+ }
+
+ if (auto n = dec.get_num_blocked(); n) {
+ std::cerr << "Still " << n << " stream(s) blocked" << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+} // namespace nghttp3
diff --git a/examples/qpack_decode.h b/examples/qpack_decode.h
new file mode 100644
index 0000000..8641b3a
--- /dev/null
+++ b/examples/qpack_decode.h
@@ -0,0 +1,93 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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.
+ */
+#ifndef QPACK_DECODE_H
+#define QPACK_DECODE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include <vector>
+#include <queue>
+#include <functional>
+#include <utility>
+#include <memory>
+#include <string>
+
+namespace nghttp3 {
+struct Request {
+ Request(int64_t stream_id, const nghttp3_buf *buf);
+ ~Request();
+
+ nghttp3_buf buf;
+ nghttp3_qpack_stream_context *sctx;
+ int64_t stream_id;
+};
+} // namespace nghttp3
+
+namespace std {
+template <> struct greater<std::shared_ptr<nghttp3::Request>> {
+ bool operator()(const std::shared_ptr<nghttp3::Request> &lhs,
+ const std::shared_ptr<nghttp3::Request> &rhs) const {
+ return nghttp3_qpack_stream_context_get_ricnt(lhs->sctx) >
+ nghttp3_qpack_stream_context_get_ricnt(rhs->sctx);
+ }
+};
+} // namespace std
+
+namespace nghttp3 {
+
+using Headers = std::vector<std::pair<std::string, std::string>>;
+
+class Decoder {
+public:
+ Decoder(size_t max_dtable_size, size_t max_blocked);
+ ~Decoder();
+
+ int init();
+ int read_encoder(nghttp3_buf *buf);
+ std::tuple<Headers, int> read_request(nghttp3_buf *buf, int64_t stream_id);
+ std::tuple<Headers, int> read_request(Request &req);
+ std::tuple<int64_t, Headers, int> process_blocked();
+ size_t get_num_blocked() const;
+
+private:
+ const nghttp3_mem *mem_;
+ nghttp3_qpack_decoder *dec_;
+ std::priority_queue<std::shared_ptr<Request>,
+ std::vector<std::shared_ptr<Request>>,
+ std::greater<std::shared_ptr<Request>>>
+ blocked_reqs_;
+ size_t max_dtable_size_;
+ size_t max_blocked_;
+};
+
+int decode(const std::string_view &outfile, const std::string_view &infile);
+
+} // namespace nghttp3
+
+#endif // QPACK_ENCODE_H
diff --git a/examples/qpack_encode.cc b/examples/qpack_encode.cc
new file mode 100644
index 0000000..867a085
--- /dev/null
+++ b/examples/qpack_encode.cc
@@ -0,0 +1,221 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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 "qpack_encode.h"
+
+#include <arpa/inet.h>
+
+#include <cerrno>
+#include <cstring>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+#include <array>
+#include <iomanip>
+#include <vector>
+
+#include "qpack.h"
+#include "template.h"
+#include "util.h"
+
+namespace nghttp3 {
+
+extern Config config;
+
+Encoder::Encoder(size_t max_dtable_size, size_t max_blocked, bool immediate_ack)
+ : mem_(nghttp3_mem_default()),
+ enc_(nullptr),
+ max_dtable_size_(max_dtable_size),
+ max_blocked_(max_blocked),
+ immediate_ack_(immediate_ack) {}
+
+Encoder::~Encoder() { nghttp3_qpack_encoder_del(enc_); }
+
+int Encoder::init() {
+ int rv;
+
+ rv = nghttp3_qpack_encoder_new(&enc_, max_dtable_size_, mem_);
+ if (rv != 0) {
+ std::cerr << "nghttp3_qpack_encoder_new: " << nghttp3_strerror(rv)
+ << std::endl;
+ return -1;
+ }
+
+ nghttp3_qpack_encoder_set_max_dtable_capacity(enc_, max_dtable_size_);
+ nghttp3_qpack_encoder_set_max_blocked_streams(enc_, max_blocked_);
+
+ return 0;
+}
+
+int Encoder::encode(nghttp3_buf *pbuf, nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t stream_id, const nghttp3_nv *nva, size_t len) {
+ auto rv =
+ nghttp3_qpack_encoder_encode(enc_, pbuf, rbuf, ebuf, stream_id, nva, len);
+ if (rv != 0) {
+ std::cerr << "nghttp3_qpack_encoder_encode: " << nghttp3_strerror(rv)
+ << std::endl;
+ return -1;
+ }
+ if (immediate_ack_) {
+ nghttp3_qpack_encoder_ack_everything(enc_);
+ }
+ return 0;
+}
+
+namespace {
+void write_encoder_stream(std::ostream &out, nghttp3_buf *ebuf) {
+ uint64_t stream_id = 0;
+ out.write(reinterpret_cast<char *>(&stream_id), sizeof(stream_id));
+ uint32_t size = htonl(nghttp3_buf_len(ebuf));
+ out.write(reinterpret_cast<char *>(&size), sizeof(size));
+ out.write(reinterpret_cast<char *>(ebuf->pos), nghttp3_buf_len(ebuf));
+}
+} // namespace
+
+namespace {
+void write_request_stream(std::ostream &out, int64_t stream_id,
+ nghttp3_buf *pbuf, nghttp3_buf *rbuf) {
+ stream_id = nghttp3_htonl64(stream_id);
+ out.write(reinterpret_cast<char *>(&stream_id), sizeof(stream_id));
+ uint32_t size = htonl(nghttp3_buf_len(pbuf) + nghttp3_buf_len(rbuf));
+ out.write(reinterpret_cast<char *>(&size), sizeof(size));
+ out.write(reinterpret_cast<char *>(pbuf->pos), nghttp3_buf_len(pbuf));
+ out.write(reinterpret_cast<char *>(rbuf->pos), nghttp3_buf_len(rbuf));
+}
+} // namespace
+
+int encode(const std::string_view &outfile, const std::string_view &infile) {
+ auto in = std::ifstream(infile.data(), std::ios::binary);
+
+ if (!in) {
+ std::cerr << "Could not open file " << infile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto out = std::ofstream(outfile.data(), std::ios::trunc | std::ios::binary);
+ if (!out) {
+ std::cerr << "Could not open file " << outfile << ": " << strerror(errno)
+ << std::endl;
+ return -1;
+ }
+
+ auto enc =
+ Encoder(config.max_dtable_size, config.max_blocked, config.immediate_ack);
+ if (enc.init() != 0) {
+ return -1;
+ }
+
+ nghttp3_buf pbuf, rbuf, ebuf;
+ nghttp3_buf_init(&pbuf);
+ nghttp3_buf_init(&rbuf);
+ nghttp3_buf_init(&ebuf);
+
+ auto mem = nghttp3_mem_default();
+ auto pbufd = defer(nghttp3_buf_free, &pbuf, mem);
+ auto rbufd = defer(nghttp3_buf_free, &rbuf, mem);
+ auto ebufd = defer(nghttp3_buf_free, &ebuf, mem);
+
+ int64_t stream_id = 1;
+ std::array<std::string, 1024> sarray;
+
+ size_t srclen = 0;
+ size_t enclen = 0;
+ size_t rslen = 0;
+ size_t eslen = 0;
+
+ for (; in;) {
+ auto nva = std::vector<nghttp3_nv>();
+ for (std::string line; std::getline(in, line);) {
+ if (line == "") {
+ break;
+ }
+
+ if (sarray.size() == nva.size()) {
+ std::cerr << "Too many headers: " << nva.size() << std::endl;
+ return -1;
+ }
+
+ sarray[nva.size()] = line;
+ const auto &s = sarray[nva.size()];
+
+ auto d = s.find('\t');
+ if (d == std::string_view::npos) {
+ std::cerr << "Could not find TAB in " << s << std::endl;
+ return -1;
+ }
+ auto name = std::string_view(s.c_str(), d);
+ auto value = std::string_view(s.c_str() + d + 1, s.size() - d - 1);
+ value.remove_prefix(std::min(value.find_first_not_of(" "), value.size()));
+
+ srclen += name.size() + value.size();
+
+ nva.emplace_back(nghttp3_nv{
+ const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(name.data())),
+ const_cast<uint8_t *>(
+ reinterpret_cast<const uint8_t *>(value.data())),
+ name.size(), value.size()});
+ }
+
+ if (nva.empty()) {
+ break;
+ }
+
+ if (auto rv =
+ enc.encode(&pbuf, &rbuf, &ebuf, stream_id, nva.data(), nva.size());
+ rv != 0) {
+ return -1;
+ }
+
+ enclen += nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf) +
+ nghttp3_buf_len(&ebuf);
+
+ if (nghttp3_buf_len(&ebuf)) {
+ write_encoder_stream(out, &ebuf);
+ }
+ write_request_stream(out, stream_id, &pbuf, &rbuf);
+
+ rslen += nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf);
+ eslen += nghttp3_buf_len(&ebuf);
+
+ nghttp3_buf_reset(&pbuf);
+ nghttp3_buf_reset(&rbuf);
+ nghttp3_buf_reset(&ebuf);
+
+ ++stream_id;
+ }
+
+ if (srclen == 0) {
+ std::cerr << "No header field processed" << std::endl;
+ } else {
+ std::cerr << srclen << " -> " << enclen << " (r:" << rslen
+ << " + e:" << eslen << ") " << std::fixed << std::setprecision(2)
+ << (1. - (static_cast<double>(enclen) / srclen)) * 100
+ << "% compressed" << std::endl;
+ }
+ return 0;
+}
+
+} // namespace nghttp3
diff --git a/examples/qpack_encode.h b/examples/qpack_encode.h
new file mode 100644
index 0000000..b310ecd
--- /dev/null
+++ b/examples/qpack_encode.h
@@ -0,0 +1,59 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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.
+ */
+#ifndef QPACK_ENCODE_H
+#define QPACK_ENCODE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <nghttp3/nghttp3.h>
+
+#include <string>
+
+namespace nghttp3 {
+
+class Encoder {
+public:
+ Encoder(size_t max_dtable_size, size_t max_blocked, bool immediate_ack);
+ ~Encoder();
+
+ int init();
+ int encode(nghttp3_buf *pbuf, nghttp3_buf *rbuf, nghttp3_buf *ebuf,
+ int64_t stream_id, const nghttp3_nv *nva, size_t len);
+
+private:
+ const nghttp3_mem *mem_;
+ nghttp3_qpack_encoder *enc_;
+ size_t max_dtable_size_;
+ size_t max_blocked_;
+ bool immediate_ack_;
+};
+
+int encode(const std::string_view &outfile, const std::string_view &infile);
+
+} // namespace nghttp3
+
+#endif // QPACK_ENCODE_H
diff --git a/examples/template.h b/examples/template.h
new file mode 100644
index 0000000..1f758b4
--- /dev/null
+++ b/examples/template.h
@@ -0,0 +1,77 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2015 ngttp2 contributors
+ *
+ * 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.
+ */
+#ifndef TEMPLATE_H
+#define TEMPLATE_H
+
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+namespace nghttp3 {
+
+// inspired by <http://blog.korfuri.fr/post/go-defer-in-cpp/>, but our
+// template can take functions returning other than void.
+template <typename F, typename... T> struct Defer {
+ Defer(F &&f, T &&...t)
+ : f(std::bind(std::forward<F>(f), std::forward<T>(t)...)) {}
+ Defer(Defer &&o) noexcept : f(std::move(o.f)) {}
+ ~Defer() { f(); }
+
+ using ResultType = typename std::result_of<typename std::decay<F>::type(
+ typename std::decay<T>::type...)>::type;
+ std::function<ResultType()> f;
+};
+
+template <typename F, typename... T> Defer<F, T...> defer(F &&f, T &&...t) {
+ return Defer<F, T...>(std::forward<F>(f), std::forward<T>(t)...);
+}
+
+template <typename T, size_t N> constexpr size_t array_size(T (&)[N]) {
+ return N;
+}
+
+template <typename T, size_t N> constexpr size_t str_size(T (&)[N]) {
+ return N - 1;
+}
+
+// User-defined literals for K, M, and G (powers of 1024)
+
+constexpr unsigned long long operator"" _k(unsigned long long k) {
+ return k * 1024;
+}
+
+constexpr unsigned long long operator"" _m(unsigned long long m) {
+ return m * 1024 * 1024;
+}
+
+constexpr unsigned long long operator"" _g(unsigned long long g) {
+ return g * 1024 * 1024 * 1024;
+}
+
+} // namespace nghttp3
+
+#endif // TEMPLATE_H
diff --git a/examples/util.cc b/examples/util.cc
new file mode 100644
index 0000000..8973825
--- /dev/null
+++ b/examples/util.cc
@@ -0,0 +1,27 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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 "util.h"
+
+namespace nghttp3 {} // namespace nghttp3
diff --git a/examples/util.h b/examples/util.h
new file mode 100644
index 0000000..8e06086
--- /dev/null
+++ b/examples/util.h
@@ -0,0 +1,64 @@
+/*
+ * nghttp3
+ *
+ * Copyright (c) 2019 nghttp3 contributors
+ *
+ * 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.
+ */
+#ifndef UTIL_H
+#define UTIL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdint.h>
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif /* HAVE_SYS_ENDIAN_H */
+
+namespace nghttp3 {
+
+#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH
+# define nghttp3_ntohl64(N) be64toh(N)
+# define nghttp3_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+# define nghttp3_bswap64(N) \
+ ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32)))
+# define nghttp3_ntohl64(N) nghttp3_bswap64(N)
+# define nghttp3_htonl64(N) nghttp3_bswap64(N)
+#endif /* !HAVE_BE64TOH */
+
+} // namespace nghttp3
+
+#endif // UTIL_H