• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 2211

09 Apr 2024 03:41PM UTC coverage: 92.633% (+0.5%) from 92.106%
2211

push

Evergreen

web-flow
Merge pull request #7300 from realm/tg/rework-metadata-storage

Rework sync user handling and metadata storage

102820 of 195548 branches covered (52.58%)

3165 of 3247 new or added lines in 46 files covered. (97.47%)

31 existing lines in 8 files now uncovered.

249584 of 269432 relevant lines covered (92.63%)

49986309.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.01
/src/realm/sync/network/websocket.cpp
1
#include <cctype>
2

3
#include <realm/sync/network/network.hpp>
4
#include <realm/sync/network/websocket.hpp>
5
#include <realm/util/buffer.hpp>
6
#include <realm/util/base64.hpp>
7
#include <realm/util/sha_crypto.hpp>
8

9
using namespace realm;
10
using namespace realm::sync;
11
using HttpError = websocket::HttpError;
12
using WebSocketError = websocket::WebSocketError;
13

14

15
namespace {
16

17
// case_insensitive_equal is used to compare, ignoring case, certain HTTP
18
// header values with their expected values.
19
bool case_insensitive_equal(StringData str1, StringData str2)
20
{
34,808✔
21
    if (str1.size() != str2.size())
34,808✔
22
        return false;
×
23

17,424✔
24
    for (size_t i = 0; i < str1.size(); ++i)
313,200✔
25
        if (std::tolower(str1[i], std::locale::classic()) != std::tolower(str2[i], std::locale::classic()))
278,392✔
26
            return false;
×
27

17,424✔
28
    return true;
34,808✔
29
}
34,808✔
30

31
// The WebSocket version is always 13 according to the standard.
32
const StringData sec_websocket_version = "13";
33

34
// The magic string is specified in the WebSocket protocol. It is used in the handshake.
35
const StringData websocket_magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
36

37
// The Sec-WebSocket-Key is a header in the client HTTP request.
38
// It is the base64 encoding of 16 random bytes.
39
std::string make_random_sec_websocket_key(std::mt19937_64& random)
40
{
31,854✔
41
    char random_bytes[16];
31,854✔
42
    std::uniform_int_distribution<> dis(std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
31,854✔
43
    for (int i = 0; i < 16; ++i) {
541,486✔
44
        random_bytes[i] = dis(random);
509,632✔
45
    }
509,632✔
46

15,824✔
47
    char out_buffer[24];
31,854✔
48
    size_t encoded_size = util::base64_encode(random_bytes, out_buffer);
31,854✔
49
    REALM_ASSERT(encoded_size == 24);
31,854✔
50

15,824✔
51
    return std::string{out_buffer, 24};
31,854✔
52
}
31,854✔
53

54
// Sec-WebSocket-Accept is a header in the server's HTTP response.
55
// It is calculated from the Sec-WebSocket-Key in the client's HHTP request
56
// as the base64 encoding of the sha1 of the concatenation of the
57
// Sec-Websocket-Key and the magic string.
58
std::string make_sec_websocket_accept(StringData sec_websocket_key)
59
{
48,680✔
60
    std::string sha1_input;
48,680✔
61
    sha1_input.reserve(sec_websocket_key.size() + websocket_magic_string.size());
48,680✔
62
    sha1_input.append(sec_websocket_key.data(), sec_websocket_key.size());
48,680✔
63
    sha1_input.append(websocket_magic_string.data(), websocket_magic_string.size());
48,680✔
64

24,222✔
65
    char sha1_output[20];
48,680✔
66
    util::sha1(sha1_input.data(), sha1_input.length(), reinterpret_cast<unsigned char*>(sha1_output));
48,680✔
67

24,222✔
68
    char base64_output[28];
48,680✔
69
    size_t base64_output_size = util::base64_encode(sha1_output, base64_output);
48,680✔
70
    REALM_ASSERT(base64_output_size == 28);
48,680✔
71

24,222✔
72
    return std::string(base64_output, 28);
48,680✔
73
}
48,680✔
74

75
// find_http_header_value() returns the value of the header \param header in the
76
// HTTP message \param message if present. If the header is absent, the function returns
77
// None.
78
util::Optional<StringData> find_http_header_value(const HTTPHeaders& headers, StringData header)
79
{
118,290✔
80
    auto it = headers.find(header);
118,290✔
81

59,066✔
82
    if (it != headers.end()) {
118,294✔
83
        return StringData(it->second);
118,294✔
84
    }
118,294✔
85
    return none;
4,294,967,294✔
86
}
4,294,967,294✔
87

88
// validate_websocket_upgrade() returns true if the HTTP \a headers
89
// contain the line
90
// Upgrade: websocket
91
bool validate_websocket_upgrade(const HTTPHeaders& headers)
92
{
17,404✔
93
    util::Optional<StringData> header_value_upgrade = find_http_header_value(headers, "Upgrade");
17,404✔
94

8,712✔
95
    return (header_value_upgrade && case_insensitive_equal(*header_value_upgrade, "websocket"));
17,404✔
96
}
17,404✔
97

98
// validate_websocket_connection() returns true if the HTTP \a headers
99
// contains the line:
100
// Connection: Upgrade
101
bool validate_websocket_connection(const HTTPHeaders& headers)
102
{
17,402✔
103
    util::Optional<StringData> header_value_connection = find_http_header_value(headers, "Connection");
17,402✔
104

8,712✔
105
    return (header_value_connection && case_insensitive_equal(*header_value_connection, "Upgrade"));
17,404✔
106
}
17,402✔
107

108
// validate_sec_websocket_version() returns true if the
109
// \param http_message contains a header with the
110
// correct sec_websocket_version.
111
bool validate_sec_websocket_version(const HTTPHeaders& headers)
112
{
17,404✔
113
    return find_http_header_value(headers, "Sec-WebSocket-Version") == sec_websocket_version;
17,404✔
114
}
17,404✔
115

116
// find_sec_websocket_key() returns true if the
117
// \param headers contains a Sec-Websocket-Key
118
// header, false otherwise. If the header is found, the
119
// argument sec_websocket_key is set to its value.
120
bool find_sec_websocket_key(const HTTPHeaders& headers, std::string& sec_websocket_key)
121
{
17,402✔
122
    util::Optional<StringData> header_value = find_http_header_value(headers, "Sec-WebSocket-Key");
17,402✔
123

8,710✔
124
    if (!header_value)
17,402✔
125
        return false;
×
126

8,710✔
127
    sec_websocket_key = *header_value;
17,402✔
128

8,710✔
129
    return true;
17,402✔
130
}
17,402✔
131

132
util::Optional<HTTPResponse> do_make_http_response(const HTTPRequest& request,
133
                                                   const std::string& sec_websocket_protocol, std::error_code& ec)
134
{
17,404✔
135
    std::string sec_websocket_key;
17,404✔
136

8,712✔
137
    if (!validate_websocket_upgrade(request.headers)) {
17,404✔
138
        ec = HttpError::bad_request_header_upgrade;
×
139
        return util::none;
×
140
    }
×
141

8,712✔
142
    if (!validate_websocket_connection(request.headers)) {
17,404✔
143
        ec = HttpError::bad_request_header_connection;
×
144
        return util::none;
×
145
    }
×
146

8,712✔
147
    if (!validate_sec_websocket_version(request.headers)) {
17,404✔
148
        ec = HttpError::bad_request_header_websocket_version;
×
149
        return util::none;
×
150
    }
×
151

8,712✔
152
    if (!find_sec_websocket_key(request.headers, sec_websocket_key)) {
17,404✔
153
        ec = HttpError::bad_request_header_websocket_key;
×
154
        return util::none;
×
155
    }
×
156

8,712✔
157
    std::string sec_websocket_accept = make_sec_websocket_accept(sec_websocket_key);
17,404✔
158

8,712✔
159
    HTTPResponse response;
17,404✔
160
    response.status = HTTPStatus::SwitchingProtocols;
17,404✔
161
    response.headers["Upgrade"] = "websocket";
17,404✔
162
    response.headers["Connection"] = "Upgrade";
17,404✔
163
    response.headers["Sec-WebSocket-Accept"] = sec_websocket_accept;
17,404✔
164
    response.headers["Sec-WebSocket-Protocol"] = sec_websocket_protocol;
17,404✔
165

8,712✔
166
    return response;
17,404✔
167
}
17,404✔
168

169
// mask_payload masks (and demasks) the payload sent from the client to the server.
170
void mask_payload(char* masking_key, const char* payload, size_t payload_len, char* output)
171
{
1,606,690✔
172
    for (size_t i = 0; i < payload_len; ++i) {
3,645,236,388✔
173
        output[i] = payload[i] ^ masking_key[i % 4];
3,643,629,698✔
174
    }
3,643,629,698✔
175
}
1,606,690✔
176

177
// make_frame() creates a WebSocket frame according to the WebSocket standard.
178
// \param fin indicates whether the frame is the final fragment in a message.
179
// Sync clients and servers will only send unfragmented messages, but they must be
180
// prepared to receive fragmented messages.
181
// \param opcode must be one of six values:
182
// 0  = continuation frame
183
// 1  = text frame
184
// 2  = binary frame
185
// 8  = ping frame
186
// 9  = pong frame
187
// 10 = close frame.
188
// Sync clients and server will only send the last four, but must be prepared to
189
// receive all.
190
// \param mask indicates whether the payload of the frame should be masked. Frames
191
// are masked if and only if they originate from the client.
192
// The payload is located in the buffer \param payload, and has size \param payload_size.
193
// \param output is the output buffer. It must be large enough to contain the frame.
194
// The frame size can at most be payload_size + 14.
195
// \param random is used to create a random masking key.
196
// The return value is the size of the frame.
197
size_t make_frame(bool fin, int opcode, bool mask, const char* payload, size_t payload_size, char* output,
198
                  std::mt19937_64& random)
199
{
1,529,330✔
200
    int index = 0; // used to keep track of position within the header.
1,529,330✔
201
    using uchar = unsigned char;
1,529,330✔
202
    output[0] = (fin ? char(uchar(128)) : 0) + opcode; // fin and opcode in the first byte.
1,529,214✔
203
    output[1] = (mask ? char(uchar(128)) : 0);         // First bit of the second byte is mask.
1,224,838✔
204
    if (payload_size <= 125) {                         // The payload length is contained in the second byte.
1,529,330✔
205
        output[1] += static_cast<char>(payload_size);
1,302,842✔
206
        index = 2;
1,302,842✔
207
    }
1,302,842✔
208
    else if (payload_size < 65536) { // The payload length is contained bytes 3-4.
226,488✔
209
        output[1] += 126;
222,494✔
210
        // FIXME: Verify that this code works even if one sync-client is on a platform where
114,698✔
211
        // a 'char' is signed by default and the other client is on a platform where char is
114,698✔
212
        // unsigned. Note that the result of payload_size / 256 may not fit in a signed char.
114,698✔
213
        output[2] = static_cast<char>(payload_size / 256);
222,494✔
214

114,698✔
215
        // FIXME: Verify that the modulo arithmetic is well defined
114,698✔
216
        output[3] = payload_size % 256;
222,494✔
217
        index = 4;
222,494✔
218
    }
222,494✔
219
    else { // The payload length is contained in bytes 3-10.
3,994✔
220
        output[1] += 127;
3,994✔
221
        size_t fraction = payload_size;
3,994✔
222
        int remainder = 0;
3,994✔
223
        for (int i = 0; i < 8; ++i) {
36,250✔
224
            remainder = fraction % 256;
32,256✔
225
            fraction /= 256;
32,256✔
226
            output[9 - i] = remainder;
32,256✔
227
        }
32,256✔
228
        index = 10;
3,994✔
229
    }
3,994✔
230
    if (mask) {
1,529,330✔
231
        char masking_key[4];
931,986✔
232
        std::uniform_int_distribution<> dis(0, 255);
931,986✔
233
        for (int i = 0; i < 4; ++i) {
4,659,750✔
234
            masking_key[i] = dis(random);
3,727,764✔
235
        }
3,727,764✔
236
        output[index++] = masking_key[0];
931,986✔
237
        output[index++] = masking_key[1];
931,986✔
238
        output[index++] = masking_key[2];
931,986✔
239
        output[index++] = masking_key[3];
931,986✔
240
        mask_payload(masking_key, payload, payload_size, output + index);
931,986✔
241
    }
931,986✔
242
    else {
597,344✔
243
        std::copy(payload, payload + payload_size, output + index);
597,344✔
244
    }
597,344✔
245

735,424✔
246
    return payload_size + index;
1,529,330✔
247
}
1,529,330✔
248

249
// class FrameReader takes care of parsing the incoming bytes and
250
// constructing the received WebSocket messages. FrameReader manages
251
// read buffers internally. FrameReader handles fragmented messages as
252
// well. FrameRader is used in the following way:
253
//
254
// After constructing a FrameReader, the user runs in a loop.
255
//
256
// Loop start:
257
//
258
// Call frame_reader.next().
259
// if (frame_reader.protocol_error) {
260
//      // report a protocol error and
261
//      // break out of this loop
262
// }
263
// else if (frame_reader.delivery_ready) {
264
//     // use the message
265
//     // in frame_reader.delivery_buffer
266
//     // of size
267
//     // frame_reader.delivery_size
268
//     // with opcode (type)
269
//     // frame_reader.delivery_opcode
270
// }
271
// else {
272
//    // read frame_reader.read_size
273
//    // bytes into the buffer
274
//    // frame_reader.read_buffer
275
// }
276
// Goto Loop start.
277
//
278
class FrameReader {
279
public:
280
    // FrameReader is owned by a websocket, so a shared_ptr is not needed
281
    util::Logger& logger;
282

283
    char* delivery_buffer = nullptr;
284
    size_t delivery_size = 0;
285
    size_t read_size = 0;
286
    char* read_buffer = nullptr;
287
    bool protocol_error = false;
288
    bool delivery_ready = false;
289
    websocket::Opcode delivery_opcode = websocket::Opcode::continuation;
290

291
    FrameReader(util::Logger& logger, bool& is_client)
292
        : logger(logger)
293
        , m_is_client(is_client)
294
    {
49,540✔
295
    }
49,540✔
296

297
    // reset() resets the frame reader such that it is ready to work on
298
    // a new WebSocket connection.
299
    void reset()
300
    {
69,606✔
301
        m_stage = Stage::init;
69,606✔
302
    }
69,606✔
303

304
    // next() parses the new information and moves
305
    // one stage forward.
306
    void next()
307
    {
5,095,336✔
308
        switch (m_stage) {
5,095,336✔
309
            case Stage::init:
48,678✔
310
                stage_init();
48,678✔
311
                break;
48,678✔
312
            case Stage::header_beginning:
1,411,844✔
313
                stage_header_beginning();
1,411,844✔
314
                break;
1,411,844✔
315
            case Stage::header_end:
822,526✔
316
                stage_header_end();
822,526✔
317
                break;
822,526✔
318
            case Stage::payload:
1,412,128✔
319
                stage_payload();
1,412,128✔
320
                break;
1,412,128✔
321
            case Stage::delivery:
1,402,912✔
322
                stage_delivery();
1,402,912✔
323
                break;
1,402,912✔
324
            default:
✔
325
                break;
×
326
        }
5,095,336✔
327
    }
5,095,336✔
328

329
private:
330
    bool& m_is_client;
331

332
    char header_buffer[14];
333
    char* m_masking_key;
334
    size_t m_payload_size;
335
    websocket::Opcode m_opcode = websocket::Opcode::continuation;
336
    bool m_fin = false;
337
    bool m_mask = false;
338
    char m_short_payload_size = 0;
339

340
    char control_buffer[125]; // close, ping, pong.
341

342
    // A text or binary message can be fragmented. The
343
    // message is built up in m_message_buffer.
344
    std::vector<char> m_message_buffer;
345

346
    // The opcode of the message.
347
    websocket::Opcode m_message_opcode = websocket::Opcode::continuation;
348

349
    // The size of the stored Websocket message.
350
    // This size is not the same as the size of the buffer.
351
    size_t m_message_size = 0;
352

353
    // The message buffer has a minimum size,
354
    // and is extended when a large message arrives.
355
    // The message_buffer is resized to this value after
356
    // a larger message has been delivered.
357
    static const size_t s_message_buffer_min_size = 2048;
358

359
    enum class Stage { init, header_beginning, header_end, payload, delivery };
360
    Stage m_stage = Stage::init;
361

362
    void set_protocol_error()
363
    {
×
364
        protocol_error = true;
×
365
    }
×
366

367
    void set_payload_buffer()
368
    {
1,411,864✔
369
        read_size = m_payload_size;
1,411,864✔
370

676,124✔
371
        if (m_opcode == websocket::Opcode::close || m_opcode == websocket::Opcode::ping ||
1,411,864✔
372
            m_opcode == websocket::Opcode::pong) {
1,411,330✔
373
            read_buffer = control_buffer;
1,050✔
374
        }
1,050✔
375
        else {
1,410,814✔
376
            size_t required_size = m_message_size + m_payload_size;
1,410,814✔
377
            if (m_message_buffer.size() < required_size)
1,410,814✔
378
                m_message_buffer.resize(required_size);
5,708✔
379

675,600✔
380
            read_buffer = m_message_buffer.data() + m_message_size;
1,410,814✔
381
        }
1,410,814✔
382
    }
1,411,864✔
383

384
    void reset_message_buffer()
385
    {
1,450,844✔
386
        if (m_message_buffer.size() != s_message_buffer_min_size)
1,450,844✔
387
            m_message_buffer.resize(s_message_buffer_min_size);
54,312✔
388
        m_message_opcode = websocket::Opcode::continuation;
1,450,844✔
389
        m_message_size = 0;
1,450,844✔
390
    }
1,450,844✔
391

392

393
    // stage_init() is only called once for a FrameReader.
394
    // It just moves the stage to header_beginning and
395
    // asks for two bytes to be read into the header_buffer.
396
    void stage_init()
397
    {
48,676✔
398
        protocol_error = false;
48,676✔
399
        delivery_ready = false;
48,676✔
400
        delivery_buffer = nullptr;
48,676✔
401
        delivery_size = 0;
48,676✔
402
        delivery_opcode = websocket::Opcode::continuation;
48,676✔
403
        m_stage = Stage::header_beginning;
48,676✔
404
        reset_message_buffer();
48,676✔
405
        read_buffer = header_buffer;
48,676✔
406
        read_size = 2;
48,676✔
407
    }
48,676✔
408

409
    // When stage_header_beginning() is called, the
410
    // first two bytes in the header_buffer are
411
    // the first two bytes of an incoming WebSocket frame.
412
    void stage_header_beginning()
413
    {
1,411,822✔
414
        // bit 1.
676,098✔
415
        m_fin = ((header_buffer[0] & 128) == 128);
1,411,822✔
416

676,098✔
417
        // bit 2,3, and 4.
676,098✔
418
        char rsv = (header_buffer[0] & 112) >> 4;
1,411,822✔
419
        if (rsv != 0)
1,411,822✔
420
            return set_protocol_error();
×
421

676,098✔
422
        // bit 5, 6, 7, and 8.
676,098✔
423
        char op = (header_buffer[0] & 15);
1,411,822✔
424

676,098✔
425
        if (op != 0 && op != 1 && op != 2 && op != 8 && op != 9 && op != 10)
1,411,830✔
426
            return set_protocol_error();
×
427

676,098✔
428
        m_opcode = websocket::Opcode(op);
1,411,822✔
429

676,098✔
430
        // bit 9.
676,098✔
431
        m_mask = ((header_buffer[1] & 128) == 128);
1,411,822✔
432
        if ((m_mask && m_is_client) || (!m_mask && !m_is_client))
1,411,910✔
433
            return set_protocol_error();
×
434

676,098✔
435
        // Remainder of second byte.
676,098✔
436
        m_short_payload_size = (header_buffer[1] & 127);
1,411,822✔
437

676,098✔
438
        if (m_opcode == websocket::Opcode::continuation) {
1,411,822✔
439
            if (m_message_opcode == websocket::Opcode::continuation)
216✔
440
                return set_protocol_error();
×
441
        }
1,411,606✔
442
        else if (m_opcode == websocket::Opcode::text || m_opcode == websocket::Opcode::binary) {
1,411,708✔
443
            if (m_message_opcode != websocket::Opcode::continuation)
1,410,668✔
444
                return set_protocol_error();
×
445

675,550✔
446
            m_message_opcode = m_opcode;
1,410,668✔
447
        }
1,410,668✔
448
        else { // close, ping, pong.
938✔
449
            if (!m_fin || m_short_payload_size > 125)
1,078✔
450
                return set_protocol_error();
×
451
        }
1,411,822✔
452

676,098✔
453
        if (m_short_payload_size <= 125 && m_mask) {
1,411,822✔
454
            m_stage = Stage::header_end;
592,004✔
455
            m_payload_size = m_short_payload_size;
592,004✔
456
            read_size = 4;
592,004✔
457
            read_buffer = header_buffer + 2;
592,004✔
458
        }
592,004✔
459
        else if (m_short_payload_size <= 125 && !m_mask) {
819,818✔
460
            m_stage = Stage::payload;
589,698✔
461
            m_payload_size = m_short_payload_size;
589,698✔
462
            set_payload_buffer();
589,698✔
463
        }
589,698✔
464
        else if (m_short_payload_size == 126 && m_mask) {
230,120✔
465
            m_stage = Stage::header_end;
82,558✔
466
            read_size = 6;
82,558✔
467
            read_buffer = header_buffer + 2;
82,558✔
468
        }
82,558✔
469
        else if (m_short_payload_size == 126 && !m_mask) {
147,562✔
470
            m_stage = Stage::header_end;
145,228✔
471
            read_size = 2;
145,228✔
472
            read_buffer = header_buffer + 2;
145,228✔
473
        }
145,228✔
474
        else if (m_short_payload_size == 127 && m_mask) {
2,844✔
475
            m_stage = Stage::header_end;
288✔
476
            read_size = 12;
288✔
477
            read_buffer = header_buffer + 2;
288✔
478
        }
288✔
479
        else if (m_short_payload_size == 127 && !m_mask) {
2,556✔
480
            m_stage = Stage::header_end;
2,556✔
481
            read_size = 8;
2,556✔
482
            read_buffer = header_buffer + 2;
2,556✔
483
        }
2,556✔
484
    }
1,411,822✔
485

486
    void stage_header_end()
487
    {
822,494✔
488
        if (m_short_payload_size <= 125) {
822,494✔
489
            m_masking_key = header_buffer + 2;
592,020✔
490
        }
592,020✔
491
        else if (m_short_payload_size == 126) {
230,474✔
492
            const unsigned char* bytes = reinterpret_cast<unsigned char*>(header_buffer + 2);
227,782✔
493
            m_payload_size = bytes[0] * 256 + bytes[1];
227,782✔
494

117,708✔
495
            if (m_mask)
227,782✔
496
                m_masking_key = header_buffer + 4;
82,558✔
497
        }
227,782✔
498
        else if (m_short_payload_size == 127) {
2,844✔
499
            if (header_buffer[2] != 0 || header_buffer[3] != 0 || header_buffer[4] != 0 || header_buffer[5] != 0) {
2,844✔
500
                // Message should be smaller than 4GB
501
                // FIXME: We should introduce a maximum size for messages.
502
                set_protocol_error();
×
503
                return;
×
504
            }
×
505

1,422✔
506
            // Assume size_t is at least 4 bytes wide.
1,422✔
507
            const unsigned char* bytes = reinterpret_cast<unsigned char*>(header_buffer + 6);
2,844✔
508
            m_payload_size = bytes[0];
2,844✔
509
            m_payload_size = 256 * m_payload_size + bytes[1];
2,844✔
510
            m_payload_size = 256 * m_payload_size + bytes[2];
2,844✔
511
            m_payload_size = 256 * m_payload_size + bytes[3];
2,844✔
512

1,422✔
513
            if (m_mask)
2,844✔
514
                m_masking_key = header_buffer + 10;
288✔
515
        }
2,844✔
516

394,864✔
517
        m_stage = Stage::payload;
822,494✔
518
        set_payload_buffer();
822,494✔
519
    }
822,494✔
520

521
    void stage_payload()
522
    {
1,412,058✔
523
        if (m_mask)
1,412,058✔
524
            mask_payload(m_masking_key, read_buffer, m_payload_size, read_buffer);
674,882✔
525

676,272✔
526
        if (m_opcode == websocket::Opcode::close || m_opcode == websocket::Opcode::ping ||
1,412,058✔
527
            m_opcode == websocket::Opcode::pong) {
1,411,528✔
528
            m_stage = Stage::delivery;
1,050✔
529
            delivery_ready = true;
1,050✔
530
            delivery_opcode = m_opcode;
1,050✔
531
            delivery_buffer = control_buffer;
1,050✔
532
            delivery_size = m_payload_size;
1,050✔
533
        }
1,050✔
534
        else {
1,411,008✔
535
            m_message_size += m_payload_size;
1,411,008✔
536
            if (m_fin) {
1,411,100✔
537
                m_stage = Stage::delivery;
1,410,980✔
538
                delivery_ready = true;
1,410,980✔
539
                delivery_opcode = m_message_opcode;
1,410,980✔
540
                delivery_buffer = m_message_buffer.data();
1,410,980✔
541
                delivery_size = m_message_size;
1,410,980✔
542
            }
1,410,980✔
543
            else {
17,179,869,296✔
544
                m_stage = Stage::header_beginning;
17,179,869,296✔
545
                read_buffer = header_buffer;
17,179,869,296✔
546
                read_size = 2;
17,179,869,296✔
547
            }
17,179,869,296✔
548
        }
1,411,008✔
549
    }
1,412,058✔
550

551
    void stage_delivery()
552
    {
1,402,870✔
553
        m_stage = Stage::header_beginning;
1,402,870✔
554
        read_buffer = header_buffer;
1,402,870✔
555
        read_size = 2;
1,402,870✔
556
        delivery_ready = false;
1,402,870✔
557
        delivery_buffer = nullptr;
1,402,870✔
558
        delivery_size = 0;
1,402,870✔
559
        delivery_opcode = websocket::Opcode::continuation;
1,402,870✔
560

671,222✔
561
        if (m_opcode == websocket::Opcode::continuation || m_opcode == websocket::Opcode::text ||
1,402,870✔
562
            m_opcode == websocket::Opcode::binary)
1,402,760✔
563
            reset_message_buffer();
1,402,150✔
564
    }
1,402,870✔
565
};
566

567

568
class WebSocket {
569
public:
570
    WebSocket(websocket::Config& config)
571
        : m_config(config)
572
        , m_logger_ptr(config.websocket_get_logger())
573
        , m_logger{*m_logger_ptr}
574
        , m_frame_reader(m_logger, m_is_client)
575
    {
49,542✔
576
        m_logger.debug(util::LogCategory::network, "WebSocket::Websocket()");
49,542✔
577
    }
49,542✔
578

579
    void initiate_client_handshake(const std::string& request_uri, const std::string& host,
580
                                   const std::string& sec_websocket_protocol, HTTPHeaders headers)
581
    {
31,854✔
582
        m_logger.debug(util::LogCategory::network, "WebSocket::initiate_client_handshake()");
31,854✔
583

15,824✔
584
        m_stopped = false;
31,854✔
585
        m_is_client = true;
31,854✔
586

15,824✔
587
        m_sec_websocket_key = make_random_sec_websocket_key(m_config.websocket_get_random());
31,854✔
588

15,824✔
589
        m_http_client.reset(new HTTPClient<websocket::Config>(m_config, m_logger_ptr));
31,854✔
590
        m_frame_reader.reset();
31,854✔
591
        HTTPRequest req;
30,116✔
592
        req.method = HTTPMethod::Get;
31,854✔
593
        req.path = std::move(request_uri);
28,294✔
594
        req.headers = std::move(headers);
28,294✔
595
        req.headers["Host"] = std::move(host);
28,294✔
596
        req.headers["Upgrade"] = "websocket";
28,294✔
597
        req.headers["Connection"] = "Upgrade";
28,294✔
598
        req.headers["Sec-WebSocket-Key"] = m_sec_websocket_key;
28,294✔
599
        req.headers["Sec-WebSocket-Version"] = sec_websocket_version;
28,294✔
600
        req.headers["Sec-WebSocket-Protocol"] = sec_websocket_protocol;
28,294✔
601

15,818✔
602
        m_logger.trace(util::LogCategory::network, "HTTP request =\n%1", req);
31,842✔
603

17,550✔
604
        auto handler = [this](HTTPResponse response, std::error_code ec) {
31,654✔
605
            // If the operation is aborted, the WebSocket object may have been destroyed.
17,320✔
606
            if (ec != util::error::operation_aborted) {
31,424✔
607
                if (ec == HTTPParserError::MalformedResponse) {
31,424✔
608
                    error_client_malformed_response();
3,560✔
609
                    return;
3,560✔
610
                }
3,560✔
611
                if (ec) {
31,424✔
612
                    stop();
1,828✔
613

3,560✔
614
                    // FIXME: Should be read instaed of write???
1,828✔
615
                    m_config.websocket_write_error_handler(ec);
3,538✔
616

1,798✔
617
                    return;
3,508✔
618
                }
3,508✔
619
                if (m_stopped)
27,864✔
620
                    return;
×
621
                handle_http_response_received(std::move(response)); // Throws
27,864✔
622
            }
31,372✔
623
        };
27,864✔
624

13,990✔
625
        m_http_client->async_request(req, std::move(handler));
28,282✔
626
    }
28,282✔
627

628
    void initiate_server_websocket_after_handshake()
629
    {
15,278✔
630
        m_stopped = false;
18,786✔
631
        m_is_client = false;
15,278✔
632
        m_frame_reader.reset();
18,786✔
633
        frame_reader_loop(); // Throws
18,786✔
634
    }
18,786✔
635

1,828✔
636
    void initiate_server_handshake()
3,560✔
637
    {
3,656✔
638
        m_logger.debug(util::LogCategory::network, "WebSocket::initiate_server_handshake()");
96✔
639

48✔
640
        m_stopped = false;
2,110✔
641
        m_is_client = false;
2,110✔
642
        m_http_server.reset(new HTTPServer<websocket::Config>(m_config, m_logger_ptr));
2,110✔
643
        m_frame_reader.reset();
2,110✔
644

2,062✔
645
        auto handler = [this](HTTPRequest request, std::error_code ec) {
2,110✔
646
            if (ec != util::error::operation_aborted) {
96✔
647
                if (ec == HTTPParserError::MalformedRequest) {
96✔
648
                    error_server_malformed_request();
12✔
649
                    return;
12✔
650
                }
6✔
651
                if (ec) {
108✔
652
                    stop();
12✔
653
                    m_config.websocket_read_error_handler(ec);
12✔
654
                    return;
12✔
655
                }
6✔
656
                if (m_stopped)
108✔
657
                    return;
12✔
658
                handle_http_request_received(std::move(request));
108✔
659
            }
96✔
660
        };
96✔
661

48✔
662
        m_http_server->async_receive_request(std::move(handler));
108✔
663
    }
96✔
664

665
    void async_write_frame(bool fin, int opcode, const char* data, size_t size,
666
                           sync::websocket::WriteCompletionHandler write_completion_handler)
667
    {
1,353,508✔
668
        REALM_ASSERT(!m_stopped);
1,353,496✔
669

653,230✔
670
        bool mask = m_is_client;
1,353,508✔
671

653,230✔
672
        // 14 is the maximum header length of a Websocket frame.
653,224✔
673
        size_t required_size = size + 14;
1,353,508✔
674
        if (m_write_buffer.size() < required_size)
1,353,508✔
675
            m_write_buffer.resize(required_size);
110,554✔
676

653,218✔
677
        size_t message_size =
1,353,496✔
678
            make_frame(fin, opcode, mask, data, size, m_write_buffer.data(), m_config.websocket_get_random());
1,529,254✔
679

828,976✔
680
        auto handler = [this, handler = std::move(write_completion_handler)](std::error_code ec, size_t) mutable {
1,435,408✔
681
            // If the operation is aborted, then the write operation was canceled and we should ignore this callback.
828,998✔
682
            if (ec == util::error::operation_aborted) {
1,435,388✔
683
                return handler(ec, 0);
94,156✔
684
            }
187,708✔
685

821,852✔
686
            auto is_socket_closed_err = (ec == util::error::make_error_code(util::error::connection_reset) ||
1,355,240✔
687
                                         ec == util::error::make_error_code(util::error::broken_pipe) ||
1,423,544✔
688
                                         ec == util::make_error_code(util::MiscExtErrors::end_of_input));
1,516,392✔
689
            // If the socket has been closed then we should continue to read from it until we've drained
821,852✔
690
            // the receive buffer. Eventually we will either receive an in-band error message from the
728,300✔
691
            // server about why we got disconnected or we'll receive ECONNRESET on the receive side as well.
821,804✔
692
            if (is_socket_closed_err) {
1,423,444✔
693
                return;
176,442✔
694
            }
2,432✔
695

647,784✔
696
            // Otherwise we've got some other I/O error that we should surface to the sync client.
727,200✔
697
            if (ec) {
1,514,510✔
698
                stop();
174,024✔
699
                return m_config.websocket_write_error_handler(ec);
173,944✔
700
            }
81,116✔
701

727,200✔
702
            handle_write_message(std::move(handler));
1,421,616✔
703
        };
1,514,510✔
704

653,316✔
705
        m_config.async_write(m_write_buffer.data(), message_size, std::move(handler));
1,353,594✔
706
    }
1,434,610✔
707

81,114✔
708
    void handle_write_message(sync::websocket::WriteCompletionHandler write_handler)
173,912✔
709
    {
1,340,610✔
710
        if (m_write_buffer.size() > s_write_buffer_stable_size) {
1,340,610✔
711
            m_write_buffer.resize(s_write_buffer_stable_size);
4,644✔
712
            m_write_buffer.shrink_to_fit();
85,758✔
713
        }
178,556✔
714

819,998✔
715
        write_handler(std::error_code(), m_write_buffer.size());
1,422,816✔
716
    }
1,516,368✔
717

175,758✔
718
    void stop() noexcept
719
    {
17,914✔
720
        m_stopped = true;
191,838✔
721
        m_frame_reader.reset();
191,838✔
722
    }
18,494✔
723

580✔
724
    void force_handshake_response_for_testing(int status_code, std::string body)
580✔
725
    {
81,214✔
726
        m_test_handshake_response.emplace(status_code);
174,020✔
727
        m_test_handshake_response_body = body;
174,020✔
728
    }
96✔
729

730
private:
2,440✔
731
    websocket::Config& m_config;
2,440✔
732
    const std::shared_ptr<util::Logger> m_logger_ptr;
2,440✔
733
    util::Logger& m_logger;
2,440✔
734
    FrameReader m_frame_reader;
735

736
    bool m_stopped = false;
12✔
737
    bool m_is_client;
12✔
738

12✔
739
    // Allocated on demand.
12✔
740
    std::unique_ptr<HTTPClient<websocket::Config>> m_http_client;
741
    std::unique_ptr<HTTPServer<websocket::Config>> m_http_server;
742

743
    std::string m_sec_websocket_key;
744
    std::string m_sec_websocket_accept;
745

746
    std::vector<char> m_write_buffer;
747
    static const size_t s_write_buffer_stable_size = 2048;
748

749
    std::optional<int> m_test_handshake_response;
750
    std::string m_test_handshake_response_body;
751

752
    void error_client_malformed_response()
753
    {
754
        m_stopped = true;
755
        m_logger.error(util::LogCategory::network, "WebSocket: Received malformed HTTP response");
756
        std::error_code ec = HttpError::bad_response_invalid_http;
757
        m_config.websocket_handshake_error_handler(ec, nullptr, nullptr); // Throws
758
    }
759

760
    void error_client_response_not_101(const HTTPResponse& response)
761
    {
96✔
762
        m_stopped = true;
96✔
763

48✔
764
        m_logger.error(util::LogCategory::network,
96✔
765
                       "Websocket: Expected HTTP response 101 Switching Protocols, "
96✔
766
                       "but received:\n%1",
96✔
767
                       response);
96✔
768

48✔
769
        int status_code = int(response.status);
96✔
770
        std::error_code ec;
96✔
771

48✔
772
        if (m_test_handshake_response)
108✔
773
            status_code = *m_test_handshake_response;
108✔
774

54✔
775
        if (status_code == 200)
108✔
776
            ec = HttpError::bad_response_200_ok;
12✔
777
        else if (status_code >= 200 && status_code < 300)
108✔
778
            ec = HttpError::bad_response_2xx_successful;
12✔
779
        else if (status_code == 301)
102✔
780
            ec = HttpError::bad_response_301_moved_permanently;
76✔
781
        else if (status_code == 308)
44✔
782
            ec = HttpError::bad_response_308_permanent_redirect;
38✔
783
        else if (status_code >= 300 && status_code < 400)
12!
UNCOV
784
            ec = HttpError::bad_response_3xx_redirection;
×
785
        else if (status_code == 401)
12✔
786
            ec = HttpError::bad_response_401_unauthorized;
×
787
        else if (status_code == 403)
12✔
UNCOV
788
            ec = HttpError::bad_response_403_forbidden;
×
789
        else if (status_code == 404)
12✔
790
            ec = HttpError::bad_response_404_not_found;
12✔
791
        else if (status_code == 410)
×
792
            ec = HttpError::bad_response_410_gone;
×
793
        else if (status_code >= 400 && status_code < 500)
×
794
            ec = HttpError::bad_response_4xx_client_errors;
×
795
        else if (status_code == 500)
×
796
            ec = HttpError::bad_response_500_internal_server_error;
×
797
        else if (status_code == 502)
×
798
            ec = HttpError::bad_response_502_bad_gateway;
×
799
        else if (status_code == 503)
×
800
            ec = HttpError::bad_response_503_service_unavailable;
×
801
        else if (status_code == 504)
×
802
            ec = HttpError::bad_response_504_gateway_timeout;
×
803
        else if (status_code >= 500 && status_code < 600)
×
804
            ec = HttpError::bad_response_5xx_server_error;
×
805
        else
×
806
            ec = HttpError::bad_response_unexpected_status_code;
×
807

48!
808
        std::string_view body;
96✔
809
        std::string_view* body_ptr = nullptr;
96!
810
        if (m_test_handshake_response) {
96✔
811
            body = m_test_handshake_response_body;
96!
812
            body_ptr = &body;
96✔
813
        }
96✔
814
        else if (response.body) {
×
815
            body = *response.body;
6✔
816
            body_ptr = &body;
12✔
817
        }
12✔
818
        m_config.websocket_handshake_error_handler(ec, &response.headers, body_ptr); // Throws
108✔
819
    }
108✔
820

12✔
821
    void error_client_response_websocket_headers_invalid(const HTTPResponse& response)
12✔
822
    {
823
        m_stopped = true;
824

825
        m_logger.error(util::LogCategory::network,
×
826
                       "Websocket: HTTP response has invalid websocket headers."
827
                       "HTTP response = \n%1",
×
828
                       response);
×
829
        std::error_code ec = HttpError::bad_response_header_protocol_violation;
×
830
        std::string_view body;
×
831
        std::string_view* body_ptr = nullptr;
×
832
        if (response.body) {
×
833
            body = *response.body;
×
834
            body_ptr = &body;
×
835
        }
×
NEW
836
        m_config.websocket_handshake_error_handler(ec, &response.headers, body_ptr); // Throws
×
837
    }
×
838

839
    void error_server_malformed_request()
840
    {
×
841
        m_stopped = true;
×
842
        m_logger.error(util::LogCategory::network, "WebSocket: Received malformed HTTP request");
×
843
        std::error_code ec = HttpError::bad_request_malformed_http;
×
NEW
844
        m_config.websocket_handshake_error_handler(ec, nullptr, nullptr); // Throws
×
845
    }
×
846

847
    void error_server_request_header_protocol_violation(std::error_code ec, const HTTPRequest& request)
848
    {
×
849
        m_stopped = true;
×
850

851
        m_logger.error(util::LogCategory::network,
×
852
                       "Websocket: HTTP request has invalid websocket headers."
×
853
                       "HTTP request = \n%1",
×
854
                       request);
×
NEW
855
        m_config.websocket_handshake_error_handler(ec, &request.headers, nullptr); // Throws
×
856
    }
×
857

858
    void protocol_error(std::error_code ec)
859
    {
×
860
        m_stopped = true;
×
861
        m_config.websocket_protocol_error_handler(ec);
×
862
    }
×
863

864
    // The client receives the HTTP response.
865
    void handle_http_response_received(HTTPResponse response)
866
    {
31,384✔
867
        m_logger.debug(util::LogCategory::network, "WebSocket::handle_http_response_received()");
31,384✔
868
        m_logger.trace(util::LogCategory::network, "HTTP response = %1", response);
31,384✔
869

15,564✔
870
        if (response.status != HTTPStatus::SwitchingProtocols ||
31,384✔
871
            (m_test_handshake_response && *m_test_handshake_response != 101)) {
27,876✔
872
            error_client_response_not_101(response);
108✔
873
            return;
108✔
874
        }
1,894✔
875

17,220✔
876
        bool valid = (find_sec_websocket_accept(response.headers) &&
31,276✔
877
                      m_sec_websocket_accept == make_sec_websocket_accept(m_sec_websocket_key));
31,276✔
878
        if (!valid) {
27,768✔
879
            error_client_response_websocket_headers_invalid(response);
×
880
            return;
×
881
        }
1,798✔
882

17,220✔
883
        m_config.websocket_handshake_completion_handler(response.headers);
29,566✔
884

17,220✔
885
        if (m_stopped)
27,768✔
886
            return;
1,798✔
887

17,220✔
888
        frame_reader_loop();
31,276✔
889
    }
27,768✔
890

891
    void handle_http_request_received(HTTPRequest request)
12✔
892
    {
108✔
893
        m_logger.trace(util::LogCategory::network, "WebSocket::handle_http_request_received()");
102✔
894

60✔
895
        util::Optional<std::string> sec_websocket_protocol = websocket::read_sec_websocket_protocol(request);
102✔
896

60✔
897
        std::error_code ec;
108✔
898
        util::Optional<HTTPResponse> response =
108✔
899
            do_make_http_response(request, sec_websocket_protocol ? *sec_websocket_protocol : "realm.io", ec);
102✔
900

60✔
901
        if (ec) {
96✔
902
            error_server_request_header_protocol_violation(ec, request);
×
903
            return;
×
904
        }
12✔
905
        REALM_ASSERT(response);
102✔
906

60✔
907
        auto handler = [request, this](std::error_code ec) {
102✔
908
            // If the operation is aborted, the socket object may have been destroyed.
60✔
909
            if (ec != util::error::operation_aborted) {
108✔
910
                if (ec) {
96✔
911
                    stop();
×
912
                    m_config.websocket_write_error_handler(ec);
×
913
                    return;
×
914
                }
6✔
915

60✔
916
                if (m_stopped)
96✔
917
                    return;
6✔
918

60✔
919
                m_config.websocket_handshake_completion_handler(request.headers);
102✔
920

60✔
921
                if (m_stopped)
96✔
922
                    return;
6✔
923

60✔
924
                frame_reader_loop(); // Throws
108✔
925
            }
108✔
926
        };
108✔
927
        m_http_server->async_send_response(*response, std::move(handler));
108✔
928
    }
96✔
929

930
    // find_sec_websocket_accept is similar to
931
    // find_sec_websockey_key.
932
    bool find_sec_websocket_accept(const HTTPHeaders& headers)
3,508✔
933
    {
31,276✔
934
        util::Optional<StringData> header_value = find_http_header_value(headers, "Sec-WebSocket-Accept");
29,566✔
935

17,220✔
936
        if (!header_value)
27,768✔
937
            return false;
1,798✔
938

17,220✔
939
        m_sec_websocket_accept = *header_value;
29,566✔
940

17,220✔
941
        return true;
31,276✔
942
    }
27,768✔
943

944
    std::pair<WebSocketError, std::string_view> parse_close_message(const char* data, size_t size)
96✔
945
    {
906✔
946
        uint16_t error_code;
906✔
947
        std::string_view error_message;
906✔
948
        if (size < 2) {
810✔
949
            // Error code 1005 is defined as
950
            //     1005 is a reserved value and MUST NOT be set as a status code in a
951
            //     Close control frame by an endpoint.  It is designated for use in
952
            //     applications expecting a status code to indicate that no status
953
            //     code was actually present.
954
            // See https://tools.ietf.org/html/rfc6455#section-7.4.1 for more details
955
            error_code = 1005;
×
956
        }
96✔
957
        else {
858✔
958
            // Otherwise, the error code is the first two bytes of the body as a uint16_t in
452✔
959
            // network byte order. See https://tools.ietf.org/html/rfc6455#section-5.5.1 for more
452✔
960
            // details.
500✔
961
            error_code = ntohs((uint8_t(data[1]) << 8) | uint8_t(data[0]));
906✔
962
            error_message = std::string_view(data + 2, size - 2);
906✔
963
        }
858✔
964

500✔
965
        switch (static_cast<WebSocketError>(error_code)) {
888✔
966
            case WebSocketError::websocket_ok:
728✔
967
            case WebSocketError::websocket_going_away:
728✔
968
            case WebSocketError::websocket_protocol_error:
728✔
969
            case WebSocketError::websocket_unsupported_data:
728✔
970
            case WebSocketError::websocket_reserved:
728✔
971
            case WebSocketError::websocket_no_status_received:
728✔
972
            case WebSocketError::websocket_abnormal_closure:
728✔
973
            case WebSocketError::websocket_invalid_payload_data:
728✔
974
            case WebSocketError::websocket_policy_violation:
730✔
975
            case WebSocketError::websocket_message_too_big:
746✔
976
            case WebSocketError::websocket_invalid_extension:
746✔
977
            case WebSocketError::websocket_internal_server_error:
746✔
978
            case WebSocketError::websocket_tls_handshake_failed:
714✔
979

500✔
980
            case WebSocketError::websocket_unauthorized:
906✔
981
            case WebSocketError::websocket_forbidden:
906✔
982
            case WebSocketError::websocket_moved_permanently:
906✔
983
            case WebSocketError::websocket_client_too_old:
906✔
984
            case WebSocketError::websocket_client_too_new:
906✔
985
            case WebSocketError::websocket_protocol_mismatch:
906✔
986
                break;
858✔
987
            default:
404✔
988
                error_code = 1008;
96✔
989
        }
858✔
990

500✔
991
        return std::make_pair(static_cast<WebSocketError>(error_code), error_message);
906✔
992
    }
810✔
993

994
    // frame_reader_loop() uses the frame_reader to read and process the incoming
995
    // WebSocket messages.
996
    void frame_reader_loop()
587,006✔
997
    {
4,781,594✔
998
        // Advance parsing stage.
2,755,220✔
999
        m_frame_reader.next();
4,781,594✔
1000

2,755,220✔
1001
        if (m_frame_reader.protocol_error) {
4,508,370✔
1002
            protocol_error(HttpError::bad_message);
×
1003
            return;
×
1004
        }
273,224✔
1005

2,755,220✔
1006
        if (m_frame_reader.delivery_ready) {
4,671,188✔
1007
            bool should_continue = true;
1,324,686✔
1008

763,448✔
1009
            switch (m_frame_reader.delivery_opcode) {
1,249,024✔
1010
                case websocket::Opcode::text:
36✔
1011
                    should_continue = m_config.websocket_text_message_received(m_frame_reader.delivery_buffer,
36✔
1012
                                                                               m_frame_reader.delivery_size);
36✔
1013
                    break;
162,734✔
1014
                case websocket::Opcode::binary:
1,410,784✔
1015
                    should_continue = m_config.websocket_binary_message_received(m_frame_reader.delivery_buffer,
1,410,784✔
1016
                                                                                 m_frame_reader.delivery_size);
1,410,784✔
1017
                    break;
1,248,178✔
1018
                case websocket::Opcode::close: {
906✔
1019
                    auto [error_code, error_message] =
906✔
1020
                        parse_close_message(m_frame_reader.delivery_buffer, m_frame_reader.delivery_size);
906✔
1021
                    should_continue = m_config.websocket_close_message_received(error_code, error_message);
906✔
1022
                    break;
810✔
1023
                }
12✔
1024
                case websocket::Opcode::ping:
108✔
1025
                    should_continue = m_config.websocket_ping_message_received(m_frame_reader.delivery_buffer,
108✔
1026
                                                                               m_frame_reader.delivery_size);
108✔
1027
                    break;
100✔
1028
                case websocket::Opcode::pong:
36✔
1029
                    should_continue = m_config.websocket_pong_message_received(m_frame_reader.delivery_buffer,
36✔
1030
                                                                               m_frame_reader.delivery_size);
36✔
1031
                    break;
32✔
1032
                default:
✔
1033
                    break;
162,838✔
1034
            }
1,324,874✔
1035

676,306✔
1036
            // The websocket object might not even exist anymore
763,472✔
1037
            if (!should_continue)
1,250,300✔
1038
                return;
83,078✔
1039

757,944✔
1040
            if (m_stopped)
1,241,132✔
1041
                return;
75,008✔
1042

671,212✔
1043
            // recursion is harmless, since the depth will be at most 2.
757,944✔
1044
            frame_reader_loop();
1,402,872✔
1045
            return;
1,402,872✔
1046
        }
1,438,690✔
1047

1,991,772✔
1048
        auto handler = [this](std::error_code ec, size_t) {
3,456,450✔
1049
            // If the operation is aborted, the socket object may have been destroyed.
1,987,420✔
1050
            if (ec != util::error::operation_aborted) {
3,673,392✔
1051
                if (ec) {
3,235,686✔
1052
                    stop();
10,440✔
1053
                    m_config.websocket_read_error_handler(ec);
10,440✔
1054
                    return;
10,440✔
1055
                }
204,620✔
1056

1,971,316✔
1057
                if (m_stopped)
3,225,246✔
1058
                    return;
195,430✔
1059

1,971,316✔
1060
                frame_reader_loop();
3,645,082✔
1061
            }
3,648,524✔
1062
        };
3,449,864✔
1063

1,991,772✔
1064
        m_config.async_read(m_frame_reader.read_buffer, m_frame_reader.read_size, std::move(handler));
3,683,538✔
1065
    }
3,259,350✔
1066
};
1067

1068

1069
const char* get_error_message(HttpError error_code)
12✔
1070
{
108✔
1071
    switch (error_code) {
96✔
1072
        case HttpError::bad_request_malformed_http:
✔
1073
            return "Bad WebSocket request malformed HTTP";
✔
1074
        case HttpError::bad_request_header_upgrade:
✔
1075
            return "Bad WebSocket request header: Upgrade";
✔
1076
        case HttpError::bad_request_header_connection:
✔
1077
            return "Bad WebSocket request header: Connection";
✔
1078
        case HttpError::bad_request_header_websocket_version:
✔
1079
            return "Bad WebSocket request header: Sec-Websocket-Version";
✔
1080
        case HttpError::bad_request_header_websocket_key:
✔
1081
            return "Bad WebSocket request header: Sec-Websocket-Key";
✔
1082
        case HttpError::bad_response_invalid_http:
✔
1083
            return "Bad WebSocket response invalid HTTP";
✔
1084
        case HttpError::bad_response_2xx_successful:
✔
1085
            return "Bad WebSocket response 2xx successful";
✔
1086
        case HttpError::bad_response_200_ok:
✔
1087
            return "Bad WebSocket response 200 ok";
✔
1088
        case HttpError::bad_response_3xx_redirection:
✔
UNCOV
1089
            return "Bad WebSocket response 3xx redirection";
✔
1090
        case HttpError::bad_response_301_moved_permanently:
64✔
1091
            return "Bad WebSocket response 301 moved permanently";
76✔
1092
        case HttpError::bad_response_308_permanent_redirect:
44✔
1093
            return "Bad WebSocket response 308 permanent redirect";
32✔
1094
        case HttpError::bad_response_4xx_client_errors:
✔
1095
            return "Bad WebSocket response 4xx client errors";
✔
1096
        case HttpError::bad_response_401_unauthorized:
✔
1097
            return "Bad WebSocket response 401 unauthorized";
✔
1098
        case HttpError::bad_response_403_forbidden:
✔
1099
            return "Bad WebSocket response 403 forbidden";
✔
1100
        case HttpError::bad_response_404_not_found:
✔
1101
            return "Bad WebSocket response 404 not found";
✔
1102
        case HttpError::bad_response_410_gone:
✔
1103
            return "Bad WebSocket response 410 gone";
✔
1104
        case HttpError::bad_response_5xx_server_error:
✔
1105
            return "Bad WebSocket response 5xx server error";
✔
1106
        case HttpError::bad_response_500_internal_server_error:
✔
1107
            return "Bad WebSocket response 500 internal server error";
✔
1108
        case HttpError::bad_response_502_bad_gateway:
✔
1109
            return "Bad WebSocket response 502 bad gateway";
✔
1110
        case HttpError::bad_response_503_service_unavailable:
✔
1111
            return "Bad WebSocket response 503 service unavailable";
✔
1112
        case HttpError::bad_response_504_gateway_timeout:
✔
1113
            return "Bad WebSocket response 504 gateway timeout";
✔
1114
        case HttpError::bad_response_unexpected_status_code:
✔
1115
            return "Bad Websocket response unexpected status code";
✔
1116
        case HttpError::bad_response_header_protocol_violation:
✔
1117
            return "Bad WebSocket response header protocol violation";
✔
1118
        case HttpError::bad_message:
✔
1119
            return "Ill-formed WebSocket message";
×
1120
    }
×
1121
    return nullptr;
×
1122
}
1123

1124

1125
class HttpErrorCategory : public std::error_category {
1126
public:
1127
    const char* name() const noexcept override final
1128
    {
×
1129
        return "realm::sync::websocket::HttpError";
×
1130
    }
1131
    std::string message(int error_code) const override final
12✔
1132
    {
108✔
1133
        const char* msg = get_error_message(HttpError(error_code));
108✔
1134
        if (!msg)
96✔
1135
            msg = "Unknown error";
12✔
1136
        std::string msg_2{msg}; // Throws (copy)
108✔
1137
        return msg_2;
108✔
1138
    }
96✔
1139
};
1140

1141
} // unnamed namespace
1142

1143
namespace realm::sync::websocket {
1144

1145
std::ostream& operator<<(std::ostream& os, WebSocketError code)
800✔
1146
{
6,244✔
1147
    /// WebSocket error codes
3,748✔
1148
    auto str = [&]() -> const char* {
6,578✔
1149
        switch (code) {
5,834✔
1150
            case WebSocketError::websocket_ok:
514✔
1151
                return "WebSocket: OK";
458✔
1152
            case WebSocketError::websocket_going_away:
✔
1153
                return "WebSocket: Going Away";
✔
1154
            case WebSocketError::websocket_protocol_error:
✔
1155
                return "WebSocket: Protocol Error";
✔
1156
            case WebSocketError::websocket_unsupported_data:
✔
1157
                return "WebSocket: Unsupported Data";
✔
1158
            case WebSocketError::websocket_reserved:
✔
1159
                return "WebSocket: Reserved";
✔
1160
            case WebSocketError::websocket_no_status_received:
✔
1161
                return "WebSocket: No Status Received";
✔
1162
            case WebSocketError::websocket_abnormal_closure:
✔
1163
                return "WebSocket: Abnormal Closure";
✔
1164
            case WebSocketError::websocket_invalid_payload_data:
✔
1165
                return "WebSocket: Invalid Payload Data";
✔
1166
            case WebSocketError::websocket_policy_violation:
✔
1167
                return "WebSocket: Policy Violation";
4✔
1168
            case WebSocketError::websocket_message_too_big:
36✔
1169
                return "WebSocket: Message Too Big";
32✔
1170
            case WebSocketError::websocket_invalid_extension:
✔
1171
                return "WebSocket: Invalid Extension";
✔
1172
            case WebSocketError::websocket_internal_server_error:
✔
1173
                return "WebSocket: Internal Server Error";
10✔
1174
            case WebSocketError::websocket_tls_handshake_failed:
90✔
1175
                return "WebSocket: TLS Handshake Failed";
80✔
1176

1177
            /// WebSocket Errors - reported by server
32✔
1178
            case WebSocketError::websocket_unauthorized:
320✔
1179
                return "WebSocket: Unauthorized";
288✔
1180
            case WebSocketError::websocket_forbidden:
✔
1181
                return "WebSocket: Forbidden";
12✔
1182
            case WebSocketError::websocket_moved_permanently:
140✔
1183
                return "WebSocket: Moved Permanently";
128✔
1184
            case WebSocketError::websocket_client_too_old:
✔
1185
                return "WebSocket: Client Too Old";
✔
1186
            case WebSocketError::websocket_client_too_new:
✔
1187
                return "WebSocket: Client Too New";
✔
1188
            case WebSocketError::websocket_protocol_mismatch:
✔
1189
                return "WebSocket: Protocol Mismatch";
1190

4✔
1191
            case WebSocketError::websocket_resolve_failed:
84✔
1192
                return "WebSocket: Resolve Failed";
160✔
1193
            case WebSocketError::websocket_connection_failed:
528✔
1194
                return "WebSocket: Connection Failed";
1,050✔
1195
            case WebSocketError::websocket_read_error:
4,738✔
1196
                return "WebSocket: Read Error";
4,136✔
1197
            case WebSocketError::websocket_write_error:
✔
1198
                return "WebSocket: Write Error";
✔
1199
            case WebSocketError::websocket_retry_error:
✔
UNCOV
1200
                return "WebSocket: Retry Error";
✔
1201
            case WebSocketError::websocket_fatal_error:
128✔
1202
                return "WebSocket: Fatal Error";
128✔
1203
        }
×
1204
        return nullptr;
×
1205
    }();
466✔
1206

3,748✔
1207
    if (str == nullptr) {
5,778✔
1208
        os << "WebSocket: Unknown Error (" << static_cast<std::underlying_type_t<WebSocketError>>(code) << ")";
×
1209
    }
800✔
1210
    else {
6,578✔
1211
        os << str;
6,578✔
1212
    }
6,578✔
1213
    return os;
6,578✔
1214
}
5,778✔
1215

1216
} // namespace realm::sync::websocket
1217

1218
bool websocket::Config::websocket_text_message_received(const char*, size_t)
1219
{
×
1220
    return true;
×
1221
}
1222

1223
bool websocket::Config::websocket_binary_message_received(const char*, size_t)
1224
{
×
1225
    return true;
×
1226
}
1227

1228
bool websocket::Config::websocket_close_message_received(WebSocketError, std::string_view)
1229
{
×
1230
    return true;
×
1231
}
1232

1233
bool websocket::Config::websocket_ping_message_received(const char*, size_t)
1234
{
×
1235
    return true;
×
1236
}
1237

1238
bool websocket::Config::websocket_pong_message_received(const char*, size_t)
1239
{
×
1240
    return true;
×
1241
}
1242

1243

1244
class websocket::Socket::Impl : public WebSocket {
1245
public:
1246
    Impl(Config& config)
1247
        : WebSocket(config) // Throws
5,626✔
1248
    {
49,546✔
1249
    }
43,920✔
1250
};
1251

1252
websocket::Socket::Socket(Config& config)
1253
    : m_impl(new Impl{config})
5,626✔
1254
{
49,550✔
1255
}
43,924✔
1256

1257
websocket::Socket::Socket(Socket&& socket) noexcept
1258
    : m_impl(std::move(socket.m_impl))
1259
{
×
1260
}
1261

5,628✔
1262
websocket::Socket::~Socket() noexcept {}
43,932✔
1263

1264
void websocket::Socket::initiate_client_handshake(const std::string& request_uri, const std::string& host,
1265
                                                  const std::string& sec_websocket_protocol, HTTPHeaders headers)
3,572✔
1266
{
31,854✔
1267
    m_impl->initiate_client_handshake(request_uri, host, sec_websocket_protocol, std::move(headers));
31,854✔
1268
}
28,282✔
1269

1270
void websocket::Socket::initiate_server_handshake()
12✔
1271
{
108✔
1272
    m_impl->initiate_server_handshake();
108✔
1273
}
96✔
1274

1275
void websocket::Socket::initiate_server_websocket_after_handshake()
2,014✔
1276
{
17,294✔
1277
    m_impl->initiate_server_websocket_after_handshake();
17,294✔
1278
}
15,280✔
1279

1280
void websocket::Socket::async_write_frame(bool fin, Opcode opcode, const char* data, size_t size,
1281
                                          WriteCompletionHandler handler)
175,760✔
1282
{
1,529,304✔
1283
    m_impl->async_write_frame(fin, int(opcode), data, size, std::move(handler));
1,529,304✔
1284
}
1,353,544✔
1285

1286
void websocket::Socket::async_write_text(const char* data, size_t size, WriteCompletionHandler handler)
4✔
1287
{
36✔
1288
    async_write_frame(true, Opcode::text, data, size, std::move(handler));
36✔
1289
}
32✔
1290

1291
void websocket::Socket::async_write_binary(const char* data, size_t size, WriteCompletionHandler handler)
175,704✔
1292
{
1,528,806✔
1293
    async_write_frame(true, Opcode::binary, data, size, std::move(handler));
1,528,806✔
1294
}
1,353,102✔
1295

1296
void websocket::Socket::async_write_close(const char* data, size_t size, WriteCompletionHandler handler)
4✔
1297
{
36✔
1298
    async_write_frame(true, Opcode::close, data, size, std::move(handler));
36✔
1299
}
32✔
1300

1301
void websocket::Socket::async_write_ping(const char* data, size_t size, WriteCompletionHandler handler)
12✔
1302
{
108✔
1303
    async_write_frame(true, Opcode::ping, data, size, std::move(handler));
108✔
1304
}
96✔
1305

1306
void websocket::Socket::async_write_pong(const char* data, size_t size, WriteCompletionHandler handler)
4✔
1307
{
36✔
1308
    async_write_frame(true, Opcode::pong, data, size, std::move(handler));
36✔
1309
}
32✔
1310

1311
void websocket::Socket::stop() noexcept
1,190✔
1312
{
9,914✔
1313
    m_impl->stop();
9,914✔
1314
}
8,724✔
1315

1316
void websocket::Socket::force_handshake_response_for_testing(int status_code, std::string body)
12✔
1317
{
108✔
1318
    m_impl->force_handshake_response_for_testing(status_code, body);
108✔
1319
}
96✔
1320

1321
util::Optional<std::string> websocket::read_sec_websocket_protocol(const HTTPRequest& request)
2,026✔
1322
{
17,404✔
1323
    const HTTPHeaders& headers = request.headers;
17,404✔
1324
    const StringData header = "Sec-WebSocket-Protocol";
17,404✔
1325
    util::Optional<StringData> value = find_http_header_value(headers, header);
17,404✔
1326
    return value ? util::Optional<std::string>(std::string(*value)) : util::none;
17,404✔
1327
}
15,378✔
1328

1329
util::Optional<HTTPResponse> websocket::make_http_response(const HTTPRequest& request,
1330
                                                           const std::string& sec_websocket_protocol,
1331
                                                           std::error_code& ec)
2,014✔
1332
{
17,296✔
1333
    return do_make_http_response(request, sec_websocket_protocol, ec);
17,296✔
1334
}
15,282✔
1335

1336
const std::error_category& websocket::http_error_category() noexcept
36✔
1337
{
260✔
1338
    static const HttpErrorCategory category = {};
260✔
1339
    return category;
260✔
1340
}
224✔
1341

1342
std::error_code websocket::make_error_code(HttpError error_code) noexcept
36✔
1343
{
260✔
1344
    return std::error_code{int(error_code), http_error_category()};
260✔
1345
}
224✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc