├── test ├── pages │ └── test.html ├── CMakeLists.txt ├── TestTypeController.cpp ├── HttpHelper.h ├── HttpHelper.cpp ├── TestPageController.h ├── TestWebServer.h ├── TestBaseController.h ├── main.cpp ├── TestApiController.h ├── HttpClient.h ├── TestTypeController.h ├── TestWebServer.cpp ├── TestPageController.cpp ├── TestBaseController.cpp ├── TestApiController.cpp └── HttpClient.cpp ├── CMakeLists.txt ├── examples ├── example3.cpp ├── CMakeLists.txt ├── web │ ├── index.html │ ├── page_a.html │ └── page_b.html ├── example2.cpp └── example1.cpp ├── .gitignore ├── appveyor.yml ├── src ├── PageController.h ├── ApiController.h ├── Response.h ├── PageController.cpp ├── IWebController.h ├── ApiController.cpp ├── Response.cpp ├── Request.h ├── WebServer.h ├── Request.cpp ├── TypeController.h └── WebServer.cpp ├── .travis.yml ├── dependency └── UnitTest │ ├── UnitTest.cpp │ ├── UnitTest.h │ └── termcolor.h ├── README.md └── LICENSE /test/pages/test.html: -------------------------------------------------------------------------------- 1 |

Test

-------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FragJage/MongooseCpp/HEAD/CMakeLists.txt -------------------------------------------------------------------------------- /examples/example3.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FragJage/MongooseCpp/HEAD/examples/example3.cpp -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FragJage/MongooseCpp/HEAD/test/CMakeLists.txt -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FragJage/MongooseCpp/HEAD/examples/CMakeLists.txt -------------------------------------------------------------------------------- /test/TestTypeController.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FragJage/MongooseCpp/HEAD/test/TestTypeController.cpp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | obj 2 | bin 3 | *.out 4 | *.layout 5 | *.depend 6 | *.dir 7 | .vs 8 | *.VC.* 9 | *.filter 10 | *.user 11 | -------------------------------------------------------------------------------- /examples/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Index

4 | Page A
5 | Page B 6 | 7 | -------------------------------------------------------------------------------- /examples/web/page_a.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Page A

4 | Page B
5 | Index 6 | 7 | -------------------------------------------------------------------------------- /examples/web/page_b.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Page B

