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

realm / realm-core / 2290

02 May 2024 08:09PM UTC coverage: 90.754% (+0.007%) from 90.747%
2290

push

Evergreen

web-flow
Fix a deadlock when accessing current user from inside an App listener (#7671)

App::switch_user() emitted changes without first releasing the lock on
m_user_mutex, leading to a deadlock if anyone inside the listener tried to
acquire the mutex. The rest of the places where we emitted changes were
correct.

The newly added wrapper catches this error when building with clang.

101922 of 180246 branches covered (56.55%)

14 of 17 new or added lines in 2 files covered. (82.35%)

45 existing lines in 15 files now uncovered.

212546 of 234199 relevant lines covered (90.75%)

5635345.89 hits per line

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

71.79
/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
{
4,160✔
21
    if (str1.size() != str2.size())
4,160✔
22
        return false;
×
23

24
    for (size_t i = 0; i < str1.size(); ++i)
37,440✔
25
        if (std::tolower(str1[i], std::locale::classic()) != std::tolower(str2[i], std::locale::classic()))
33,280✔
26
            return false;
×
27

28
    return true;
4,160✔
29
}
4,160✔
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
{
3,646✔
41
    char random_bytes[16];
3,646✔
42
    std::uniform_int_distribution<> dis(std::numeric_limits<char>::min(), std::numeric_limits<char>::max());
3,646✔
43
    for (int i = 0; i < 16; ++i) {
61,980✔
44
        random_bytes[i] = dis(random);
58,334✔
45
    }
58,334✔
46

47
    char out_buffer[24];
3,646✔
48
    size_t encoded_size = util::base64_encode(random_bytes, out_buffer);
3,646✔
49
    REALM_ASSERT(encoded_size == 24);
3,646✔
50

51
    return std::string{out_buffer, 24};
3,646✔
52
}
3,646✔
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
{
5,664✔
60
    std::string sha1_input;
5,664✔
61
    sha1_input.reserve(sec_websocket_key.size() + websocket_magic_string.size());
5,664✔
62
    sha1_input.append(sec_websocket_key.data(), sec_websocket_key.size());
5,664✔
63
    sha1_input.append(websocket_magic_string.data(), websocket_magic_string.size());
5,664✔
64

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

68
    char base64_output[28];
5,664✔
69
    size_t base64_output_size = util::base64_encode(sha1_output, base64_output);
5,664✔
70
    REALM_ASSERT(base64_output_size == 28);
5,664✔
71

72
    return std::string(base64_output, 28);
5,664✔
73
}
5,664✔
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
{
13,984✔
80
    auto it = headers.find(header);
13,984✔
81

82
    if (it != headers.end()) {
13,984✔
83
        return StringData(it->second);
13,984✔
84
    }
13,984✔
UNCOV
85
    return none;
×
86
}
13,984✔
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
{
2,080✔
93
    util::Optional<StringData> header_value_upgrade = find_http_header_value(headers, "Upgrade");
2,080✔
94

95
    return (header_value_upgrade && case_insensitive_equal(*header_value_upgrade, "websocket"));
2,080✔
96
}
2,080✔
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
{
2,080✔
103
    util::Optional<StringData> header_value_connection = find_http_header_value(headers, "Connection");
2,080✔
104

105
    return (header_value_connection && case_insensitive_equal(*header_value_connection, "Upgrade"));
2,080✔
106
}
2,080✔
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
{
2,080✔
113
    return find_http_header_value(headers, "Sec-WebSocket-Version") == sec_websocket_version;
2,080✔
114
}
2,080✔
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
{
2,080✔
122
    util::Optional<StringData> header_value = find_http_header_value(headers, "Sec-WebSocket-Key");
2,080✔
123

124
    if (!header_value)
2,080✔
125
        return false;
×
126

127
    sec_websocket_key = *header_value;
2,080✔
128

129
    return true;
2,080✔
130
}
2,080✔
131

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

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

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

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

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

157
    std::string sec_websocket_accept = make_sec_websocket_accept(sec_websocket_key);
2,080✔
158

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

166
    return response;
2,080✔
167
}
2,080✔
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
{
172,702✔
172
    for (size_t i = 0; i < payload_len; ++i) {
405,040,848✔
173
        output[i] = payload[i] ^ masking_key[i % 4];
404,868,146✔
174
    }
404,868,146✔
175
}
172,702✔
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
{
165,224✔
200
    int index = 0; // used to keep track of position within the header.
165,224✔
201
    using uchar = unsigned char;
165,224✔
202
    output[0] = (fin ? char(uchar(128)) : 0) + opcode; // fin and opcode in the first byte.
165,224✔
203
    output[1] = (mask ? char(uchar(128)) : 0);         // First bit of the second byte is mask.
165,224✔
204
    if (payload_size <= 125) {                         // The payload length is contained in the second byte.
165,224✔
205
        output[1] += static_cast<char>(payload_size);
138,970✔
206
        index = 2;
138,970✔
207
    }
138,970✔
208
    else if (payload_size < 65536) { // The payload length is contained bytes 3-4.
26,254✔
209
        output[1] += 126;
25,800✔
210
        // FIXME: Verify that this code works even if one sync-client is on a platform where
211
        // a 'char' is signed by default and the other client is on a platform where char is
212
        // unsigned. Note that the result of payload_size / 256 may not fit in a signed char.
213
        output[2] = static_cast<char>(payload_size / 256);
25,800✔
214

215
        // FIXME: Verify that the modulo arithmetic is well defined
216
        output[3] = payload_size % 256;
25,800✔
217
        index = 4;
25,800✔
218
    }
25,800✔
219
    else { // The payload length is contained in bytes 3-10.
454✔
220
        output[1] += 127;
454✔
221
        size_t fraction = payload_size;
454✔
222
        int remainder = 0;
454✔
223
        for (int i = 0; i < 8; ++i) {
4,038✔
224
            remainder = fraction % 256;
3,584✔
225
            fraction /= 256;
3,584✔
226
            output[9 - i] = remainder;
3,584✔
227
        }
3,584✔
228
        index = 10;
454✔
229
    }
454✔
230
    if (mask) {
165,224✔
231
        char masking_key[4];
101,620✔
232
        std::uniform_int_distribution<> dis(0, 255);
101,620✔
233
        for (int i = 0; i < 4; ++i) {
508,120✔
234
            masking_key[i] = dis(random);
406,500✔
235
        }
406,500✔
236
        output[index++] = masking_key[0];
101,620✔
237
        output[index++] = masking_key[1];
101,620✔
238
        output[index++] = masking_key[2];
101,620✔
239
        output[index++] = masking_key[3];
101,620✔
240
        mask_payload(masking_key, payload, payload_size, output + index);
101,620✔
241
    }
101,620✔
242
    else {
63,604✔
243
        std::copy(payload, payload + payload_size, output + index);
63,604✔
244
    }
63,604✔
245

246
    return payload_size + index;
165,224✔
247
}
165,224✔
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)
2,598✔
293
        , m_is_client(is_client)
