File Coverage

/usr/local/lib/perl5/site_perl/5.26.1/x86_64-linux/Protocol/HTTP.x/i/panda/protocol/http/Message.h
Criterion Covered Total %
statement 0 3 0.0
branch 0 6 0.0
condition n/a
subroutine n/a
pod n/a
total 0 9 0.0


line stmt bran cond sub pod time code
1             #pragma once
2             #include "Body.h"
3             #include "error.h"
4             #include "Headers.h"
5             #include "compression/Compressor.h"
6             #include
7             #include
8             #include
9              
10             namespace panda { namespace protocol { namespace http {
11              
12             enum class State {headers, body, chunk, chunk_body, chunk_trailer, done, error};
13              
14             using compression::Compression;
15             using compression::is_valid_compression;
16              
17             struct Request;
18             using panda::uri::URI;
19             using panda::uri::URISP;
20              
21 0 0         struct Message : virtual Refcnt {
    0          
22             template struct Builder;
23             using wrapped_chunk = std::array;
24              
25             Headers headers;
26             Body body;
27             bool chunked = false;
28             int http_version = 0;
29              
30 0           struct CompressionParams {
31             Compression::Type type = Compression::IDENTITY;
32             Compression::Level level = Compression::Level::min;
33             } compression;
34              
35             mutable compression::CompressorPtr compressor;
36              
37 0 0         Message () {}
38              
39             Message (Headers&& headers, Body&& body, bool chunked = false, int http_version = 0) :
40             headers(std::move(headers)), body(std::move(body)), chunked(chunked), http_version(http_version)
41             {}
42              
43             bool keep_alive () const;
44             void keep_alive (bool val) { val ? headers.connection("keep-alive") : headers.connection("close"); }
45              
46             void compress (Compression::Type type, Compression::Level level = Compression::Level::min) {
47             compression = CompressionParams{type, level};
48             }
49              
50             wrapped_chunk make_chunk (const string& s) const { return make_chunk(s, compressor); }
51             wrapped_chunk final_chunk (const string& s = "") const { return final_chunk(s, compressor); }
52              
53             protected:
54             struct SerializationContext {
55             int http_version;
56             const Body* body;
57             GenericHeaders<2> handled_headers;
58             Compression::Type compression;
59             compression::CompressorPtr compressor;
60             const Request* request;
61             bool chunked;
62             };
63              
64             static string to_string (const std::vector& pieces);
65              
66             static inline string _compression_string(Compression::Type type) noexcept {
67             switch (type) {
68             case Compression::GZIP: return "gzip";
69             case Compression::DEFLATE: return "deflate";
70             case Compression::BROTLI: return "br";
71             case Compression::IDENTITY: return string{};
72             }
73             std::terminate();
74             }
75              
76             inline string _content_encoding(const SerializationContext& ctx) const noexcept {
77             if (!headers.has("Content-Encoding") && ctx.compressor) {
78             return _compression_string(ctx.compression);
79             }
80             return string{};
81             }
82              
83             inline void _compile_prepare (SerializationContext& ctx) const {
84             if (ctx.chunked) {
85             ctx.http_version = 11;
86             // special header, to prevent multiple TEnc headers
87             ctx.handled_headers.set("Transfer-Encoding", "chunked");
88             } else {
89             ctx.http_version = http_version;
90             }
91              
92             // content-length logic is in request/response because it slightly differs
93             }
94              
95              
96             template
97             inline std::vector _to_vector (SerializationContext& ctx, PrepareHeaders&& prepare, CompileHeaders&& compile) const {
98             _prepare_compressor(ctx, compression.level);
99              
100             Body mutable_body;
101             if (ctx.compressor && !ctx.chunked) {
102             compress_body(*ctx.compressor, *ctx.body, mutable_body);
103             ctx.body = &mutable_body;
104             }
105              
106             prepare();
107             auto compiled_headers = compile();
108             if (!ctx.body->length()) return {compiled_headers};
109             auto sz = ctx.body->parts.size();
110              
111             std::vector result;
112             if (ctx.chunked) {
113             result.reserve(1 + sz * 3 + 1);
114             result.emplace_back(compiled_headers);
115             auto append_piecewise = [&](auto& piece) { result.emplace_back(piece); };
116             _serialize_body(ctx, append_piecewise);
117             } else {
118             result.reserve(1 + sz);
119             result.emplace_back(compiled_headers);
120             for (auto& part : ctx.body->parts) result.emplace_back(part);
121             }
122              
123             return result;
124             }
125              
126             static inline compression::CompressorPtr _get_compressor (Compression::Type type, Compression::Level level) {
127             if (type != Compression::IDENTITY) {
128             auto c = compression::instantiate(type);
129             if (c) {
130             c->prepare_compress(level);
131             return c;
132             }
133             }
134             return compression::CompressorPtr{};
135             }
136              
137             inline wrapped_chunk make_chunk (const string& s, const compression::CompressorPtr& compr) const {
138             if (!s) return {"", "", ""};
139             if(compr) {
140             return wrap_into_chunk(compr->compress(s));
141             } else {
142             return wrap_into_chunk(s);
143             }
144             }
145              
146             private:
147             inline wrapped_chunk wrap_into_chunk (const string& s) const {
148             if (!s) return {"", "", ""};
149             return {string::from_number(s.length(), 16) + "\r\n", s, "\r\n"};
150             }
151              
152             inline wrapped_chunk final_chunk (const string& s, const compression::CompressorPtr& compr) const {
153             if (compr) {
154             string content;
155             if (s) { content += compr->compress(s); }
156             content += compr->flush();
157             auto chunk = wrap_into_chunk(content);
158             chunk[2] += "0\r\n\r\n";
159             return chunk;
160             } else {
161             if (s) {
162             return {
163             string::from_number(s.length(), 16) + "\r\n",
164             s,
165             "\r\n0\r\n\r\n"
166             };
167             }
168             else return {"", "","0\r\n\r\n"};
169             }
170             }
171              
172             void compress_body(compression::Compressor& compressor, const Body& src, Body& dst) const;
173              
174             template
175             inline void _append_chunk (wrapped_chunk chunk, Fn&& fn) const {
176             for (auto& piece : chunk) if (piece) { fn(piece); }
177             }
178              
179             template
180             inline void _serialize_body (SerializationContext& ctx, Fn&& fn) const {
181             for (auto& part : ctx.body->parts) {
182             _append_chunk(make_chunk(part, ctx.compressor), fn);
183             }
184             _append_chunk(final_chunk("", ctx.compressor), fn);
185             }
186              
187             inline void _prepare_compressor (SerializationContext& ctx, Compression::Level level) const {
188             auto value = _get_compressor(ctx.compression, level);
189             if (value) { compressor = ctx.compressor = value; }
190             else { ctx.compression = Compression::IDENTITY; } // reset back
191             }
192              
193             //friend struct Parser; friend struct RequestParser; friend struct ResponseParser;
194             };
195             using MessageSP = iptr;
196              
197             template
198             struct Message::Builder {
199             T& headers (Headers&& headers) {
200             _message->headers = std::move(headers);
201             return self();
202             }
203              
204             T& header (const string& k, const string& v) {
205             _message->headers.add(k, v);
206             return self();
207             }
208              
209             T& body (Body&& val, const string& content_type = "") {
210             _message->body = std::move(val);
211             if (content_type) _message->headers.add("Content-Type", content_type);
212             return self();
213             }
214              
215             T& body (const string& body, const string& content_type = "") {
216             _message->body = body;
217             if (content_type) _message->headers.add("Content-Type", content_type);
218             return self();
219             }
220              
221             T& http_version (int http_version) {
222             _message->http_version = http_version;
223             return self();
224             }
225              
226             T& chunked (const string& content_type = "") {
227             _message->chunked = true;
228             if (content_type) _message->headers.add("Content-Type", content_type);
229             return self();
230             }
231              
232             T& compress (Compression::Type method, Compression::Level level = Compression::Level::min) {
233             _message->compress(method, level);
234             return self();
235             }
236              
237             MP build () { return _message; }
238              
239             protected:
240             MP _message;
241              
242             Builder (const MP& msg) : _message(msg) {}
243              
244             T& self () { return static_cast(*this); }
245             };
246              
247              
248             }}}