4 | Page A
5 | Index 6 | 7 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | 5 | image: Visual Studio 2015 6 | 7 | platform: Win32 8 | configuration: Release 9 | 10 | build: 11 | project: builds\msvc\MongooseCpp.sln 12 | 13 | after_build: 14 | - builds\msvc\bin\unittest.exe -------------------------------------------------------------------------------- /test/HttpHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPHELPER_H 2 | #define HTTPHELPER_H 3 | 4 | #include "WebServer.h" 5 | #include "HttpClient.h" 6 | 7 | class HttpHelper 8 | { 9 | public: 10 | static bool WaitResponse(MongooseCpp::WebServer& server, HttpClient& client); 11 | }; 12 | 13 | #endif // HTTPHELPER_H 14 | -------------------------------------------------------------------------------- /test/HttpHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpHelper.h" 2 | 3 | bool HttpHelper::WaitResponse(MongooseCpp::WebServer& server, HttpClient& client) 4 | { 5 | int i, imax=25; 6 | for(i=0; i 2 | #include "PageController.h" 3 | #include "WebServer.h" 4 | #include "HttpClient.h" 5 | #include "HttpHelper.h" 6 | #include "UnitTest/UnitTest.h" 7 | 8 | using namespace std; 9 | 10 | class TestPageController : public TestClass 11 | { 12 | public: 13 | TestPageController(); 14 | ~TestPageController(); 15 | 16 | bool WithDirListing(); 17 | bool WithoutDirListing(); 18 | bool SimplePage(); 19 | bool Error404(); 20 | }; 21 | -------------------------------------------------------------------------------- /src/PageController.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGECONTROLLER_H 2 | #define PAGECONTROLLER_H 3 | 4 | #include 5 | #include "IWebController.h" 6 | #include "mongoose/mongoose.h" 7 | 8 | namespace MongooseCpp { 9 | 10 | class PageController : public IWebController 11 | { 12 | public: 13 | PageController(const std::string& documentRoot, bool enableDirectoryListing); 14 | virtual ~PageController(); 15 | bool Process(Request& request, Response& response); 16 | 17 | private: 18 | std::string m_DocumentRoot; 19 | bool m_EnableDirectoryListing; 20 | }; 21 | 22 | } 23 | 24 | #endif // PAGECONTROLLER_H 25 | -------------------------------------------------------------------------------- /src/ApiController.h: -------------------------------------------------------------------------------- 1 | #ifndef APICONTROLLER_H 2 | #define APICONTROLLER_H 3 | 4 | #include "IWebController.h" 5 | 6 | namespace MongooseCpp { 7 | 8 | class ApiController : public IWebController 9 | { 10 | public: 11 | ApiController(); 12 | virtual ~ApiController(); 13 | bool Process(Request& request, Response& response); 14 | virtual bool Get(Request& request, Response& response); 15 | virtual bool Put(Request& request, Response& response); 16 | virtual bool Post(Request& request, Response& response); 17 | virtual bool Delete(Request& request, Response& response); 18 | }; 19 | 20 | } 21 | 22 | #endif // APICONTROLLER_H 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: gcc 3 | 4 | env: 5 | global: 6 | - CXXCOMPILER=g++-4.9 7 | - CCCOMPILER=gcc-4.9 8 | 9 | addons: 10 | apt: 11 | sources: ['ubuntu-toolchain-r-test'] 12 | packages: ['gcc-4.9','g++-4.9','valgrind'] 13 | 14 | install: 15 | - sudo pip install -U "cpp-coveralls" 16 | 17 | script: 18 | - cmake . -G"Unix Makefiles" -DCMAKE_CC_COMPILER=$(which $CCCOMPILER) -DCMAKE_CXX_COMPILER=$(which $CXXCOMPILER) 19 | - make 20 | - ./test/bin/unittest 21 | 22 | after_success: 23 | ls /home/travis/build/FragJage/MongooseCpp/test/CMakeFiles/unittest.dir; 24 | coveralls --include src --gcov-options '\-lp' --gcov 'gcov-4.9'; 25 | valgrind --error-exitcode=1 --leak-check=full ./test/bin/unittest; 26 | -------------------------------------------------------------------------------- /test/TestWebServer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "IWebController.h" 3 | #include "WebServer.h" 4 | #include "UnitTest/UnitTest.h" 5 | 6 | using namespace std; 7 | 8 | class EmptyController : public MongooseCpp::IWebController 9 | { 10 | public: 11 | EmptyController(); 12 | ~EmptyController(); 13 | bool Process(MongooseCpp::Request& request, MongooseCpp::Response& response); 14 | }; 15 | 16 | class TestWebServer : public TestClass 17 | { 18 | public: 19 | TestWebServer(); 20 | ~TestWebServer(); 21 | 22 | bool IncorrectStar(); 23 | bool MissingChar(); 24 | bool OptionalAtEnd(); 25 | bool StartAndStop(); 26 | 27 | private: 28 | EmptyController emptyCtrl; 29 | }; 30 | -------------------------------------------------------------------------------- /test/TestBaseController.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "IWebController.h" 3 | #include "WebServer.h" 4 | #include "UnitTest/UnitTest.h" 5 | #include "HttpClient.h" 6 | 7 | using namespace std; 8 | 9 | class BaseController : public MongooseCpp::IWebController 10 | { 11 | public: 12 | BaseController(); 13 | ~BaseController(); 14 | bool Process(MongooseCpp::Request& request, MongooseCpp::Response& response); 15 | }; 16 | 17 | class TestBaseController : public TestClass 18 | { 19 | public: 20 | TestBaseController(); 21 | ~TestBaseController(); 22 | 23 | bool ReadHttpMethod(); 24 | bool ReadParameters(); 25 | bool Error404(); 26 | 27 | private: 28 | MongooseCpp::WebServer server; 29 | HttpClient client; 30 | BaseController myBaseCtrl; 31 | }; 32 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "TestWebServer.h" 3 | #include "TestBaseController.h" 4 | #include "TestPageController.h" 5 | #include "TestApiController.h" 6 | #include "TestTypeController.h" 7 | 8 | 9 | using namespace std; 10 | 11 | int main(void) 12 | { 13 | int ret = 0; 14 | UnitTest unitTest; 15 | 16 | 17 | try 18 | { 19 | unitTest.addTestClass(new TestWebServer()); 20 | unitTest.addTestClass(new TestBaseController()); 21 | unitTest.addTestClass(new TestPageController()); 22 | unitTest.addTestClass(new TestApiController()); 23 | unitTest.addTestClass(new TestTypeController()); 24 | } 25 | catch(const exception &e) 26 | { 27 | unitTest.displayError(e.what()); 28 | ret = -1; 29 | } 30 | 31 | if(ret!=-1) 32 | if(!unitTest.run()) ret = 1; 33 | 34 | return ret; 35 | } 36 | -------------------------------------------------------------------------------- /src/Response.h: -------------------------------------------------------------------------------- 1 | #ifndef RESPONSE_H 2 | #define RESPONSE_H 3 | 4 | #include 5 | #include 6 | #include "mongoose/mongoose.h" 7 | 8 | namespace MongooseCpp { 9 | 10 | class Response 11 | { 12 | public: 13 | Response(); 14 | ~Response(); 15 | 16 | void SetHeader(std::string headerKey, std::string value); 17 | void SetHeader(std::string key, int value); 18 | std::string GetHeader(std::string headerKey); 19 | bool HasHeader(std::string headerKey); 20 | void SetContent(std::string content); 21 | std::string GetData(); 22 | void SetStatut(int number); 23 | 24 | private: 25 | std::string m_Protocol; 26 | int m_Statut; 27 | std::string m_StatutMsg; 28 | std::map m_Headers; 29 | std::string m_Content; 30 | }; 31 | 32 | } 33 | 34 | #endif // RESPONSE_H 35 | -------------------------------------------------------------------------------- /src/PageController.cpp: -------------------------------------------------------------------------------- 1 | #include "PageController.h" 2 | 3 | using namespace std; 4 | namespace MongooseCpp { 5 | 6 | PageController::PageController(const string& documentRoot, bool enableDirectoryListing) : m_DocumentRoot(documentRoot), m_EnableDirectoryListing(enableDirectoryListing) 7 | { 8 | } 9 | 10 | PageController::~PageController() 11 | { 12 | } 13 | 14 | #ifndef _MSC_VER 15 | #pragma GCC diagnostic push 16 | #pragma GCC diagnostic ignored "-Wunused-parameter" //This is a virtual function 17 | #endif 18 | 19 | bool PageController::Process(Request& request, Response& response) 20 | { 21 | static struct mg_serve_http_opts s_http_server_opts; 22 | s_http_server_opts.document_root = m_DocumentRoot.c_str(); 23 | if(m_EnableDirectoryListing) 24 | s_http_server_opts.enable_directory_listing = "yes"; 25 | else 26 | s_http_server_opts.enable_directory_listing = "no"; 27 | struct mg_connection* nc = request.GetMgConnection(); 28 | struct http_message* msg = request.GetHttpMsg(); 29 | 30 | mg_serve_http(nc, msg, s_http_server_opts); 31 | return true; 32 | } 33 | 34 | #ifndef _MSC_VER 35 | #pragma GCC diagnostic pop 36 | #endif 37 | } 38 | -------------------------------------------------------------------------------- /test/TestApiController.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ApiController.h" 3 | #include "WebServer.h" 4 | #include "UnitTest/UnitTest.h" 5 | #include "HttpClient.h" 6 | 7 | using namespace std; 8 | 9 | class ApiController : public MongooseCpp::ApiController 10 | { 11 | public: 12 | ApiController(); 13 | ~ApiController(); 14 | bool Get(MongooseCpp::Request& request, MongooseCpp::Response& response); 15 | bool Post(MongooseCpp::Request& request, MongooseCpp::Response& response); 16 | bool Put(MongooseCpp::Request& request, MongooseCpp::Response& response); 17 | bool Delete(MongooseCpp::Request& request, MongooseCpp::Response& response); 18 | }; 19 | 20 | class TestApiController : public TestClass 21 | { 22 | public: 23 | TestApiController(); 24 | ~TestApiController(); 25 | 26 | bool GETmethod(); 27 | bool POSTmethod(); 28 | bool PUTmethod(); 29 | bool DELETEmethod(); 30 | bool UnknownMethod(); 31 | 32 | private: 33 | MongooseCpp::WebServer server; 34 | HttpClient client; 35 | ApiController myApiCtrl; 36 | MongooseCpp::ApiController myEmptyCtrl; 37 | }; 38 | -------------------------------------------------------------------------------- /src/IWebController.h: -------------------------------------------------------------------------------- 1 | /*** LICENCE ***************************************************************************************/ 2 | /* 3 | WebServer - Simple class to manage a HTTP web server 4 | 5 | This file is part of WebServer. 6 | 7 | WebServer is free software : you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | WebServer is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with WebServer. If not, see . 19 | */ 20 | /***************************************************************************************************/ 21 | 22 | #ifndef IWebController_H 23 | #define IWebController_H 24 | 25 | #include "Request.h" 26 | #include "Response.h" 27 | 28 | namespace MongooseCpp { 29 | 30 | class IWebController 31 | { 32 | public: 33 | virtual ~IWebController() {}; 34 | virtual bool Process(Request& request, Response& response) = 0; 35 | }; 36 | 37 | } 38 | 39 | #endif // IWebController_H 40 | -------------------------------------------------------------------------------- /test/HttpClient.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPCLIENT_H 2 | #define HTTPCLIENT_H 3 | 4 | #include 5 | 6 | #ifdef __MINGW32__ 7 | #define _WIN32_WINNT 0x501 /* for getaddrinfo */ 8 | #endif 9 | 10 | #ifdef WIN32 11 | #include 12 | #include 13 | #else 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include /* close */ 19 | #include /* gethostbyname */ 20 | #define SOCKET_ERROR -1 21 | #ifndef INVALID_SOCKET 22 | #define INVALID_SOCKET -1 23 | #endif 24 | #ifndef closesocket 25 | #define closesocket(s) close(s) 26 | #endif 27 | typedef int SOCKET; 28 | typedef struct sockaddr_in SOCKADDR_IN; 29 | typedef struct sockaddr SOCKADDR; 30 | typedef struct in_addr IN_ADDR; 31 | #endif 32 | 33 | class HttpClient 34 | { 35 | public: 36 | HttpClient(); 37 | ~HttpClient(); 38 | 39 | bool SendRequest(const std::string& method, const std::string& host, int port, const std::string& path, const std::string& body=""); 40 | bool WaitingResponse(unsigned int millisecond=100); 41 | std::string ReceiveResponse(); 42 | std::string GetStatus(); 43 | std::string GetBody(); 44 | 45 | private: 46 | SOCKET m_Sock; 47 | std::string m_Page; 48 | void openSocket(std::string host, int port); 49 | }; 50 | 51 | #endif // HTTPCLIENT_H 52 | -------------------------------------------------------------------------------- /examples/example2.cpp: -------------------------------------------------------------------------------- 1 | /***********************************************************************************************/ 2 | /** **/ 3 | /** EXAMPLE 2 **/ 4 | /** **/ 5 | /** Web server with page controller **/ 6 | /** **/ 7 | /***********************************************************************************************/ 8 | 9 | #include 10 | #include 11 | #include "WebServer.h" 12 | #include "PageController.h" 13 | 14 | using namespace std; 15 | using namespace MongooseCpp; 16 | 17 | 18 | int main() 19 | { 20 | WebServer myServer; 21 | PageController myController("./", true); 22 | 23 | myServer.AddRoute("/examples/*", &myController); 24 | 25 | if(!myServer.Start()) 26 | { 27 | stError myError = myServer.GetLastError(); 28 | cout << "Unable to start WebServer : " << myError.Message << endl; 29 | return -1; 30 | } 31 | 32 | cout << "WebServer is started on port 8000" << endl; 33 | cout << "Try http://127.0.0.1:8000/examples/web" << endl; 34 | cout << "Press CTRL+C to stop." << endl; 35 | 36 | while(true) 37 | { 38 | myServer.Poll(); 39 | } 40 | 41 | myServer.Stop(); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/ApiController.cpp: -------------------------------------------------------------------------------- 1 | #include "ApiController.h" 2 | 3 | namespace MongooseCpp { 4 | 5 | ApiController::ApiController() 6 | { 7 | } 8 | 9 | ApiController::~ApiController() 10 | { 11 | } 12 | 13 | bool ApiController::Process(Request& request, Response& response) 14 | { 15 | switch(request.GetMethod()) 16 | { 17 | case Request::HttpMethod::mthGET : 18 | return Get(request, response); 19 | case Request::HttpMethod::mthPUT : 20 | return Put(request, response); 21 | case Request::HttpMethod::mthPOST : 22 | return Post(request, response); 23 | case Request::HttpMethod::mthDELETE : 24 | return Delete(request, response); 25 | default : 26 | response.SetStatut(405); 27 | return true; 28 | } 29 | } 30 | 31 | #ifndef _MSC_VER 32 | #pragma GCC diagnostic push 33 | #pragma GCC diagnostic ignored "-Wunused-parameter" //This is virtual functions 34 | #endif 35 | 36 | bool ApiController::Get(Request& request, Response& response) 37 | { 38 | response.SetStatut(405); 39 | return true; 40 | } 41 | 42 | bool ApiController::Put(Request& request, Response& response) 43 | { 44 | response.SetStatut(405); 45 | return true; 46 | } 47 | 48 | bool ApiController::Post(Request& request, Response& response) 49 | { 50 | response.SetStatut(405); 51 | return true; 52 | } 53 | 54 | bool ApiController::Delete(Request& request, Response& response) 55 | { 56 | response.SetStatut(405); 57 | return true; 58 | } 59 | 60 | #ifndef _MSC_VER 61 | #pragma GCC diagnostic pop 62 | #endif 63 | } 64 | -------------------------------------------------------------------------------- /dependency/UnitTest/UnitTest.cpp: -------------------------------------------------------------------------------- 1 | #include "UnitTest.h" 2 | 3 | 4 | using namespace std; 5 | 6 | UnitTest::UnitTest() : m_TestClassList() 7 | { 8 | } 9 | 10 | UnitTest::~UnitTest() 11 | { 12 | vector::iterator iTestClass = m_TestClassList.begin(); 13 | vector::iterator iEndTestClass = m_TestClassList.end(); 14 | 15 | while (iTestClass != iEndTestClass) 16 | { 17 | delete *iTestClass; 18 | ++iTestClass; 19 | } 20 | m_TestClassList.clear(); 21 | } 22 | 23 | bool UnitTest::run() 24 | { 25 | vector::iterator iTestClass = m_TestClassList.begin(); 26 | vector::iterator iEndTestClass = m_TestClassList.end(); 27 | bool ret = true; 28 | int runCount = 0; 29 | int errorCount = 0; 30 | 31 | while (iTestClass != iEndTestClass) 32 | { 33 | if(!(*iTestClass)->runTests(&runCount, &errorCount)) ret = false; 34 | ++iTestClass; 35 | } 36 | 37 | std::cout << endl; 38 | std::cout << termcolor::lightYellow << "TOTAL Runs : " << runCount; 39 | if(errorCount>0) 40 | std::cout << termcolor::lightRed << " Errors : " << errorCount; 41 | else 42 | std::cout << termcolor::lightGreen << " Errors : 0"; 43 | std::cout << endl << endl; 44 | 45 | std::cout << termcolor::white; 46 | std::cout.copyfmt(std::ios(nullptr)); 47 | 48 | return ret; 49 | } 50 | 51 | void UnitTest::addTestClass(ITestClass* testClass) 52 | { 53 | m_TestClassList.push_back(testClass); 54 | } 55 | 56 | 57 | void UnitTest::displayError(const string& message) 58 | { 59 | std::cout << termcolor::lightRed << "ERROR : " << message < 2 | #include "Response.h" 3 | 4 | using namespace std; 5 | 6 | namespace MongooseCpp { 7 | 8 | Response::Response() : m_Protocol("HTTP/1.1"), m_Statut(200), m_StatutMsg(), m_Headers(), m_Content() 9 | { 10 | } 11 | 12 | Response::~Response() 13 | { 14 | } 15 | 16 | void Response::SetStatut(int number) 17 | { 18 | m_Statut = number; 19 | //m_StatutMsg = mg_status_message(number); 20 | } 21 | 22 | void Response::SetHeader(string headerKey, string value) 23 | { 24 | m_Headers[headerKey] = value; 25 | } 26 | 27 | void Response::SetHeader(string key, int value) 28 | { 29 | ostringstream stream; 30 | 31 | stream << value; 32 | m_Headers[key] = stream.str(); 33 | } 34 | 35 | string Response::GetHeader(string headerKey) 36 | { 37 | if(m_Headers.find(headerKey) == m_Headers.end()) return ""; 38 | return m_Headers[headerKey]; 39 | } 40 | 41 | bool Response::HasHeader(string headerKey) 42 | { 43 | return (m_Headers.find(headerKey) != m_Headers.end()); 44 | } 45 | 46 | void Response::SetContent(string content) 47 | { 48 | m_Content = content; 49 | } 50 | 51 | string Response::GetData() 52 | { 53 | ostringstream stream; 54 | map::iterator it; 55 | 56 | 57 | stream << m_Protocol <<" "<< m_Statut; 58 | if(m_StatutMsg!="") stream <<" "<< m_StatutMsg; 59 | stream << "\r\n"; 60 | 61 | SetHeader("Content-Length", m_Content.size()); 62 | for(it=m_Headers.begin(); it!=m_Headers.end(); ++it) 63 | { 64 | stream << it->first <<": "<< it->second << "\r\n"; 65 | } 66 | stream << "\r\n"; 67 | stream << m_Content; 68 | 69 | return stream.str(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /test/TestTypeController.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "TypeController.h" 5 | #include "WebServer.h" 6 | #include "UnitTest/UnitTest.h" 7 | #include "HttpClient.h" 8 | 9 | 10 | class Book 11 | { 12 | public: 13 | Book() : Book(0, "", "", 0) {}; 14 | Book(int _ref, const std::string& _title, const std::string& _author, int _stock); 15 | int Ref; 16 | std::string Title; 17 | std::string Author; 18 | int Stock; 19 | }; 20 | 21 | class JsonController : public MongooseCpp::TypeController 22 | { 23 | public: 24 | JsonController(); 25 | ~JsonController(); 26 | 27 | bool GetList(MongooseCpp::Request& request, MongooseCpp::Response& response); 28 | bool Get(const unsigned int& Id, Book& book, MongooseCpp::Response& response); 29 | bool Post(const Book& book, MongooseCpp::Response& response); 30 | bool Put(const unsigned int& Id, const Book& book, MongooseCpp::Response& response); 31 | bool Delete(const unsigned int& Id, MongooseCpp::Response& response); 32 | 33 | std::string ToString(const Book& book); 34 | void ToObject(std::string value, Book& book); 35 | protected: 36 | unsigned int ToId(std::string value); 37 | private: 38 | std::vector m_Datas; 39 | }; 40 | 41 | class TestTypeController : public TestClass 42 | { 43 | public: 44 | TestTypeController(); 45 | ~TestTypeController(); 46 | 47 | bool GetList(); 48 | bool GetObject(); 49 | bool CreateObject(); 50 | bool ModifyObject(); 51 | bool DeleteObject(); 52 | 53 | private: 54 | MongooseCpp::WebServer server; 55 | HttpClient client; 56 | JsonController myJsonCtrl; 57 | }; 58 | -------------------------------------------------------------------------------- /src/Request.h: -------------------------------------------------------------------------------- 1 | #ifndef REQUEST_H 2 | #define REQUEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "mongoose/mongoose.h" 8 | 9 | namespace MongooseCpp { 10 | 11 | class Request 12 | { 13 | public: 14 | enum HttpMethod {mthGET, mthPOST, mthPUT, mthDELETE, mthPATCH, mthHEAD, mthUNKNOWN}; 15 | Request(); 16 | Request(struct mg_connection* conn, struct http_message* httpMsg); 17 | Request(const Request& resquest); 18 | Request& operator=(Request& other); 19 | ~Request(); 20 | 21 | std::string GetUri(); 22 | std::string GetBody(); 23 | std::string GetUriPart(unsigned int part); 24 | HttpMethod GetMethod(); 25 | bool ExistsParameter(const std::string& key); 26 | std::string GetParameter(const std::string& key); 27 | std::string GetQueryParameter(const std::string& key); 28 | std::string GetBodyParameter(const std::string& key); 29 | template T GetParameter(const std::string& key) 30 | { 31 | std::string value = GetParameter(key); 32 | std::istringstream iss(value); 33 | T val; 34 | iss >> val; 35 | return val; 36 | } 37 | void SetParameter(const std::string& key, const std::string& value); 38 | struct mg_connection* GetMgConnection() const; 39 | struct http_message* GetHttpMsg() const; 40 | 41 | protected: 42 | 43 | private: 44 | void Initialization(struct mg_connection* conn, struct http_message* httpMsg); 45 | struct http_message* m_HttpMsg; 46 | struct mg_connection* m_Conn; 47 | HttpMethod m_Method; 48 | std::string m_Uri; 49 | std::vector m_UriPart; 50 | std::map m_Parameters; 51 | }; 52 | 53 | } 54 | 55 | #endif // REQUEST_H 56 | -------------------------------------------------------------------------------- /src/WebServer.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSERVER_H 2 | #define WEBSERVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "mongoose/mongoose.h" 8 | #include "IWebController.h" 9 | 10 | 11 | namespace MongooseCpp { 12 | 13 | enum ParamType { Fixed, Required, Optional}; 14 | 15 | struct stParam 16 | { 17 | stParam() : name(), paramType(Fixed) {}; 18 | std::string name; 19 | ParamType paramType; 20 | }; 21 | 22 | struct stController 23 | { 24 | stController() : controller(nullptr), paramPartQuery() {}; 25 | IWebController* controller; 26 | std::vector paramPartQuery; 27 | }; 28 | 29 | struct stError 30 | { 31 | stError() : Code(), Message() {}; 32 | std::string Code; 33 | std::string Message; 34 | }; 35 | 36 | class WebServer 37 | { 38 | public: 39 | WebServer(); 40 | WebServer(int TCPPort); 41 | ~WebServer(); 42 | 43 | bool Start(); 44 | void Stop(); 45 | void Poll(unsigned int milliTimeout=100); 46 | bool AddRoute(const std::string& route, IWebController* controller); 47 | stError GetLastError(); 48 | static void StaticEventHandler(struct mg_connection* nc, int ev, void* ev_data, void* user_data); 49 | void EventHandler(struct mg_connection* nc, int ev, struct http_message* msg); 50 | 51 | protected: 52 | 53 | private: 54 | bool matchUrl(Request& request, const struct stController& controller); 55 | 56 | enum ParseStep { FixedUri, Parameter, Next}; 57 | void setLastError(std::string code, std::string msg); 58 | 59 | bool m_isStarted; 60 | stError m_LastError; 61 | int m_TCPPort; 62 | std::vector m_Controllers; 63 | 64 | struct mg_mgr m_MgManager; 65 | struct mg_connection *m_MgConnection; 66 | }; 67 | 68 | } 69 | #endif // WEBSERVER_H 70 | -------------------------------------------------------------------------------- /examples/example1.cpp: -------------------------------------------------------------------------------- 1 | /***********************************************************************************************/ 2 | /** **/ 3 | /** EXAMPLE 1 **/ 4 | /** **/ 5 | /** Simple web server **/ 6 | /** **/ 7 | /***********************************************************************************************/ 8 | 9 | #include 10 | #include 11 | #include "WebServer.h" 12 | #include "IWebController.h" 13 | 14 | using namespace std; 15 | using namespace MongooseCpp; 16 | 17 | class HelloController : public IWebController 18 | { 19 | bool Process(MongooseCpp::Request& request, MongooseCpp::Response& response) 20 | { 21 | ostringstream oss; 22 | string name; 23 | 24 | oss << "" << endl; 25 | name = request.GetQueryParameter("name"); 26 | if(name!="") 27 | oss << "Hello " << request.GetQueryParameter("name") << endl; 28 | else 29 | oss << "Hello, what's your name ?" << endl; 30 | oss << "" << endl; 31 | 32 | response.SetContent(oss.str()); 33 | return true; 34 | }; 35 | }; 36 | 37 | int main() 38 | { 39 | WebServer myServer; 40 | HelloController myController; 41 | 42 | myServer.AddRoute("/hello", &myController); 43 | 44 | if(!myServer.Start()) 45 | { 46 | stError myError = myServer.GetLastError(); 47 | cout << "Unable to start WebServer : " << myError.Message << endl; 48 | return -1; 49 | } 50 | 51 | cout << "WebServer is started on port 8000" << endl; 52 | cout << "Try http://127.0.0.1:8000/hello or http://127.0.0.1:8000/hello?name=John" << endl; 53 | cout << "Press CTRL+C to stop." << endl; 54 | 55 | while(true) 56 | { 57 | myServer.Poll(); 58 | } 59 | 60 | myServer.Stop(); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /test/TestWebServer.cpp: -------------------------------------------------------------------------------- 1 | #include "TestWebServer.h" 2 | 3 | using namespace std; 4 | 5 | EmptyController::EmptyController() {} 6 | EmptyController::~EmptyController() {} 7 | 8 | #ifndef _MSC_VER 9 | #pragma GCC diagnostic push 10 | #pragma GCC diagnostic ignored "-Wunused-parameter" //This is virtual functions 11 | #endif 12 | 13 | bool EmptyController::Process(MongooseCpp::Request& request, MongooseCpp::Response& response) 14 | { 15 | response.SetStatut(405); 16 | return true; 17 | } 18 | 19 | #ifndef _MSC_VER 20 | #pragma GCC diagnostic pop 21 | #endif 22 | 23 | TestWebServer::TestWebServer() : TestClass("WebServer", this) 24 | { 25 | addTest("Incorrect star", &TestWebServer::IncorrectStar); 26 | addTest("Missing ] or }", &TestWebServer::MissingChar); 27 | addTest("OptionalAtEnd", &TestWebServer::OptionalAtEnd); 28 | addTest("Start and stop", &TestWebServer::StartAndStop); 29 | } 30 | 31 | TestWebServer::~TestWebServer() 32 | { 33 | } 34 | 35 | bool TestWebServer::IncorrectStar() 36 | { 37 | MongooseCpp::WebServer server; 38 | MongooseCpp::stError myErr; 39 | 40 | assert(false==server.AddRoute("/base/*/[id]", &emptyCtrl)); 41 | myErr = server.GetLastError(); 42 | assert("ADDROUTE3"==myErr.Code); 43 | 44 | assert(false==server.AddRoute("/base/mod*", &emptyCtrl)); 45 | myErr = server.GetLastError(); 46 | assert("ADDROUTE3"==myErr.Code); 47 | 48 | return true; 49 | } 50 | 51 | bool TestWebServer::MissingChar() 52 | { 53 | MongooseCpp::WebServer server; 54 | MongooseCpp::stError myErr; 55 | 56 | assert(false==server.AddRoute("/base/{controller/id", &emptyCtrl)); 57 | myErr = server.GetLastError(); 58 | assert("ADDROUTE1"==myErr.Code); 59 | 60 | assert(false==server.AddRoute("/base/[id", &emptyCtrl)); 61 | myErr = server.GetLastError(); 62 | assert("ADDROUTE2"==myErr.Code); 63 | 64 | return true; 65 | } 66 | 67 | bool TestWebServer::OptionalAtEnd() 68 | { 69 | MongooseCpp::WebServer server; 70 | MongooseCpp::stError myErr; 71 | 72 | assert(false==server.AddRoute("/base/[id]/{controller}", &emptyCtrl)); 73 | myErr = server.GetLastError(); 74 | assert("ADDROUTE4"==myErr.Code); 75 | 76 | assert(false==server.AddRoute("/base/[id]/web", &emptyCtrl)); 77 | myErr = server.GetLastError(); 78 | assert("ADDROUTE4"==myErr.Code); 79 | 80 | return true; 81 | } 82 | 83 | bool TestWebServer::StartAndStop() 84 | { 85 | MongooseCpp::WebServer server; 86 | 87 | server.Start(); 88 | server.Poll(100); 89 | server.Stop(); 90 | 91 | return true; 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/FragJage/MongooseCpp.svg?branch=master)](https://travis-ci.org/FragJage/MongooseCpp) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/yr84g6ic47jix751?svg=true)](https://ci.appveyor.com/project/FragJage/MongooseCpp) 3 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/cb3f187d93bb4687a14e9ef2b6250838)](https://www.codacy.com/app/FragJage/MongooseCpp?utm_source=github.com&utm_medium=referral&utm_content=FragJage/MongooseCpp&utm_campaign=Badge_Grade) 4 | 5 | MongooseCpp 6 | =========== 7 | Mongoose c++ embeded web server cross-platform. 8 | 9 | Introduction 10 | ============ 11 | Embedded web server write in c++ and based on Mongoose. Build on Travis and Appveyor factories. A good solution to add an user interface for your programs. 12 | 13 | Features 14 | ======== 15 | - Web server with controller and configurable route. 16 | - Page controller to display statics pages. 17 | - Api controller to manage GET, POST, PUT and DELETE methods. 18 | - Type controler to manage a web api with an object. 19 | - Interface to create any controllers. 20 | 21 | How to use 22 | ========== 23 | Add src and dependency/mongoose folders into your project and see examples folder. 24 | 25 | To build, add define MG_ENABLE_CALLBACK_USERDATA. You can see CMakeLists.txt, Code::Blocks or MSVC projects in builds folder. 26 | 27 | For example : 28 | 29 | int main() 30 | { 31 | WebServer myServer; 32 | HelloController helloController; 33 | PageController pageController("./", true); 34 | JsonController jsonController; 35 | MultiController multiController; 36 | 37 | myServer.AddRoute("/hello", &helloController); 38 | myServer.AddRoute("/examples/*", &pageController); 39 | myServer.AddRoute("/api/v1/books/[Id]", &jsonController); 40 | myServer.AddRoute("/multi/{controller}/[Id]", &multiController); 41 | 42 | myServer.Start(); 43 | while(true) 44 | { 45 | myServer.Poll(); 46 | } 47 | myServer.Stop(); 48 | return 0; 49 | } 50 | 51 | 52 | Licence 53 | ======= 54 | MongooseCpp is free software : you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 55 | 56 | MongooseCpp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 57 | 58 | You should have received a copy of the GNU General Public License along with MongooseCpp. If not, see http://www.gnu.org/licenses/. 59 | -------------------------------------------------------------------------------- /test/TestPageController.cpp: -------------------------------------------------------------------------------- 1 | #include "TestPageController.h" 2 | 3 | using namespace std; 4 | 5 | TestPageController::TestPageController() : TestClass("PageController", this) 6 | { 7 | addTest("WithDirListing", &TestPageController::WithDirListing); 8 | addTest("WithoutDirListing", &TestPageController::WithoutDirListing); 9 | addTest("SimplePage", &TestPageController::SimplePage); 10 | addTest("Error 404", &TestPageController::Error404); 11 | } 12 | 13 | TestPageController::~TestPageController() 14 | { 15 | } 16 | 17 | bool TestPageController::WithDirListing() 18 | { 19 | string result; 20 | MongooseCpp::PageController pageCtrl("./test", true); 21 | MongooseCpp::WebServer server(8001); 22 | HttpClient client; 23 | 24 | 25 | server.AddRoute("/pages/*", &pageCtrl); 26 | server.Start(); 27 | 28 | client.SendRequest("GET", "127.0.0.1", 8001, "/pages/"); 29 | assert(true==HttpHelper::WaitResponse(server, client)); 30 | result = client.GetStatus(); 31 | assert("200"==result); 32 | 33 | server.Stop(); 34 | return true; 35 | } 36 | 37 | bool TestPageController::WithoutDirListing() 38 | { 39 | string result; 40 | MongooseCpp::PageController pageCtrl("./test", false); 41 | MongooseCpp::WebServer server(8001); 42 | HttpClient client; 43 | 44 | 45 | server.AddRoute("/pages/*", &pageCtrl); 46 | server.Start(); 47 | 48 | client.SendRequest("GET", "127.0.0.1", 8001, "/pages/"); 49 | assert(true==HttpHelper::WaitResponse(server, client)); 50 | result = client.GetStatus(); 51 | assert("403"==result); 52 | 53 | server.Stop(); 54 | return true; 55 | } 56 | 57 | bool TestPageController::SimplePage() 58 | { 59 | string result; 60 | MongooseCpp::PageController pageCtrl("./test", false); 61 | MongooseCpp::WebServer server(8001); 62 | HttpClient client; 63 | 64 | 65 | server.AddRoute("/pages/*", &pageCtrl); 66 | server.Start(); 67 | 68 | client.SendRequest("GET", "127.0.0.1", 8001, "/pages/test.html"); 69 | assert(true==HttpHelper::WaitResponse(server, client)); 70 | result = client.GetStatus(); 71 | assert("200"==result); 72 | result = client.GetBody(); 73 | assert("