2,598✔
294
    {
5,748✔
295
    }
5,748✔
296

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

304
    // next() parses the new information and moves
305
    // one stage forward.
306
    void next()
307
    {
544,060✔
308
        switch (m_stage) {
544,060✔
309
            case Stage::init:
5,664✔
310
                stage_init();
5,664✔
311
                break;
5,664✔
312
            case Stage::header_beginning:
150,408✔
313
                stage_header_beginning();
150,408✔
314
                break;
150,408✔
315
            case Stage::header_end:
88,448✔
316
                stage_header_end();
88,448✔
317
                break;
88,448✔
318
            case Stage::payload:
150,412✔
319
                stage_payload();
150,412✔
320
                break;
150,412✔
321
            case Stage::delivery:
149,280✔
322
                stage_delivery();
149,280✔
323
                break;
149,280✔
324
            default:
✔
325
                break;
×
326
        }
544,060✔
327
    }
544,060✔
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
    {
150,406✔
369
        read_size = m_payload_size;
150,406✔
370

371
        if (m_opcode == websocket::Opcode::close || m_opcode == websocket::Opcode::ping ||
150,406✔
372
            m_opcode == websocket::Opcode::pong) {
150,406✔
373
            read_buffer = control_buffer;
114✔
374
        }
114✔
375
        else {
150,292✔
376
            size_t required_size = m_message_size + m_payload_size;
150,292✔
377
            if (m_message_buffer.size() < required_size)
150,292✔
378
                m_message_buffer.resize(required_size);
624✔
379

380
            read_buffer = m_message_buffer.data() + m_message_size;
150,292✔
381
        }
150,292✔
382
    }
150,406✔
383

384
    void reset_message_buffer()
385
    {
154,860✔
386
        if (m_message_buffer.size() != s_message_buffer_min_size)
154,860✔
387
            m_message_buffer.resize(s_message_buffer_min_size);
6,280✔
388
        m_message_opcode = websocket::Opcode::continuation;
154,860✔
389
        m_message_size = 0;
154,860✔
390
    }
154,860✔
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
    {
5,662✔
398
        protocol_error = false;
5,662✔
399
        delivery_ready = false;
5,662✔
400
        delivery_buffer = nullptr;
5,662✔
401
        delivery_size = 0;
5,662✔
402
        delivery_opcode = websocket::Opcode::continuation;
5,662✔
403
        m_stage = Stage::header_beginning;
5,662✔
404
        reset_message_buffer();
5,662✔
405
        read_buffer = header_buffer;
5,662✔
406
        read_size = 2;
5,662✔
407
    }
