diff --git a/libs/crossplatform/include/CrossPlatformUtils.h b/libs/crossplatform/include/CrossPlatformUtils.h index 8b8d4a0ae..d2cef4c89 100644 --- a/libs/crossplatform/include/CrossPlatformUtils.h +++ b/libs/crossplatform/include/CrossPlatformUtils.h @@ -108,6 +108,12 @@ class CrossPlatformUtils * @return The umask value as a std::filesystem::perms type */ static std::filesystem::perms GetCurrentUmask(); + + /** + * @brief Get CRLF line endings regardless of platform + * @return + */ + static std::string Crlf(); }; #endif /* CROSSPLATFORM_UTILS_H */ diff --git a/libs/crossplatform/src/CrossPlatformUtils.cpp b/libs/crossplatform/src/CrossPlatformUtils.cpp index 53e146186..5fbcf3285 100644 --- a/libs/crossplatform/src/CrossPlatformUtils.cpp +++ b/libs/crossplatform/src/CrossPlatformUtils.cpp @@ -88,4 +88,10 @@ const std::pair CrossPlatformUtils::ExecCommand(const std::str return std::make_pair(result, ret_code); } +std::string CrossPlatformUtils::Crlf() +{ + static const std::string crlf = CRLF; + return crlf; +} + // end of CrossplatformUtils.cpp diff --git a/libs/crossplatform/src/Darwin/constants.h b/libs/crossplatform/src/Darwin/constants.h index 1fef04e4a..bb7a4ad0e 100644 --- a/libs/crossplatform/src/Darwin/constants.h +++ b/libs/crossplatform/src/Darwin/constants.h @@ -12,5 +12,6 @@ constexpr const char* USER_PROFILE = "HOME"; constexpr const char* PACK_ROOT_DIR = "/arm/packs"; constexpr const char* CACHE_DIR = "/.cache"; constexpr const char* HOST_TYPE = "mac"; +constexpr const char* CRLF = "\r\n"; #endif // CROSSPLATFORM_CONSTANTS_H diff --git a/libs/crossplatform/src/Emscripten/constants.h b/libs/crossplatform/src/Emscripten/constants.h index 23dac8bd7..87564fe05 100644 --- a/libs/crossplatform/src/Emscripten/constants.h +++ b/libs/crossplatform/src/Emscripten/constants.h @@ -12,5 +12,6 @@ constexpr const char* USER_PROFILE = ""; constexpr const char* PACK_ROOT_DIR = ""; constexpr const char* CACHE_DIR = ""; constexpr const char* HOST_TYPE = ""; +constexpr const char* CRLF = ""; #endif // CROSSPLATFORM_CONSTANTS_H diff --git a/libs/crossplatform/src/Linux/constants.h b/libs/crossplatform/src/Linux/constants.h index b433b0e33..f6447e1ac 100644 --- a/libs/crossplatform/src/Linux/constants.h +++ b/libs/crossplatform/src/Linux/constants.h @@ -13,5 +13,6 @@ constexpr const char* PACK_ROOT_DIR = "/arm/packs"; constexpr const char* PROC_SELF_EXE = "/proc/self/exe"; constexpr const char* CACHE_DIR = "/.cache"; constexpr const char* HOST_TYPE = "linux"; +constexpr const char* CRLF = "\r\n"; #endif // CROSSPLATFORM_CONSTANTS_H diff --git a/libs/crossplatform/src/Windows/constants.h b/libs/crossplatform/src/Windows/constants.h index 7fb342352..232fb674c 100644 --- a/libs/crossplatform/src/Windows/constants.h +++ b/libs/crossplatform/src/Windows/constants.h @@ -12,5 +12,6 @@ constexpr const char* USER_PROFILE = "USERPROFILE"; constexpr const char* PACK_ROOT_DIR = "\\Arm\\Packs"; constexpr const char* CACHE_DIR = "\\AppData\\Local"; constexpr const char* HOST_TYPE = "win"; +constexpr const char* CRLF = "\n"; #endif // CROSSPLATFORM_CONSTANTS_H diff --git a/tools/projmgr/include/ProjMgrRpcServer.h b/tools/projmgr/include/ProjMgrRpcServer.h index 699cc0996..ad3207c5c 100644 --- a/tools/projmgr/include/ProjMgrRpcServer.h +++ b/tools/projmgr/include/ProjMgrRpcServer.h @@ -56,13 +56,23 @@ class ProjMgrRpcServer { */ void SetContentLengthHeader(bool value) { m_contextLength = value; } + /** + * @brief get request from stdin with content length header + * @return string request + */ + const std::string GetRequestFromStdinWithLength(void); + + /** + * @brief get request from stdin + * @param string request + */ + const std::string GetRequestFromStdin(void); + protected: ProjMgr& m_manager; bool m_debug = false; bool m_shutdown = false; bool m_contextLength = false; - const std::string GetRequestFromStdinWithLength(void); - const std::string GetRequestFromStdin(void); }; #endif // PROJMGRRPCSERVER_H diff --git a/tools/projmgr/src/ProjMgrRpcServer.cpp b/tools/projmgr/src/ProjMgrRpcServer.cpp index 0d9f81e35..d0ffdeedb 100644 --- a/tools/projmgr/src/ProjMgrRpcServer.cpp +++ b/tools/projmgr/src/ProjMgrRpcServer.cpp @@ -9,6 +9,8 @@ #include "ProjMgrLogger.h" #include "ProjMgr.h" #include "ProductInfo.h" + +#include "CrossPlatformUtils.h" #include #include @@ -31,10 +33,13 @@ const string ProjMgrRpcServer::GetRequestFromStdinWithLength(void) { string line; int contentLength = 0; const string& header = CONTENT_LENGTH_HEADER; - while (getline(cin, line) && !line.empty() && !cin.fail()) { + while (getline(cin, line) && !cin.fail()) { if (line.find(header) == 0) { contentLength = RteUtils::StringToInt(line.substr(header.length()), 0); } + if (line.empty() || line.front() == '\r' || line.front() == '\n') { + break; + } } string request(contentLength, '\0'); cin.read(&request[0], contentLength); @@ -138,7 +143,9 @@ bool ProjMgrRpcServer::Run(void) { // Send response if (m_contextLength) { - cout << CONTENT_LENGTH_HEADER << response.size() << std::endl << std::endl << response << std::flush; + // compliant to https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#baseProtocol + cout << CONTENT_LENGTH_HEADER << response.size() << + CrossPlatformUtils::Crlf() << CrossPlatformUtils::Crlf() << response << std::flush; } else { cout << response << std::endl; } diff --git a/tools/projmgr/test/src/ProjMgrRpcTests.cpp b/tools/projmgr/test/src/ProjMgrRpcTests.cpp index eee1123ca..018360ae1 100644 --- a/tools/projmgr/test/src/ProjMgrRpcTests.cpp +++ b/tools/projmgr/test/src/ProjMgrRpcTests.cpp @@ -10,6 +10,7 @@ #include "ProjMgrRpcServerData.h" #include "ProjMgrLogger.h" +#include "CrossPlatformUtils.h" #include "ProductInfo.h" using namespace std; @@ -20,6 +21,7 @@ class ProjMgrRpcTests : public ProjMgr, public ::testing::Test { virtual ~ProjMgrRpcTests() {} string FormatRequest(const int id, const string& method, const json& params); vector RunRpcMethods(const string& strIn); + string RunRpcMethodsWithContent(const string& strIn); }; string ProjMgrRpcTests::FormatRequest(const int id, const string& method, const json& params = json()) { @@ -47,6 +49,30 @@ vector ProjMgrRpcTests::RunRpcMethods(const string& strIn) { return responses; } +string ProjMgrRpcTests::RunRpcMethodsWithContent(const string& strIn) { + StdStreamRedirect streamRedirect; + streamRedirect.SetInString(strIn); + char* argv[] = { (char*)"csolution", (char*)"rpc", (char*)"--content-length" }; + EXPECT_EQ(0, RunProjMgr(3, argv, 0)); + return streamRedirect.GetOutString(); +} + +TEST_F(ProjMgrRpcTests, ContentLength) { + StdStreamRedirect streamRedirect; + ProjMgrRpcServer server(*this); + const auto& request = FormatRequest(1, "GetVersion"); + + auto requestWithHeader = "Content-Length:46\n\n" + request; + streamRedirect.SetInString(requestWithHeader); + auto parsedRequest = server.GetRequestFromStdinWithLength(); + EXPECT_EQ(request, parsedRequest); + + requestWithHeader = "Content-Length:46\r\n\r\n" + request; + streamRedirect.SetInString(requestWithHeader); + parsedRequest = server.GetRequestFromStdinWithLength(); + EXPECT_EQ(request, parsedRequest); +} + TEST_F(ProjMgrRpcTests, RpcGetVersion) { const auto& requests = FormatRequest(1, "GetVersion"); const auto& responses = RunRpcMethods(requests); @@ -55,6 +81,12 @@ TEST_F(ProjMgrRpcTests, RpcGetVersion) { EXPECT_EQ(string(VERSION_STRING), responses[0]["result"]); } +TEST_F(ProjMgrRpcTests, RpcGetVersionWithContent) { + const auto& requests = "Content-Length:46\n\n" + FormatRequest(1, "GetVersion"); + const auto& responses = RunRpcMethodsWithContent(requests); + EXPECT_TRUE(responses.find(CrossPlatformUtils::Crlf() + CrossPlatformUtils::Crlf() + "{") != string::npos); +} + TEST_F(ProjMgrRpcTests, RpcLoadSolution) { const string& csolution = testinput_folder + "/TestRpc/minimal.csolution.yml"; const auto& requests =