blob: d11fddad6e72f825f805744cf2d7cb83e8fe130c [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
Brian Silverman41cdd3e2019-01-19 19:48:58 -08003/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "wpi/WebSocket.h" // NOLINT(build/include_order)
9
10#include "WebSocketTest.h"
11#include "wpi/Base64.h"
12#include "wpi/HttpParser.h"
13#include "wpi/SmallString.h"
14#include "wpi/raw_uv_ostream.h"
15#include "wpi/sha1.h"
16
17namespace wpi {
18
19class WebSocketServerTest : public WebSocketTest {
20 public:
21 WebSocketServerTest() {
22 resp.headersComplete.connect([this](bool) { headersDone = true; });
23
24 serverPipe->Listen([this]() {
25 auto conn = serverPipe->Accept();
26 ws = WebSocket::CreateServer(*conn, "foo", "13");
27 if (setupWebSocket) setupWebSocket();
28 });
29 clientPipe->Connect(pipeName, [this]() {
30 clientPipe->StartRead();
31 clientPipe->data.connect([this](uv::Buffer& buf, size_t size) {
32 StringRef data{buf.base, size};
33 if (!headersDone) {
34 data = resp.Execute(data);
35 if (resp.HasError()) Finish();
36 ASSERT_EQ(resp.GetError(), HPE_OK)
37 << http_errno_name(resp.GetError());
38 if (data.empty()) return;
39 }
40 wireData.insert(wireData.end(), data.bytes_begin(), data.bytes_end());
41 if (handleData) handleData(data);
42 });
43 clientPipe->end.connect([this]() { Finish(); });
44 });
45 }
46
47 std::function<void()> setupWebSocket;
48 std::function<void(StringRef)> handleData;
49 std::vector<uint8_t> wireData;
50 std::shared_ptr<WebSocket> ws;
51 HttpParser resp{HttpParser::kResponse};
52 bool headersDone = false;
53};
54
55//
56// Terminate closes the endpoint but doesn't send a close frame.
57//
58
59TEST_F(WebSocketServerTest, Terminate) {
60 int gotClosed = 0;
61 setupWebSocket = [&] {
62 ws->open.connect([&](StringRef) { ws->Terminate(); });
63 ws->closed.connect([&](uint16_t code, StringRef reason) {
64 ++gotClosed;
65 ASSERT_EQ(code, 1006) << "reason: " << reason;
66 });
67 };
68
69 loop->Run();
70
71 ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
72 ASSERT_EQ(gotClosed, 1);
73}
74
75TEST_F(WebSocketServerTest, TerminateCode) {
76 int gotClosed = 0;
77 setupWebSocket = [&] {
78 ws->open.connect([&](StringRef) { ws->Terminate(1000); });
79 ws->closed.connect([&](uint16_t code, StringRef reason) {
80 ++gotClosed;
81 ASSERT_EQ(code, 1000) << "reason: " << reason;
82 });
83 };
84
85 loop->Run();
86
87 ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
88 ASSERT_EQ(gotClosed, 1);
89}
90
91TEST_F(WebSocketServerTest, TerminateReason) {
92 int gotClosed = 0;
93 setupWebSocket = [&] {
94 ws->open.connect([&](StringRef) { ws->Terminate(1000, "reason"); });
95 ws->closed.connect([&](uint16_t code, StringRef reason) {
96 ++gotClosed;
97 ASSERT_EQ(code, 1000);
98 ASSERT_EQ(reason, "reason");
99 });
100 };
101
102 loop->Run();
103
104 ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
105 ASSERT_EQ(gotClosed, 1);
106}
107
108//
109// Close() sends a close frame.
110//
111
112TEST_F(WebSocketServerTest, CloseBasic) {
113 int gotClosed = 0;
114 setupWebSocket = [&] {
115 ws->open.connect([&](StringRef) { ws->Close(); });
116 ws->closed.connect([&](uint16_t code, StringRef reason) {
117 ++gotClosed;
118 ASSERT_EQ(code, 1005) << "reason: " << reason;
119 });
120 };
121 // need to respond with close for server to finish shutdown
122 auto message = BuildMessage(0x08, true, true, {});
123 handleData = [&](StringRef) {
124 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
125 };
126
127 loop->Run();
128
129 auto expectData = BuildMessage(0x08, true, false, {});
130 ASSERT_EQ(wireData, expectData);
131 ASSERT_EQ(gotClosed, 1);
132}
133
134TEST_F(WebSocketServerTest, CloseCode) {
135 int gotClosed = 0;
136 setupWebSocket = [&] {
137 ws->open.connect([&](StringRef) { ws->Close(1000); });
138 ws->closed.connect([&](uint16_t code, StringRef reason) {
139 ++gotClosed;
140 ASSERT_EQ(code, 1000) << "reason: " << reason;
141 });
142 };
143 // need to respond with close for server to finish shutdown
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800144 const uint8_t contents[] = {0x03u, 0xe8u};
145 auto message = BuildMessage(0x08, true, true, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800146 handleData = [&](StringRef) {
147 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
148 };
149
150 loop->Run();
151
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800152 auto expectData = BuildMessage(0x08, true, false, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800153 ASSERT_EQ(wireData, expectData);
154 ASSERT_EQ(gotClosed, 1);
155}
156
157TEST_F(WebSocketServerTest, CloseReason) {
158 int gotClosed = 0;
159 setupWebSocket = [&] {
160 ws->open.connect([&](StringRef) { ws->Close(1000, "hangup"); });
161 ws->closed.connect([&](uint16_t code, StringRef reason) {
162 ++gotClosed;
163 ASSERT_EQ(code, 1000);
164 ASSERT_EQ(reason, "hangup");
165 });
166 };
167 // need to respond with close for server to finish shutdown
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800168 const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
169 auto message = BuildMessage(0x08, true, true, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800170 handleData = [&](StringRef) {
171 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
172 };
173
174 loop->Run();
175
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800176 auto expectData = BuildMessage(0x08, true, false, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800177 ASSERT_EQ(wireData, expectData);
178 ASSERT_EQ(gotClosed, 1);
179}
180
181//
182// Receiving a close frame results in closure and echoing the close frame.
183//
184
185TEST_F(WebSocketServerTest, ReceiveCloseBasic) {
186 int gotClosed = 0;
187 setupWebSocket = [&] {
188 ws->closed.connect([&](uint16_t code, StringRef reason) {
189 ++gotClosed;
190 ASSERT_EQ(code, 1005) << "reason: " << reason;
191 });
192 };
193 auto message = BuildMessage(0x08, true, true, {});
194 resp.headersComplete.connect([&](bool) {
195 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
196 });
197
198 loop->Run();
199
200 // the endpoint should echo the message
201 auto expectData = BuildMessage(0x08, true, false, {});
202 ASSERT_EQ(wireData, expectData);
203 ASSERT_EQ(gotClosed, 1);
204}
205
206TEST_F(WebSocketServerTest, ReceiveCloseCode) {
207 int gotClosed = 0;
208 setupWebSocket = [&] {
209 ws->closed.connect([&](uint16_t code, StringRef reason) {
210 ++gotClosed;
211 ASSERT_EQ(code, 1000) << "reason: " << reason;
212 });
213 };
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800214 const uint8_t contents[] = {0x03u, 0xe8u};
215 auto message = BuildMessage(0x08, true, true, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800216 resp.headersComplete.connect([&](bool) {
217 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
218 });
219
220 loop->Run();
221
222 // the endpoint should echo the message
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800223 auto expectData = BuildMessage(0x08, true, false, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800224 ASSERT_EQ(wireData, expectData);
225 ASSERT_EQ(gotClosed, 1);
226}
227
228TEST_F(WebSocketServerTest, ReceiveCloseReason) {
229 int gotClosed = 0;
230 setupWebSocket = [&] {
231 ws->closed.connect([&](uint16_t code, StringRef reason) {
232 ++gotClosed;
233 ASSERT_EQ(code, 1000);
234 ASSERT_EQ(reason, "hangup");
235 });
236 };
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800237 const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
238 auto message = BuildMessage(0x08, true, true, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800239 resp.headersComplete.connect([&](bool) {
240 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
241 });
242
243 loop->Run();
244
245 // the endpoint should echo the message
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800246 auto expectData = BuildMessage(0x08, true, false, contents);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800247 ASSERT_EQ(wireData, expectData);
248 ASSERT_EQ(gotClosed, 1);
249}
250
251//
252// If an unknown opcode is received, the receiving endpoint MUST _Fail the
253// WebSocket Connection_.
254//
255
256class WebSocketServerBadOpcodeTest
257 : public WebSocketServerTest,
258 public ::testing::WithParamInterface<uint8_t> {};
259
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800260INSTANTIATE_TEST_SUITE_P(WebSocketServerBadOpcodeTests,
261 WebSocketServerBadOpcodeTest,
262 ::testing::Values(3, 4, 5, 6, 7, 0xb, 0xc, 0xd, 0xe,
263 0xf));
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800264
265TEST_P(WebSocketServerBadOpcodeTest, Receive) {
266 int gotCallback = 0;
267 std::vector<uint8_t> data(4, 0x03);
268 setupWebSocket = [&] {
269 ws->closed.connect([&](uint16_t code, StringRef reason) {
270 ++gotCallback;
271 ASSERT_EQ(code, 1002) << "reason: " << reason;
272 });
273 };
274 auto message = BuildMessage(GetParam(), true, true, data);
275 resp.headersComplete.connect([&](bool) {
276 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
277 });
278
279 loop->Run();
280
281 ASSERT_EQ(gotCallback, 1);
282}
283
284//
285// Control frames themselves MUST NOT be fragmented.
286//
287
288class WebSocketServerControlFrameTest
289 : public WebSocketServerTest,
290 public ::testing::WithParamInterface<uint8_t> {};
291
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800292INSTANTIATE_TEST_SUITE_P(WebSocketServerControlFrameTests,
293 WebSocketServerControlFrameTest,
294 ::testing::Values(0x8, 0x9, 0xa));
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800295
296TEST_P(WebSocketServerControlFrameTest, ReceiveFragment) {
297 int gotCallback = 0;
298 std::vector<uint8_t> data(4, 0x03);
299 setupWebSocket = [&] {
300 ws->closed.connect([&](uint16_t code, StringRef reason) {
301 ++gotCallback;
302 ASSERT_EQ(code, 1002) << "reason: " << reason;
303 });
304 };
305 auto message = BuildMessage(GetParam(), false, true, data);
306 resp.headersComplete.connect([&](bool) {
307 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
308 });
309
310 loop->Run();
311
312 ASSERT_EQ(gotCallback, 1);
313}
314
315//
316// A fragmented message consists of a single frame with the FIN bit
317// clear and an opcode other than 0, followed by zero or more frames
318// with the FIN bit clear and the opcode set to 0, and terminated by
319// a single frame with the FIN bit set and an opcode of 0.
320//
321
322// No previous message
323TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFrame) {
324 int gotCallback = 0;
325 std::vector<uint8_t> data(4, 0x03);
326 setupWebSocket = [&] {
327 ws->closed.connect([&](uint16_t code, StringRef reason) {
328 ++gotCallback;
329 ASSERT_EQ(code, 1002) << "reason: " << reason;
330 });
331 };
332 auto message = BuildMessage(0x00, false, true, data);
333 resp.headersComplete.connect([&](bool) {
334 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
335 });
336
337 loop->Run();
338
339 ASSERT_EQ(gotCallback, 1);
340}
341
342// No previous message with FIN=1.
343TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFragment) {
344 int gotCallback = 0;
345 std::vector<uint8_t> data(4, 0x03);
346 setupWebSocket = [&] {
347 ws->closed.connect([&](uint16_t code, StringRef reason) {
348 ++gotCallback;
349 ASSERT_EQ(code, 1002) << "reason: " << reason;
350 });
351 };
352 auto message = BuildMessage(0x01, true, true, {}); // FIN=1
353 auto message2 = BuildMessage(0x00, false, true, data);
354 resp.headersComplete.connect([&](bool) {
355 clientPipe->Write({uv::Buffer(message), uv::Buffer(message2)},
356 [&](auto bufs, uv::Error) {});
357 });
358
359 loop->Run();
360
361 ASSERT_EQ(gotCallback, 1);
362}
363
364// Incomplete fragment
365TEST_F(WebSocketServerTest, ReceiveFragmentInvalidIncomplete) {
366 int gotCallback = 0;
367 setupWebSocket = [&] {
368 ws->closed.connect([&](uint16_t code, StringRef reason) {
369 ++gotCallback;
370 ASSERT_EQ(code, 1002) << "reason: " << reason;
371 });
372 };
373 auto message = BuildMessage(0x01, false, true, {});
374 auto message2 = BuildMessage(0x00, false, true, {});
375 auto message3 = BuildMessage(0x01, true, true, {});
376 resp.headersComplete.connect([&](bool) {
377 clientPipe->Write(
378 {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
379 [&](auto bufs, uv::Error) {});
380 });
381
382 loop->Run();
383
384 ASSERT_EQ(gotCallback, 1);
385}
386
387// Normally fragments are combined into a single callback
388TEST_F(WebSocketServerTest, ReceiveFragment) {
389 int gotCallback = 0;
390
391 std::vector<uint8_t> data(4, 0x03);
392 std::vector<uint8_t> data2(4, 0x04);
393 std::vector<uint8_t> data3(4, 0x05);
394 std::vector<uint8_t> combData{data};
395 combData.insert(combData.end(), data2.begin(), data2.end());
396 combData.insert(combData.end(), data3.begin(), data3.end());
397
398 setupWebSocket = [&] {
399 ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
400 ++gotCallback;
401 ws->Terminate();
402 ASSERT_TRUE(fin);
403 std::vector<uint8_t> recvData{inData.begin(), inData.end()};
404 ASSERT_EQ(combData, recvData);
405 });
406 };
407
408 auto message = BuildMessage(0x02, false, true, data);
409 auto message2 = BuildMessage(0x00, false, true, data2);
410 auto message3 = BuildMessage(0x00, true, true, data3);
411 resp.headersComplete.connect([&](bool) {
412 clientPipe->Write(
413 {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
414 [&](auto bufs, uv::Error) {});
415 });
416
417 loop->Run();
418
419 ASSERT_EQ(gotCallback, 1);
420}
421
422// But can be configured for multiple callbacks
423TEST_F(WebSocketServerTest, ReceiveFragmentSeparate) {
424 int gotCallback = 0;
425
426 std::vector<uint8_t> data(4, 0x03);
427 std::vector<uint8_t> data2(4, 0x04);
428 std::vector<uint8_t> data3(4, 0x05);
429 std::vector<uint8_t> combData{data};
430 combData.insert(combData.end(), data2.begin(), data2.end());
431 combData.insert(combData.end(), data3.begin(), data3.end());
432
433 setupWebSocket = [&] {
434 ws->SetCombineFragments(false);
435 ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
436 std::vector<uint8_t> recvData{inData.begin(), inData.end()};
437 switch (++gotCallback) {
438 case 1:
439 ASSERT_FALSE(fin);
440 ASSERT_EQ(data, recvData);
441 break;
442 case 2:
443 ASSERT_FALSE(fin);
444 ASSERT_EQ(data2, recvData);
445 break;
446 case 3:
447 ws->Terminate();
448 ASSERT_TRUE(fin);
449 ASSERT_EQ(data3, recvData);
450 break;
451 default:
452 FAIL() << "too many callbacks";
453 break;
454 }
455 });
456 };
457
458 auto message = BuildMessage(0x02, false, true, data);
459 auto message2 = BuildMessage(0x00, false, true, data2);
460 auto message3 = BuildMessage(0x00, true, true, data3);
461 resp.headersComplete.connect([&](bool) {
462 clientPipe->Write(
463 {uv::Buffer(message), uv::Buffer(message2), uv::Buffer(message3)},
464 [&](auto bufs, uv::Error) {});
465 });
466
467 loop->Run();
468
469 ASSERT_EQ(gotCallback, 3);
470}
471
472//
473// Maximum message size is limited.
474//
475
476// Single message
477TEST_F(WebSocketServerTest, ReceiveTooLarge) {
478 int gotCallback = 0;
479 std::vector<uint8_t> data(2048, 0x03u);
480 setupWebSocket = [&] {
481 ws->SetMaxMessageSize(1024);
482 ws->binary.connect([&](auto, bool) {
483 ws->Terminate();
484 FAIL() << "Should not have gotten unmasked message";
485 });
486 ws->closed.connect([&](uint16_t code, StringRef reason) {
487 ++gotCallback;
488 ASSERT_EQ(code, 1009) << "reason: " << reason;
489 });
490 };
491 auto message = BuildMessage(0x01, true, true, data);
492 resp.headersComplete.connect([&](bool) {
493 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
494 });
495
496 loop->Run();
497
498 ASSERT_EQ(gotCallback, 1);
499}
500
501// Applied across fragments if combining
502TEST_F(WebSocketServerTest, ReceiveTooLargeFragmented) {
503 int gotCallback = 0;
504 std::vector<uint8_t> data(768, 0x03u);
505 setupWebSocket = [&] {
506 ws->SetMaxMessageSize(1024);
507 ws->binary.connect([&](auto, bool) {
508 ws->Terminate();
509 FAIL() << "Should not have gotten unmasked message";
510 });
511 ws->closed.connect([&](uint16_t code, StringRef reason) {
512 ++gotCallback;
513 ASSERT_EQ(code, 1009) << "reason: " << reason;
514 });
515 };
516 auto message = BuildMessage(0x01, false, true, data);
517 auto message2 = BuildMessage(0x00, true, true, data);
518 resp.headersComplete.connect([&](bool) {
519 clientPipe->Write({uv::Buffer(message), uv::Buffer(message2)},
520 [&](auto bufs, uv::Error) {});
521 });
522
523 loop->Run();
524
525 ASSERT_EQ(gotCallback, 1);
526}
527
528//
529// Send and receive data.
530//
531
532class WebSocketServerDataTest : public WebSocketServerTest,
533 public ::testing::WithParamInterface<size_t> {};
534
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800535INSTANTIATE_TEST_SUITE_P(WebSocketServerDataTests, WebSocketServerDataTest,
536 ::testing::Values(0, 1, 125, 126, 65535, 65536));
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800537
538TEST_P(WebSocketServerDataTest, SendText) {
539 int gotCallback = 0;
540 std::vector<uint8_t> data(GetParam(), ' ');
541 setupWebSocket = [&] {
542 ws->open.connect([&](StringRef) {
543 ws->SendText(uv::Buffer(data), [&](auto bufs, uv::Error) {
544 ++gotCallback;
545 ws->Terminate();
546 ASSERT_FALSE(bufs.empty());
547 ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
548 });
549 });
550 };
551
552 loop->Run();
553
554 auto expectData = BuildMessage(0x01, true, false, data);
555 ASSERT_EQ(wireData, expectData);
556 ASSERT_EQ(gotCallback, 1);
557}
558
559TEST_P(WebSocketServerDataTest, SendBinary) {
560 int gotCallback = 0;
561 std::vector<uint8_t> data(GetParam(), 0x03u);
562 setupWebSocket = [&] {
563 ws->open.connect([&](StringRef) {
564 ws->SendBinary(uv::Buffer(data), [&](auto bufs, uv::Error) {
565 ++gotCallback;
566 ws->Terminate();
567 ASSERT_FALSE(bufs.empty());
568 ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
569 });
570 });
571 };
572
573 loop->Run();
574
575 auto expectData = BuildMessage(0x02, true, false, data);
576 ASSERT_EQ(wireData, expectData);
577 ASSERT_EQ(gotCallback, 1);
578}
579
580TEST_P(WebSocketServerDataTest, SendPing) {
581 int gotCallback = 0;
582 std::vector<uint8_t> data(GetParam(), 0x03u);
583 setupWebSocket = [&] {
584 ws->open.connect([&](StringRef) {
585 ws->SendPing(uv::Buffer(data), [&](auto bufs, uv::Error) {
586 ++gotCallback;
587 ws->Terminate();
588 ASSERT_FALSE(bufs.empty());
589 ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
590 });
591 });
592 };
593
594 loop->Run();
595
596 auto expectData = BuildMessage(0x09, true, false, data);
597 ASSERT_EQ(wireData, expectData);
598 ASSERT_EQ(gotCallback, 1);
599}
600
601TEST_P(WebSocketServerDataTest, SendPong) {
602 int gotCallback = 0;
603 std::vector<uint8_t> data(GetParam(), 0x03u);
604 setupWebSocket = [&] {
605 ws->open.connect([&](StringRef) {
606 ws->SendPong(uv::Buffer(data), [&](auto bufs, uv::Error) {
607 ++gotCallback;
608 ws->Terminate();
609 ASSERT_FALSE(bufs.empty());
610 ASSERT_EQ(bufs[0].base, reinterpret_cast<const char*>(data.data()));
611 });
612 });
613 };
614
615 loop->Run();
616
617 auto expectData = BuildMessage(0x0a, true, false, data);
618 ASSERT_EQ(wireData, expectData);
619 ASSERT_EQ(gotCallback, 1);
620}
621
622TEST_P(WebSocketServerDataTest, ReceiveText) {
623 int gotCallback = 0;
624 std::vector<uint8_t> data(GetParam(), ' ');
625 setupWebSocket = [&] {
626 ws->text.connect([&](StringRef inData, bool fin) {
627 ++gotCallback;
628 ws->Terminate();
629 ASSERT_TRUE(fin);
630 std::vector<uint8_t> recvData;
631 recvData.insert(recvData.end(), inData.bytes_begin(), inData.bytes_end());
632 ASSERT_EQ(data, recvData);
633 });
634 };
635 auto message = BuildMessage(0x01, true, true, data);
636 resp.headersComplete.connect([&](bool) {
637 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
638 });
639
640 loop->Run();
641
642 ASSERT_EQ(gotCallback, 1);
643}
644
645TEST_P(WebSocketServerDataTest, ReceiveBinary) {
646 int gotCallback = 0;
647 std::vector<uint8_t> data(GetParam(), 0x03u);
648 setupWebSocket = [&] {
649 ws->binary.connect([&](ArrayRef<uint8_t> inData, bool fin) {
650 ++gotCallback;
651 ws->Terminate();
652 ASSERT_TRUE(fin);
653 std::vector<uint8_t> recvData{inData.begin(), inData.end()};
654 ASSERT_EQ(data, recvData);
655 });
656 };
657 auto message = BuildMessage(0x02, true, true, data);
658 resp.headersComplete.connect([&](bool) {
659 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
660 });
661
662 loop->Run();
663
664 ASSERT_EQ(gotCallback, 1);
665}
666
667TEST_P(WebSocketServerDataTest, ReceivePing) {
668 int gotCallback = 0;
669 std::vector<uint8_t> data(GetParam(), 0x03u);
670 setupWebSocket = [&] {
671 ws->ping.connect([&](ArrayRef<uint8_t> inData) {
672 ++gotCallback;
673 ws->Terminate();
674 std::vector<uint8_t> recvData{inData.begin(), inData.end()};
675 ASSERT_EQ(data, recvData);
676 });
677 };
678 auto message = BuildMessage(0x09, true, true, data);
679 resp.headersComplete.connect([&](bool) {
680 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
681 });
682
683 loop->Run();
684
685 ASSERT_EQ(gotCallback, 1);
686}
687
688TEST_P(WebSocketServerDataTest, ReceivePong) {
689 int gotCallback = 0;
690 std::vector<uint8_t> data(GetParam(), 0x03u);
691 setupWebSocket = [&] {
692 ws->pong.connect([&](ArrayRef<uint8_t> inData) {
693 ++gotCallback;
694 ws->Terminate();
695 std::vector<uint8_t> recvData{inData.begin(), inData.end()};
696 ASSERT_EQ(data, recvData);
697 });
698 };
699 auto message = BuildMessage(0x0a, true, true, data);
700 resp.headersComplete.connect([&](bool) {
701 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
702 });
703
704 loop->Run();
705
706 ASSERT_EQ(gotCallback, 1);
707}
708
709//
710// The server must close the connection if an unmasked frame is received.
711//
712
713TEST_P(WebSocketServerDataTest, ReceiveUnmasked) {
714 int gotCallback = 0;
715 std::vector<uint8_t> data(GetParam(), ' ');
716 setupWebSocket = [&] {
717 ws->text.connect([&](StringRef, bool) {
718 ws->Terminate();
719 FAIL() << "Should not have gotten unmasked message";
720 });
721 ws->closed.connect([&](uint16_t code, StringRef reason) {
722 ++gotCallback;
723 ASSERT_EQ(code, 1002) << "reason: " << reason;
724 });
725 };
726 auto message = BuildMessage(0x01, true, false, data);
727 resp.headersComplete.connect([&](bool) {
728 clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {});
729 });
730
731 loop->Run();
732
733 ASSERT_EQ(gotCallback, 1);
734}
735
736} // namespace wpi