5,662✔
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
    {
150,394✔
414
        // bit 1.
415
        m_fin = ((header_buffer[0] & 128) == 128);
150,394✔
416

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

422
        // bit 5, 6, 7, and 8.
423
        char op = (header_buffer[0] & 15);
150,394✔
424

425
        if (op != 0 && op != 1 && op != 2 && op != 8 && op != 9 && op != 10)
150,394✔
426
            return set_protocol_error();
×
427

428
        m_opcode = websocket::Opcode(op);
150,394✔
429

430
        // bit 9.
431
        m_mask = ((header_buffer[1] & 128) == 128);
150,394✔
432
        if ((m_mask && m_is_client) || (!m_mask && !m_is_client))
150,400✔
433
            return set_protocol_error();
×
434

435
        // Remainder of second byte.
436
        m_short_payload_size = (header_buffer[1] & 127);
150,394✔
437

438
        if (m_opcode == websocket::Opcode::continuation) {
150,394✔
439
            if (m_message_opcode == websocket::Opcode::continuation)
24✔
440
                return set_protocol_error();
×
441
        }
24✔
442
        else if (m_opcode == websocket::Opcode::text || m_opcode == websocket::Opcode::binary) {
150,370✔
443
            if (m_message_opcode != websocket::Opcode::continuation)
150,258✔
444
                return set_protocol_error();
×
445

446
            m_message_opcode = m_opcode;
150,258✔
447
        }
150,258✔
448
        else { // close, ping, pong.
112✔
449
            if (!m_fin || m_short_payload_size > 125)
114✔
450
                return set_protocol_error();
×
451
        }
112✔
452

453
        if (m_short_payload_size <= 125 && m_mask) {
150,394✔
454
            m_stage = Stage::header_end;
61,728✔
455
            m_payload_size = m_short_payload_size;
61,728✔
456
            read_size = 4;
61,728✔
457
            read_buffer = header_buffer + 2;
61,728✔
458
        }
61,728✔
459
        else if (m_short_payload_size <= 125 && !m_mask) {
88,666✔
460
            m_stage = Stage::payload;
61,994✔
461
            m_payload_size = m_short_payload_size;
61,994✔
462
            set_payload_buffer();
61,994✔
463
        }
61,994✔
464
        else if (m_short_payload_size == 126 && m_mask) {
26,672✔
465
            m_stage = Stage::header_end;
9,322✔
466
            read_size = 6;
9,322✔
467
            read_buffer = header_buffer + 2;
9,322✔
468
        }
9,322✔
469
        else if (m_short_payload_size == 126 && !m_mask) {
17,350✔
470
            m_stage = Stage::header_end;
17,088✔
471
            read_size = 2;
17,088✔
472
            read_buffer = header_buffer + 2;
17,088✔
473
        }
17,088✔
474
        else if (m_short_payload_size == 127 && m_mask) {
316✔
475
            m_stage = Stage::header_end;
32✔
476
            read_size = 12;
32✔
477
            read_buffer = header_buffer + 2;
32✔
478
        }
32✔
479
        else if (m_short_payload_size == 127 && !m_mask) {
284✔
480
            m_stage = Stage::header_end;
284✔
481
            read_size = 8;
284✔
482
            read_buffer = header_buffer + 2;
284✔
483
        }
284✔
484
    }
150,394✔
485

486
    void stage_header_end()
487
    {
88,448✔
488
        if (m_short_payload_size <= 125) {
88,448✔
489
            m_masking_key = header_buffer + 2;
61,732✔
490
        }
61,732✔
491
        else if (m_short_payload_size == 126) {
26,716✔
492
            const unsigned char* bytes = reinterpret_cast<unsigned char*>(header_buffer + 2);
26,408✔
493
            m_payload_size = bytes[0] * 256 + bytes[1];
26,408✔
494

495
            if (m_mask)
26,408✔
496
                m_masking_key = header_buffer + 4;
9,322✔
497
        }
26,408✔
498
        else if (m_short_payload_size == 127) {
316✔
499
            if (header_buffer[2] != 0 || header_buffer[3] != 0 || header_buffer[4] != 0 || header_buffer[5] != 0) {
316✔
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

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

513
            if (m_mask)
316✔
514
                m_masking_key = header_buffer + 10;
32✔
515
        }
316✔
516

517
        m_stage = Stage::payload;
88,448✔
518
        set_payload_buffer();
88,448✔
519
    }
88,448✔
520

521
    void stage_payload()
522
    {
150,410✔
523
        if (m_mask)
150,410✔
524
            mask_payload(m_masking_key, read_buffer, m_payload_size, read_buffer);
71,082✔
525

526
        if (m_opcode == websocket::Opcode::close || m_opcode == websocket::Opcode::ping ||
150,410✔
527
            m_opcode == websocket::Opcode::pong) {
150,410✔
528
            m_stage = Stage::delivery;
114✔
529
            delivery_ready = true;
114✔
530
            delivery_opcode = m_opcode;
114✔
531
            delivery_buffer = control_buffer;
114✔
532
            delivery_size = m_payload_size;
114✔
533
        }
114✔
534
        else {
150,296✔
535
            m_message_size += m_payload_size;
150,296✔
536
            if (m_fin) {
150,296✔
537
                m_stage = Stage::delivery;
150,252✔
538
                delivery_ready = true;
150,252✔
539
                delivery_opcode = m_message_opcode;
150,252✔
540
                delivery_buffer = m_message_buffer.data();
150,252✔
541
                delivery_size = m_message_size;
150,252✔
542
            }
150,252✔
543
            else {
44✔
544
                m_stage = Stage::header_beginning;
44✔
545
                read_buffer = header_buffer;
44✔
546
                read_size = 2;
44✔
547
            }
44✔
548
        }
150,296✔
549
    }
150,410✔
550

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

561
        if (m_opcode == websocket::Opcode::continuation || m_opcode == websocket::Opcode::text ||
149,278✔
562
            m_opcode == websocket::Opcode::binary)
149,278✔
563
            reset_message_buffer();
149,194✔
564
    }
