1 #include "boost/beast/core/flat_buffer.hpp" 2 #include "boost/beast/http/serializer.hpp" 3 #include "http/http_response.hpp" 4 5 #include <filesystem> 6 #include <fstream> 7 #include <thread> 8 9 #include "gtest/gtest.h" 10 namespace 11 { 12 void addHeaders(crow::Response& res) 13 { 14 res.addHeader("myheader", "myvalue"); 15 res.keepAlive(true); 16 res.result(boost::beast::http::status::ok); 17 } 18 void verifyHeaders(crow::Response& res) 19 { 20 EXPECT_EQ(res.getHeaderValue("myheader"), "myvalue"); 21 EXPECT_EQ(res.keepAlive(), true); 22 EXPECT_EQ(res.result(), boost::beast::http::status::ok); 23 } 24 25 std::string makeFile(std::string_view sampleData) 26 { 27 std::filesystem::path path = std::filesystem::temp_directory_path(); 28 path /= "bmcweb_http_response_test_XXXXXXXXXXX"; 29 std::string stringPath = path.string(); 30 int fd = mkstemp(stringPath.data()); 31 EXPECT_GT(fd, 0); 32 EXPECT_EQ(write(fd, sampleData.data(), sampleData.size()), 33 sampleData.size()); 34 close(fd); 35 return stringPath; 36 } 37 38 void readHeader(boost::beast::http::serializer<false, bmcweb::FileBody>& sr) 39 { 40 while (!sr.is_header_done()) 41 { 42 boost::system::error_code ec; 43 sr.next(ec, [&sr](const boost::system::error_code& ec2, 44 const auto& buffer) { 45 ASSERT_FALSE(ec2); 46 sr.consume(boost::beast::buffer_bytes(buffer)); 47 }); 48 ASSERT_FALSE(ec); 49 } 50 } 51 52 std::string collectFromBuffers( 53 const auto& buffer, 54 boost::beast::http::serializer<false, bmcweb::FileBody>& sr) 55 { 56 std::string ret; 57 58 for (auto iter = boost::asio::buffer_sequence_begin(buffer); 59 iter != boost::asio::buffer_sequence_end(buffer); ++iter) 60 { 61 const auto& innerBuf = *iter; 62 auto view = std::string_view(static_cast<const char*>(innerBuf.data()), 63 innerBuf.size()); 64 ret += view; 65 sr.consume(innerBuf.size()); 66 } 67 return ret; 68 } 69 70 std::string 71 readBody(boost::beast::http::serializer<false, bmcweb::FileBody>& sr) 72 { 73 std::string ret; 74 while (!sr.is_done()) 75 { 76 boost::system::error_code ec; 77 sr.next(ec, [&sr, &ret](const boost::system::error_code& ec2, 78 const auto& buffer) { 79 ASSERT_FALSE(ec2); 80 ret += collectFromBuffers(buffer, sr); 81 }); 82 EXPECT_FALSE(ec); 83 } 84 85 return ret; 86 } 87 std::string getData(crow::Response::file_response& m) 88 { 89 boost::beast::http::serializer<false, bmcweb::FileBody> sr{m}; 90 std::stringstream ret; 91 sr.split(true); 92 readHeader(sr); 93 return readBody(sr); 94 } 95 TEST(HttpResponse, Defaults) 96 { 97 crow::Response res; 98 EXPECT_EQ( 99 boost::variant2::holds_alternative<crow::Response::string_response>( 100 res.response), 101 true); 102 } 103 TEST(HttpResponse, Headers) 104 { 105 crow::Response res; 106 addHeaders(res); 107 verifyHeaders(res); 108 } 109 TEST(HttpResponse, StringBody) 110 { 111 crow::Response res; 112 addHeaders(res); 113 std::string_view bodyvalue = "this is my new body"; 114 res.write({bodyvalue.data(), bodyvalue.length()}); 115 EXPECT_EQ(*res.body(), bodyvalue); 116 verifyHeaders(res); 117 } 118 TEST(HttpResponse, FileBody) 119 { 120 crow::Response res; 121 addHeaders(res); 122 std::string path = makeFile("sample text"); 123 res.openFile(path); 124 125 verifyHeaders(res); 126 std::filesystem::remove(path); 127 } 128 TEST(HttpResponse, FileBodyWithFd) 129 { 130 crow::Response res; 131 addHeaders(res); 132 std::string path = makeFile("sample text"); 133 FILE* fd = fopen(path.c_str(), "r+"); 134 res.openFd(fileno(fd)); 135 verifyHeaders(res); 136 fclose(fd); 137 std::filesystem::remove(path); 138 } 139 140 TEST(HttpResponse, Base64FileBodyWithFd) 141 { 142 crow::Response res; 143 addHeaders(res); 144 std::string path = makeFile("sample text"); 145 FILE* fd = fopen(path.c_str(), "r+"); 146 res.openFd(fileno(fd), bmcweb::EncodingType::Base64); 147 verifyHeaders(res); 148 fclose(fd); 149 std::filesystem::remove(path); 150 } 151 152 TEST(HttpResponse, BodyTransitions) 153 { 154 crow::Response res; 155 addHeaders(res); 156 std::string path = makeFile("sample text"); 157 res.openFile(path); 158 159 EXPECT_EQ(boost::variant2::holds_alternative<crow::Response::file_response>( 160 res.response), 161 true); 162 163 verifyHeaders(res); 164 res.write("body text"); 165 166 EXPECT_EQ( 167 boost::variant2::holds_alternative<crow::Response::string_response>( 168 res.response), 169 true); 170 171 verifyHeaders(res); 172 std::filesystem::remove(path); 173 } 174 175 void testFileData(crow::Response& res, const std::string& data) 176 { 177 auto& fb = 178 boost::variant2::get<crow::Response::file_response>(res.response); 179 EXPECT_EQ(getData(fb), data); 180 } 181 182 TEST(HttpResponse, Base64FileBodyWriter) 183 { 184 crow::Response res; 185 std::string data = "sample text"; 186 std::string path = makeFile(data); 187 FILE* f = fopen(path.c_str(), "r+"); 188 res.openFd(fileno(f), bmcweb::EncodingType::Base64); 189 testFileData(res, crow::utility::base64encode(data)); 190 fclose(f); 191 std::filesystem::remove(path); 192 } 193 194 std::string generateBigdata() 195 { 196 std::string result; 197 while (result.size() < 10000) 198 { 199 result += "sample text"; 200 } 201 return result; 202 } 203 204 TEST(HttpResponse, Base64FileBodyWriterLarge) 205 { 206 crow::Response res; 207 std::string data = generateBigdata(); 208 std::string path = makeFile(data); 209 { 210 boost::beast::file_posix file; 211 boost::system::error_code ec; 212 file.open(path.c_str(), boost::beast::file_mode::read, ec); 213 EXPECT_EQ(ec.value(), 0); 214 res.openFd(file.native_handle(), bmcweb::EncodingType::Base64); 215 testFileData(res, crow::utility::base64encode(data)); 216 } 217 218 std::filesystem::remove(path); 219 } 220 221 TEST(HttpResponse, FileBodyWriterLarge) 222 { 223 crow::Response res; 224 std::string data = generateBigdata(); 225 std::string path = makeFile(data); 226 { 227 boost::beast::file_posix file; 228 boost::system::error_code ec; 229 file.open(path.c_str(), boost::beast::file_mode::read, ec); 230 EXPECT_EQ(ec.value(), 0); 231 res.openFd(file.native_handle()); 232 testFileData(res, data); 233 } 234 std::filesystem::remove(path); 235 } 236 237 } // namespace 238