ServerCommon.cpp (3607B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 4 #include "ServerCommon.h" 5 6 #include "../util/TestHelper.h" 7 #include "Result.h" 8 9 using SFS::test::BufferedLogData; 10 using SFS::test::StatusCodeException; 11 using SFS::test::details::BaseServerImpl; 12 using namespace SFS; 13 using namespace std::string_literals; 14 15 static constexpr const char* c_listenHostName = "localhost"; 16 17 static std::string ToString(httplib::StatusCode status) 18 { 19 return std::to_string(status) + " " + std::string(httplib::status_message(status)); 20 } 21 22 StatusCodeException::StatusCodeException(httplib::StatusCode status) : m_status(status), m_message(::ToString(m_status)) 23 { 24 } 25 26 const char* StatusCodeException::what() const noexcept 27 { 28 return m_message.c_str(); 29 } 30 31 httplib::StatusCode StatusCodeException::GetStatusCode() const 32 { 33 return m_status; 34 } 35 36 static SFS::LogData ToLogData(const BufferedLogData& data) 37 { 38 return {LogSeverity::Info, data.message.c_str(), data.file.c_str(), data.line, data.function.c_str(), data.time}; 39 } 40 41 void BaseServerImpl::Start() 42 { 43 ConfigureServerSettings(); 44 ConfigureRequestHandlers(); 45 46 m_port = m_server.bind_to_any_port(c_listenHostName); 47 m_listenerThread = std::thread([&]() { m_server.listen_after_bind(); }); 48 } 49 50 void BaseServerImpl::ConfigureServerSettings() 51 { 52 m_server.set_logger([&](const httplib::Request& req, const httplib::Response& res) { 53 BUFFER_LOG("Request: " + req.method + " " + req.path + " " + req.version); 54 BUFFER_LOG("Request Body: " + req.body); 55 56 BUFFER_LOG("Response: " + res.version + " " + ::ToString(static_cast<httplib::StatusCode>(res.status)) + " " + 57 res.reason); 58 BUFFER_LOG("Response body: " + res.body); 59 }); 60 61 m_server.set_exception_handler([&](const httplib::Request&, httplib::Response& res, std::exception_ptr ep) { 62 try 63 { 64 std::rethrow_exception(ep); 65 } 66 catch (std::exception& e) 67 { 68 m_lastException = Result(Result::HttpUnexpected, e.what()); 69 } 70 catch (...) 71 { 72 m_lastException = Result(Result::HttpUnexpected, "Unknown Exception"); 73 } 74 75 ProcessBufferedLogs(); 76 77 res.status = httplib::StatusCode::InternalServerError_500; 78 }); 79 80 // Keeping this interval to a minimum ensures tests run quicker 81 m_server.set_keep_alive_timeout(1); // 1 second 82 } 83 84 void BaseServerImpl::BufferLog(const BufferedLogData& data) 85 { 86 std::lock_guard guard(m_logMutex); 87 m_bufferedLog.push_back(data); 88 } 89 90 BufferedLogData BaseServerImpl::BuildBufferedLogData(const std::string& message, 91 const char* file, 92 unsigned line, 93 const char* function) 94 { 95 return BufferedLogData{GetLogIdentifier() + ": " + message, file, line, function, std::chrono::system_clock::now()}; 96 } 97 98 void BaseServerImpl::ProcessBufferedLogs() 99 { 100 for (const auto& data : m_bufferedLog) 101 { 102 LogCallbackToTest(ToLogData(data)); 103 } 104 m_bufferedLog.clear(); 105 } 106 107 Result BaseServerImpl::Stop() 108 { 109 if (m_listenerThread.joinable()) 110 { 111 m_server.stop(); 112 m_listenerThread.join(); 113 } 114 ProcessBufferedLogs(); 115 return m_lastException.value_or(Result::Success); 116 } 117 118 std::string BaseServerImpl::GetUrl() const 119 { 120 return "http://"s + c_listenHostName + ":"s + std::to_string(m_port); 121 }