149,278✔
565
};
566

567

568
class WebSocket {
569
public:
570
    WebSocket(websocket::Config& config)
571
        : m_config(config)
2,598✔
572
        , m_logger_ptr(config.websocket_get_logger())
2,598✔
573
        , m_logger{*m_logger_ptr}
2,598✔
574
        , m_frame_reader(m_logger, m_is_client)
2,598✔
575
    {
5,748✔
576
        m_logger.debug(util::LogCategory::network, "WebSocket::Websocket()");
5,748✔
577
    }
5,748✔
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
    {
3,646✔
582
        m_logger.debug(util::LogCategory::network, "WebSocket::initiate_client_handshake()");
3,646✔
583

584
        m_stopped = false;
3,646✔
585
        m_is_client = true;
3,646✔
586

587
        m_sec_websocket_key = make_random_sec_websocket_key(m_config.websocket_get_random());
3,646✔
588

589
        m_http_client.reset(new HTTPClient<websocket::Config>(m_config, m_logger_ptr));
3,646✔
590
        m_frame_reader.reset();
3,646✔
591

592
        if (m_test_handshake_response) {
3,646✔
593
            HTTPResponse test_response;
12✔
594
            test_response.status = HTTPStatus(*m_test_handshake_response);
12✔
595
            test_response.body = std::move(m_test_handshake_response_body);
12✔
596
            m_test_handshake_response.reset();
12✔
597
            m_test_handshake_response_body.clear();
12✔
598
            handle_http_response_received(std::move(test_response)); // Throws
12✔
599
            return;
12✔
600
        }
12✔
601

602
        HTTPRequest req;
3,634✔
603
        req.method = HTTPMethod::Get;
3,634✔
604
        req.path = std::move(request_uri);
3,634✔
605
        req.headers = std::move(headers);
3,634✔
606
        req.headers["Host"] = std::move(host);
3,634✔
607
        req.headers["Upgrade"] = "websocket";
3,634✔
608
        req.headers["Connection"] = "Upgrade";
3,634✔
609
        req.headers["Sec-WebSocket-Key"] = m_sec_websocket_key;
3,634✔
610
        req.headers["Sec-WebSocket-Version"] = sec_websocket_version;
3,634✔
611
        req.headers["Sec-WebSocket-Protocol"] = sec_websocket_protocol;
3,634✔
612

613
        m_logger.trace(util::LogCategory::network, "HTTP request =\n%1", req);
3,634✔
614

615
        auto handler = [this](HTTPResponse response, std::error_code ec) {
3,634✔
616
            // If the operation is aborted, the WebSocket object may have been destroyed.
617
            if (ec != util::error::operation_aborted) {
3,584✔
618
                if (ec == HTTPParserError::MalformedResponse) {
3,584✔
619
                    error_client_malformed_response();
×
620
                    return;
×
621
                }
×
622
                if (ec) {
3,584✔
623
                    stop();
×
624

625
                    // FIXME: Should be read instaed of write???
626
                    m_config.websocket_write_error_handler(ec);
×
627

628
                    return;
×
629
                }
×
630
                if (m_stopped)
3,584✔
631
                    return;
×
632
                handle_http_response_received(std::move(response)); // Throws
3,584✔
633
            }
3,584✔
634
        };
3,582✔
635

636
        m_http_client->async_request(req, std::move(handler));
3,634✔
637
    }
3,634✔
638

639
    void initiate_server_websocket_after_handshake()
640
    {
2,068✔
641
        m_stopped = false;
2,068✔
642
        m_is_client = false;
2,068✔
643
        m_frame_reader.reset();
2,068✔
644
        frame_reader_loop(); // Throws
2,068✔
645
    }
2,068✔
646

647
    void initiate_server_handshake()
648
    {
12✔
649
        m_logger.debug(util::LogCategory::network, "WebSocket::initiate_server_handshake()");
12✔
650

651
        m_stopped = false;
12✔
652
        m_is_client = false;
12✔
653
        m_http_server.reset(new HTTPServer<websocket::Config>(m_config, m_logger_ptr));
12✔
654
        m_frame_reader.reset();
12✔
655

656
        auto handler = [this](HTTPRequest request, std::error_code ec) {
12✔
657
            if (ec != util::error::operation_aborted) {
12✔
658
                if (ec == HTTPParserError::MalformedRequest) {
12✔
659
                    error_server_malformed_request();
×
660
                    return;
×
661
                }
×
662
                if (ec) {
12✔
663
                    stop();
×
664
                    m_config.websocket_read_error_handler(ec);
×
665
                    return;
×
666
                }
×
667
                if (m_stopped)
12✔
668
                    return;
×
669
                handle_http_request_received(std::move(request));
12✔
670
            }
12✔
671
        };
12✔
672

673
        m_http_server->async_receive_request(std::move(handler));
12✔
674
    }
12✔
675

676
    void async_write_frame(bool fin, int opcode, const char* data, size_t size,
677
                           sync::websocket::WriteCompletionHandler write_completion_handler)
678
    {
165,238✔
679
        REALM_ASSERT(!m_stopped);
165,238✔
680

681
        bool mask = m_is_client;
165,238✔
682

683
        // 14 is the maximum header length of a Websocket frame.
684
        size_t required_size = size + 14;
165,238✔
685
        if (m_write_buffer.size() < required_size)
165,238✔
686
            m_write_buffer.resize(required_size);
14,490✔
687

688
        size_t message_size =
165,238✔
689
            make_frame(fin, opcode, mask, data, size, m_write_buffer.data(), m_config.websocket_get_random());
165,238✔
690

691
        auto handler = [this, handler = std::move(write_completion_handler)](std::error_code ec, size_t) mutable {
165,238✔
692
            // If the operation is aborted, then the write operation was canceled and we should ignore this callback.
693
            if (ec == util::error::operation_aborted) {
165,200✔
694
                return handler(ec, 0);
1,582✔
695
            }
1,582✔
696

697
            auto is_socket_closed_err = (ec == util::error::make_error_code(util::error::connection_reset) ||
163,618✔
698
                                         ec == util::error::make_error_code(util::error::broken_pipe) ||
163,628✔
699
                                         ec == util::make_error_code(util::MiscExtErrors::end_of_input));
163,626✔
700
            // If the socket has been closed then we should continue to read from it until we've drained
701
            // the receive buffer. Eventually we will either receive an in-band error message from the
702
            // server about why we got disconnected or we'll receive ECONNRESET on the receive side as well.
703
            if (is_socket_closed_err) {
163,618✔
704
                return;
72✔
705
            }
72✔
706

707
            // Otherwise we've got some other I/O error that we should surface to the sync client.
708
            if (ec) {
163,546✔
709
                stop();
×
710
                return m_config.websocket_write_error_handler(ec);
×
711
            }
×
712

713
            handle_write_message(std::move(handler));
163,546✔
714
        };
163,546✔
715

716
        m_config.async_write(m_write_buffer.data(), message_size, std::move(handler));
165,238✔
717
    }
165,238✔
718

719
    void handle_write_message(sync::websocket::WriteCompletionHandler write_handler)
720
    {
163,554✔
721
        if (m_write_buffer.size() > s_write_buffer_stable_size) {
163,554✔
722
            m_write_buffer.resize(s_write_buffer_stable_size);
570✔
723
            m_write_buffer.shrink_to_fit();
570✔
724
        }
570✔
725

726
        write_handler(std::error_code(), m_write_buffer.size());
163,554✔
727
    }
163,554✔
728

729
    void stop() noexcept
730
    {
2,518✔
731
        m_stopped = true;
2,518✔
732
        m_frame_reader.reset();
2,518✔
733
    }
2,518✔
734

735
    void force_handshake_response_for_testing(int status_code, std::string body)
736
    {
12✔
737
        m_test_handshake_response.emplace(status_code);
12✔
738
        m_test_handshake_response_body = body;
12✔
739
    }
12✔
740

741
private:
742
    websocket::Config& m_config;
743
    const std::shared_ptr<util::Logger> m_logger_ptr;
744
    util::Logger& m_logger;
745
    FrameReader m_frame_reader;
746

747
    bool m_stopped = false;
748
    bool m_is_client;
749

750
    // Allocated on demand.
751
    std::unique_ptr<HTTPClient<websocket::Config>> m_http_client;
752
    std::unique_ptr<HTTPServer<websocket::Config>> m_http_server;
753

754
    std::string m_sec_websocket_key;
755
    std::string m_sec_websocket_accept;
756

757
    std::vector<char> m_write_buffer;
758
    static const size_t s_write_buffer_stable_size = 2048;
759

760
    std::optional<int> m_test_handshake_response;
761
    std::string m_test_handshake_response_body;
762

763
    void error_client_malformed_response()
764
    {
×
765
        m_stopped = true;
×
766
        m_logger.error(util::LogCategory::network, "WebSocket: Received malformed HTTP response");
×
767
        std::error_code ec = HttpError::bad_response_invalid_http;
×
768
        m_config.websocket_handshake_error_handler(ec, nullptr, {}); // Throws
×
769
    }
×
770

771
    void error_client_response_not_101(const HTTPResponse& response)
772
    {
12✔
773
        m_stopped = true;
12✔
774

775
        m_logger.error(util::LogCategory::network,
12✔
776
                       "Websocket: Expected HTTP response 101 Switching Protocols, "
12✔
777
                       "but received:\n%1",
12✔
778
                       response);
12✔
779

780
        int status_code = int(response.status);
12✔
781
        std::error_code ec;
12✔
782

783
        if (status_code == 200)
12✔
784
            ec = HttpError::bad_response_200_ok;
×
785
        else if (status_code >= 200 && status_code < 300)
12✔
786
            ec = HttpError::bad_response_2xx_successful;
×
787
        else if (status_code == 301)
12✔
788
            ec = HttpError::bad_response_301_moved_permanently;
×
789
        else if (status_code == 308)
12✔
790
            ec = HttpError::bad_response_308_permanent_redirect;
12✔
791
        else if (status_code >= 300 && status_code < 400)
×
792
            ec = HttpError::bad_response_3xx_redirection;
×
793
        else if (status_code == 401)
×
794
            ec = HttpError::bad_response_401_unauthorized;
×
795
        else if (status_code == 403)
×
796
            ec = HttpError::bad_response_403_forbidden;
×
797
        else if (status_code == 404)
×
798
            ec = HttpError::bad_response_404_not_found;
×
799
        else if (status_code == 410)
×
800
            ec = HttpError::bad_response_410_gone;
×
801
        else if (status_code >= 400 && status_code < 500)
×
802
            ec = HttpError::bad_response_4xx_client_errors;
×
803
        else if (status_code == 500)
×
804
            ec = HttpError::bad_response_500_internal_server_error;
×
805
        else if (status_code == 502)
×
806
            ec = HttpError::bad_response_502_bad_gateway;
×
807
        else if (status_code == 503)
×
808
            ec = HttpError::bad_response_503_service_unavailable;
×
809
        else if (status_code == 504)
×
810
            ec = HttpError::bad_response_504_gateway_timeout;
×
811
        else if (status_code >= 500 && status_code < 600)
×
812
            ec = HttpError::bad_response_5xx_server_error;
×
813
        else
×
814
            ec = HttpError::bad_response_unexpected_status_code;
×
815

816
        std::string_view body;
12✔
817
        if (response.body) {
12✔
818
            body = *response.body;
12✔
819
        }
12✔
820
        m_config.websocket_handshake_error_handler(ec, &response.headers, body); // Throws
12✔
821
    }
12✔
822

823
    void error_client_response_websocket_headers_invalid(const HTTPResponse& response)
824
    {
×
825
        m_stopped = true;
×
826

827
        m_logger.error(util::LogCategory::network,
×
828
                       "Websocket: HTTP response has invalid websocket headers."
×
829
                       "HTTP response = \n%1",
×
830
                       response);
×
831
        std::error_code ec = HttpError::bad_response_header_protocol_violation;
×
832
        std::string_view body;
×
833
        if (response.body) {
×
834
            body = *response.body;
×
835
        }
×
836
        m_config.websocket_handshake_error_handler(ec, &response.headers, body); // 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;
×
844
        m_config.websocket_handshake_error_handler(ec, 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);
×
855
        m_config.websocket_handshake_error_handler(ec, &request.headers, {}); // 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
    {
3,596✔
867
        m_logger.debug(util::LogCategory::network, "WebSocket::handle_http_response_received()");
3,596✔
868
        m_logger.trace(util::LogCategory::network, "HTTP response = %1", response);
3,596✔
869

870
        if (response.status != HTTPStatus::SwitchingProtocols) {
3,596✔
871
            error_client_response_not_101(response);
12✔
872
            return;
12✔
873
        }
12✔
874

875
        bool valid = (find_sec_websocket_accept(response.headers) &&
3,584✔
876
                      m_sec_websocket_accept == make_sec_websocket_accept(m_sec_websocket_key));
3,584✔
877
        if (!valid) {
3,584✔
878
            error_client_response_websocket_headers_invalid(response);
×
879
            return;
×
880
        }
×
881

882
        m_config.websocket_handshake_completion_handler(response.headers);
3,584✔
883

884
        if (m_stopped)
3,584✔
885
            return;
×
886

887
        frame_reader_loop();
3,584✔
888
    }
3,584✔
889

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

894
        util::Optional<std::string> sec_websocket_protocol = websocket::read_sec_websocket_protocol(request);
12✔
895

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

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

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

915
                if (m_stopped)
12✔
916
                    return;
×
917

918
                m_config.websocket_handshake_completion_handler(request.headers);
12✔
919

920
                if (m_stopped)
12✔
921
                    return;
×
922

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

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

935
        if (!header_value)
3,584✔
936
            return false;
×
937

938
        m_sec_websocket_accept = *header_value;
3,584✔
939

940
        return true;
3,584✔
941
    }
3,584✔
942

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

964
        switch (static_cast<WebSocketError>(error_code)) {
98✔
965
            case WebSocketError::websocket_ok:
62✔
966
            case WebSocketError::websocket_going_away:
62✔
967
            case WebSocketError::websocket_protocol_error:
62✔
968
            case WebSocketError::websocket_unsupported_data:
62✔
969
            case WebSocketError::websocket_reserved:
62✔
970
            case WebSocketError::websocket_no_status_received:
62✔
971
            case WebSocketError::websocket_abnormal_closure:
62✔
972
            case WebSocketError::websocket_invalid_payload_data:
62✔
973
            case WebSocketError::websocket_policy_violation:
62✔
974
            case WebSocketError::websocket_message_too_big:
66✔
975
            case WebSocketError::websocket_invalid_extension:
66✔
976
            case WebSocketError::websocket_internal_server_error:
66✔
977
            case WebSocketError::websocket_tls_handshake_failed:
66✔
978

979
            case WebSocketError::websocket_unauthorized:
98✔
980
            case WebSocketError::websocket_forbidden:
98✔
981
            case WebSocketError::websocket_moved_permanently:
98✔
982
            case WebSocketError::websocket_client_too_old:
98✔
983
            case WebSocketError::websocket_client_too_new:
98✔
984
            case WebSocketError::websocket_protocol_mismatch:
98✔
985
                break;
98✔
986
            default:
✔
987
                error_code = 1008;
×
988
        }
98✔
989

990
        return std::make_pair(static_cast<WebSocketError>(error_code), error_message);
98✔
991
    }
