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