blob: 83561bf1ab49abd166677c48fe7c84d3cacf8f9b [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "wpi/WebSocket.h" // NOLINT(build/include_order)
6
7#include "WebSocketTest.h"
8#include "wpi/Base64.h"
9#include "wpi/HttpParser.h"
10#include "wpi/SmallString.h"
11#include "wpi/raw_uv_ostream.h"
12#include "wpi/sha1.h"
13
14namespace wpi {
15
16class WebSocketServerTest : public WebSocketTest {
17 public:
18 WebSocketServerTest() {
19 resp.headersComplete.connect([this](bool) { headersDone = true; });
20
21 serverPipe->Listen([this]() {
22 auto conn = serverPipe->Accept();
23 ws = WebSocket::CreateServer(*conn, "foo", "13");
Austin Schuh812d0d12021-11-04 20:16:48 -070024 if (setupWebSocket) {
25 setupWebSocket();
26 }
Brian Silverman8fce7482020-01-05 13:18:21 -080027 });
28 clientPipe->Connect(pipeName, [this]() {
29 clientPipe->StartRead();
30 clientPipe->data.connect([this](uv::Buffer& buf, size_t size) {
Austin Schuh812d0d12021-11-04 20:16:48 -070031 std::string_view data{buf.base, size};
Brian Silverman8fce7482020-01-05 13:18:21 -080032 if (!headersDone) {
33 data = resp.Execute(data);
Austin Schuh812d0d12021-11-04 20:16:48 -070034 if (resp.HasError()) {
35 Finish();
36 }
Brian Silverman8fce7482020-01-05 13:18:21 -080037 ASSERT_EQ(resp.GetError(), HPE_OK)
38 << http_errno_name(resp.GetError());
Austin Schuh812d0d12021-11-04 20:16:48 -070039 if (data.empty()) {
40 return;
41 }
Brian Silverman8fce7482020-01-05 13:18:21 -080042 }
Austin Schuh812d0d12021-11-04 20:16:48 -070043 wireData.insert(wireData.end(), data.begin(), data.end());
44 if (handleData) {
45 handleData(data);
46 }
Brian Silverman8fce7482020-01-05 13:18:21 -080047 });
48 clientPipe->end.connect([this]() { Finish(); });
49 });
50 }
51
52 std::function<void()> setupWebSocket;
Austin Schuh812d0d12021-11-04 20:16:48 -070053 std::function<void(std::string_view)> handleData;
Brian Silverman8fce7482020-01-05 13:18:21 -080054 std::vector<uint8_t> wireData;
55 std::shared_ptr<WebSocket> ws;
56 HttpParser resp{HttpParser::kResponse};
57 bool headersDone = false;
58};
59
60//
61// Terminate closes the endpoint but doesn't send a close frame.
62//
63
64TEST_F(WebSocketServerTest, Terminate) {
65 int gotClosed = 0;
66 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -070067 ws->open.connect([&](std::string_view) { ws->Terminate(); });
68 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -080069 ++gotClosed;
70 ASSERT_EQ(code, 1006) << "reason: " << reason;
71 });
72 };
73
74 loop->Run();
75
76 ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
77 ASSERT_EQ(gotClosed, 1);
78}
79
80TEST_F(WebSocketServerTest, TerminateCode) {
81 int gotClosed = 0;
82 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -070083 ws->open.connect([&](std::string_view) { ws->Terminate(1000); });
84 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -080085 ++gotClosed;
86 ASSERT_EQ(code, 1000) << "reason: " << reason;
87 });
88 };
89
90 loop->Run();
91
92 ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
93 ASSERT_EQ(gotClosed, 1);
94}
95
96TEST_F(WebSocketServerTest, TerminateReason) {
97 int gotClosed = 0;
98 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -070099 ws->open.connect([&](std::string_view) { ws->Terminate(1000, "reason"); });
100 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800101 ++gotClosed;
102 ASSERT_EQ(code, 1000);
103 ASSERT_EQ(reason, "reason");
104 });
105 };
106
107 loop->Run();
108
109 ASSERT_TRUE(wireData.empty()); // terminate doesn't send data
110 ASSERT_EQ(gotClosed, 1);
111}
112
113//
114// Close() sends a close frame.
115//
116
117TEST_F(WebSocketServerTest, CloseBasic) {
118 int gotClosed = 0;
119 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700120 ws->open.connect([&](std::string_view) { ws->Close(); });
121 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800122 ++gotClosed;
123 ASSERT_EQ(code, 1005) << "reason: " << reason;
124 });
125 };
126 // need to respond with close for server to finish shutdown
127 auto message = BuildMessage(0x08, true, true, {});
Austin Schuh812d0d12021-11-04 20:16:48 -0700128 handleData = [&](std::string_view) {
129 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800130 };
131
132 loop->Run();
133
134 auto expectData = BuildMessage(0x08, true, false, {});
135 ASSERT_EQ(wireData, expectData);
136 ASSERT_EQ(gotClosed, 1);
137}
138
139TEST_F(WebSocketServerTest, CloseCode) {
140 int gotClosed = 0;
141 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700142 ws->open.connect([&](std::string_view) { ws->Close(1000); });
143 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800144 ++gotClosed;
145 ASSERT_EQ(code, 1000) << "reason: " << reason;
146 });
147 };
148 // need to respond with close for server to finish shutdown
149 const uint8_t contents[] = {0x03u, 0xe8u};
150 auto message = BuildMessage(0x08, true, true, contents);
Austin Schuh812d0d12021-11-04 20:16:48 -0700151 handleData = [&](std::string_view) {
152 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800153 };
154
155 loop->Run();
156
157 auto expectData = BuildMessage(0x08, true, false, contents);
158 ASSERT_EQ(wireData, expectData);
159 ASSERT_EQ(gotClosed, 1);
160}
161
162TEST_F(WebSocketServerTest, CloseReason) {
163 int gotClosed = 0;
164 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700165 ws->open.connect([&](std::string_view) { ws->Close(1000, "hangup"); });
166 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800167 ++gotClosed;
168 ASSERT_EQ(code, 1000);
169 ASSERT_EQ(reason, "hangup");
170 });
171 };
172 // need to respond with close for server to finish shutdown
173 const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
174 auto message = BuildMessage(0x08, true, true, contents);
Austin Schuh812d0d12021-11-04 20:16:48 -0700175 handleData = [&](std::string_view) {
176 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800177 };
178
179 loop->Run();
180
181 auto expectData = BuildMessage(0x08, true, false, contents);
182 ASSERT_EQ(wireData, expectData);
183 ASSERT_EQ(gotClosed, 1);
184}
185
186//
187// Receiving a close frame results in closure and echoing the close frame.
188//
189
190TEST_F(WebSocketServerTest, ReceiveCloseBasic) {
191 int gotClosed = 0;
192 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700193 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800194 ++gotClosed;
195 ASSERT_EQ(code, 1005) << "reason: " << reason;
196 });
197 };
198 auto message = BuildMessage(0x08, true, true, {});
199 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700200 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800201 });
202
203 loop->Run();
204
205 // the endpoint should echo the message
206 auto expectData = BuildMessage(0x08, true, false, {});
207 ASSERT_EQ(wireData, expectData);
208 ASSERT_EQ(gotClosed, 1);
209}
210
211TEST_F(WebSocketServerTest, ReceiveCloseCode) {
212 int gotClosed = 0;
213 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700214 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800215 ++gotClosed;
216 ASSERT_EQ(code, 1000) << "reason: " << reason;
217 });
218 };
219 const uint8_t contents[] = {0x03u, 0xe8u};
220 auto message = BuildMessage(0x08, true, true, contents);
221 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700222 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800223 });
224
225 loop->Run();
226
227 // the endpoint should echo the message
228 auto expectData = BuildMessage(0x08, true, false, contents);
229 ASSERT_EQ(wireData, expectData);
230 ASSERT_EQ(gotClosed, 1);
231}
232
233TEST_F(WebSocketServerTest, ReceiveCloseReason) {
234 int gotClosed = 0;
235 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700236 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800237 ++gotClosed;
238 ASSERT_EQ(code, 1000);
239 ASSERT_EQ(reason, "hangup");
240 });
241 };
242 const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'};
243 auto message = BuildMessage(0x08, true, true, contents);
244 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700245 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800246 });
247
248 loop->Run();
249
250 // the endpoint should echo the message
251 auto expectData = BuildMessage(0x08, true, false, contents);
252 ASSERT_EQ(wireData, expectData);
253 ASSERT_EQ(gotClosed, 1);
254}
255
256//
257// If an unknown opcode is received, the receiving endpoint MUST _Fail the
258// WebSocket Connection_.
259//
260
261class WebSocketServerBadOpcodeTest
262 : public WebSocketServerTest,
263 public ::testing::WithParamInterface<uint8_t> {};
264
265INSTANTIATE_TEST_SUITE_P(WebSocketServerBadOpcodeTests,
266 WebSocketServerBadOpcodeTest,
267 ::testing::Values(3, 4, 5, 6, 7, 0xb, 0xc, 0xd, 0xe,
268 0xf));
269
270TEST_P(WebSocketServerBadOpcodeTest, Receive) {
271 int gotCallback = 0;
272 std::vector<uint8_t> data(4, 0x03);
273 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700274 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800275 ++gotCallback;
276 ASSERT_EQ(code, 1002) << "reason: " << reason;
277 });
278 };
279 auto message = BuildMessage(GetParam(), true, true, data);
280 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700281 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800282 });
283
284 loop->Run();
285
286 ASSERT_EQ(gotCallback, 1);
287}
288
289//
290// Control frames themselves MUST NOT be fragmented.
291//
292
293class WebSocketServerControlFrameTest
294 : public WebSocketServerTest,
295 public ::testing::WithParamInterface<uint8_t> {};
296
297INSTANTIATE_TEST_SUITE_P(WebSocketServerControlFrameTests,
298 WebSocketServerControlFrameTest,
299 ::testing::Values(0x8, 0x9, 0xa));
300
301TEST_P(WebSocketServerControlFrameTest, ReceiveFragment) {
302 int gotCallback = 0;
303 std::vector<uint8_t> data(4, 0x03);
304 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700305 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800306 ++gotCallback;
307 ASSERT_EQ(code, 1002) << "reason: " << reason;
308 });
309 };
310 auto message = BuildMessage(GetParam(), false, true, data);
311 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700312 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800313 });
314
315 loop->Run();
316
317 ASSERT_EQ(gotCallback, 1);
318}
319
320//
321// A fragmented message consists of a single frame with the FIN bit
322// clear and an opcode other than 0, followed by zero or more frames
323// with the FIN bit clear and the opcode set to 0, and terminated by
324// a single frame with the FIN bit set and an opcode of 0.
325//
326
327// No previous message
328TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFrame) {
329 int gotCallback = 0;
330 std::vector<uint8_t> data(4, 0x03);
331 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700332 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800333 ++gotCallback;
334 ASSERT_EQ(code, 1002) << "reason: " << reason;
335 });
336 };
337 auto message = BuildMessage(0x00, false, true, data);
338 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700339 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800340 });
341
342 loop->Run();
343
344 ASSERT_EQ(gotCallback, 1);
345}
346
347// No previous message with FIN=1.
348TEST_F(WebSocketServerTest, ReceiveFragmentInvalidNoPrevFragment) {
349 int gotCallback = 0;
350 std::vector<uint8_t> data(4, 0x03);
351 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700352 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800353 ++gotCallback;
354 ASSERT_EQ(code, 1002) << "reason: " << reason;
355 });
356 };
357 auto message = BuildMessage(0x01, true, true, {}); // FIN=1
358 auto message2 = BuildMessage(0x00, false, true, data);
359 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700360 clientPipe->Write({{message}, {message2}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800361 });
362
363 loop->Run();
364
365 ASSERT_EQ(gotCallback, 1);
366}
367
368// Incomplete fragment
369TEST_F(WebSocketServerTest, ReceiveFragmentInvalidIncomplete) {
370 int gotCallback = 0;
371 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700372 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800373 ++gotCallback;
374 ASSERT_EQ(code, 1002) << "reason: " << reason;
375 });
376 };
377 auto message = BuildMessage(0x01, false, true, {});
378 auto message2 = BuildMessage(0x00, false, true, {});
379 auto message3 = BuildMessage(0x01, true, true, {});
380 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700381 clientPipe->Write({{message}, {message2}, {message3}},
382 [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800383 });
384
385 loop->Run();
386
387 ASSERT_EQ(gotCallback, 1);
388}
389
390// Normally fragments are combined into a single callback
391TEST_F(WebSocketServerTest, ReceiveFragment) {
392 int gotCallback = 0;
393
394 std::vector<uint8_t> data(4, 0x03);
395 std::vector<uint8_t> data2(4, 0x04);
396 std::vector<uint8_t> data3(4, 0x05);
397 std::vector<uint8_t> combData{data};
398 combData.insert(combData.end(), data2.begin(), data2.end());
399 combData.insert(combData.end(), data3.begin(), data3.end());
400
401 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700402 ws->binary.connect([&](auto inData, bool fin) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800403 ++gotCallback;
404 ws->Terminate();
405 ASSERT_TRUE(fin);
406 std::vector<uint8_t> recvData{inData.begin(), inData.end()};
407 ASSERT_EQ(combData, recvData);
408 });
409 };
410
411 auto message = BuildMessage(0x02, false, true, data);
412 auto message2 = BuildMessage(0x00, false, true, data2);
413 auto message3 = BuildMessage(0x00, true, true, data3);
414 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700415 clientPipe->Write({{message}, {message2}, {message3}},
416 [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800417 });
418
419 loop->Run();
420
421 ASSERT_EQ(gotCallback, 1);
422}
423
424// But can be configured for multiple callbacks
425TEST_F(WebSocketServerTest, ReceiveFragmentSeparate) {
426 int gotCallback = 0;
427
428 std::vector<uint8_t> data(4, 0x03);
429 std::vector<uint8_t> data2(4, 0x04);
430 std::vector<uint8_t> data3(4, 0x05);
431 std::vector<uint8_t> combData{data};
432 combData.insert(combData.end(), data2.begin(), data2.end());
433 combData.insert(combData.end(), data3.begin(), data3.end());
434
435 setupWebSocket = [&] {
436 ws->SetCombineFragments(false);
Austin Schuh812d0d12021-11-04 20:16:48 -0700437 ws->binary.connect([&](auto inData, bool fin) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800438 std::vector<uint8_t> recvData{inData.begin(), inData.end()};
439 switch (++gotCallback) {
440 case 1:
441 ASSERT_FALSE(fin);
442 ASSERT_EQ(data, recvData);
443 break;
444 case 2:
445 ASSERT_FALSE(fin);
446 ASSERT_EQ(data2, recvData);
447 break;
448 case 3:
449 ws->Terminate();
450 ASSERT_TRUE(fin);
451 ASSERT_EQ(data3, recvData);
452 break;
453 default:
454 FAIL() << "too many callbacks";
455 break;
456 }
457 });
458 };
459
460 auto message = BuildMessage(0x02, false, true, data);
461 auto message2 = BuildMessage(0x00, false, true, data2);
462 auto message3 = BuildMessage(0x00, true, true, data3);
463 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700464 clientPipe->Write({{message}, {message2}, {message3}},
465 [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800466 });
467
468 loop->Run();
469
470 ASSERT_EQ(gotCallback, 3);
471}
472
473//
474// Maximum message size is limited.
475//
476
477// Single message
478TEST_F(WebSocketServerTest, ReceiveTooLarge) {
479 int gotCallback = 0;
480 std::vector<uint8_t> data(2048, 0x03u);
481 setupWebSocket = [&] {
482 ws->SetMaxMessageSize(1024);
483 ws->binary.connect([&](auto, bool) {
484 ws->Terminate();
485 FAIL() << "Should not have gotten unmasked message";
486 });
Austin Schuh812d0d12021-11-04 20:16:48 -0700487 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800488 ++gotCallback;
489 ASSERT_EQ(code, 1009) << "reason: " << reason;
490 });
491 };
492 auto message = BuildMessage(0x01, true, true, data);
493 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700494 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800495 });
496
497 loop->Run();
498
499 ASSERT_EQ(gotCallback, 1);
500}
501
502// Applied across fragments if combining
503TEST_F(WebSocketServerTest, ReceiveTooLargeFragmented) {
504 int gotCallback = 0;
505 std::vector<uint8_t> data(768, 0x03u);
506 setupWebSocket = [&] {
507 ws->SetMaxMessageSize(1024);
508 ws->binary.connect([&](auto, bool) {
509 ws->Terminate();
510 FAIL() << "Should not have gotten unmasked message";
511 });
Austin Schuh812d0d12021-11-04 20:16:48 -0700512 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800513 ++gotCallback;
514 ASSERT_EQ(code, 1009) << "reason: " << reason;
515 });
516 };
517 auto message = BuildMessage(0x01, false, true, data);
518 auto message2 = BuildMessage(0x00, true, true, data);
519 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700520 clientPipe->Write({{message}, {message2}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800521 });
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
535INSTANTIATE_TEST_SUITE_P(WebSocketServerDataTests, WebSocketServerDataTest,
536 ::testing::Values(0, 1, 125, 126, 65535, 65536));
537
538TEST_P(WebSocketServerDataTest, SendText) {
539 int gotCallback = 0;
540 std::vector<uint8_t> data(GetParam(), ' ');
541 setupWebSocket = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700542 ws->open.connect([&](std::string_view) {
543 ws->SendText({{data}}, [&](auto bufs, uv::Error) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800544 ++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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700563 ws->open.connect([&](std::string_view) {
564 ws->SendBinary({{data}}, [&](auto bufs, uv::Error) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800565 ++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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700584 ws->open.connect([&](std::string_view) {
585 ws->SendPing({{data}}, [&](auto bufs, uv::Error) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800586 ++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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700605 ws->open.connect([&](std::string_view) {
606 ws->SendPong({{data}}, [&](auto bufs, uv::Error) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800607 ++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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700626 ws->text.connect([&](std::string_view inData, bool fin) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800627 ++gotCallback;
628 ws->Terminate();
629 ASSERT_TRUE(fin);
630 std::vector<uint8_t> recvData;
Austin Schuh812d0d12021-11-04 20:16:48 -0700631 recvData.insert(recvData.end(), inData.begin(), inData.end());
Brian Silverman8fce7482020-01-05 13:18:21 -0800632 ASSERT_EQ(data, recvData);
633 });
634 };
635 auto message = BuildMessage(0x01, true, true, data);
636 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700637 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800638 });
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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700649 ws->binary.connect([&](auto inData, bool fin) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800650 ++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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700659 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800660 });
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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700671 ws->ping.connect([&](auto inData) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800672 ++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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700680 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800681 });
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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700692 ws->pong.connect([&](auto inData) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800693 ++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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700701 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800702 });
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 = [&] {
Austin Schuh812d0d12021-11-04 20:16:48 -0700717 ws->text.connect([&](std::string_view, bool) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800718 ws->Terminate();
719 FAIL() << "Should not have gotten unmasked message";
720 });
Austin Schuh812d0d12021-11-04 20:16:48 -0700721 ws->closed.connect([&](uint16_t code, std::string_view reason) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800722 ++gotCallback;
723 ASSERT_EQ(code, 1002) << "reason: " << reason;
724 });
725 };
726 auto message = BuildMessage(0x01, true, false, data);
727 resp.headersComplete.connect([&](bool) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700728 clientPipe->Write({{message}}, [&](auto bufs, uv::Error) {});
Brian Silverman8fce7482020-01-05 13:18:21 -0800729 });
730
731 loop->Run();
732
733 ASSERT_EQ(gotCallback, 1);
734}
735
736} // namespace wpi