98✔
992

993
    // frame_reader_loop() uses the frame_reader to read and process the incoming
994
    // WebSocket messages.
995
    void frame_reader_loop()
996
    {
544,048✔
997
        // Advance parsing stage.
998
        m_frame_reader.next();
544,048✔
999

1000
        if (m_frame_reader.protocol_error) {
544,048✔
1001
            protocol_error(HttpError::bad_message);
×
1002
            return;
×
1003
        }
×
1004

1005
        if (m_frame_reader.delivery_ready) {
544,048✔
1006
            bool should_continue = true;
150,356✔
1007

1008
            switch (m_frame_reader.delivery_opcode) {
150,356✔
1009
                case websocket::Opcode::text:
4✔
1010
                    should_continue = m_config.websocket_text_message_received(m_frame_reader.delivery_buffer,
4✔
1011
                                                                               m_frame_reader.delivery_size);
4✔
1012
                    break;
4✔
1013
                case websocket::Opcode::binary:
150,222✔
1014
                    should_continue = m_config.websocket_binary_message_received(m_frame_reader.delivery_buffer,
150,222✔
1015
                                                                                 m_frame_reader.delivery_size);
150,222✔
1016
                    break;
150,222✔
1017
                case websocket::Opcode::close: {
98✔
1018
                    auto [error_code, error_message] =
98✔
1019
                        parse_close_message(m_frame_reader.delivery_buffer, m_frame_reader.delivery_size);
98✔
1020
                    should_continue = m_config.websocket_close_message_received(error_code, error_message);
98✔
1021
                    break;
98✔
1022
                }
×
1023
                case websocket::Opcode::ping:
12✔
1024
                    should_continue = m_config.websocket_ping_message_received(m_frame_reader.delivery_buffer,
12✔
1025
                                                                               m_frame_reader.delivery_size);
12✔
1026
                    break;
12✔
1027
                case websocket::Opcode::pong:
4✔
1028
                    should_continue = m_config.websocket_pong_message_received(m_frame_reader.delivery_buffer,
4✔
1029
                                                                               m_frame_reader.delivery_size);
4✔
1030
                    break;
4✔
1031
                default:
✔
1032
                    break;
×
1033
            }
150,356✔
1034

1035
            // The websocket object might not even exist anymore
1036
            if (!should_continue)
150,422✔
1037
                return;
1,146✔
1038

1039
            if (m_stopped)
149,276✔
1040
                return;
×
1041

1042
            // recursion is harmless, since the depth will be at most 2.
1043
            frame_reader_loop();
149,276✔
1044
            return;
149,276✔
1045
        }
149,276✔
1046

1047
        auto handler = [this](std::error_code ec, size_t) {
393,692✔
1048
            // If the operation is aborted, the socket object may have been destroyed.
1049
            if (ec != util::error::operation_aborted) {
392,742✔
1050
                if (ec) {
390,538✔
1051
                    stop();
1,290✔
1052
                    m_config.websocket_read_error_handler(ec);
1,290✔
1053
                    return;
1,290✔
1054
                }
1,290✔
1055

1056
                if (m_stopped)
389,248✔
1057
                    return;
×
1058

1059
                frame_reader_loop();
389,248✔
1060
            }
389,248✔
1061
        };
392,742✔
1062

1063
        m_config.async_read(m_frame_reader.read_buffer, m_frame_reader.read_size, std::move(handler));
393,692✔
1064
    }
