opal-up 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/up_ext/App.h +665 -544
- data/ext/up_ext/AsyncSocket.h +307 -284
- data/ext/up_ext/AsyncSocketData.h +35 -51
- data/ext/up_ext/BloomFilter.h +37 -42
- data/ext/up_ext/ChunkedEncoding.h +174 -175
- data/ext/up_ext/ClientApp.h +20 -23
- data/ext/up_ext/HttpContext.h +476 -381
- data/ext/up_ext/HttpContextData.h +20 -20
- data/ext/up_ext/HttpErrors.h +14 -10
- data/ext/up_ext/HttpParser.h +631 -563
- data/ext/up_ext/HttpResponse.h +526 -460
- data/ext/up_ext/HttpResponseData.h +59 -55
- data/ext/up_ext/HttpRouter.h +328 -310
- data/ext/up_ext/Loop.h +174 -168
- data/ext/up_ext/LoopData.h +60 -67
- data/ext/up_ext/MoveOnlyFunction.h +71 -80
- data/ext/up_ext/PerMessageDeflate.h +218 -198
- data/ext/up_ext/ProxyParser.h +100 -99
- data/ext/up_ext/QueryParser.h +91 -84
- data/ext/up_ext/TopicTree.h +273 -268
- data/ext/up_ext/Utilities.h +25 -25
- data/ext/up_ext/WebSocket.h +376 -310
- data/ext/up_ext/WebSocketContext.h +487 -372
- data/ext/up_ext/WebSocketContextData.h +74 -62
- data/ext/up_ext/WebSocketData.h +53 -46
- data/ext/up_ext/WebSocketExtensions.h +194 -178
- data/ext/up_ext/WebSocketHandshake.h +115 -110
- data/ext/up_ext/WebSocketProtocol.h +441 -398
- data/ext/up_ext/up_ext.c +43 -5
- data/lib/up/ruby/cluster.rb +29 -6
- data/lib/up/version.rb +1 -1
- metadata +2 -2
@@ -23,65 +23,49 @@
|
|
23
23
|
namespace uWS {
|
24
24
|
|
25
25
|
struct BackPressure {
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
buffer.erase(0, pendingRemoval);
|
41
|
-
pendingRemoval = 0;
|
42
|
-
}
|
43
|
-
}
|
44
|
-
size_t length() {
|
45
|
-
return buffer.length() - pendingRemoval;
|
46
|
-
}
|
47
|
-
void clear() {
|
48
|
-
pendingRemoval = 0;
|
49
|
-
buffer.clear();
|
50
|
-
}
|
51
|
-
void reserve(size_t length) {
|
52
|
-
buffer.reserve(length + pendingRemoval);
|
53
|
-
}
|
54
|
-
void resize(size_t length) {
|
55
|
-
buffer.resize(length + pendingRemoval);
|
56
|
-
}
|
57
|
-
const char *data() {
|
58
|
-
return buffer.data() + pendingRemoval;
|
59
|
-
}
|
60
|
-
size_t size() {
|
61
|
-
return length();
|
62
|
-
}
|
63
|
-
/* The total length, incuding pending removal */
|
64
|
-
size_t totalLength() {
|
65
|
-
return buffer.length();
|
26
|
+
std::string buffer;
|
27
|
+
unsigned int pendingRemoval = 0;
|
28
|
+
BackPressure(BackPressure &&other) {
|
29
|
+
buffer = std::move(other.buffer);
|
30
|
+
pendingRemoval = other.pendingRemoval;
|
31
|
+
}
|
32
|
+
BackPressure() = default;
|
33
|
+
void append(const char *data, size_t length) { buffer.append(data, length); }
|
34
|
+
void erase(unsigned int length) {
|
35
|
+
pendingRemoval += length;
|
36
|
+
/* Always erase a minimum of 1/32th the current backpressure */
|
37
|
+
if (pendingRemoval > (buffer.length() >> 5)) {
|
38
|
+
buffer.erase(0, pendingRemoval);
|
39
|
+
pendingRemoval = 0;
|
66
40
|
}
|
41
|
+
}
|
42
|
+
size_t length() { return buffer.length() - pendingRemoval; }
|
43
|
+
void clear() {
|
44
|
+
pendingRemoval = 0;
|
45
|
+
buffer.clear();
|
46
|
+
}
|
47
|
+
void reserve(size_t length) { buffer.reserve(length + pendingRemoval); }
|
48
|
+
void resize(size_t length) { buffer.resize(length + pendingRemoval); }
|
49
|
+
const char *data() { return buffer.data() + pendingRemoval; }
|
50
|
+
size_t size() { return length(); }
|
51
|
+
/* The total length, incuding pending removal */
|
52
|
+
size_t totalLength() { return buffer.length(); }
|
67
53
|
};
|
68
54
|
|
69
55
|
/* Depending on how we want AsyncSocket to function, this will need to change */
|
70
56
|
|
71
|
-
template <bool SSL>
|
72
|
-
|
73
|
-
|
74
|
-
BackPressure buffer;
|
57
|
+
template <bool SSL> struct AsyncSocketData {
|
58
|
+
/* This will do for now */
|
59
|
+
BackPressure buffer;
|
75
60
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
}
|
61
|
+
/* Allow move constructing us */
|
62
|
+
AsyncSocketData(BackPressure &&backpressure)
|
63
|
+
: buffer(std::move(backpressure)) {}
|
80
64
|
|
81
|
-
|
82
|
-
|
65
|
+
/* Or emppty */
|
66
|
+
AsyncSocketData() = default;
|
83
67
|
};
|
84
68
|
|
85
|
-
}
|
69
|
+
} // namespace uWS
|
86
70
|
|
87
71
|
#endif // UWS_ASYNCSOCKETDATA_H
|
data/ext/up_ext/BloomFilter.h
CHANGED
@@ -21,63 +21,58 @@
|
|
21
21
|
/* This filter has no false positives or collisions for the standard
|
22
22
|
* and non-standard common request headers */
|
23
23
|
|
24
|
+
#include <bitset>
|
24
25
|
#include <cstdint>
|
25
26
|
#include <string_view>
|
26
|
-
#include <bitset>
|
27
27
|
|
28
28
|
namespace uWS {
|
29
29
|
|
30
30
|
struct BloomFilter {
|
31
31
|
private:
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
std::bitset<256> filter;
|
33
|
+
static inline uint32_t perfectHash(uint32_t features) {
|
34
|
+
return features *= 1843993368;
|
35
|
+
}
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
37
|
+
union ScrambleArea {
|
38
|
+
unsigned char p[4];
|
39
|
+
uint32_t val;
|
40
|
+
};
|
41
|
+
|
42
|
+
ScrambleArea getFeatures(std::string_view key) {
|
43
|
+
ScrambleArea s;
|
44
|
+
s.p[0] = reinterpret_cast<const unsigned char &>(key[0]);
|
45
|
+
s.p[1] = reinterpret_cast<const unsigned char &>(key[key.length() - 1]);
|
46
|
+
s.p[2] = reinterpret_cast<const unsigned char &>(key[key.length() - 2]);
|
47
|
+
s.p[3] = reinterpret_cast<const unsigned char &>(key[key.length() >> 1]);
|
48
|
+
return s;
|
49
|
+
}
|
50
50
|
|
51
51
|
public:
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
}
|
56
|
-
|
57
|
-
ScrambleArea s = getFeatures(key);
|
58
|
-
s.val = perfectHash(s.val);
|
59
|
-
return filter[s.p[0]] &&
|
60
|
-
filter[s.p[1]] &&
|
61
|
-
filter[s.p[2]] &&
|
62
|
-
filter[s.p[3]];
|
52
|
+
bool mightHave(std::string_view key) {
|
53
|
+
if (key.length() < 2) {
|
54
|
+
return true;
|
63
55
|
}
|
64
56
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
filter[s.p[0]] = 1;
|
70
|
-
filter[s.p[1]] = 1;
|
71
|
-
filter[s.p[2]] = 1;
|
72
|
-
filter[s.p[3]] = 1;
|
73
|
-
}
|
74
|
-
}
|
57
|
+
ScrambleArea s = getFeatures(key);
|
58
|
+
s.val = perfectHash(s.val);
|
59
|
+
return filter[s.p[0]] && filter[s.p[1]] && filter[s.p[2]] && filter[s.p[3]];
|
60
|
+
}
|
75
61
|
|
76
|
-
|
77
|
-
|
62
|
+
void add(std::string_view key) {
|
63
|
+
if (key.length() >= 2) {
|
64
|
+
ScrambleArea s = getFeatures(key);
|
65
|
+
s.val = perfectHash(s.val);
|
66
|
+
filter[s.p[0]] = 1;
|
67
|
+
filter[s.p[1]] = 1;
|
68
|
+
filter[s.p[2]] = 1;
|
69
|
+
filter[s.p[3]] = 1;
|
78
70
|
}
|
71
|
+
}
|
72
|
+
|
73
|
+
void reset() { filter.reset(); }
|
79
74
|
};
|
80
75
|
|
81
|
-
}
|
76
|
+
} // namespace uWS
|
82
77
|
|
83
78
|
#endif // UWS_BLOOMFILTER_H
|
@@ -20,217 +20,216 @@
|
|
20
20
|
|
21
21
|
/* Independent chunked encoding parser, used by HttpParser. */
|
22
22
|
|
23
|
-
#include <string>
|
24
|
-
#include <cstring>
|
25
|
-
#include <algorithm>
|
26
|
-
#include <string_view>
|
27
23
|
#include "MoveOnlyFunction.h"
|
24
|
+
#include <algorithm>
|
25
|
+
#include <cstring>
|
28
26
|
#include <optional>
|
27
|
+
#include <string>
|
28
|
+
#include <string_view>
|
29
29
|
|
30
30
|
namespace uWS {
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
constexpr uint64_t STATE_HAS_SIZE =
|
33
|
+
1ull << (sizeof(uint64_t) * 8 - 1); // 0x80000000;
|
34
|
+
constexpr uint64_t STATE_IS_CHUNKED =
|
35
|
+
1ull << (sizeof(uint64_t) * 8 - 2); // 0x40000000;
|
36
|
+
constexpr uint64_t STATE_SIZE_MASK =
|
37
|
+
~(3ull << (sizeof(uint64_t) * 8 - 2)); // 0x3FFFFFFF;
|
38
|
+
constexpr uint64_t STATE_IS_ERROR = ~0ull; // 0xFFFFFFFF;
|
39
|
+
constexpr uint64_t STATE_SIZE_OVERFLOW =
|
40
|
+
0x0Full << (sizeof(uint64_t) * 8 - 8); // 0x0F000000;
|
41
|
+
|
42
|
+
inline uint64_t chunkSize(uint64_t state) { return state & STATE_SIZE_MASK; }
|
43
|
+
|
44
|
+
/* Reads hex number until CR or out of data to consume. Updates state. Returns
|
45
|
+
* bytes consumed. */
|
46
|
+
inline void consumeHexNumber(std::string_view &data, uint64_t &state) {
|
47
|
+
/* Consume everything higher than 32 */
|
48
|
+
while (data.length() && data.data()[0] > 32) {
|
49
|
+
|
50
|
+
unsigned char digit = (unsigned char)data.data()[0];
|
51
|
+
if (digit >= 'a') {
|
52
|
+
digit = (unsigned char)(digit - ('a' - ':'));
|
53
|
+
} else if (digit >= 'A') {
|
54
|
+
digit = (unsigned char)(digit - ('A' - ':'));
|
55
|
+
}
|
56
|
+
|
57
|
+
unsigned int number = ((unsigned int)digit - (unsigned int)'0');
|
37
58
|
|
38
|
-
|
39
|
-
|
59
|
+
if (number > 16 || (chunkSize(state) & STATE_SIZE_OVERFLOW)) {
|
60
|
+
state = STATE_IS_ERROR;
|
61
|
+
return;
|
40
62
|
}
|
41
63
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
64
|
+
// extract state bits
|
65
|
+
uint64_t bits = /*state &*/ STATE_IS_CHUNKED;
|
66
|
+
|
67
|
+
state = (state & STATE_SIZE_MASK) * 16ull + number;
|
68
|
+
|
69
|
+
state |= bits;
|
70
|
+
data.remove_prefix(1);
|
71
|
+
}
|
72
|
+
/* Consume everything not /n */
|
73
|
+
while (data.length() && data.data()[0] != '\n') {
|
74
|
+
data.remove_prefix(1);
|
75
|
+
}
|
76
|
+
/* Now we stand on \n so consume it and enable size */
|
77
|
+
if (data.length()) {
|
78
|
+
state += 2; // include the two last /r/n
|
79
|
+
state |= STATE_HAS_SIZE | STATE_IS_CHUNKED;
|
80
|
+
data.remove_prefix(1);
|
81
|
+
}
|
82
|
+
}
|
46
83
|
|
47
|
-
|
48
|
-
if (digit >= 'a') {
|
49
|
-
digit = (unsigned char) (digit - ('a' - ':'));
|
50
|
-
} else if (digit >= 'A') {
|
51
|
-
digit = (unsigned char) (digit - ('A' - ':'));
|
52
|
-
}
|
84
|
+
inline void decChunkSize(uint64_t &state, unsigned int by) {
|
53
85
|
|
54
|
-
|
86
|
+
// unsigned int bits = state & STATE_IS_CHUNKED;
|
55
87
|
|
56
|
-
|
57
|
-
state = STATE_IS_ERROR;
|
58
|
-
return;
|
59
|
-
}
|
88
|
+
state = (state & ~STATE_SIZE_MASK) | (chunkSize(state) - by);
|
60
89
|
|
61
|
-
|
62
|
-
|
90
|
+
// state |= bits;
|
91
|
+
}
|
63
92
|
|
64
|
-
|
93
|
+
inline bool hasChunkSize(uint64_t state) { return state & STATE_HAS_SIZE; }
|
65
94
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
while (data.length() && data.data()[0] != '\n') {
|
71
|
-
data.remove_prefix(1);
|
72
|
-
}
|
73
|
-
/* Now we stand on \n so consume it and enable size */
|
74
|
-
if (data.length()) {
|
75
|
-
state += 2; // include the two last /r/n
|
76
|
-
state |= STATE_HAS_SIZE | STATE_IS_CHUNKED;
|
77
|
-
data.remove_prefix(1);
|
78
|
-
}
|
79
|
-
}
|
95
|
+
/* Are we in the middle of parsing chunked encoding? */
|
96
|
+
inline bool isParsingChunkedEncoding(uint64_t state) {
|
97
|
+
return state & ~STATE_SIZE_MASK;
|
98
|
+
}
|
80
99
|
|
81
|
-
|
100
|
+
inline bool isParsingInvalidChunkedEncoding(uint64_t state) {
|
101
|
+
return state == STATE_IS_ERROR;
|
102
|
+
}
|
82
103
|
|
83
|
-
|
104
|
+
/* Returns next chunk (empty or not), or if all data was consumed, nullopt is
|
105
|
+
* returned. */
|
106
|
+
static std::optional<std::string_view>
|
107
|
+
getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) {
|
84
108
|
|
85
|
-
|
109
|
+
while (data.length()) {
|
86
110
|
|
87
|
-
|
88
|
-
|
111
|
+
// if in "drop trailer mode", just drop up to what we have as size
|
112
|
+
if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) &&
|
113
|
+
chunkSize(state)) {
|
89
114
|
|
90
|
-
|
91
|
-
return state & STATE_HAS_SIZE;
|
92
|
-
}
|
115
|
+
// printf("Parsing trailer now\n");
|
93
116
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
}
|
117
|
+
while (data.length() && chunkSize(state)) {
|
118
|
+
data.remove_prefix(1);
|
119
|
+
decChunkSize(state, 1);
|
98
120
|
|
99
|
-
|
100
|
-
return state == STATE_IS_ERROR;
|
101
|
-
}
|
121
|
+
if (chunkSize(state) == 0) {
|
102
122
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
while (data.length()) {
|
107
|
-
|
108
|
-
// if in "drop trailer mode", just drop up to what we have as size
|
109
|
-
if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) {
|
110
|
-
|
111
|
-
//printf("Parsing trailer now\n");
|
112
|
-
|
113
|
-
while(data.length() && chunkSize(state)) {
|
114
|
-
data.remove_prefix(1);
|
115
|
-
decChunkSize(state, 1);
|
116
|
-
|
117
|
-
if (chunkSize(state) == 0) {
|
118
|
-
|
119
|
-
/* This is an actual place where we need 0 as state */
|
120
|
-
state = 0;
|
121
|
-
|
122
|
-
/* The parser MUST stop consuming here */
|
123
|
-
return std::nullopt;
|
124
|
-
}
|
125
|
-
}
|
126
|
-
continue;
|
127
|
-
}
|
128
|
-
|
129
|
-
if (!hasChunkSize(state)) {
|
130
|
-
consumeHexNumber(data, state);
|
131
|
-
if (isParsingInvalidChunkedEncoding(state)) {
|
132
|
-
return std::nullopt;
|
133
|
-
}
|
134
|
-
if (hasChunkSize(state) && chunkSize(state) == 2) {
|
135
|
-
|
136
|
-
//printf("Setting state to trailer-parsing and emitting empty chunk\n");
|
137
|
-
|
138
|
-
// set trailer state and increase size to 4
|
139
|
-
if (trailer) {
|
140
|
-
state = 4 /*| STATE_IS_CHUNKED*/ | STATE_HAS_SIZE;
|
141
|
-
} else {
|
142
|
-
state = 2 /*| STATE_IS_CHUNKED*/ | STATE_HAS_SIZE;
|
143
|
-
}
|
144
|
-
|
145
|
-
return std::string_view(nullptr, 0);
|
146
|
-
}
|
147
|
-
continue;
|
148
|
-
}
|
149
|
-
|
150
|
-
// do we have data to emit all?
|
151
|
-
if (data.length() >= chunkSize(state)) {
|
152
|
-
// emit all but 2 bytes then reset state to 0 and goto beginning
|
153
|
-
// not fin
|
154
|
-
std::string_view emitSoon;
|
155
|
-
bool shouldEmit = false;
|
156
|
-
if (chunkSize(state) > 2) {
|
157
|
-
emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
|
158
|
-
shouldEmit = true;
|
159
|
-
}
|
160
|
-
data.remove_prefix(chunkSize(state));
|
161
|
-
state = STATE_IS_CHUNKED;
|
162
|
-
if (shouldEmit) {
|
163
|
-
return emitSoon;
|
164
|
-
}
|
165
|
-
continue;
|
166
|
-
} else {
|
167
|
-
/* We will consume all our input data */
|
168
|
-
std::string_view emitSoon;
|
169
|
-
if (chunkSize(state) > 2) {
|
170
|
-
uint64_t maximalAppEmit = chunkSize(state) - 2;
|
171
|
-
if (data.length() > maximalAppEmit) {
|
172
|
-
emitSoon = data.substr(0, maximalAppEmit);
|
173
|
-
} else {
|
174
|
-
//cb(data);
|
175
|
-
emitSoon = data;
|
176
|
-
}
|
177
|
-
}
|
178
|
-
decChunkSize(state, (unsigned int) data.length());
|
179
|
-
state |= STATE_IS_CHUNKED;
|
180
|
-
// new: decrease data by its size (bug)
|
181
|
-
data.remove_prefix(data.length()); // ny bug fix för getNextChunk
|
182
|
-
if (emitSoon.length()) {
|
183
|
-
return emitSoon;
|
184
|
-
} else {
|
185
|
-
return std::nullopt;
|
186
|
-
}
|
187
|
-
}
|
188
|
-
}
|
123
|
+
/* This is an actual place where we need 0 as state */
|
124
|
+
state = 0;
|
189
125
|
|
190
|
-
|
126
|
+
/* The parser MUST stop consuming here */
|
127
|
+
return std::nullopt;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
continue;
|
191
131
|
}
|
192
132
|
|
193
|
-
|
194
|
-
|
133
|
+
if (!hasChunkSize(state)) {
|
134
|
+
consumeHexNumber(data, state);
|
135
|
+
if (isParsingInvalidChunkedEncoding(state)) {
|
136
|
+
return std::nullopt;
|
137
|
+
}
|
138
|
+
if (hasChunkSize(state) && chunkSize(state) == 2) {
|
195
139
|
|
196
|
-
|
197
|
-
|
198
|
-
uint64_t *state;
|
199
|
-
bool trailer;
|
140
|
+
// printf("Setting state to trailer-parsing and emitting empty
|
141
|
+
// chunk\n");
|
200
142
|
|
201
|
-
|
202
|
-
|
143
|
+
// set trailer state and increase size to 4
|
144
|
+
if (trailer) {
|
145
|
+
state = 4 /*| STATE_IS_CHUNKED*/ | STATE_HAS_SIZE;
|
146
|
+
} else {
|
147
|
+
state = 2 /*| STATE_IS_CHUNKED*/ | STATE_HAS_SIZE;
|
203
148
|
}
|
204
149
|
|
205
|
-
|
150
|
+
return std::string_view(nullptr, 0);
|
151
|
+
}
|
152
|
+
continue;
|
153
|
+
}
|
206
154
|
|
155
|
+
// do we have data to emit all?
|
156
|
+
if (data.length() >= chunkSize(state)) {
|
157
|
+
// emit all but 2 bytes then reset state to 0 and goto beginning
|
158
|
+
// not fin
|
159
|
+
std::string_view emitSoon;
|
160
|
+
bool shouldEmit = false;
|
161
|
+
if (chunkSize(state) > 2) {
|
162
|
+
emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
|
163
|
+
shouldEmit = true;
|
164
|
+
}
|
165
|
+
data.remove_prefix(chunkSize(state));
|
166
|
+
state = STATE_IS_CHUNKED;
|
167
|
+
if (shouldEmit) {
|
168
|
+
return emitSoon;
|
169
|
+
}
|
170
|
+
continue;
|
171
|
+
} else {
|
172
|
+
/* We will consume all our input data */
|
173
|
+
std::string_view emitSoon;
|
174
|
+
if (chunkSize(state) > 2) {
|
175
|
+
uint64_t maximalAppEmit = chunkSize(state) - 2;
|
176
|
+
if (data.length() > maximalAppEmit) {
|
177
|
+
emitSoon = data.substr(0, maximalAppEmit);
|
178
|
+
} else {
|
179
|
+
// cb(data);
|
180
|
+
emitSoon = data;
|
207
181
|
}
|
182
|
+
}
|
183
|
+
decChunkSize(state, (unsigned int)data.length());
|
184
|
+
state |= STATE_IS_CHUNKED;
|
185
|
+
// new: decrease data by its size (bug)
|
186
|
+
data.remove_prefix(data.length()); // ny bug fix för getNextChunk
|
187
|
+
if (emitSoon.length()) {
|
188
|
+
return emitSoon;
|
189
|
+
} else {
|
190
|
+
return std::nullopt;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
208
194
|
|
209
|
-
|
210
|
-
|
211
|
-
}
|
195
|
+
return std::nullopt;
|
196
|
+
}
|
212
197
|
|
213
|
-
|
214
|
-
|
215
|
-
}
|
198
|
+
/* This is really just a wrapper for convenience */
|
199
|
+
struct ChunkIterator {
|
216
200
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
return chunk.value();
|
222
|
-
}
|
201
|
+
std::string_view *data;
|
202
|
+
std::optional<std::string_view> chunk;
|
203
|
+
uint64_t *state;
|
204
|
+
bool trailer;
|
223
205
|
|
224
|
-
|
225
|
-
|
226
|
-
|
206
|
+
ChunkIterator(std::string_view *data, uint64_t *state, bool trailer = false)
|
207
|
+
: data(data), state(state), trailer(trailer) {
|
208
|
+
chunk = uWS::getNextChunk(*data, *state, trailer);
|
209
|
+
}
|
227
210
|
|
228
|
-
|
229
|
-
chunk = uWS::getNextChunk(*data, *state, trailer);
|
230
|
-
return *this;
|
231
|
-
}
|
211
|
+
ChunkIterator() {}
|
232
212
|
|
233
|
-
|
234
|
-
|
213
|
+
ChunkIterator begin() { return *this; }
|
214
|
+
|
215
|
+
ChunkIterator end() { return ChunkIterator(); }
|
216
|
+
|
217
|
+
std::string_view operator*() {
|
218
|
+
if (!chunk.has_value()) {
|
219
|
+
std::abort();
|
220
|
+
}
|
221
|
+
return chunk.value();
|
222
|
+
}
|
223
|
+
|
224
|
+
bool operator!=(const ChunkIterator &other) const {
|
225
|
+
return other.chunk.has_value() != chunk.has_value();
|
226
|
+
}
|
227
|
+
|
228
|
+
ChunkIterator &operator++() {
|
229
|
+
chunk = uWS::getNextChunk(*data, *state, trailer);
|
230
|
+
return *this;
|
231
|
+
}
|
232
|
+
};
|
233
|
+
} // namespace uWS
|
235
234
|
|
236
235
|
#endif // UWS_CHUNKEDENCODING_H
|
data/ext/up_ext/ClientApp.h
CHANGED
@@ -4,33 +4,30 @@
|
|
4
4
|
|
5
5
|
namespace uWS {
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
struct WebSocketClientBehavior {
|
8
|
+
MoveOnlyFunction<void()> open;
|
9
|
+
MoveOnlyFunction<void()> message;
|
10
|
+
MoveOnlyFunction<void()> close;
|
11
|
+
// MoveOnlyFunction<void()> failed;
|
12
|
+
};
|
12
13
|
|
13
|
-
|
14
|
+
struct ClientApp {
|
14
15
|
|
15
|
-
|
16
|
+
WebSocketContext<0, false, int> *webSocketContext;
|
17
|
+
// behöver ett nytt http context med minimal klient, som slår om till den
|
18
|
+
// riktiga websocketcontext om samma storlek på httpsocket och websocket blir
|
19
|
+
// det enkel övergång
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
|
21
|
+
ClientApp(WebSocketClientBehavior &&behavior) {
|
22
|
+
// webSocketContext = WebSocketContext<0, false, int>::create();
|
23
|
+
}
|
20
24
|
|
21
|
-
|
22
|
-
//webSocketContext = WebSocketContext<0, false, int>::create();
|
23
|
-
}
|
25
|
+
ClientApp &&connect(std::string url, std::string protocol = "") {
|
24
26
|
|
25
|
-
|
27
|
+
return std::move(*this);
|
28
|
+
}
|
26
29
|
|
27
|
-
|
28
|
-
|
30
|
+
void run() {}
|
31
|
+
};
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
}
|
33
|
-
|
34
|
-
};
|
35
|
-
|
36
|
-
}
|
33
|
+
} // namespace uWS
|