1 /*
2  * nghttp3
3  *
4  * Copyright (c) 2019 nghttp3 contributors
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "qpack_encode.h"
26 
27 #include <arpa/inet.h>
28 
29 #include <cerrno>
30 #include <cstring>
31 #include <cassert>
32 #include <iostream>
33 #include <fstream>
34 #include <algorithm>
35 #include <array>
36 #include <iomanip>
37 #include <vector>
38 
39 #include "qpack.h"
40 #include "template.h"
41 #include "util.h"
42 
43 namespace nghttp3 {
44 
45 extern Config config;
46 
Encoder(size_t max_dtable_size,size_t max_blocked,bool immediate_ack)47 Encoder::Encoder(size_t max_dtable_size, size_t max_blocked, bool immediate_ack)
48     : mem_(nghttp3_mem_default()),
49       enc_(nullptr),
50       max_dtable_size_(max_dtable_size),
51       max_blocked_(max_blocked),
52       immediate_ack_(immediate_ack) {}
53 
~Encoder()54 Encoder::~Encoder() { nghttp3_qpack_encoder_del(enc_); }
55 
init()56 int Encoder::init() {
57   int rv;
58 
59   rv = nghttp3_qpack_encoder_new(&enc_, mem_);
60   if (rv != 0) {
61     std::cerr << "nghttp3_qpack_encoder_new: " << nghttp3_strerror(rv)
62               << std::endl;
63     return -1;
64   }
65 
66   nghttp3_qpack_encoder_set_hard_max_dtable_capacity(enc_, max_dtable_size_);
67 
68   rv = nghttp3_qpack_encoder_set_max_dtable_capacity(enc_, max_dtable_size_);
69   if (rv != 0) {
70     std::cerr << "nghttp3_qpack_encoder_set_max_dtable_capacity: "
71               << nghttp3_strerror(rv) << std::endl;
72     return -1;
73   }
74 
75   nghttp3_qpack_encoder_set_max_blocked_streams(enc_, max_blocked_);
76 
77   return 0;
78 }
79 
encode(nghttp3_buf * pbuf,nghttp3_buf * rbuf,nghttp3_buf * ebuf,int64_t stream_id,const nghttp3_nv * nva,size_t len)80 int Encoder::encode(nghttp3_buf *pbuf, nghttp3_buf *rbuf, nghttp3_buf *ebuf,
81                     int64_t stream_id, const nghttp3_nv *nva, size_t len) {
82   auto rv =
83       nghttp3_qpack_encoder_encode(enc_, pbuf, rbuf, ebuf, stream_id, nva, len);
84   if (rv != 0) {
85     std::cerr << "nghttp3_qpack_encoder_encode: " << nghttp3_strerror(rv)
86               << std::endl;
87     return -1;
88   }
89   if (immediate_ack_) {
90     nghttp3_qpack_encoder_ack_everything(enc_);
91   }
92   return 0;
93 }
94 
95 namespace {
write_encoder_stream(std::ostream & out,nghttp3_buf * ebuf)96 void write_encoder_stream(std::ostream &out, nghttp3_buf *ebuf) {
97   uint64_t stream_id = 0;
98   out.write(reinterpret_cast<char *>(&stream_id), sizeof(stream_id));
99   uint32_t size = htonl(nghttp3_buf_len(ebuf));
100   out.write(reinterpret_cast<char *>(&size), sizeof(size));
101   out.write(reinterpret_cast<char *>(ebuf->pos), nghttp3_buf_len(ebuf));
102 }
103 } // namespace
104 
105 namespace {
write_request_stream(std::ostream & out,int64_t stream_id,nghttp3_buf * pbuf,nghttp3_buf * rbuf)106 void write_request_stream(std::ostream &out, int64_t stream_id,
107                           nghttp3_buf *pbuf, nghttp3_buf *rbuf) {
108   stream_id = nghttp3_htonl64(stream_id);
109   out.write(reinterpret_cast<char *>(&stream_id), sizeof(stream_id));
110   uint32_t size = htonl(nghttp3_buf_len(pbuf) + nghttp3_buf_len(rbuf));
111   out.write(reinterpret_cast<char *>(&size), sizeof(size));
112   out.write(reinterpret_cast<char *>(pbuf->pos), nghttp3_buf_len(pbuf));
113   out.write(reinterpret_cast<char *>(rbuf->pos), nghttp3_buf_len(rbuf));
114 }
115 } // namespace
116 
encode(const std::string_view & outfile,const std::string_view & infile)117 int encode(const std::string_view &outfile, const std::string_view &infile) {
118   auto in = std::ifstream(infile.data(), std::ios::binary);
119 
120   if (!in) {
121     std::cerr << "Could not open file " << infile << ": " << strerror(errno)
122               << std::endl;
123     return -1;
124   }
125 
126   auto out = std::ofstream(outfile.data(), std::ios::trunc | std::ios::binary);
127   if (!out) {
128     std::cerr << "Could not open file " << outfile << ": " << strerror(errno)
129               << std::endl;
130     return -1;
131   }
132 
133   auto enc =
134       Encoder(config.max_dtable_size, config.max_blocked, config.immediate_ack);
135   if (enc.init() != 0) {
136     return -1;
137   }
138 
139   nghttp3_buf pbuf, rbuf, ebuf;
140   nghttp3_buf_init(&pbuf);
141   nghttp3_buf_init(&rbuf);
142   nghttp3_buf_init(&ebuf);
143 
144   auto mem = nghttp3_mem_default();
145   auto pbufd = defer(nghttp3_buf_free, &pbuf, mem);
146   auto rbufd = defer(nghttp3_buf_free, &rbuf, mem);
147   auto ebufd = defer(nghttp3_buf_free, &ebuf, mem);
148 
149   int64_t stream_id = 1;
150   std::array<std::string, 1024> sarray;
151 
152   size_t srclen = 0;
153   size_t enclen = 0;
154   size_t rslen = 0;
155   size_t eslen = 0;
156 
157   for (; in;) {
158     auto nva = std::vector<nghttp3_nv>();
159     for (std::string line; std::getline(in, line);) {
160       if (line == "") {
161         break;
162       }
163 
164       if (sarray.size() == nva.size()) {
165         std::cerr << "Too many headers: " << nva.size() << std::endl;
166         return -1;
167       }
168 
169       sarray[nva.size()] = line;
170       const auto &s = sarray[nva.size()];
171 
172       auto d = s.find('\t');
173       if (d == std::string_view::npos) {
174         std::cerr << "Could not find TAB in " << s << std::endl;
175         return -1;
176       }
177       auto name = std::string_view(s.c_str(), d);
178       auto value = std::string_view(s.c_str() + d + 1, s.size() - d - 1);
179       value.remove_prefix(std::min(value.find_first_not_of(" "), value.size()));
180 
181       srclen += name.size() + value.size();
182 
183       nva.emplace_back(nghttp3_nv{
184           const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(name.data())),
185           const_cast<uint8_t *>(
186               reinterpret_cast<const uint8_t *>(value.data())),
187           name.size(), value.size()});
188     }
189 
190     if (nva.empty()) {
191       break;
192     }
193 
194     if (auto rv =
195             enc.encode(&pbuf, &rbuf, &ebuf, stream_id, nva.data(), nva.size());
196         rv != 0) {
197       return -1;
198     }
199 
200     enclen += nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf) +
201               nghttp3_buf_len(&ebuf);
202 
203     if (nghttp3_buf_len(&ebuf)) {
204       write_encoder_stream(out, &ebuf);
205     }
206     write_request_stream(out, stream_id, &pbuf, &rbuf);
207 
208     rslen += nghttp3_buf_len(&pbuf) + nghttp3_buf_len(&rbuf);
209     eslen += nghttp3_buf_len(&ebuf);
210 
211     nghttp3_buf_reset(&pbuf);
212     nghttp3_buf_reset(&rbuf);
213     nghttp3_buf_reset(&ebuf);
214 
215     ++stream_id;
216   }
217 
218   if (srclen == 0) {
219     std::cerr << "No header field processed" << std::endl;
220   } else {
221     std::cerr << srclen << " -> " << enclen << " (r:" << rslen
222               << " + e:" << eslen << ") " << std::fixed << std::setprecision(2)
223               << (1. - (static_cast<double>(enclen) / srclen)) * 100
224               << "% compressed" << std::endl;
225   }
226   return 0;
227 }
228 
229 } // namespace nghttp3
230