393,692✔
1065
};
1066

1067

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

1123

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

1140
} // unnamed namespace
1141

1142
namespace realm::sync::websocket {
1143

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

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

1190
            case WebSocketError::websocket_resolve_failed:
4✔
1191
                return "WebSocket: Resolve Failed";
4✔
1192
            case WebSocketError::websocket_connection_failed:
108✔
1193
                return "WebSocket: Connection Failed";
108✔
1194
            case WebSocketError::websocket_read_error:
642✔
1195
                return "WebSocket: Read Error";
642✔
1196
            case WebSocketError::websocket_write_error:
✔
1197
                return "WebSocket: Write Error";
×
1198
            case WebSocketError::websocket_retry_error:
✔
1199
                return "WebSocket: Retry Error";
×
1200
            case WebSocketError::websocket_fatal_error:
✔
1201
                return "WebSocket: Fatal Error";
×
1202
        }
882✔
1203
        return nullptr;
×
1204
    }();
882✔
1205

1206
    if (str == nullptr) {
882✔
1207
        os << "WebSocket: Unknown Error (" << static_cast<std::underlying_type_t<WebSocketError>>(code) << ")";
×
1208
    }
×
1209
    else {
882✔
1210
        os << str;
882✔
1211
    }
882✔
1212
    return os;
