line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#pragma once |
2
|
|
|
|
|
|
|
#include "Frame.h" |
3
|
|
|
|
|
|
|
#include "HeaderValueParamsParser.h" |
4
|
|
|
|
|
|
|
#include |
5
|
|
|
|
|
|
|
#include |
6
|
|
|
|
|
|
|
#include |
7
|
|
|
|
|
|
|
#include |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
namespace panda { namespace protocol { namespace websocket { |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
class DeflateExt { |
12
|
|
|
|
|
|
|
private: |
13
|
|
|
|
|
|
|
// tail empty frame 0x00 0x00 0xff 0xff |
14
|
|
|
|
|
|
|
static const constexpr unsigned TRAILER_SIZE = 4; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
// zlib doc: |
17
|
|
|
|
|
|
|
// In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six |
18
|
|
|
|
|
|
|
// to avoid repeated flush markers due to avail_out == 0 on return. |
19
|
|
|
|
|
|
|
static const constexpr unsigned TRAILER_RESERVED = TRAILER_SIZE + 3; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
public: |
22
|
|
|
|
|
|
|
|
23
|
473
|
|
|
|
|
|
struct Config { |
24
|
|
|
|
|
|
|
bool client_no_context_takeover = false; // sent is only, when it is true; always parsed |
25
|
|
|
|
|
|
|
bool server_no_context_takeover = false; // sent is only, when it is true; always parsed |
26
|
|
|
|
|
|
|
std::uint8_t server_max_window_bits = 15; |
27
|
|
|
|
|
|
|
std::uint8_t client_max_window_bits = 15; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
// copied from parser config, no direct usage |
30
|
|
|
|
|
|
|
size_t max_message_size = 0; |
31
|
|
|
|
|
|
|
// non-negotiatiable settings |
32
|
|
|
|
|
|
|
int mem_level = 8; |
33
|
|
|
|
|
|
|
int compression_level = Z_DEFAULT_COMPRESSION; |
34
|
|
|
|
|
|
|
int strategy = Z_DEFAULT_STRATEGY; |
35
|
|
|
|
|
|
|
size_t compression_threshold = 1410; // try to fit into TCP frame |
36
|
|
|
|
|
|
|
}; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
struct EffectiveConfig { |
39
|
|
|
|
|
|
|
static const constexpr int HAS_CLIENT_NO_CONTEXT_TAKEOVER = 1 << 0; |
40
|
|
|
|
|
|
|
static const constexpr int HAS_SERVER_NO_CONTEXT_TAKEOVER = 1 << 1; |
41
|
|
|
|
|
|
|
static const constexpr int HAS_SERVER_MAX_WINDOW_BITS = 1 << 2; |
42
|
|
|
|
|
|
|
static const constexpr int HAS_CLIENT_MAX_WINDOW_BITS = 1 << 3; |
43
|
|
|
|
|
|
|
enum class NegotiationsResult { SUCCESS, NOT_FOUND, ERROR }; |
44
|
|
|
|
|
|
|
|
45
|
151
|
|
|
|
|
|
EffectiveConfig(const Config& cfg_, NegotiationsResult result_): cfg{cfg_}, result{result_} {} |
46
|
24
|
|
|
|
|
|
EffectiveConfig(NegotiationsResult result_): result{result_} {} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
explicit operator bool() const { return result == NegotiationsResult::SUCCESS; } |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
Config cfg; |
51
|
|
|
|
|
|
|
int flags = 0; |
52
|
|
|
|
|
|
|
NegotiationsResult result; |
53
|
|
|
|
|
|
|
}; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
struct NegotiationsResult { |
56
|
|
|
|
|
|
|
enum class Result { SUCCESS, NOT_FOUND, ERROR }; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Config cfg; |
60
|
|
|
|
|
|
|
int flags = 0; |
61
|
|
|
|
|
|
|
Result result = Result::ERROR; |
62
|
|
|
|
|
|
|
}; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
enum class Role { CLIENT, SERVER }; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
static const char* extension_name; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
static panda::optional bootstrap(); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
static EffectiveConfig select(const HeaderValues& values, const Config& cfg, Role role); |
71
|
|
|
|
|
|
|
static void request(HeaderValues& ws_extensions, const Config& cfg); |
72
|
|
|
|
|
|
|
static DeflateExt* uplift(const EffectiveConfig& cfg, HeaderValues& extensions, Role role); |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
~DeflateExt(); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
void reset_tx(); |
77
|
|
|
|
|
|
|
void reset() { |
78
|
|
|
|
|
|
|
reset_rx(); |
79
|
|
|
|
|
|
|
reset_tx(); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
string& compress(string& str, bool final); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
template |
84
|
|
|
|
|
|
|
It compress(It payload_begin, It payload_end, bool final) { |
85
|
|
|
|
|
|
|
// we should not try to compress empty last piece |
86
|
|
|
|
|
|
|
assert(!(final && (payload_begin == payload_end))); |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
It it_in = payload_begin; |
89
|
|
|
|
|
|
|
It it_out = payload_begin; |
90
|
|
|
|
|
|
|
tx_stream.avail_out = 0; |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
string* chunk_out = nullptr; |
93
|
|
|
|
|
|
|
while(it_in != payload_end) { |
94
|
|
|
|
|
|
|
auto it_next = it_in + 1; |
95
|
|
|
|
|
|
|
string chunk_in = *it_in; |
96
|
|
|
|
|
|
|
tx_stream.next_in = (Bytef*)(chunk_in.data()); |
97
|
|
|
|
|
|
|
tx_stream.avail_in = static_cast(chunk_in.length()); |
98
|
|
|
|
|
|
|
// for last fragment we either complete frame(Z_SYNC_FLUSH) or message(flush_policy()) |
99
|
|
|
|
|
|
|
// otherwise no flush it perfromed |
100
|
|
|
|
|
|
|
auto flush = (it_next == payload_end) ? Z_SYNC_FLUSH : Z_NO_FLUSH; |
101
|
|
|
|
|
|
|
auto avail_out = tx_stream.avail_out; |
102
|
|
|
|
|
|
|
do { |
103
|
|
|
|
|
|
|
deflate_iteration(flush, [&](){ |
104
|
|
|
|
|
|
|
bool reserve_something = false; |
105
|
|
|
|
|
|
|
// the current chunk is already filled, try the next one |
106
|
|
|
|
|
|
|
if (it_out < it_next) { |
107
|
|
|
|
|
|
|
if (chunk_out) { |
108
|
|
|
|
|
|
|
auto tx_out = avail_out - tx_stream.avail_out; |
109
|
|
|
|
|
|
|
chunk_out->length(chunk_out->length() + tx_out); |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
chunk_out = &(*it_out++); |
112
|
|
|
|
|
|
|
*chunk_out = string(chunk_out->length()); |
113
|
|
|
|
|
|
|
tx_stream.next_out = reinterpret_cast(chunk_out->buf()); |
114
|
|
|
|
|
|
|
avail_out = tx_stream.avail_out = static_cast(chunk_out->capacity()); |
115
|
|
|
|
|
|
|
reserve_something = avail_out < TRAILER_RESERVED; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
else { |
118
|
|
|
|
|
|
|
// there are no more chunks, resize the last/current one |
119
|
|
|
|
|
|
|
reserve_something = true; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
if (reserve_something) { |
122
|
|
|
|
|
|
|
avail_out = reserve_for_trailer(*chunk_out); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
}); |
125
|
|
|
|
|
|
|
} while(tx_stream.avail_in); |
126
|
|
|
|
|
|
|
auto tx_out = avail_out - tx_stream.avail_out; |
127
|
|
|
|
|
|
|
if (chunk_out) { chunk_out->length(chunk_out->length() + tx_out); } |
128
|
|
|
|
|
|
|
it_in = it_next; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
if(final) { |
131
|
|
|
|
|
|
|
size_t tail_left = TRAILER_SIZE; |
132
|
|
|
|
|
|
|
while(tail_left){ |
133
|
|
|
|
|
|
|
--it_out; |
134
|
|
|
|
|
|
|
string& chunk = *it_out; |
135
|
|
|
|
|
|
|
int delta = chunk.length() - tail_left; |
136
|
|
|
|
|
|
|
if (delta >= 0) { |
137
|
|
|
|
|
|
|
chunk.length(delta); |
138
|
|
|
|
|
|
|
tail_left = 0; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
else { |
141
|
|
|
|
|
|
|
tail_left -= chunk.length(); |
142
|
|
|
|
|
|
|
chunk.length(0); |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
++it_out; |
146
|
|
|
|
|
|
|
if(reset_after_tx) reset_tx(); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
return it_out; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
bool uncompress(Frame& frame); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
const Config& effective_config() const { return effective_cfg; } |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
private: |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
template |
158
|
22
|
|
|
|
|
|
inline void deflate_iteration(int flush, F&& drain) { |
159
|
23
|
100
|
|
|
|
|
do { |
160
|
23
|
100
|
|
|
|
|
if (!tx_stream.avail_in && flush == Z_NO_FLUSH) return; |
|
|
50
|
|
|
|
|
|
161
|
23
|
50
|
|
|
|
|
size_t min_out_sz = flush == Z_SYNC_FLUSH ? TRAILER_RESERVED : 1; |
162
|
23
|
100
|
|
|
|
|
if (tx_stream.avail_out < min_out_sz) drain(); |
163
|
23
|
50
|
|
|
|
|
assert(tx_stream.avail_out > 0); |
164
|
23
|
50
|
|
|
|
|
assert(!(flush == Z_SYNC_FLUSH && tx_stream.avail_out < TRAILER_RESERVED)); |
|
|
50
|
|
|
|
|
|
165
|
23
|
|
|
|
|
|
auto r = deflate(&tx_stream, flush); |
166
|
|
|
|
|
|
|
// no known cases, when user input data might lead to the error |
167
|
23
|
50
|
|
|
|
|
assert(r >= 0); |
168
|
|
|
|
|
|
|
(void)r; |
169
|
23
|
|
|
|
|
|
} while (tx_stream.avail_out == 0); |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
1
|
|
|
|
|
|
unsigned reserve_for_trailer(string &buff) { |
173
|
1
|
|
|
|
|
|
auto length = buff.capacity(); |
174
|
1
|
|
|
|
|
|
buff.reserve(length + TRAILER_RESERVED); |
175
|
1
|
|
|
|
|
|
buff.length(length); |
176
|
1
|
|
|
|
|
|
tx_stream.next_out = reinterpret_cast(buff.buf() + length); |
177
|
1
|
|
|
|
|
|
tx_stream.avail_out += TRAILER_RESERVED; |
178
|
1
|
|
|
|
|
|
return TRAILER_RESERVED; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
void reset_rx(); |
183
|
|
|
|
|
|
|
bool uncompress_impl(Frame& frame); |
184
|
|
|
|
|
|
|
bool uncompress_check_overflow(Frame& frame, const string& acc); |
185
|
|
|
|
|
|
|
void rx_increase_buffer(string& acc); |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
DeflateExt(const Config& cfg, Role role); |
188
|
|
|
|
|
|
|
Config effective_cfg; |
189
|
|
|
|
|
|
|
size_t message_size; |
190
|
|
|
|
|
|
|
size_t max_message_size; |
191
|
|
|
|
|
|
|
z_stream rx_stream; |
192
|
|
|
|
|
|
|
z_stream tx_stream; |
193
|
|
|
|
|
|
|
bool reset_after_tx; |
194
|
|
|
|
|
|
|
bool reset_after_rx; |
195
|
|
|
|
|
|
|
}; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
}}} |