1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #include "http/server_sent_event.hpp"
4 #include "http/server_sent_event_impl.hpp"
5 #include "http_request.hpp"
6 #include "test_stream.hpp"
7
8 #include <boost/asio/buffer.hpp>
9 #include <boost/asio/io_context.hpp>
10 #include <boost/asio/read.hpp>
11
12 #include <chrono>
13 #include <memory>
14 #include <string>
15 #include <string_view>
16 #include <utility>
17
18 #include "gtest/gtest.h"
19 namespace crow
20 {
21 namespace sse_socket
22 {
23
24 namespace
25 {
26
TEST(ServerSentEvent,SseWorks)27 TEST(ServerSentEvent, SseWorks)
28 {
29 boost::asio::io_context io;
30 TestStream stream(io);
31 TestStream out(io);
32 stream.connect(out);
33
34 Request req;
35
36 bool openCalled = false;
37 auto openHandler =
38 [&openCalled](Connection&, const Request& /*handedReq*/) {
39 openCalled = true;
40 };
41 bool closeCalled = false;
42 auto closeHandler = [&closeCalled](Connection&) { closeCalled = true; };
43
44 std::shared_ptr<ConnectionImpl<TestStream>> conn =
45 std::make_shared<ConnectionImpl<TestStream>>(std::move(stream),
46 openHandler, closeHandler);
47 conn->start(req);
48 // Connect
49 {
50 constexpr std::string_view expected =
51 "HTTP/1.1 200 OK\r\n"
52 "Content-Type: text/event-stream\r\n"
53 "\r\n";
54
55 while (out.str().size() != expected.size() || !openCalled)
56 {
57 io.run_for(std::chrono::milliseconds(1));
58 }
59
60 std::string eventContent;
61 eventContent.resize(expected.size());
62 boost::asio::read(out, boost::asio::buffer(eventContent));
63
64 EXPECT_EQ(eventContent, expected);
65 EXPECT_TRUE(openCalled);
66 EXPECT_FALSE(closeCalled);
67 EXPECT_TRUE(out.str().empty());
68 }
69 // Send one event
70 {
71 conn->sendSseEvent("TestEventId", "TestEventContent");
72 std::string_view expected = "id: TestEventId\n"
73 "data: TestEventContent\n"
74 "\n";
75
76 while (out.str().size() < expected.size())
77 {
78 io.run_for(std::chrono::milliseconds(1));
79 }
80 EXPECT_EQ(out.str(), expected);
81
82 std::string eventContent;
83 eventContent.resize(expected.size());
84 boost::asio::read(out, boost::asio::buffer(eventContent));
85
86 EXPECT_EQ(eventContent, expected);
87 EXPECT_TRUE(out.str().empty());
88 }
89 // Send second event
90 {
91 conn->sendSseEvent("TestEventId2", "TestEvent\nContent2");
92 constexpr std::string_view expected =
93 "id: TestEventId2\n"
94 "data: TestEvent\n"
95 "data: Content2\n"
96 "\n";
97
98 while (out.str().size() < expected.size())
99 {
100 io.run_for(std::chrono::milliseconds(1));
101 }
102
103 std::string eventContent;
104 eventContent.resize(expected.size());
105 boost::asio::read(out, boost::asio::buffer(eventContent));
106 EXPECT_EQ(eventContent, expected);
107 EXPECT_TRUE(out.str().empty());
108 }
109 // close the remote
110 {
111 out.close();
112 while (!closeCalled)
113 {
114 io.run_for(std::chrono::milliseconds(1));
115 }
116 }
117 }
118 } // namespace
119
120 } // namespace sse_socket
121 } // namespace crow
122