882✔
1213
}
882✔
1214

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

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

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

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

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

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

1242

1243
class websocket::Socket::Impl : public WebSocket {
1244
public:
1245
    Impl(Config& config)
1246
        : WebSocket(config) // Throws
2,598✔
1247
    {
5,750✔
1248
    }
5,750✔
1249
};
1250

1251
websocket::Socket::Socket(Config& config)
1252
    : m_impl(new Impl{config})
2,598✔
1253
{
5,750✔
1254
}
5,750✔
1255

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

1261
websocket::Socket::~Socket() noexcept {}
5,750✔
1262

1263
void websocket::Socket::initiate_client_handshake(const std::string& request_uri, const std::string& host,
1264
                                                  const std::string& sec_websocket_protocol, HTTPHeaders headers)
1265
{
3,646✔
1266
    m_impl->initiate_client_handshake(request_uri, host, sec_websocket_protocol, std::move(headers));
3,646✔
1267
}
3,646✔
1268

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

1274
void websocket::Socket::initiate_server_websocket_after_handshake()
1275
{
2,068✔
1276
    m_impl->initiate_server_websocket_after_handshake();
2,068✔
1277
}
2,068✔
1278

1279
void websocket::Socket::async_write_frame(bool fin, Opcode opcode, const char* data, size_t size,
1280
                                          WriteCompletionHandler handler)
1281
{
165,226✔
1282
    m_impl->async_write_frame(fin, int(opcode), data, size, std::move(handler));
165,226✔
1283
}
165,226✔
1284

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

1290
void websocket::Socket::async_write_binary(const char* data, size_t size, WriteCompletionHandler handler)
1291
{
165,174✔
1292
    async_write_frame(true, Opcode::binary, data, size, std::move(handler));
165,174✔
1293
}
165,174✔
1294

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

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

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

1310
void websocket::Socket::stop() noexcept
1311
{
1,228✔
1312
    m_impl->stop();
1,228✔
1313
}
1,228✔
1314

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

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

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

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

1341
std::error_code websocket::make_error_code(HttpError error_code) noexcept
1342
{
36✔
1343
    return std::error_code{int(error_code), http_error_category()};
36✔
1344
}
36✔
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