Test

"==result); 74 | 75 | server.Stop(); 76 | return true; 77 | } 78 | 79 | bool TestPageController::Error404() 80 | { 81 | string result; 82 | MongooseCpp::PageController pageCtrl("./test", false); 83 | MongooseCpp::WebServer server(8001); 84 | HttpClient client; 85 | 86 | 87 | server.AddRoute("/pages/*", &pageCtrl); 88 | server.Start(); 89 | 90 | client.SendRequest("GET", "127.0.0.1", 8001, "/pages/test404.html"); 91 | assert(true==HttpHelper::WaitResponse(server, client)); 92 | result = client.GetStatus(); 93 | assert("404"==result); 94 | 95 | server.Stop(); 96 | return true; 97 | } 98 | -------------------------------------------------------------------------------- /src/Request.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Request.h" 3 | 4 | using namespace std; 5 | namespace MongooseCpp { 6 | 7 | Request::Request() : m_HttpMsg(nullptr), m_Conn(nullptr), m_Method(Request::mthUNKNOWN), m_Uri(), m_UriPart(), m_Parameters() 8 | { 9 | } 10 | 11 | Request::Request(struct mg_connection* conn, struct http_message* httpMsg) : Request() 12 | { 13 | Initialization(conn, httpMsg); 14 | } 15 | 16 | Request::Request(const Request& resquest) : Request() 17 | { 18 | Initialization(resquest.GetMgConnection(), resquest.GetHttpMsg()); 19 | } 20 | 21 | Request& Request::operator=(Request& resquest) 22 | { 23 | Initialization(resquest.GetMgConnection(), resquest.GetHttpMsg()); 24 | return *this; 25 | } 26 | 27 | void Request::Initialization(struct mg_connection* conn, struct http_message* httpMsg) 28 | { 29 | string method(httpMsg->method.p, httpMsg->method.len); 30 | 31 | if(method=="GET") 32 | m_Method = Request::mthGET; 33 | else if(method=="POST") 34 | m_Method = Request::mthPOST; 35 | else if(method=="PUT") 36 | m_Method = Request::mthPUT; 37 | else if(method=="DELETE") 38 | m_Method = Request::mthDELETE; 39 | else if(method=="PATCH") 40 | m_Method = Request::mthPATCH; 41 | else if(method=="HEAD") 42 | m_Method = Request::mthHEAD; 43 | else m_Method = Request::mthUNKNOWN; 44 | 45 | m_Uri = string(httpMsg->uri.p, httpMsg->uri.len); 46 | istringstream iss(m_Uri); 47 | string token; 48 | 49 | while (getline(iss, token, '/')) 50 | { 51 | m_UriPart.push_back(token); 52 | } 53 | 54 | m_HttpMsg = httpMsg; 55 | m_Conn = conn; 56 | } 57 | 58 | Request::~Request() 59 | { 60 | } 61 | 62 | Request::HttpMethod Request::GetMethod() 63 | { 64 | return m_Method; 65 | } 66 | 67 | string Request::GetUri() 68 | { 69 | return m_Uri; 70 | } 71 | 72 | string Request::GetBody() 73 | { 74 | return string(m_HttpMsg->body.p, m_HttpMsg->body.len); 75 | } 76 | 77 | string Request::GetUriPart(unsigned int part) 78 | { 79 | if(part>=m_UriPart.size()) return ""; 80 | return m_UriPart[part]; 81 | } 82 | 83 | string Request::GetBodyParameter(const string& key) 84 | { 85 | char param[128]; 86 | int len; 87 | 88 | len = mg_get_http_var(&m_HttpMsg->body, key.c_str(), param, sizeof(param)); 89 | if(len<0) return ""; 90 | return string(param, len); 91 | } 92 | 93 | string Request::GetQueryParameter(const string& key) 94 | { 95 | char param[128]; 96 | int len; 97 | 98 | len = mg_get_http_var(&m_HttpMsg->query_string, key.c_str(), param, sizeof(param)); 99 | if(len<0) return ""; 100 | return string(param, len); 101 | } 102 | 103 | bool Request::ExistsParameter(const string& key) 104 | { 105 | return (m_Parameters.find(key) != m_Parameters.end()); 106 | } 107 | 108 | string Request::GetParameter(const string& key) 109 | { 110 | if(m_Parameters.find(key) == m_Parameters.end()) return ""; 111 | return m_Parameters[key]; 112 | } 113 | 114 | void Request::SetParameter(const string& key, const string& value) 115 | { 116 | m_Parameters[key] = value; 117 | } 118 | 119 | struct mg_connection* Request::GetMgConnection() const 120 | { 121 | return m_Conn; 122 | } 123 | 124 | struct http_message* Request::GetHttpMsg() const 125 | { 126 | return m_HttpMsg; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/TypeController.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPECONTROLLER_H 2 | #define TYPECONTROLLER_H 3 | 4 | #include "ApiController.h" 5 | 6 | namespace MongooseCpp { 7 | 8 | template 9 | class TypeController : public ApiController 10 | { 11 | public: 12 | TypeController() {}; 13 | virtual ~TypeController() {}; 14 | 15 | bool Process(Request& request, Response& response) 16 | { 17 | Tobject object; 18 | Tid id; 19 | 20 | switch(request.GetMethod()) 21 | { 22 | case Request::HttpMethod::mthGET : 23 | if(!request.ExistsParameter("Id")) 24 | { 25 | GetList(request, response); 26 | return true; 27 | } 28 | id = ToId(request.GetParameter("Id")); 29 | if(Get(id, object, response)) 30 | 31 | { 32 | response.SetContent(ToString(object)); 33 | } 34 | return true; 35 | case Request::HttpMethod::mthPUT : 36 | if(!request.ExistsParameter("Id")) 37 | { 38 | response.SetStatut(500); 39 | response.SetContent("Id parameter is required"); 40 | return true; 41 | } 42 | id = ToId(request.GetParameter("Id")); 43 | ToObject(request.GetBody(), object); 44 | if(Put(id, object, response)) 45 | { 46 | response.SetStatut(200); 47 | response.SetContent("OK"); 48 | } 49 | return true; 50 | case Request::HttpMethod::mthPOST : 51 | ToObject(request.GetBody(), object); 52 | Post(object, response); 53 | return true; 54 | case Request::HttpMethod::mthDELETE : 55 | if(!request.ExistsParameter("Id")) 56 | { 57 | response.SetStatut(500); 58 | response.SetContent("Id parameter is required"); 59 | return true; 60 | } 61 | id = ToId(request.GetParameter("Id")); 62 | if(Delete(id, response)) 63 | { 64 | response.SetStatut(200); 65 | response.SetContent("OK"); 66 | } 67 | return true; 68 | default: 69 | response.SetStatut(405); 70 | return true; 71 | } 72 | response.SetStatut(405); 73 | return true; 74 | } 75 | 76 | virtual bool GetList(Request& request, Response& response) = 0; 77 | virtual bool Get(const Tid& id, Tobject& object, Response& response) = 0; 78 | virtual bool Put(const Tid& id, const Tobject& object, Response& response) = 0; 79 | virtual bool Post(const Tobject& object, Response& response) = 0; 80 | virtual bool Delete(const Tid& id, Response& response) = 0; 81 | 82 | protected: 83 | virtual std::string ToString(const Tobject& object) = 0; 84 | virtual Tid ToId(std::string value) = 0; 85 | virtual void ToObject(std::string value, Tobject& object) = 0; 86 | }; 87 | 88 | } 89 | 90 | #endif // TYPECONTROLLER_H 91 | -------------------------------------------------------------------------------- /test/TestBaseController.cpp: -------------------------------------------------------------------------------- 1 | #include "TestBaseController.h" 2 | #include "HttpHelper.h" 3 | 4 | using namespace std; 5 | 6 | BaseController::BaseController() {} 7 | BaseController::~BaseController() {} 8 | 9 | bool BaseController::Process(MongooseCpp::Request& request, MongooseCpp::Response& response) 10 | { 11 | string controller = request.GetParameter("controller"); 12 | 13 | if(controller=="method") 14 | { 15 | switch(request.GetMethod()) 16 | { 17 | case MongooseCpp::Request::mthGET : 18 | response.SetContent("mthGET"); 19 | return true; 20 | case MongooseCpp::Request::mthPOST : 21 | response.SetContent("mthPOST"); 22 | return true; 23 | case MongooseCpp::Request::mthPATCH : 24 | response.SetContent("mthPATCH"); 25 | return true; 26 | case MongooseCpp::Request::mthHEAD : 27 | response.SetContent("mthHEAD"); 28 | return true; 29 | default: 30 | response.SetStatut(404); 31 | return true; 32 | } 33 | } 34 | else 35 | { 36 | string content = controller; 37 | if(request.ExistsParameter("id")) 38 | content +=",id="+request.GetParameter("id"); 39 | response.SetContent(content); 40 | return true; 41 | } 42 | } 43 | 44 | TestBaseController::TestBaseController() : TestClass("BaseController", this) 45 | { 46 | addTest("ReadHttpMethod", &TestBaseController::ReadHttpMethod); 47 | addTest("ReadParameters", &TestBaseController::ReadParameters); 48 | addTest("Error 404", &TestBaseController::Error404); 49 | 50 | server.AddRoute("/base/{controller}/[id]", &myBaseCtrl); 51 | server.Start(); 52 | } 53 | 54 | TestBaseController::~TestBaseController() 55 | { 56 | server.Stop(); 57 | } 58 | 59 | bool TestBaseController::ReadHttpMethod() 60 | { 61 | string result; 62 | 63 | client.SendRequest("GET", "127.0.0.1", 8000, "/base/method"); 64 | assert(true==HttpHelper::WaitResponse(server, client)); 65 | result = client.GetBody(); 66 | assert("mthGET"==result); 67 | 68 | client.SendRequest("POST", "127.0.0.1", 8000, "/base/method", "BODY"); 69 | assert(true==HttpHelper::WaitResponse(server, client)); 70 | result = client.GetBody(); 71 | assert("mthPOST"==result); 72 | 73 | client.SendRequest("PATCH", "127.0.0.1", 8000, "/base/method", "BODY"); 74 | assert(true==HttpHelper::WaitResponse(server, client)); 75 | result = client.GetBody(); 76 | assert("mthPATCH"==result); 77 | 78 | client.SendRequest("HEAD", "127.0.0.1", 8000, "/base/method"); 79 | assert(true==HttpHelper::WaitResponse(server, client)); 80 | result = client.GetBody(); 81 | assert("mthHEAD"==result); 82 | 83 | return true; 84 | } 85 | 86 | bool TestBaseController::ReadParameters() 87 | { 88 | string result; 89 | 90 | client.SendRequest("GET", "127.0.0.1", 8000, "/base/modules"); 91 | assert(true==HttpHelper::WaitResponse(server, client)); 92 | result = client.GetBody(); 93 | assert("modules"==result); 94 | 95 | client.SendRequest("GET", "127.0.0.1", 8000, "/base/devices/5"); 96 | assert(true==HttpHelper::WaitResponse(server, client)); 97 | result = client.GetBody(); 98 | assert("devices,id=5"==result); 99 | 100 | return true; 101 | } 102 | 103 | bool TestBaseController::Error404() 104 | { 105 | string result; 106 | 107 | client.SendRequest("GET", "127.0.0.1", 8000, "/baseerr"); 108 | assert(true==HttpHelper::WaitResponse(server, client)); 109 | result = client.GetStatus(); 110 | assert("404"==result); 111 | 112 | return true; 113 | } 114 | -------------------------------------------------------------------------------- /dependency/UnitTest/UnitTest.h: -------------------------------------------------------------------------------- 1 | #ifndef _FRAGUNITTEST_H 2 | #define _FRAGUNITTEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include"UnitTest/termcolor.h" 9 | 10 | class ITestClass 11 | { 12 | public: 13 | virtual ~ITestClass() {}; 14 | virtual bool runTests(int* prunCount, int* perrorCount) = 0; 15 | virtual std::string getClassName() = 0; 16 | }; 17 | 18 | template 19 | class TestClass : public ITestClass 20 | { 21 | public: 22 | typedef bool (T::*TestMethod)(); 23 | TestClass(std::string className, T* testClass) 24 | { 25 | m_ClassName = className; 26 | m_TestClass = testClass; 27 | }; 28 | 29 | ~TestClass() 30 | { 31 | }; 32 | 33 | void addTest(std::string title, TestMethod testMethod) 34 | { 35 | m_NameList.push_back(title); 36 | m_MethodList.push_back(testMethod); 37 | }; 38 | 39 | bool runTests(int* prunCount, int* perrorCount) 40 | { 41 | TestMethod testMethod; 42 | int i, len , padLenght; 43 | int imax = m_NameList.size(); 44 | int runCount = 0; 45 | int errorCount = 0; 46 | bool result = true; 47 | std::ios::fmtflags oldFmt(std::cout.flags()); 48 | 49 | 50 | padLenght = m_ClassName.length(); 51 | for(i=0; i padLenght) padLenght = len; 55 | } 56 | padLenght += 5; 57 | 58 | std::cout << termcolor::lightYellow << "- " << m_ClassName << " " << std::setfill('-') << std::setw(padLenght+10 - m_ClassName.length()) << "-" << std::endl; 59 | for(i=0; i*testMethod)()) 67 | std::cout << termcolor::lightGreen << "OK"; 68 | else 69 | { 70 | result = false; 71 | errorCount++; 72 | std::cout << termcolor::lightRed << "KO"; 73 | } 74 | } 75 | catch(const std::exception & e) 76 | { 77 | result = false; 78 | errorCount++; 79 | std::cout << termcolor::lightRed << "FAILED" << std::endl; 80 | std::cout << termcolor::white << "EXCEPTION" << std::endl << e.what(); 81 | } 82 | std::cout << std::endl; 83 | } 84 | 85 | std::cout << std::endl; 86 | std::cout << termcolor::lightYellow << " Runs : " << runCount; 87 | if(errorCount>0) 88 | std::cout << termcolor::lightRed << " Errors : " << errorCount; 89 | else 90 | std::cout << termcolor::lightGreen << " Errors : 0"; 91 | std::cout << std::endl << std::endl; 92 | 93 | std::cout.flags(oldFmt); 94 | 95 | *prunCount += runCount; 96 | *perrorCount += errorCount; 97 | 98 | return result; 99 | }; 100 | 101 | std::string getClassName() 102 | { 103 | return m_ClassName; 104 | } 105 | 106 | private: 107 | std::string m_ClassName; 108 | std::vector m_NameList; 109 | std::vector m_MethodList; 110 | T* m_TestClass; 111 | }; 112 | 113 | class UnitTest 114 | { 115 | public: 116 | UnitTest(); 117 | ~UnitTest(); 118 | bool run(); 119 | void addTestClass(ITestClass* testClass); 120 | void displayError(const std::string& message); 121 | 122 | private: 123 | std::vector m_TestClassList; 124 | }; 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /test/TestApiController.cpp: -------------------------------------------------------------------------------- 1 | #include "TestApiController.h" 2 | #include "WebServer.h" 3 | #include "HttpClient.h" 4 | #include "HttpHelper.h" 5 | 6 | using namespace std; 7 | 8 | ApiController::ApiController() {} 9 | ApiController::~ApiController() {} 10 | 11 | #ifndef _MSC_VER 12 | #pragma GCC diagnostic push 13 | #pragma GCC diagnostic ignored "-Wunused-parameter" //This is virtual functions 14 | #endif 15 | 16 | bool ApiController::Get(MongooseCpp::Request& request, MongooseCpp::Response& response) 17 | { 18 | response.SetContent("mthGET"); 19 | return true; 20 | } 21 | 22 | bool ApiController::Post(MongooseCpp::Request& request, MongooseCpp::Response& response) 23 | { 24 | response.SetContent("mthPOST"); 25 | return true; 26 | } 27 | 28 | bool ApiController::Put(MongooseCpp::Request& request, MongooseCpp::Response& response) 29 | { 30 | response.SetContent("mthPUT"); 31 | return true; 32 | } 33 | 34 | bool ApiController::Delete(MongooseCpp::Request& request, MongooseCpp::Response& response) 35 | { 36 | response.SetContent("mthDELETE"); 37 | return true; 38 | } 39 | 40 | #ifndef _MSC_VER 41 | #pragma GCC diagnostic pop 42 | #endif 43 | 44 | TestApiController::TestApiController() : TestClass("TestApiController", this), server(8002) 45 | { 46 | addTest("GETmethod", &TestApiController::GETmethod); 47 | addTest("POSTmethod", &TestApiController::POSTmethod); 48 | addTest("PUTmethod", &TestApiController::PUTmethod); 49 | addTest("DELETEmethod", &TestApiController::DELETEmethod); 50 | addTest("UnknownMethod", &TestApiController::UnknownMethod); 51 | 52 | server.AddRoute("/api/v1", &myApiCtrl); 53 | server.AddRoute("/api/empty", &myEmptyCtrl); 54 | server.Start(); 55 | } 56 | 57 | TestApiController::~TestApiController() 58 | { 59 | server.Stop(); 60 | } 61 | 62 | bool TestApiController::GETmethod() 63 | { 64 | string result; 65 | 66 | client.SendRequest("GET", "127.0.0.1", 8002, "/api/v1"); 67 | assert(true==HttpHelper::WaitResponse(server, client)); 68 | result = client.GetBody(); 69 | assert("mthGET"==result); 70 | 71 | client.SendRequest("GET", "127.0.0.1", 8002, "/api/empty"); 72 | assert(true==HttpHelper::WaitResponse(server, client)); 73 | result = client.GetStatus(); 74 | assert("405"==result); 75 | 76 | return true; 77 | } 78 | 79 | bool TestApiController::POSTmethod() 80 | { 81 | string result; 82 | 83 | client.SendRequest("POST", "127.0.0.1", 8002, "/api/v1", "BODY"); 84 | assert(true==HttpHelper::WaitResponse(server, client)); 85 | result = client.GetBody(); 86 | assert("mthPOST"==result); 87 | 88 | client.SendRequest("POST", "127.0.0.1", 8002, "/api/empty", "BODY"); 89 | assert(true==HttpHelper::WaitResponse(server, client)); 90 | result = client.GetStatus(); 91 | assert("405"==result); 92 | 93 | return true; 94 | } 95 | 96 | bool TestApiController::PUTmethod() 97 | { 98 | string result; 99 | 100 | client.SendRequest("PUT", "127.0.0.1", 8002, "/api/v1", "BODY"); 101 | assert(true==HttpHelper::WaitResponse(server, client)); 102 | result = client.GetBody(); 103 | assert("mthPUT"==result); 104 | 105 | client.SendRequest("PUT", "127.0.0.1", 8002, "/api/empty", "BODY"); 106 | assert(true==HttpHelper::WaitResponse(server, client)); 107 | result = client.GetStatus(); 108 | assert("405"==result); 109 | 110 | return true; 111 | } 112 | 113 | bool TestApiController::DELETEmethod() 114 | { 115 | string result; 116 | 117 | client.SendRequest("DELETE", "127.0.0.1", 8002, "/api/v1", "BODY"); 118 | assert(true==HttpHelper::WaitResponse(server, client)); 119 | result = client.GetBody(); 120 | assert("mthDELETE"==result); 121 | 122 | client.SendRequest("DELETE", "127.0.0.1", 8002, "/api/empty", "BODY"); 123 | assert(true==HttpHelper::WaitResponse(server, client)); 124 | result = client.GetStatus(); 125 | assert("405"==result); 126 | 127 | return true; 128 | } 129 | 130 | bool TestApiController::UnknownMethod() 131 | { 132 | string result; 133 | 134 | client.SendRequest("DELZZZ", "127.0.0.1", 8002, "/api/v1", "BODY"); 135 | assert(true==HttpHelper::WaitResponse(server, client)); 136 | result = client.GetStatus(); 137 | assert("405"==result); 138 | 139 | client.SendRequest("DELZZZ", "127.0.0.1", 8002, "/api/empty", "BODY"); 140 | assert(true==HttpHelper::WaitResponse(server, client)); 141 | result = client.GetStatus(); 142 | assert("405"==result); 143 | 144 | return true; 145 | } 146 | -------------------------------------------------------------------------------- /test/HttpClient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "HttpClient.h" 5 | 6 | #define BUFLEN 256 7 | 8 | using namespace std; 9 | 10 | HttpClient::HttpClient() : m_Sock(), m_Page() 11 | { 12 | #if defined(WIN32) 13 | WSADATA WSAData; 14 | if(WSAStartup(MAKEWORD(2,2), &WSAData)) 15 | { 16 | cout <<"Error, WSAStartup failed." << endl; 17 | exit(-1); 18 | } 19 | #endif 20 | } 21 | 22 | HttpClient::~HttpClient() 23 | { 24 | #if defined(WIN32) 25 | WSACleanup(); 26 | #endif 27 | } 28 | 29 | void HttpClient::openSocket(string host, int port) 30 | { 31 | SOCKADDR_IN sin; 32 | int status; 33 | struct addrinfo hints, *res; 34 | 35 | memset(&hints, 0, sizeof(hints)); 36 | if ((status = getaddrinfo(host.c_str(), nullptr, &hints, &res)) != 0) 37 | { 38 | cout << "Error " << status << " : " << gai_strerror(status) << endl; 39 | exit(-1); 40 | } 41 | 42 | if((m_Sock = socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET) 43 | { 44 | cout << "Error " << errno << " : Unable to create the socket" << endl; 45 | exit(-1); 46 | } 47 | 48 | auto sockaddr_ipv4 = reinterpret_cast(res->ai_addr); 49 | sin.sin_addr = sockaddr_ipv4->sin_addr; 50 | sin.sin_family = AF_INET; 51 | sin.sin_port = htons(port); 52 | memset(&sin.sin_zero, 0, sizeof(sin.sin_zero)); 53 | freeaddrinfo(res); 54 | 55 | if(connect(m_Sock, (SOCKADDR *)&sin, sizeof(sin)) == SOCKET_ERROR) 56 | { 57 | cout << "Error, socket connection failed" << endl; 58 | exit(-1); 59 | } 60 | } 61 | 62 | bool HttpClient::SendRequest(const string& method, const string& host, int port, const string& path, const string& body) 63 | { 64 | openSocket(host, port); 65 | 66 | ostringstream request; 67 | request << method << " " << path << " HTTP/1.1\r\n"; 68 | request << "Host: " << host << ":" << port << "\r\n"; 69 | 70 | if(body=="") 71 | { 72 | request << "\r\n"; 73 | } 74 | else 75 | { 76 | request << "Content-Type: application/x-www-form-urlencoded\r\n"; 77 | request << "Content-Length: " << body.size() << "\r\n"; 78 | request << "\r\n"; 79 | request << body; 80 | request << "\r\n"; 81 | } 82 | 83 | string stringBuffer = request.str(); 84 | char* charBuffer = new char [stringBuffer.size()+1]; 85 | unsigned int sendMax = stringBuffer.size(); 86 | unsigned int sendSum = 0; 87 | int sendByte = -1; 88 | 89 | #ifdef _MSC_VER 90 | strcpy_s(charBuffer, stringBuffer.size()+1, stringBuffer.c_str()); 91 | #else 92 | strcpy(charBuffer, stringBuffer.c_str()); 93 | #endif 94 | 95 | while(sendSum < sendMax) 96 | { 97 | sendByte = send(m_Sock, charBuffer+sendSum, sendMax-sendSum, 0); 98 | sendSum+=sendByte; 99 | if(sendByte<1) break; 100 | } 101 | 102 | delete[] charBuffer; 103 | return (sendByte>0); 104 | } 105 | 106 | bool HttpClient::WaitingResponse(unsigned int milliTimeout) 107 | { 108 | struct timeval timeout; 109 | timeout.tv_sec = 0; 110 | timeout.tv_usec = milliTimeout; 111 | 112 | fd_set readfs; 113 | FD_ZERO(&readfs); 114 | FD_SET(m_Sock,&readfs); 115 | int nb = select(m_Sock+1, &readfs, nullptr, nullptr, &timeout); 116 | 117 | return (nb>0); 118 | } 119 | 120 | string HttpClient::ReceiveResponse() 121 | { 122 | char recvBuffer[BUFLEN]; 123 | string stringBuffer = ""; 124 | 125 | m_Page = ""; 126 | while(WaitingResponse(100)) 127 | { 128 | int recvByte = recv(m_Sock, recvBuffer, BUFLEN-1, 0); 129 | if(recvByte>0) 130 | { 131 | recvBuffer[recvByte] = '\0'; 132 | stringBuffer.append(recvBuffer); 133 | } 134 | if(recvByte==0) break; 135 | } 136 | 137 | closesocket(m_Sock); 138 | m_Page = stringBuffer; 139 | return stringBuffer; 140 | } 141 | 142 | string HttpClient::GetStatus() 143 | { 144 | if(m_Page.substr(0, 4)!="HTTP") return ""; 145 | 146 | return m_Page.substr(m_Page.find(" ")+1, 3); 147 | } 148 | 149 | string HttpClient::GetBody() 150 | { 151 | size_t bodyPos = m_Page.find("\r\n\r\n"); 152 | if(bodyPos == string::npos) return ""; 153 | 154 | size_t lenPos = m_Page.substr(0, bodyPos).find("Content-Length:"); 155 | if(lenPos == string::npos) return ""; 156 | 157 | size_t lenEnd = m_Page.find("\r\n", lenPos+15); 158 | if(lenEnd == string::npos) return ""; 159 | 160 | int len; 161 | istringstream iss(m_Page.substr(lenPos+15, lenEnd)); 162 | iss >> len; 163 | 164 | return m_Page.substr(m_Page.find("\r\n\r\n")+4, len); 165 | } 166 | -------------------------------------------------------------------------------- /src/WebServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "WebServer.h" 3 | 4 | using namespace std; 5 | 6 | #ifdef __MINGW32__ 7 | static std::string to_string(int data) 8 | { 9 | std::stringstream s; 10 | s<(user_data); 35 | if(server==nullptr) return; 36 | 37 | struct http_message* msg = (struct http_message*) ev_data; 38 | if(msg==nullptr) return; 39 | 40 | server->EventHandler(nc, ev, msg); 41 | } 42 | 43 | void WebServer::EventHandler(struct mg_connection* nc, int ev, struct http_message* msg) 44 | { 45 | if(ev != MG_EV_HTTP_REQUEST) return; 46 | 47 | vector::const_iterator it; 48 | bool match = false; 49 | Request request(nc, msg); 50 | Response response; 51 | 52 | response.SetHeader("Server", "webServer/1.0.0"); 53 | //stream << "timeout=" << m_timeOut << ", max=" << m_maxConn; 54 | //response->SetHeader("Keep-Alive", stream.str()); 55 | 56 | for(it=m_Controllers.begin(); it!=m_Controllers.end(); ++it) 57 | { 58 | if(matchUrl(request, *it)) 59 | { 60 | if(it->controller->Process(request, response)) 61 | { 62 | string data = response.GetData(); 63 | if(data!="") mg_send(nc, data.c_str(), data.size()); 64 | match = true; 65 | break; 66 | } 67 | } 68 | } 69 | 70 | if(!match) 71 | mg_http_send_error(nc, 404, ""); 72 | } 73 | 74 | bool WebServer::Start() 75 | { 76 | string TCPPort = to_string(m_TCPPort); 77 | 78 | if(m_isStarted) Stop(); 79 | mg_mgr_init(&m_MgManager, nullptr); 80 | m_MgConnection = mg_bind(&m_MgManager, TCPPort.c_str(), WebServer::StaticEventHandler, this); 81 | if (m_MgConnection == nullptr) 82 | { 83 | setLastError("START1", "Failed to create listener"); 84 | return false; 85 | } 86 | mg_set_protocol_http_websocket(m_MgConnection); 87 | 88 | m_isStarted = true; 89 | return true; 90 | } 91 | 92 | void WebServer::Stop() 93 | { 94 | mg_mgr_free(&m_MgManager); 95 | m_isStarted = false; 96 | } 97 | 98 | void WebServer::Poll(unsigned int milliTimeout) 99 | { 100 | mg_mgr_poll(&m_MgManager, milliTimeout); 101 | } 102 | 103 | bool WebServer::AddRoute(const string& route, IWebController* controller) 104 | { // /web/* 105 | // /api/v1/modules/[id] 106 | // /api/v1/devices/[id]/* 107 | // /api/v1/{controller}/[id] 108 | 109 | bool atTheEnd = false; 110 | bool hasOptional = false; 111 | char firstChar; 112 | istringstream iss(route); 113 | string token; 114 | stParam myParam; 115 | stController myCtrl; 116 | 117 | while (getline(iss, token, '/')) 118 | { 119 | if(atTheEnd) 120 | { 121 | setLastError("ADDROUTE3", "Character '*' must be at the end of the route."); 122 | return false; 123 | } 124 | 125 | if((token.find('*')!=string::npos)&&(token!="*")) 126 | { 127 | setLastError("ADDROUTE3", "Character '*' must be alone at the end of the route."); 128 | return false; 129 | } 130 | 131 | if (token != "") 132 | firstChar = token.front(); 133 | else 134 | firstChar = '\0'; 135 | 136 | switch(firstChar) 137 | { 138 | case '{' : 139 | if(token.back()!='}') 140 | { 141 | setLastError("ADDROUTE1", "Missing character '}'."); 142 | return false; 143 | } 144 | if(hasOptional) 145 | { 146 | setLastError("ADDROUTE4", "Optional parameter must be all at the end of the route."); 147 | return false; 148 | } 149 | myParam.name = token.substr(1, token.size()-2); 150 | myParam.paramType = ParamType::Required; 151 | break; 152 | 153 | case '[' : 154 | if(token.back()!=']') 155 | { 156 | setLastError("ADDROUTE2", "Missing character ']'."); 157 | return false; 158 | } 159 | hasOptional = true; 160 | myParam.name = token.substr(1, token.size()-2); 161 | myParam.paramType = ParamType::Optional; 162 | break; 163 | 164 | case '*' : 165 | atTheEnd = true; 166 | myParam.name = "*"; 167 | myParam.paramType = ParamType::Optional; 168 | break; 169 | 170 | default : 171 | if(hasOptional) 172 | { 173 | setLastError("ADDROUTE4", "Optional parameter must be all at the end of the route."); 174 | return false; 175 | } 176 | myParam.name = token; 177 | myParam.paramType = ParamType::Fixed; 178 | } 179 | myCtrl.paramPartQuery.push_back(myParam); 180 | } 181 | 182 | myCtrl.controller = controller; 183 | m_Controllers.push_back(myCtrl); 184 | return true; 185 | } 186 | 187 | bool WebServer::matchUrl(Request& request, const struct stController& controller) 188 | { // /web/* 189 | // /api/v1/modules/[id] 190 | // /api/v1/devices/[id]/* 191 | 192 | int i=0; 193 | string uriPart; 194 | vector::const_iterator it = controller.paramPartQuery.begin(); 195 | vector::const_iterator itEnd = controller.paramPartQuery.end(); 196 | 197 | while(it != itEnd) 198 | { 199 | uriPart = request.GetUriPart(i); 200 | switch(it->paramType) 201 | { 202 | case ParamType::Fixed : 203 | if(uriPart!=it->name) return false; 204 | break; 205 | case ParamType::Required : 206 | if(uriPart=="") return false; 207 | request.SetParameter(it->name, uriPart); 208 | break; 209 | case ParamType::Optional : 210 | if(it->name=="*") return true; 211 | if(uriPart!="") request.SetParameter(it->name, uriPart); 212 | break; 213 | } 214 | ++it; 215 | ++i; 216 | } 217 | 218 | if(request.GetUriPart(i)!="") return false; 219 | return true; 220 | } 221 | 222 | stError WebServer::GetLastError() 223 | { 224 | return m_LastError; 225 | } 226 | 227 | void WebServer::setLastError(string code, string msg) 228 | { 229 | m_LastError.Code = code; 230 | m_LastError.Message = msg; 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /dependency/UnitTest/termcolor.h: -------------------------------------------------------------------------------- 1 | //! 2 | //! termcolor 3 | //! ~~~~~~~~~ 4 | //! 5 | //! termcolor is a header-only c++ library for printing colored messages 6 | //! to the terminal. Written just for fun with a help of the Force. 7 | //! 8 | //! :copyright: (c) 2013 by Igor Kalnitsky 9 | //! :license: BSD, see LICENSE for details 10 | //! 11 | 12 | #ifndef TERMCOLOR_HPP_ 13 | #define TERMCOLOR_HPP_ 14 | 15 | // the following snippet of code detects the current OS and 16 | // defines the appropriate macro that is used to wrap some 17 | // platform specific things 18 | #if defined(_WIN32) || defined(_WIN64) 19 | # define OS_WINDOWS 20 | #elif defined(__APPLE__) 21 | # define OS_MACOS 22 | #elif defined(linux) || defined(__linux) 23 | # define OS_LINUX 24 | #else 25 | # error unsupported platform 26 | #endif 27 | 28 | #ifndef _fileno 29 | #ifndef _MSC_VER 30 | #define _fileno(F) ((F)->_file) 31 | #endif 32 | #endif 33 | 34 | // This headers provides the `isatty()`/`fileno()` functions, 35 | // which are used for testing whether a standart stream refers 36 | // to the terminal. As for Windows, we also need WinApi funcs 37 | // for changing colors attributes of the terminal. 38 | #if defined(OS_MACOS) || defined(OS_LINUX) 39 | # include 40 | #elif defined(OS_WINDOWS) 41 | # include 42 | # include 43 | #endif 44 | 45 | 46 | #include 47 | #include 48 | 49 | 50 | 51 | namespace termcolor 52 | { 53 | // Forward declaration of the `__internal` namespace. 54 | // All comments are below. 55 | namespace __internal 56 | { 57 | inline FILE* get_standard_stream(const std::ostream& stream); 58 | inline bool is_atty(const std::ostream& stream); 59 | 60 | #if defined(OS_WINDOWS) 61 | void win_change_attributes(std::ostream& stream, int foreground, int background=-1); 62 | #endif 63 | } 64 | 65 | 66 | inline 67 | std::ostream& reset(std::ostream& stream) 68 | { 69 | if (__internal::is_atty(stream)) 70 | { 71 | #if defined(OS_MACOS) || defined(OS_LINUX) 72 | stream << "\033[00m"; 73 | #elif defined(OS_WINDOWS) 74 | __internal::win_change_attributes(stream, -1, -1); 75 | #endif 76 | } 77 | return stream; 78 | } 79 | 80 | 81 | inline 82 | std::ostream& bold(std::ostream& stream) 83 | { 84 | if (__internal::is_atty(stream)) 85 | { 86 | #if defined(OS_MACOS) || defined(OS_LINUX) 87 | stream << "\033[1m"; 88 | #elif defined(OS_WINDOWS) 89 | #endif 90 | } 91 | return stream; 92 | } 93 | 94 | 95 | inline 96 | std::ostream& dark(std::ostream& stream) 97 | { 98 | if (__internal::is_atty(stream)) 99 | { 100 | #if defined(OS_MACOS) || defined(OS_LINUX) 101 | stream << "\033[2m"; 102 | #elif defined(OS_WINDOWS) 103 | #endif 104 | } 105 | return stream; 106 | } 107 | 108 | 109 | inline 110 | std::ostream& underline(std::ostream& stream) 111 | { 112 | if (__internal::is_atty(stream)) 113 | { 114 | #if defined(OS_MACOS) || defined(OS_LINUX) 115 | stream << "\033[4m"; 116 | #elif defined(OS_WINDOWS) 117 | #endif 118 | } 119 | return stream; 120 | } 121 | 122 | 123 | inline 124 | std::ostream& blink(std::ostream& stream) 125 | { 126 | if (__internal::is_atty(stream)) 127 | { 128 | #if defined(OS_MACOS) || defined(OS_LINUX) 129 | stream << "\033[5m"; 130 | #elif defined(OS_WINDOWS) 131 | #endif 132 | } 133 | return stream; 134 | } 135 | 136 | 137 | inline 138 | std::ostream& reverse(std::ostream& stream) 139 | { 140 | if (__internal::is_atty(stream)) 141 | { 142 | #if defined(OS_MACOS) || defined(OS_LINUX) 143 | stream << "\033[7m"; 144 | #elif defined(OS_WINDOWS) 145 | #endif 146 | } 147 | return stream; 148 | } 149 | 150 | 151 | inline 152 | std::ostream& concealed(std::ostream& stream) 153 | { 154 | if (__internal::is_atty(stream)) 155 | { 156 | #if defined(OS_MACOS) || defined(OS_LINUX) 157 | stream << "\033[8m"; 158 | #elif defined(OS_WINDOWS) 159 | #endif 160 | } 161 | return stream; 162 | } 163 | 164 | 165 | inline 166 | std::ostream& grey(std::ostream& stream) 167 | { 168 | if (__internal::is_atty(stream)) 169 | { 170 | #if defined(OS_MACOS) || defined(OS_LINUX) 171 | stream << "\033[30m"; 172 | #elif defined(OS_WINDOWS) 173 | __internal::win_change_attributes(stream, 174 | 0 // grey (black) 175 | ); 176 | #endif 177 | } 178 | return stream; 179 | } 180 | 181 | inline 182 | std::ostream& red(std::ostream& stream) 183 | { 184 | if (__internal::is_atty(stream)) 185 | { 186 | #if defined(OS_MACOS) || defined(OS_LINUX) 187 | stream << "\033[31m"; 188 | #elif defined(OS_WINDOWS) 189 | __internal::win_change_attributes(stream, 190 | FOREGROUND_RED 191 | ); 192 | #endif 193 | } 194 | return stream; 195 | } 196 | 197 | inline 198 | std::ostream& green(std::ostream& stream) 199 | { 200 | if (__internal::is_atty(stream)) 201 | { 202 | #if defined(OS_MACOS) || defined(OS_LINUX) 203 | stream << "\033[32m"; 204 | #elif defined(OS_WINDOWS) 205 | __internal::win_change_attributes(stream, 206 | FOREGROUND_GREEN 207 | ); 208 | #endif 209 | } 210 | return stream; 211 | } 212 | 213 | inline 214 | std::ostream& yellow(std::ostream& stream) 215 | { 216 | if (__internal::is_atty(stream)) 217 | { 218 | #if defined(OS_MACOS) || defined(OS_LINUX) 219 | stream << "\033[33m"; 220 | #elif defined(OS_WINDOWS) 221 | __internal::win_change_attributes(stream, 222 | FOREGROUND_GREEN | FOREGROUND_RED 223 | ); 224 | #endif 225 | } 226 | return stream; 227 | } 228 | 229 | inline 230 | std::ostream& blue(std::ostream& stream) 231 | { 232 | if (__internal::is_atty(stream)) 233 | { 234 | #if defined(OS_MACOS) || defined(OS_LINUX) 235 | stream << "\033[34m"; 236 | #elif defined(OS_WINDOWS) 237 | __internal::win_change_attributes(stream, 238 | FOREGROUND_BLUE 239 | ); 240 | #endif 241 | } 242 | return stream; 243 | } 244 | 245 | inline 246 | std::ostream& magenta(std::ostream& stream) 247 | { 248 | if (__internal::is_atty(stream)) 249 | { 250 | #if defined(OS_MACOS) || defined(OS_LINUX) 251 | stream << "\033[35m"; 252 | #elif defined(OS_WINDOWS) 253 | __internal::win_change_attributes(stream, 254 | FOREGROUND_BLUE | FOREGROUND_RED 255 | ); 256 | #endif 257 | } 258 | return stream; 259 | } 260 | 261 | inline 262 | std::ostream& cyan(std::ostream& stream) 263 | { 264 | if (__internal::is_atty(stream)) 265 | { 266 | #if defined(OS_MACOS) || defined(OS_LINUX) 267 | stream << "\033[36m"; 268 | #elif defined(OS_WINDOWS) 269 | __internal::win_change_attributes(stream, 270 | FOREGROUND_BLUE | FOREGROUND_GREEN 271 | ); 272 | #endif 273 | } 274 | return stream; 275 | } 276 | 277 | inline 278 | std::ostream& white(std::ostream& stream) 279 | { 280 | if (__internal::is_atty(stream)) 281 | { 282 | #if defined(OS_MACOS) || defined(OS_LINUX) 283 | stream << "\033[37m"; 284 | #elif defined(OS_WINDOWS) 285 | __internal::win_change_attributes(stream, 286 | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED 287 | ); 288 | #endif 289 | } 290 | return stream; 291 | } 292 | 293 | 294 | 295 | inline 296 | std::ostream& lightRed(std::ostream& stream) 297 | { 298 | if (__internal::is_atty(stream)) 299 | { 300 | #if defined(OS_MACOS) || defined(OS_LINUX) 301 | stream << "\033[91m"; 302 | #elif defined(OS_WINDOWS) 303 | __internal::win_change_attributes(stream, 304 | FOREGROUND_RED | FOREGROUND_INTENSITY 305 | ); 306 | #endif 307 | } 308 | return stream; 309 | } 310 | 311 | inline 312 | std::ostream& lightGreen(std::ostream& stream) 313 | { 314 | if (__internal::is_atty(stream)) 315 | { 316 | #if defined(OS_MACOS) || defined(OS_LINUX) 317 | stream << "\033[92m"; 318 | #elif defined(OS_WINDOWS) 319 | __internal::win_change_attributes(stream, 320 | FOREGROUND_GREEN | FOREGROUND_INTENSITY 321 | ); 322 | #endif 323 | } 324 | return stream; 325 | } 326 | 327 | inline 328 | std::ostream& lightYellow(std::ostream& stream) 329 | { 330 | if (__internal::is_atty(stream)) 331 | { 332 | #if defined(OS_MACOS) || defined(OS_LINUX) 333 | stream << "\033[93m"; 334 | #elif defined(OS_WINDOWS) 335 | __internal::win_change_attributes(stream, 336 | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY 337 | ); 338 | #endif 339 | } 340 | return stream; 341 | } 342 | 343 | inline 344 | std::ostream& lightBlue(std::ostream& stream) 345 | { 346 | if (__internal::is_atty(stream)) 347 | { 348 | #if defined(OS_MACOS) || defined(OS_LINUX) 349 | stream << "\033[94m"; 350 | #elif defined(OS_WINDOWS) 351 | __internal::win_change_attributes(stream, 352 | FOREGROUND_BLUE | FOREGROUND_INTENSITY 353 | ); 354 | #endif 355 | } 356 | return stream; 357 | } 358 | 359 | inline 360 | std::ostream& lightMagenta(std::ostream& stream) 361 | { 362 | if (__internal::is_atty(stream)) 363 | { 364 | #if defined(OS_MACOS) || defined(OS_LINUX) 365 | stream << "\033[95m"; 366 | #elif defined(OS_WINDOWS) 367 | __internal::win_change_attributes(stream, 368 | FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY 369 | ); 370 | #endif 371 | } 372 | return stream; 373 | } 374 | 375 | inline 376 | std::ostream& lightCyan(std::ostream& stream) 377 | { 378 | if (__internal::is_atty(stream)) 379 | { 380 | #if defined(OS_MACOS) || defined(OS_LINUX) 381 | stream << "\033[36m"; 382 | #elif defined(OS_WINDOWS) 383 | __internal::win_change_attributes(stream, 384 | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY 385 | ); 386 | #endif 387 | } 388 | return stream; 389 | } 390 | 391 | inline 392 | std::ostream& lightWhite(std::ostream& stream) 393 | { 394 | if (__internal::is_atty(stream)) 395 | { 396 | #if defined(OS_MACOS) || defined(OS_LINUX) 397 | stream << "\033[37m"; 398 | #elif defined(OS_WINDOWS) 399 | __internal::win_change_attributes(stream, 400 | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY 401 | ); 402 | #endif 403 | } 404 | return stream; 405 | } 406 | 407 | inline 408 | std::ostream& on_grey(std::ostream& stream) 409 | { 410 | if (__internal::is_atty(stream)) 411 | { 412 | #if defined(OS_MACOS) || defined(OS_LINUX) 413 | stream << "\033[40m"; 414 | #elif defined(OS_WINDOWS) 415 | __internal::win_change_attributes(stream, -1, 416 | 0 // grey (black) 417 | ); 418 | #endif 419 | } 420 | return stream; 421 | } 422 | 423 | inline 424 | std::ostream& on_red(std::ostream& stream) 425 | { 426 | if (__internal::is_atty(stream)) 427 | { 428 | #if defined(OS_MACOS) || defined(OS_LINUX) 429 | stream << "\033[41m"; 430 | #elif defined(OS_WINDOWS) 431 | __internal::win_change_attributes(stream, -1, 432 | BACKGROUND_RED 433 | ); 434 | #endif 435 | } 436 | return stream; 437 | } 438 | 439 | inline 440 | std::ostream& on_green(std::ostream& stream) 441 | { 442 | if (__internal::is_atty(stream)) 443 | { 444 | #if defined(OS_MACOS) || defined(OS_LINUX) 445 | stream << "\033[42m"; 446 | #elif defined(OS_WINDOWS) 447 | __internal::win_change_attributes(stream, -1, 448 | BACKGROUND_GREEN 449 | ); 450 | #endif 451 | } 452 | return stream; 453 | } 454 | 455 | inline 456 | std::ostream& on_yellow(std::ostream& stream) 457 | { 458 | if (__internal::is_atty(stream)) 459 | { 460 | #if defined(OS_MACOS) || defined(OS_LINUX) 461 | stream << "\033[43m"; 462 | #elif defined(OS_WINDOWS) 463 | __internal::win_change_attributes(stream, -1, 464 | BACKGROUND_GREEN | BACKGROUND_RED 465 | ); 466 | #endif 467 | } 468 | return stream; 469 | } 470 | 471 | inline 472 | std::ostream& on_blue(std::ostream& stream) 473 | { 474 | if (__internal::is_atty(stream)) 475 | { 476 | #if defined(OS_MACOS) || defined(OS_LINUX) 477 | stream << "\033[44m"; 478 | #elif defined(OS_WINDOWS) 479 | __internal::win_change_attributes(stream, -1, 480 | BACKGROUND_BLUE 481 | ); 482 | #endif 483 | } 484 | return stream; 485 | } 486 | 487 | inline 488 | std::ostream& on_magenta(std::ostream& stream) 489 | { 490 | if (__internal::is_atty(stream)) 491 | { 492 | #if defined(OS_MACOS) || defined(OS_LINUX) 493 | stream << "\033[45m"; 494 | #elif defined(OS_WINDOWS) 495 | __internal::win_change_attributes(stream, -1, 496 | BACKGROUND_BLUE | BACKGROUND_RED 497 | ); 498 | #endif 499 | } 500 | return stream; 501 | } 502 | 503 | inline 504 | std::ostream& on_cyan(std::ostream& stream) 505 | { 506 | if (__internal::is_atty(stream)) 507 | { 508 | #if defined(OS_MACOS) || defined(OS_LINUX) 509 | stream << "\033[46m"; 510 | #elif defined(OS_WINDOWS) 511 | __internal::win_change_attributes(stream, -1, 512 | BACKGROUND_GREEN | BACKGROUND_BLUE 513 | ); 514 | #endif 515 | } 516 | return stream; 517 | } 518 | 519 | inline 520 | std::ostream& on_white(std::ostream& stream) 521 | { 522 | if (__internal::is_atty(stream)) 523 | { 524 | #if defined(OS_MACOS) || defined(OS_LINUX) 525 | stream << "\033[47m"; 526 | #elif defined(OS_WINDOWS) 527 | __internal::win_change_attributes(stream, -1, 528 | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED 529 | ); 530 | #endif 531 | } 532 | 533 | return stream; 534 | } 535 | 536 | 537 | 538 | //! Since C++ hasn't a way to hide something in the header from 539 | //! the outer access, I have to introduce this namespace which 540 | //! is used for internal purpose and should't be access from 541 | //! the user code. 542 | namespace __internal 543 | { 544 | //! Since C++ hasn't a true way to extract stream handler 545 | //! from the a given `std::ostream` object, I have to write 546 | //! this kind of hack. 547 | inline 548 | FILE* get_standard_stream(const std::ostream& stream) 549 | { 550 | if (&stream == &std::cout) 551 | return stdout; 552 | else if ((&stream == &std::cerr) || (&stream == &std::clog)) 553 | return stderr; 554 | 555 | return nullptr; 556 | } 557 | 558 | 559 | //! Test whether a given `std::ostream` object refers to 560 | //! a terminal. 561 | inline 562 | bool is_atty(const std::ostream& stream) 563 | { 564 | FILE* std_stream = get_standard_stream(stream); 565 | 566 | #if defined(OS_MACOS) || defined(OS_LINUX) 567 | return ::isatty(fileno(std_stream)); 568 | #elif defined(OS_WINDOWS) 569 | return ::_isatty(_fileno(std_stream))!=0; 570 | #endif 571 | } 572 | 573 | 574 | #if defined(OS_WINDOWS) 575 | //! Change Windows Terminal colors attribute. If some 576 | //! parameter is `-1` then attribute won't changed. 577 | inline 578 | void win_change_attributes(std::ostream& stream, int foreground, int background) 579 | { 580 | // yeah, i know.. it's ugly, it's windows. 581 | static WORD defaultAttributes = 0; 582 | 583 | // get terminal handle 584 | HANDLE hTerminal = INVALID_HANDLE_VALUE; 585 | if (&stream == &std::cout) 586 | hTerminal = GetStdHandle(STD_OUTPUT_HANDLE); 587 | else if (&stream == &std::cerr) 588 | hTerminal = GetStdHandle(STD_ERROR_HANDLE); 589 | 590 | // save default terminal attributes if it unsaved 591 | if (!defaultAttributes) 592 | { 593 | CONSOLE_SCREEN_BUFFER_INFO info; 594 | if (!GetConsoleScreenBufferInfo(hTerminal, &info)) 595 | return; 596 | defaultAttributes = info.wAttributes; 597 | } 598 | 599 | // restore all default settings 600 | if (foreground == -1 && background == -1) 601 | { 602 | SetConsoleTextAttribute(hTerminal, defaultAttributes); 603 | return; 604 | } 605 | 606 | // get current settings 607 | CONSOLE_SCREEN_BUFFER_INFO info; 608 | if (!GetConsoleScreenBufferInfo(hTerminal, &info)) 609 | return; 610 | 611 | if (foreground != -1) 612 | { 613 | info.wAttributes &= ~(info.wAttributes & 0x0F); 614 | info.wAttributes |= static_cast(foreground); 615 | } 616 | 617 | if (background != -1) 618 | { 619 | info.wAttributes &= ~(info.wAttributes & 0xF0); 620 | info.wAttributes |= static_cast(background); 621 | } 622 | 623 | SetConsoleTextAttribute(hTerminal, info.wAttributes); 624 | } 625 | #endif // OS_WINDOWS 626 | 627 | } // namespace __internal 628 | 629 | } // namespace termcolor 630 | 631 | 632 | #undef OS_WINDOWS 633 | #undef OS_MACOS 634 | #undef OS_LINUX 635 | 636 | #endif // TERMCOLOR_HPP_ 637 | --------------------------------------------------------------------------------