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