Compare commits
	
		
			50 Commits
		
	
	
		
			feature/no
			...
			feature/se
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 1ac02fdc0e | ||
|  | 687956358d | ||
|  | 1a42c92325 | ||
|  | 6bb00b6788 | ||
|  | 12f6cd878d | ||
|  | 9aacebbbaf | ||
|  | 701c3745c2 | ||
|  | 156288b17b | ||
|  | ed0e23e8a5 | ||
|  | 4c4f99606e | ||
|  | a61586c846 | ||
|  | d64d50c978 | ||
|  | a64b7b0c4a | ||
|  | 0caeb81327 | ||
|  | edac7a0171 | ||
|  | abfadad2e9 | ||
|  | 2dc1547bbd | ||
|  | 5eb23c9764 | ||
|  | 9f4b2856b0 | ||
|  | b5fc10326e | ||
|  | 8d3a47a873 | ||
|  | 4df58f3059 | ||
|  | 06b8cb8d3b | ||
|  | ff81f5b496 | ||
|  | c89f73006e | ||
|  | c28951f049 | ||
|  | dfaaaca223 | ||
|  | c7f0bf3d64 | ||
|  | 234ce4c173 | ||
|  | f60293b2e7 | ||
|  | 9441095637 | ||
|  | f82d38f758 | ||
|  | a7f42f35db | ||
|  | cb1d1bfd85 | ||
|  | 28c3f2ea26 | ||
|  | 8dc132dbd3 | ||
|  | 98e2fbca6a | ||
|  | fa7f0fadde | ||
|  | 2732dfd0f1 | ||
|  | 2e4c4b72b6 | ||
|  | fc21ad519b | ||
|  | c65cfd3d26 | ||
|  | 8955462f73 | ||
|  | 205c8c15bd | ||
|  | 78198a0147 | ||
|  | d561e1141e | ||
|  | 753fc845ac | ||
|  | 5dbc00bbfe | ||
|  | 14ec8522ef | ||
|  | 0c2d1c22bc | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,2 @@ | ||||
| build | ||||
| *.pyc | ||||
|   | ||||
| @@ -37,6 +37,7 @@ set( IXWEBSOCKET_SOURCES | ||||
|     ixwebsocket/IXWebSocketHttpHeaders.cpp | ||||
|     ixwebsocket/IXHttpClient.cpp | ||||
|     ixwebsocket/IXUrlParser.cpp | ||||
|     ixwebsocket/LUrlParser.cpp | ||||
|     ixwebsocket/IXSelectInterrupt.cpp | ||||
|     ixwebsocket/IXSelectInterruptFactory.cpp | ||||
|     ixwebsocket/IXConnectionState.cpp | ||||
| @@ -65,6 +66,7 @@ set( IXWEBSOCKET_HEADERS | ||||
|     ixwebsocket/libwshandshake.hpp | ||||
|     ixwebsocket/IXHttpClient.h | ||||
|     ixwebsocket/IXUrlParser.h | ||||
|     ixwebsocket/LUrlParser.h | ||||
|     ixwebsocket/IXSelectInterrupt.h | ||||
|     ixwebsocket/IXSelectInterruptFactory.h | ||||
|     ixwebsocket/IXConnectionState.h | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| 1.4.3 | ||||
| 2.0.0 | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| docker/Dockerfile.fedora | ||||
| docker/Dockerfile.ubuntu_xenial | ||||
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
								
							| @@ -10,8 +10,7 @@ | ||||
| * iOS | ||||
| * Linux | ||||
| * Android | ||||
|  | ||||
| The code was made to compile once on Windows but support is currently broken on this platform. | ||||
| * Windows (no TLS) | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| @@ -38,7 +37,7 @@ webSocket.setOnMessageCallback( | ||||
|        const ix::WebSocketOpenInfo& openInfo, | ||||
|        const ix::WebSocketCloseInfo& closeInfo) | ||||
|     { | ||||
|         if (messageType == ix::WebSocket_MessageType_Message) | ||||
|         if (messageType == ix::WebSocketMessageType::Message) | ||||
|         { | ||||
|             std::cout << str << std::endl; | ||||
|         } | ||||
| @@ -78,7 +77,7 @@ server.setOnConnectionCallback( | ||||
|                const ix::WebSocketOpenInfo& openInfo, | ||||
|                const ix::WebSocketCloseInfo& closeInfo) | ||||
|             { | ||||
|                 if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                 if (messageType == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     std::cerr << "New connection" << std::endl; | ||||
|  | ||||
| @@ -97,7 +96,7 @@ server.setOnConnectionCallback( | ||||
|                         std::cerr << it.first << ": " << it.second << std::endl; | ||||
|                     } | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Message) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     // For an echo server, we just send back to the client whatever was received by the server | ||||
|                     // All connected clients are available in an std::set. See the broadcast cpp example. | ||||
| @@ -199,6 +198,8 @@ make install # will install to /usr/local on Unix, on macOS it is a good idea to | ||||
|  | ||||
| Headers and a static library will be installed to the target dir. | ||||
|  | ||||
| A [conan](https://conan.io/) file is available at [conan-IXWebSocket](https://github.com/Zinnion/conan-IXWebSocket). | ||||
|  | ||||
| There is a unittest which can be executed by typing `make test`. | ||||
|  | ||||
| There is a Dockerfile for running some code on Linux. To use docker-compose you must make a docker container first. | ||||
| @@ -300,10 +301,10 @@ If the connection was closed and sending failed, the return value will be set to | ||||
|  | ||||
| `getReadyState()` returns the state of the connection. There are 4 possible states. | ||||
|  | ||||
| 1. WebSocket_ReadyState_Connecting - The connection is not yet open. | ||||
| 2. WebSocket_ReadyState_Open       - The connection is open and ready to communicate. | ||||
| 3. WebSocket_ReadyState_Closing    - The connection is in the process of closing. | ||||
| 4. WebSocket_MessageType_Close     - The connection is closed or could not be opened. | ||||
| 1. ReadyState::Connecting - The connection is not yet open. | ||||
| 2. ReadyState::Open       - The connection is open and ready to communicate. | ||||
| 3. ReadyState::Closing    - The connection is in the process of closing. | ||||
| 4. ReadyState::Closed     - The connection is closed or could not be opened. | ||||
|  | ||||
| ### Open and Close notifications | ||||
|  | ||||
| @@ -318,7 +319,7 @@ webSocket.setOnMessageCallback( | ||||
|        const ix::WebSocketOpenInfo& openInfo, | ||||
|        const ix::WebSocketCloseInfo& closeInfo) | ||||
|     { | ||||
|         if (messageType == ix::WebSocket_MessageType_Open) | ||||
|         if (messageType == ix::WebSocketMessageType::Open) | ||||
|         { | ||||
|             std::cout << "send greetings" << std::endl; | ||||
|  | ||||
| @@ -329,7 +330,7 @@ webSocket.setOnMessageCallback( | ||||
|                 std::cout << it.first << ": " << it.second << std::endl; | ||||
|             } | ||||
|         } | ||||
|         else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|         else if (messageType == ix::WebSocketMessageType::Close) | ||||
|         { | ||||
|             std::cout << "disconnected" << std::endl; | ||||
|  | ||||
| @@ -344,7 +345,7 @@ webSocket.setOnMessageCallback( | ||||
|  | ||||
| ### Error notification | ||||
|  | ||||
| A message will be fired when there is an error with the connection. The message type will be `ix::WebSocket_MessageType_Error`. Multiple fields will be available on the event to describe the error. | ||||
| A message will be fired when there is an error with the connection. The message type will be `ix::WebSocketMessageType::Error`. Multiple fields will be available on the event to describe the error. | ||||
|  | ||||
| ``` | ||||
| webSocket.setOnMessageCallback( | ||||
| @@ -355,7 +356,7 @@ webSocket.setOnMessageCallback( | ||||
|        const ix::WebSocketOpenInfo& openInfo, | ||||
|        const ix::WebSocketCloseInfo& closeInfo) | ||||
|     { | ||||
|         if (messageType == ix::WebSocket_MessageType_Error) | ||||
|         if (messageType == ix::WebSocketMessageType::Error) | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "Error: "         << error.reason      << std::endl; | ||||
| @@ -395,8 +396,8 @@ webSocket.setOnMessageCallback( | ||||
|        const ix::WebSocketOpenInfo& openInfo, | ||||
|        const ix::WebSocketCloseInfo& closeInfo) | ||||
|     { | ||||
|         if (messageType == ix::WebSocket_MessageType_Ping || | ||||
|             messageType == ix::WebSocket_MessageType_Pong) | ||||
|         if (messageType == ix::WebSocketMessageType::Ping || | ||||
|             messageType == ix::WebSocketMessageType::Pong) | ||||
|         { | ||||
|             std::cout << "pong data: " << str << std::endl; | ||||
|         } | ||||
|   | ||||
| @@ -1,10 +1,14 @@ | ||||
| image: | ||||
| - Visual Studio 2017 | ||||
| - Ubuntu | ||||
|  | ||||
| install: | ||||
| - ls -al | ||||
| - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" | ||||
| - python test/run.py | ||||
| - cd test | ||||
| - mkdir build | ||||
| - cd build | ||||
| - cmake -G"NMake Makefiles" .. | ||||
| - nmake | ||||
| - ixwebsocket_unittest.exe | ||||
|  | ||||
| build: off | ||||
|   | ||||
| @@ -35,7 +35,6 @@ namespace ix | ||||
|         _activeJobs.erase(_id); | ||||
|     } | ||||
|  | ||||
|     // we want hostname to be copied, not passed as a const reference | ||||
|     struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname, | ||||
|                                             int port, | ||||
|                                             std::string& errMsg) | ||||
|   | ||||
| @@ -47,13 +47,12 @@ namespace ix | ||||
|  | ||||
|         std::string protocol, host, path, query; | ||||
|         int port; | ||||
|         bool websocket = false; | ||||
|  | ||||
|         if (!UrlParser::parse(url, protocol, host, path, query, port, websocket)) | ||||
|         if (!UrlParser::parse(url, protocol, host, path, query, port)) | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "Cannot parse url: " << url; | ||||
|             return std::make_tuple(code, HttpErrorCode_UrlMalformed, | ||||
|             return std::make_tuple(code, HttpErrorCode::UrlMalformed, | ||||
|                                    headers, payload, ss.str(), | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -64,7 +63,7 @@ namespace ix | ||||
|  | ||||
|         if (!_socket) | ||||
|         { | ||||
|             return std::make_tuple(code, HttpErrorCode_CannotCreateSocket, | ||||
|             return std::make_tuple(code, HttpErrorCode::CannotCreateSocket, | ||||
|                                    headers, payload, errorMsg, | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -117,7 +116,7 @@ namespace ix | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "Cannot connect to url: " << url; | ||||
|             return std::make_tuple(code, HttpErrorCode_CannotConnect, | ||||
|             return std::make_tuple(code, HttpErrorCode::CannotConnect, | ||||
|                                    headers, payload, ss.str(), | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -143,7 +142,7 @@ namespace ix | ||||
|         if (!_socket->writeBytes(req, isCancellationRequested)) | ||||
|         { | ||||
|             std::string errorMsg("Cannot send request"); | ||||
|             return std::make_tuple(code, HttpErrorCode_SendError, | ||||
|             return std::make_tuple(code, HttpErrorCode::SendError, | ||||
|                                    headers, payload, errorMsg, | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -157,7 +156,7 @@ namespace ix | ||||
|         if (!lineValid) | ||||
|         { | ||||
|             std::string errorMsg("Cannot retrieve status line"); | ||||
|             return std::make_tuple(code, HttpErrorCode_CannotReadStatusLine, | ||||
|             return std::make_tuple(code, HttpErrorCode::CannotReadStatusLine, | ||||
|                                    headers, payload, errorMsg, | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -172,7 +171,7 @@ namespace ix | ||||
|         if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1) | ||||
|         { | ||||
|             std::string errorMsg("Cannot parse response code from status line"); | ||||
|             return std::make_tuple(code, HttpErrorCode_MissingStatus, | ||||
|             return std::make_tuple(code, HttpErrorCode::MissingStatus, | ||||
|                                    headers, payload, errorMsg, | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -184,7 +183,7 @@ namespace ix | ||||
|         if (!headersValid) | ||||
|         { | ||||
|             std::string errorMsg("Cannot parse http headers"); | ||||
|             return std::make_tuple(code, HttpErrorCode_HeaderParsingError, | ||||
|             return std::make_tuple(code, HttpErrorCode::HeaderParsingError, | ||||
|                                    headers, payload, errorMsg, | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -195,7 +194,7 @@ namespace ix | ||||
|             if (headers.find("Location") == headers.end()) | ||||
|             { | ||||
|                 std::string errorMsg("Missing location header for redirect"); | ||||
|                 return std::make_tuple(code, HttpErrorCode_MissingLocation, | ||||
|                 return std::make_tuple(code, HttpErrorCode::MissingLocation, | ||||
|                                        headers, payload, errorMsg, | ||||
|                                        uploadSize, downloadSize); | ||||
|             } | ||||
| @@ -204,7 +203,7 @@ namespace ix | ||||
|             { | ||||
|                 std::stringstream ss; | ||||
|                 ss << "Too many redirects: " << redirects; | ||||
|                 return std::make_tuple(code, HttpErrorCode_TooManyRedirects, | ||||
|                 return std::make_tuple(code, HttpErrorCode::TooManyRedirects, | ||||
|                                        headers, payload, ss.str(), | ||||
|                                        uploadSize, downloadSize); | ||||
|             } | ||||
| @@ -216,7 +215,7 @@ namespace ix | ||||
|  | ||||
|         if (verb == "HEAD") | ||||
|         { | ||||
|             return std::make_tuple(code, HttpErrorCode_Ok, | ||||
|             return std::make_tuple(code, HttpErrorCode::Ok, | ||||
|                                    headers, payload, std::string(), | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -237,7 +236,7 @@ namespace ix | ||||
|             if (!chunkResult.first) | ||||
|             { | ||||
|                 errorMsg = "Cannot read chunk"; | ||||
|                 return std::make_tuple(code, HttpErrorCode_ChunkReadError, | ||||
|                 return std::make_tuple(code, HttpErrorCode::ChunkReadError, | ||||
|                                        headers, payload, errorMsg, | ||||
|                                        uploadSize, downloadSize); | ||||
|             } | ||||
| @@ -255,7 +254,7 @@ namespace ix | ||||
|  | ||||
|                 if (!lineResult.first) | ||||
|                 { | ||||
|                     return std::make_tuple(code, HttpErrorCode_ChunkReadError, | ||||
|                     return std::make_tuple(code, HttpErrorCode::ChunkReadError, | ||||
|                                            headers, payload, errorMsg, | ||||
|                                            uploadSize, downloadSize); | ||||
|                 } | ||||
| @@ -282,7 +281,7 @@ namespace ix | ||||
|                 if (!chunkResult.first) | ||||
|                 { | ||||
|                     errorMsg = "Cannot read chunk"; | ||||
|                     return std::make_tuple(code, HttpErrorCode_ChunkReadError, | ||||
|                     return std::make_tuple(code, HttpErrorCode::ChunkReadError, | ||||
|                                            headers, payload, errorMsg, | ||||
|                                            uploadSize, downloadSize); | ||||
|                 } | ||||
| @@ -293,7 +292,7 @@ namespace ix | ||||
|  | ||||
|                 if (!lineResult.first) | ||||
|                 { | ||||
|                     return std::make_tuple(code, HttpErrorCode_ChunkReadError, | ||||
|                     return std::make_tuple(code, HttpErrorCode::ChunkReadError, | ||||
|                                            headers, payload, errorMsg, | ||||
|                                            uploadSize, downloadSize); | ||||
|                 } | ||||
| @@ -308,7 +307,7 @@ namespace ix | ||||
|         else | ||||
|         { | ||||
|             std::string errorMsg("Cannot read http body"); | ||||
|             return std::make_tuple(code, HttpErrorCode_CannotReadBody, | ||||
|             return std::make_tuple(code, HttpErrorCode::CannotReadBody, | ||||
|                                    headers, payload, errorMsg, | ||||
|                                    uploadSize, downloadSize); | ||||
|         } | ||||
| @@ -322,14 +321,14 @@ namespace ix | ||||
|             if (!gzipInflate(payload, decompressedPayload)) | ||||
|             { | ||||
|                 std::string errorMsg("Error decompressing payload"); | ||||
|                 return std::make_tuple(code, HttpErrorCode_Gzip, | ||||
|                 return std::make_tuple(code, HttpErrorCode::Gzip, | ||||
|                                        headers, payload, errorMsg, | ||||
|                                        uploadSize, downloadSize); | ||||
|             } | ||||
|             payload = decompressedPayload; | ||||
|         } | ||||
|  | ||||
|         return std::make_tuple(code, HttpErrorCode_Ok, | ||||
|         return std::make_tuple(code, HttpErrorCode::Ok, | ||||
|                                headers, payload, std::string(), | ||||
|                                uploadSize, downloadSize); | ||||
|     } | ||||
|   | ||||
| @@ -19,23 +19,23 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     enum HttpErrorCode | ||||
|     enum class HttpErrorCode | ||||
|     { | ||||
|         HttpErrorCode_Ok = 0, | ||||
|         HttpErrorCode_CannotConnect = 1, | ||||
|         HttpErrorCode_Timeout = 2, | ||||
|         HttpErrorCode_Gzip = 3, | ||||
|         HttpErrorCode_UrlMalformed = 4, | ||||
|         HttpErrorCode_CannotCreateSocket = 5, | ||||
|         HttpErrorCode_SendError = 6, | ||||
|         HttpErrorCode_ReadError = 7, | ||||
|         HttpErrorCode_CannotReadStatusLine = 8, | ||||
|         HttpErrorCode_MissingStatus = 9, | ||||
|         HttpErrorCode_HeaderParsingError = 10, | ||||
|         HttpErrorCode_MissingLocation = 11, | ||||
|         HttpErrorCode_TooManyRedirects = 12, | ||||
|         HttpErrorCode_ChunkReadError = 13, | ||||
|         HttpErrorCode_CannotReadBody = 14 | ||||
|         Ok                       = 0, | ||||
|         CannotConnect            = 1, | ||||
|         Timeout                  = 2, | ||||
|         Gzip                     = 3, | ||||
|         UrlMalformed             = 4, | ||||
|         CannotCreateSocket       = 5, | ||||
|         SendError                = 6, | ||||
|         ReadError                = 7, | ||||
|         CannotReadStatusLine     = 8, | ||||
|         MissingStatus            = 9, | ||||
|         HeaderParsingError       = 10, | ||||
|         MissingLocation          = 11, | ||||
|         TooManyRedirects         = 12, | ||||
|         ChunkReadError           = 13, | ||||
|         CannotReadBody           = 14 | ||||
|     }; | ||||
|  | ||||
|     using HttpResponse = std::tuple<int, // status | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| /* | ||||
|  *  IXNetSystem.cpp | ||||
|  *  Author: Benjamin Sergeant | ||||
|  *  Author: Korchynskyi Dmytro | ||||
|  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||
|  */ | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class SelectInterruptEventFd : public SelectInterrupt { | ||||
|     class SelectInterruptEventFd final : public SelectInterrupt { | ||||
|     public: | ||||
|         SelectInterruptEventFd(); | ||||
|         virtual ~SelectInterruptEventFd(); | ||||
|   | ||||
| @@ -40,6 +40,8 @@ namespace ix | ||||
|  | ||||
|     bool SelectInterruptPipe::init(std::string& errorMsg) | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_fildesMutex); | ||||
|  | ||||
|         // calling init twice is a programming error | ||||
|         assert(_fildes[kPipeReadIndex] == -1); | ||||
|         assert(_fildes[kPipeWriteIndex] == -1); | ||||
| @@ -108,6 +110,8 @@ namespace ix | ||||
|  | ||||
|     bool SelectInterruptPipe::notify(uint64_t value) | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_fildesMutex); | ||||
|  | ||||
|         int fd = _fildes[kPipeWriteIndex]; | ||||
|         if (fd == -1) return false; | ||||
|  | ||||
| @@ -118,6 +122,8 @@ namespace ix | ||||
|     // TODO: return max uint64_t for errors ? | ||||
|     uint64_t SelectInterruptPipe::read() | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_fildesMutex); | ||||
|  | ||||
|         int fd = _fildes[kPipeReadIndex]; | ||||
|  | ||||
|         uint64_t value = 0; | ||||
| @@ -133,6 +139,8 @@ namespace ix | ||||
|  | ||||
|     int SelectInterruptPipe::getFd() const | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_fildesMutex); | ||||
|  | ||||
|         return _fildes[kPipeReadIndex]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -10,10 +10,11 @@ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <string> | ||||
| #include <mutex> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class SelectInterruptPipe : public SelectInterrupt { | ||||
|     class SelectInterruptPipe final : public SelectInterrupt { | ||||
|     public: | ||||
|         SelectInterruptPipe(); | ||||
|         virtual ~SelectInterruptPipe(); | ||||
| @@ -30,6 +31,7 @@ namespace ix | ||||
|         // happens between a control thread and a background thread, which is | ||||
|         // blocked on select. | ||||
|         int _fildes[2]; | ||||
|         mutable std::mutex _fildesMutex; | ||||
|  | ||||
|         // Used to identify the read/write idx | ||||
|         static const int kPipeReadIndex; | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
|  | ||||
| #ifdef min | ||||
| #undef min | ||||
| @@ -45,14 +44,14 @@ namespace ix | ||||
|         close(); | ||||
|     } | ||||
|  | ||||
|     PollResultType Socket::poll(int timeoutSecs) | ||||
|     PollResultType Socket::poll(int timeoutMs) | ||||
|     { | ||||
|         if (_sockfd == -1) | ||||
|         { | ||||
|             return PollResultType::Error; | ||||
|         } | ||||
|  | ||||
|         return isReadyToRead(1000 * timeoutSecs); | ||||
|         return isReadyToRead(timeoutMs); | ||||
|     } | ||||
|  | ||||
|     PollResultType Socket::select(bool readyToRead, int timeoutMs) | ||||
| @@ -74,7 +73,7 @@ namespace ix | ||||
|  | ||||
|         struct timeval timeout; | ||||
|         timeout.tv_sec = timeoutMs / 1000; | ||||
|         timeout.tv_usec = (timeoutMs < 1000) ? 0 : 1000 * (timeoutMs % 1000); | ||||
|         timeout.tv_usec = 1000 * (timeoutMs % 1000); | ||||
|  | ||||
|         // Compute the highest fd. | ||||
|         int sockfd = _sockfd; | ||||
| @@ -160,8 +159,6 @@ namespace ix | ||||
|  | ||||
|     ssize_t Socket::send(char* buffer, size_t length) | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_socketMutex); | ||||
|  | ||||
|         int flags = 0; | ||||
| #ifdef MSG_NOSIGNAL | ||||
|         flags = MSG_NOSIGNAL; | ||||
| @@ -177,8 +174,6 @@ namespace ix | ||||
|  | ||||
|     ssize_t Socket::recv(void* buffer, size_t length) | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_socketMutex); | ||||
|  | ||||
|         int flags = 0; | ||||
| #ifdef MSG_NOSIGNAL | ||||
|         flags = MSG_NOSIGNAL; | ||||
| @@ -189,11 +184,27 @@ namespace ix | ||||
|  | ||||
|     int Socket::getErrno() | ||||
|     { | ||||
|         int err; | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|         return WSAGetLastError(); | ||||
|         err = WSAGetLastError(); | ||||
| #else | ||||
|         return errno; | ||||
|         err = errno; | ||||
| #endif | ||||
|  | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     bool Socket::isWaitNeeded() | ||||
|     { | ||||
|         int err = getErrno(); | ||||
|  | ||||
|         if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Socket::closeSocket(int fd) | ||||
| @@ -228,8 +239,7 @@ namespace ix | ||||
|                 return ret == len; | ||||
|             } | ||||
|             // There is possibly something to be writen, try again | ||||
|             else if (ret < 0 && (getErrno() == EWOULDBLOCK || | ||||
|                                  getErrno() == EAGAIN)) | ||||
|             else if (ret < 0 && Socket::isWaitNeeded()) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
| @@ -257,8 +267,7 @@ namespace ix | ||||
|                 return true; | ||||
|             } | ||||
|             // There is possibly something to be read, try again | ||||
|             else if (ret < 0 && (getErrno() == EWOULDBLOCK || | ||||
|                                  getErrno() == EAGAIN)) | ||||
|             else if (ret < 0 && Socket::isWaitNeeded()) | ||||
|             { | ||||
|                 // Wait with a 1ms timeout until the socket is ready to read. | ||||
|                 // This way we are not busy looping | ||||
| @@ -317,13 +326,12 @@ namespace ix | ||||
|             size_t size = std::min(kChunkSize, length - output.size()); | ||||
|             ssize_t ret = recv((char*)&_readBuffer[0], size); | ||||
|  | ||||
|             if (ret <= 0 && (getErrno() != EWOULDBLOCK && | ||||
|                              getErrno() != EAGAIN)) | ||||
|             if (ret <= 0 && !Socket::isWaitNeeded()) | ||||
|             { | ||||
|                 // Error | ||||
|                 return std::make_pair(false, std::string()); | ||||
|             } | ||||
|             else if (ret > 0) | ||||
|             else | ||||
|             { | ||||
|                 output.insert(output.end(), | ||||
|                               _readBuffer.begin(), | ||||
|   | ||||
| @@ -16,6 +16,20 @@ | ||||
| #ifdef _WIN32 | ||||
| #include <BaseTsd.h> | ||||
| typedef SSIZE_T ssize_t; | ||||
|  | ||||
| #undef EWOULDBLOCK | ||||
| #undef EAGAIN | ||||
| #undef EINPROGRESS | ||||
| #undef EBADF | ||||
| #undef EINVAL | ||||
|  | ||||
| // map to WSA error codes | ||||
| #define EWOULDBLOCK    WSAEWOULDBLOCK | ||||
| #define EAGAIN         WSATRY_AGAIN | ||||
| #define EINPROGRESS    WSAEINPROGRESS | ||||
| #define EBADF          WSAEBADF | ||||
| #define EINVAL         WSAEINVAL | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #include "IXCancellationRequest.h" | ||||
| @@ -27,12 +41,12 @@ namespace ix | ||||
|  | ||||
|     enum class PollResultType | ||||
|     { | ||||
|         ReadyForRead = 0, | ||||
|         ReadyForWrite = 1, | ||||
|         Timeout = 2, | ||||
|         Error = 3, | ||||
|         SendRequest = 4, | ||||
|         CloseRequest = 5 | ||||
|         ReadyForRead    = 0, | ||||
|         ReadyForWrite   = 1, | ||||
|         Timeout         = 2, | ||||
|         Error           = 3, | ||||
|         SendRequest     = 4, | ||||
|         CloseRequest    = 5 | ||||
|     }; | ||||
|  | ||||
|     class Socket { | ||||
| @@ -41,10 +55,8 @@ namespace ix | ||||
|         virtual ~Socket(); | ||||
|         bool init(std::string& errorMsg); | ||||
|  | ||||
|         void configure(); | ||||
|  | ||||
|         // Functions to check whether there is activity on the socket | ||||
|         PollResultType poll(int timeoutSecs = kDefaultPollTimeout); | ||||
|         PollResultType poll(int timeoutMs = kDefaultPollTimeout); | ||||
|         bool wakeUpFromPoll(uint8_t wakeUpCode); | ||||
|  | ||||
|         PollResultType isReadyToWrite(int timeoutMs); | ||||
| @@ -76,14 +88,14 @@ namespace ix | ||||
|             const CancellationRequest& isCancellationRequested); | ||||
|  | ||||
|         static int getErrno(); | ||||
|         static bool isWaitNeeded(); | ||||
|         static void closeSocket(int fd); | ||||
|  | ||||
|         // Used as special codes for pipe communication | ||||
|         static const uint64_t kSendRequest; | ||||
|         static const uint64_t kCloseRequest; | ||||
|  | ||||
|     protected: | ||||
|         void closeSocket(int fd); | ||||
|  | ||||
|         std::atomic<int> _sockfd; | ||||
|         std::mutex _socketMutex; | ||||
|  | ||||
|   | ||||
| @@ -20,8 +20,6 @@ | ||||
| #include <unistd.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| #include <iostream> | ||||
|  | ||||
| #include <errno.h> | ||||
| #define socketerrno errno | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class SocketAppleSSL : public Socket | ||||
|     class SocketAppleSSL final : public Socket | ||||
|     { | ||||
|     public: | ||||
|         SocketAppleSSL(int fd = -1); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "IXSocketConnect.h" | ||||
| #include "IXDNSLookup.h" | ||||
| #include "IXNetSystem.h" | ||||
| #include "IXSocket.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <fcntl.h> | ||||
| @@ -18,18 +19,6 @@ | ||||
| # include <linux/tcp.h> | ||||
| #endif | ||||
|  | ||||
| namespace | ||||
| { | ||||
|     void closeSocket(int fd) | ||||
|     { | ||||
| #ifdef _WIN32 | ||||
|         closesocket(fd); | ||||
| #else | ||||
|         ::close(fd); | ||||
| #endif | ||||
|     } | ||||
| } | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     // | ||||
| @@ -56,11 +45,12 @@ namespace ix | ||||
|         // block us for too long | ||||
|         SocketConnect::configure(fd); | ||||
|  | ||||
|         if (::connect(fd, address->ai_addr, address->ai_addrlen) == -1 | ||||
|             && errno != EINPROGRESS && errno != 0) | ||||
|         int res = ::connect(fd, address->ai_addr, address->ai_addrlen); | ||||
|  | ||||
|         if (res == -1 && !Socket::isWaitNeeded()) | ||||
|         { | ||||
|             errMsg = strerror(errno); | ||||
|             closeSocket(fd); | ||||
|             errMsg = strerror(Socket::getErrno()); | ||||
|             Socket::closeSocket(fd); | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
| @@ -68,15 +58,17 @@ namespace ix | ||||
|         { | ||||
|             if (isCancellationRequested && isCancellationRequested()) // Must handle timeout as well | ||||
|             { | ||||
|                 closeSocket(fd); | ||||
|                 Socket::closeSocket(fd); | ||||
|                 errMsg = "Cancelled"; | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
|             // Use select to check the status of the new connection | ||||
|             // On Linux the timeout needs to be re-initialized everytime | ||||
|             // http://man7.org/linux/man-pages/man2/select.2.html | ||||
|             struct timeval timeout; | ||||
|             timeout.tv_sec = 0; | ||||
|             timeout.tv_usec = 10 * 1000; // 10ms timeout | ||||
|  | ||||
|             fd_set wfds; | ||||
|             fd_set efds; | ||||
|  | ||||
| @@ -85,11 +77,13 @@ namespace ix | ||||
|             FD_ZERO(&efds); | ||||
|             FD_SET(fd, &efds); | ||||
|  | ||||
|             if (select(fd + 1, nullptr, &wfds, &efds, &timeout) < 0 && | ||||
|                 (errno == EBADF || errno == EINVAL)) | ||||
|             // Use select to check the status of the new connection | ||||
|             res = select(fd + 1, nullptr, &wfds, &efds, &timeout); | ||||
|  | ||||
|             if (res < 0 && (Socket::getErrno() == EBADF || Socket::getErrno() == EINVAL)) | ||||
|             { | ||||
|                 closeSocket(fd); | ||||
|                 errMsg = std::string("Connect error, select error: ") + strerror(errno); | ||||
|                 Socket::closeSocket(fd); | ||||
|                 errMsg = std::string("Connect error, select error: ") + strerror(Socket::getErrno()); | ||||
|                 return -1; | ||||
|             } | ||||
|  | ||||
| @@ -110,7 +104,7 @@ namespace ix | ||||
|                 optval != 0) | ||||
| #endif | ||||
|             { | ||||
|                 closeSocket(fd); | ||||
|                 Socket::closeSocket(fd); | ||||
|                 errMsg = strerror(optval); | ||||
|                 return -1; | ||||
|             } | ||||
| @@ -121,7 +115,7 @@ namespace ix | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         closeSocket(fd); | ||||
|         Socket::closeSocket(fd); | ||||
|         errMsg = "connect timed out after 60 seconds"; | ||||
|         return -1; | ||||
|     } | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
| #include "IXSocketConnect.h" | ||||
|  | ||||
| #include <cassert> | ||||
| #include <iostream> | ||||
|  | ||||
| #include <openssl/x509v3.h> | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class SocketOpenSSL : public Socket | ||||
|     class SocketOpenSSL final : public Socket | ||||
|     { | ||||
|     public: | ||||
|         SocketOpenSSL(int fd = -1); | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     class SocketSChannel : public Socket | ||||
|     class SocketSChannel final : public Socket | ||||
|     { | ||||
|     public: | ||||
|         SocketSChannel(); | ||||
|   | ||||
| @@ -30,7 +30,9 @@ namespace ix | ||||
|         _host(host), | ||||
|         _backlog(backlog), | ||||
|         _maxConnections(maxConnections), | ||||
|         _serverFd(-1), | ||||
|         _stop(false), | ||||
|         _stopGc(false), | ||||
|         _connectionStateFactory(&ConnectionState::createConnectionState) | ||||
|     { | ||||
|  | ||||
| @@ -77,7 +79,7 @@ namespace ix | ||||
|                << "at address " << _host << ":" << _port | ||||
|                << " : " << strerror(Socket::getErrno()); | ||||
|  | ||||
|             ::close(_serverFd); | ||||
|             Socket::closeSocket(_serverFd); | ||||
|             return std::make_pair(false, ss.str()); | ||||
|         } | ||||
|  | ||||
| @@ -101,7 +103,7 @@ namespace ix | ||||
|                << "at address " << _host << ":" << _port | ||||
|                << " : " << strerror(Socket::getErrno()); | ||||
|  | ||||
|             ::close(_serverFd); | ||||
|             Socket::closeSocket(_serverFd); | ||||
|             return std::make_pair(false, ss.str()); | ||||
|         } | ||||
|  | ||||
| @@ -115,7 +117,7 @@ namespace ix | ||||
|                << "at address " << _host << ":" << _port | ||||
|                << " : " << strerror(Socket::getErrno()); | ||||
|  | ||||
|             ::close(_serverFd); | ||||
|             Socket::closeSocket(_serverFd); | ||||
|             return std::make_pair(false, ss.str()); | ||||
|         } | ||||
|  | ||||
| @@ -124,9 +126,15 @@ namespace ix | ||||
|  | ||||
|     void SocketServer::start() | ||||
|     { | ||||
|         if (_thread.joinable()) return; // we've already been started | ||||
|         if (!_thread.joinable()) | ||||
|         { | ||||
|             _thread = std::thread(&SocketServer::run, this); | ||||
|         } | ||||
|  | ||||
|         _thread = std::thread(&SocketServer::run, this); | ||||
|         if (!_gcThread.joinable()) | ||||
|         { | ||||
|             _gcThread = std::thread(&SocketServer::runGC, this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void SocketServer::wait() | ||||
| @@ -142,24 +150,24 @@ namespace ix | ||||
|  | ||||
|     void SocketServer::stop() | ||||
|     { | ||||
|         while (true) | ||||
|         // Stop accepting connections, and close the 'accept' thread | ||||
|         if (_thread.joinable()) | ||||
|         { | ||||
|             if (closeTerminatedThreads()) break; | ||||
|  | ||||
|             // wait 10ms and try again later. | ||||
|             // we could have a timeout, but if we exit of here | ||||
|             // we leaked threads, it is quite bad. | ||||
|             std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||||
|             _stop = true; | ||||
|             _thread.join(); | ||||
|             _stop = false; | ||||
|         } | ||||
|  | ||||
|         if (!_thread.joinable()) return; // nothing to do | ||||
|  | ||||
|         _stop = true; | ||||
|         _thread.join(); | ||||
|         _stop = false; | ||||
|         // Join all threads and make sure that all connections are terminated | ||||
|         if (_gcThread.joinable()) | ||||
|         { | ||||
|             _stopGc = true; | ||||
|             _gcThread.join(); | ||||
|             _stopGc = false; | ||||
|         } | ||||
|  | ||||
|         _conditionVariable.notify_one(); | ||||
|         ::close(_serverFd); | ||||
|         Socket::closeSocket(_serverFd); | ||||
|     } | ||||
|  | ||||
|     void SocketServer::setConnectionStateFactory( | ||||
| @@ -175,7 +183,7 @@ namespace ix | ||||
|     // field becomes true, and we can use that to know that we can join that thread | ||||
|     // and remove it from our _connectionsThreads data structure (a list). | ||||
|     // | ||||
|     bool SocketServer::closeTerminatedThreads() | ||||
|     void SocketServer::closeTerminatedThreads() | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); | ||||
|         auto it = _connectionsThreads.begin(); | ||||
| @@ -195,8 +203,6 @@ namespace ix | ||||
|             if (thread.joinable()) thread.join(); | ||||
|             it = _connectionsThreads.erase(it); | ||||
|         } | ||||
|  | ||||
|         return _connectionsThreads.empty(); | ||||
|     } | ||||
|  | ||||
|     void SocketServer::run() | ||||
| @@ -208,12 +214,6 @@ namespace ix | ||||
|         { | ||||
|             if (_stop) return; | ||||
|  | ||||
|             // Garbage collection to shutdown/join threads for closed connections. | ||||
|             // We could run this in its own thread, so that we dont need to accept | ||||
|             // a new connection to close a thread. | ||||
|             // We could also use a condition variable to be notify when we need to do this | ||||
|             closeTerminatedThreads(); | ||||
|  | ||||
|             // Use select to check whether a new connection is in progress | ||||
|             fd_set rfds; | ||||
|             struct timeval timeout; | ||||
| @@ -242,17 +242,18 @@ namespace ix | ||||
|             // Accept a connection. | ||||
|             struct sockaddr_in client; // client address information | ||||
|             int clientFd;              // socket connected to client | ||||
|             socklen_t addressLen = sizeof(socklen_t); | ||||
|             socklen_t addressLen = sizeof(client); | ||||
|             memset(&client, 0, sizeof(client)); | ||||
|  | ||||
|             if ((clientFd = accept(_serverFd, (struct sockaddr *)&client, &addressLen)) < 0) | ||||
|             { | ||||
|                 if (Socket::getErrno() != EWOULDBLOCK) | ||||
|                 if (!Socket::isWaitNeeded()) | ||||
|                 { | ||||
|                     // FIXME: that error should be propagated | ||||
|                     int err = Socket::getErrno(); | ||||
|                     std::stringstream ss; | ||||
|                     ss << "SocketServer::run() error accepting connection: " | ||||
|                        << strerror(Socket::getErrno()); | ||||
|                        << err << ", " << strerror(err); | ||||
|                     logError(ss.str()); | ||||
|                 } | ||||
|                 continue; | ||||
| @@ -266,7 +267,7 @@ namespace ix | ||||
|                    << "Not accepting connection"; | ||||
|                 logError(ss.str()); | ||||
|  | ||||
|                 ::close(clientFd); | ||||
|                 Socket::closeSocket(clientFd); | ||||
|  | ||||
|                 continue; | ||||
|             } | ||||
| @@ -280,7 +281,7 @@ namespace ix | ||||
|             if (_stop) return; | ||||
|  | ||||
|             // Launch the handleConnection work asynchronously in its own thread. | ||||
|             std::lock_guard<std::mutex> lock(_conditionVariableMutex); | ||||
|             std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); | ||||
|             _connectionsThreads.push_back(std::make_pair( | ||||
|                     connectionState, | ||||
|                     std::thread(&SocketServer::handleConnection, | ||||
| @@ -289,5 +290,30 @@ namespace ix | ||||
|                                 connectionState))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     size_t SocketServer::getConnectionsThreadsCount() | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lock(_connectionsThreadsMutex); | ||||
|         return _connectionsThreads.size(); | ||||
|     } | ||||
|  | ||||
|     void SocketServer::runGC() | ||||
|     { | ||||
|         for (;;) | ||||
|         { | ||||
|             // Garbage collection to shutdown/join threads for closed connections. | ||||
|             closeTerminatedThreads(); | ||||
|  | ||||
|             // We quit this thread if all connections are closed and we received | ||||
|             // a stop request by setting _stopGc to true. | ||||
|             if (_stopGc && getConnectionsThreadsCount() == 0) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             // Sleep a little bit then keep cleaning up | ||||
|             std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -74,6 +74,12 @@ namespace ix | ||||
|         // background thread to wait for incoming connections | ||||
|         std::atomic<bool> _stop; | ||||
|         std::thread _thread; | ||||
|         void run(); | ||||
|  | ||||
|         // background thread to cleanup (join) terminated threads | ||||
|         std::atomic<bool> _stopGc; | ||||
|         std::thread _gcThread; | ||||
|         void runGC(); | ||||
|  | ||||
|         // the list of (connectionState, threads) for each connections | ||||
|         ConnectionThreads _connectionsThreads; | ||||
| @@ -87,13 +93,12 @@ namespace ix | ||||
|         // the factory to create ConnectionState objects | ||||
|         ConnectionStateFactory _connectionStateFactory; | ||||
|  | ||||
|         // Methods | ||||
|         void run(); | ||||
|         virtual void handleConnection(int fd, | ||||
|                                       std::shared_ptr<ConnectionState> connectionState) = 0; | ||||
|         virtual size_t getConnectedClientsCount() = 0; | ||||
|  | ||||
|         // Returns true if all connection threads are joined | ||||
|         bool closeTerminatedThreads(); | ||||
|         void closeTerminatedThreads(); | ||||
|         size_t getConnectionsThreadsCount(); | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -5,43 +5,30 @@ | ||||
|  */ | ||||
|  | ||||
| #include "IXUrlParser.h" | ||||
|  | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
|  | ||||
| #include "LUrlParser.h" | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     // | ||||
|     // The only difference between those 2 regex is the protocol | ||||
|     // | ||||
|     std::regex UrlParser::_httpRegex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)"); | ||||
|     std::regex UrlParser::_webSocketRegex("(ws|wss)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)"); | ||||
|  | ||||
|     bool UrlParser::parse(const std::string& url, | ||||
|                           std::string& protocol, | ||||
|                           std::string& host, | ||||
|                           std::string& path, | ||||
|                           std::string& query, | ||||
|                           int& port, | ||||
|                           bool websocket) | ||||
|                           int& port) | ||||
|     { | ||||
|         std::cmatch what; | ||||
|         if (!regex_match(url.c_str(), what, | ||||
|                          websocket ? _webSocketRegex : _httpRegex)) | ||||
|         LUrlParser::clParseURL res = LUrlParser::clParseURL::ParseURL(url); | ||||
|  | ||||
|         if (!res.IsValid()) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         std::string portStr; | ||||
|         protocol = res.m_Scheme; | ||||
|         host     = res.m_Host; | ||||
|         path     = res.m_Path; | ||||
|         query    = res.m_Query; | ||||
|  | ||||
|         protocol = std::string(what[1].first, what[1].second); | ||||
|         host     = std::string(what[2].first, what[2].second); | ||||
|         portStr  = std::string(what[3].first, what[3].second); | ||||
|         path     = std::string(what[4].first, what[4].second); | ||||
|         query    = std::string(what[5].first, what[5].second); | ||||
|  | ||||
|         if (portStr.empty()) | ||||
|         if (!res.GetPort(&port)) | ||||
|         { | ||||
|             if (protocol == "ws" || protocol == "http") | ||||
|             { | ||||
| @@ -58,12 +45,6 @@ namespace ix | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << portStr; | ||||
|             ss >> port; | ||||
|         } | ||||
|  | ||||
|         if (path.empty()) | ||||
|         { | ||||
| @@ -83,22 +64,4 @@ namespace ix | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     void UrlParser::printUrl(const std::string& url, bool websocket) | ||||
|     { | ||||
|         std::string protocol, host, path, query; | ||||
|         int port {0}; | ||||
|  | ||||
|         if (!parse(url, protocol, host, path, query, port, websocket)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         std::cout << "[" << url << "]" << std::endl; | ||||
|         std::cout << protocol << std::endl; | ||||
|         std::cout << host << std::endl; | ||||
|         std::cout << port << std::endl; | ||||
|         std::cout << path << std::endl; | ||||
|         std::cout << query << std::endl; | ||||
|         std::cout << "-------------------------------" << std::endl; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <regex> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
| @@ -19,13 +18,6 @@ namespace ix | ||||
|                           std::string& host, | ||||
|                           std::string& path, | ||||
|                           std::string& query, | ||||
|                           int& port, | ||||
|                           bool websocket); | ||||
|  | ||||
|         static void printUrl(const std::string& url, bool websocket); | ||||
|  | ||||
|     private: | ||||
|         static std::regex _httpRegex; | ||||
|         static std::regex _webSocketRegex; | ||||
|                           int& port); | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -8,22 +8,26 @@ | ||||
| #include "IXSetThreadName.h" | ||||
| #include "IXWebSocketHandshake.h" | ||||
|  | ||||
| #include <iostream> | ||||
| #include <cmath> | ||||
| #include <cassert> | ||||
|  | ||||
| namespace | ||||
| { | ||||
|     uint64_t calculateRetryWaitMilliseconds(uint64_t retry_count) | ||||
|     uint64_t calculateRetryWaitMilliseconds(uint32_t retry_count) | ||||
|     { | ||||
|         // This will overflow quite fast for large value of retry_count | ||||
|         // and will become 0, in which case the wait time will be none | ||||
|         // and we'll be constantly retrying to connect. | ||||
|         uint64_t wait_time = ((uint64_t) std::pow(2, retry_count) * 100L); | ||||
|         uint64_t wait_time; | ||||
|  | ||||
|         // cap the wait time to 10s, or to retry_count == 10 for which wait_time > 10s | ||||
|         uint64_t tenSeconds = 10 * 1000; | ||||
|         return (wait_time > tenSeconds || retry_count > 10) ? tenSeconds : wait_time; | ||||
|         if (retry_count <= 6) | ||||
|         { | ||||
|             // max wait_time is 6400 ms (2 ^ 6 = 64) | ||||
|             wait_time = ((uint64_t)std::pow(2, retry_count) * 100L); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             wait_time = 10 * 1000; // 10 sec | ||||
|         } | ||||
|  | ||||
|         return wait_time; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -47,7 +51,7 @@ namespace ix | ||||
|         _ws.setOnCloseCallback( | ||||
|             [this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) | ||||
|             { | ||||
|                 _onMessageCallback(WebSocket_MessageType_Close, "", wireSize, | ||||
|                 _onMessageCallback(WebSocketMessageType::Close, "", wireSize, | ||||
|                                    WebSocketErrorInfo(), WebSocketOpenInfo(), | ||||
|                                    WebSocketCloseInfo(code, reason, remote)); | ||||
|             } | ||||
| @@ -140,24 +144,16 @@ namespace ix | ||||
|  | ||||
|     void WebSocket::stop() | ||||
|     { | ||||
|         bool automaticReconnection = _automaticReconnection; | ||||
|  | ||||
|         // This value needs to be forced when shutting down, it is restored later | ||||
|         _automaticReconnection = false; | ||||
|  | ||||
|         close(); | ||||
|  | ||||
|         if (!_thread.joinable()) | ||||
|         if (_thread.joinable()) | ||||
|         { | ||||
|             _automaticReconnection = automaticReconnection; | ||||
|             return; | ||||
|             // wait until working thread will exit | ||||
|             // it will exit after close operation is finished | ||||
|             _stop = true; | ||||
|             _thread.join(); | ||||
|             _stop = false; | ||||
|         } | ||||
|  | ||||
|         _stop = true; | ||||
|         _thread.join(); | ||||
|         _stop = false; | ||||
|  | ||||
|         _automaticReconnection = automaticReconnection; | ||||
|     } | ||||
|  | ||||
|     WebSocketInitResult WebSocket::connect(int timeoutSecs) | ||||
| @@ -176,7 +172,7 @@ namespace ix | ||||
|             return status; | ||||
|         } | ||||
|  | ||||
|         _onMessageCallback(WebSocket_MessageType_Open, "", 0, | ||||
|         _onMessageCallback(WebSocketMessageType::Open, "", 0, | ||||
|                            WebSocketErrorInfo(), | ||||
|                            WebSocketOpenInfo(status.uri, status.headers), | ||||
|                            WebSocketCloseInfo()); | ||||
| @@ -199,7 +195,7 @@ namespace ix | ||||
|             return status; | ||||
|         } | ||||
|  | ||||
|         _onMessageCallback(WebSocket_MessageType_Open, "", 0, | ||||
|         _onMessageCallback(WebSocketMessageType::Open, "", 0, | ||||
|                            WebSocketErrorInfo(), | ||||
|                            WebSocketOpenInfo(status.uri, status.headers), | ||||
|                            WebSocketCloseInfo()); | ||||
| @@ -208,75 +204,71 @@ namespace ix | ||||
|  | ||||
|     bool WebSocket::isConnected() const | ||||
|     { | ||||
|         return getReadyState() == WebSocket_ReadyState_Open; | ||||
|         return getReadyState() == ReadyState::Open; | ||||
|     } | ||||
|  | ||||
|     bool WebSocket::isClosing() const | ||||
|     { | ||||
|         return getReadyState() == WebSocket_ReadyState_Closing; | ||||
|         return getReadyState() == ReadyState::Closing; | ||||
|     } | ||||
|  | ||||
|     void WebSocket::close() | ||||
|     void WebSocket::close(uint16_t code, | ||||
|                           const std::string& reason) | ||||
|     { | ||||
|         _ws.close(); | ||||
|         _ws.close(code, reason); | ||||
|     } | ||||
|  | ||||
|     void WebSocket::reconnectPerpetuallyIfDisconnected() | ||||
|     void WebSocket::checkConnection(bool firstConnectionAttempt) | ||||
|     { | ||||
|         uint64_t retries = 0; | ||||
|         WebSocketErrorInfo connectErr; | ||||
|         ix::WebSocketInitResult status; | ||||
|         using millis = std::chrono::duration<double, std::milli>; | ||||
|  | ||||
|         uint32_t retries = 0; | ||||
|         millis duration; | ||||
|  | ||||
|         // Try to connect only once when we don't have automaticReconnection setup | ||||
|         if (!isConnected() && !isClosing() && !_stop && !_automaticReconnection) | ||||
|         // Try to connect perpertually | ||||
|         while (true) | ||||
|         { | ||||
|             status = connect(_handshakeTimeoutSecs); | ||||
|             if (isConnected() || isClosing() || _stop) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if (!firstConnectionAttempt && !_automaticReconnection) | ||||
|             { | ||||
|                 // Do not attempt to reconnect | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             firstConnectionAttempt = false; | ||||
|  | ||||
|             // Only sleep if we are retrying | ||||
|             if (duration.count() > 0) | ||||
|             { | ||||
|                 // to do: make sleeping conditional | ||||
|                 std::this_thread::sleep_for(duration); | ||||
|             } | ||||
|  | ||||
|             // Try to connect synchronously | ||||
|             ix::WebSocketInitResult status = connect(_handshakeTimeoutSecs); | ||||
|  | ||||
|             if (!status.success) | ||||
|             { | ||||
|                 duration = millis(calculateRetryWaitMilliseconds(retries++)); | ||||
|                 WebSocketErrorInfo connectErr; | ||||
|  | ||||
|                 connectErr.retries = retries; | ||||
|                 connectErr.wait_time = duration.count(); | ||||
|                 connectErr.reason = status.errorStr; | ||||
|                 connectErr.http_status = status.http_status; | ||||
|                 _onMessageCallback(WebSocket_MessageType_Error, "", 0, | ||||
|                                    connectErr, WebSocketOpenInfo(), | ||||
|                                    WebSocketCloseInfo()); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Otherwise try to reconnect perpertually | ||||
|             while (true) | ||||
|             { | ||||
|                 if (isConnected() || isClosing() || _stop || !_automaticReconnection) | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|                 status = connect(_handshakeTimeoutSecs); | ||||
|  | ||||
|                 if (!status.success) | ||||
|                 if (_automaticReconnection) | ||||
|                 { | ||||
|                     duration = millis(calculateRetryWaitMilliseconds(retries++)); | ||||
|  | ||||
|                     connectErr.retries = retries; | ||||
|                     connectErr.wait_time = duration.count(); | ||||
|                     connectErr.reason = status.errorStr; | ||||
|                     connectErr.http_status = status.http_status; | ||||
|                     _onMessageCallback(WebSocket_MessageType_Error, "", 0, | ||||
|                                        connectErr, WebSocketOpenInfo(), | ||||
|                                        WebSocketCloseInfo()); | ||||
|                      | ||||
|                     // Only sleep if we aren't in the middle of stopping | ||||
|                     if (!_stop) | ||||
|                     { | ||||
|                         std::this_thread::sleep_for(duration); | ||||
|                     } | ||||
|                     connectErr.retries = retries; | ||||
|                 } | ||||
|                  | ||||
|                 connectErr.reason      = status.errorStr; | ||||
|                 connectErr.http_status = status.http_status; | ||||
|  | ||||
|                 _onMessageCallback(WebSocketMessageType::Error, "", 0, | ||||
|                                    connectErr, WebSocketOpenInfo(), | ||||
|                                    WebSocketCloseInfo()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -285,22 +277,27 @@ namespace ix | ||||
|     { | ||||
|         setThreadName(getUrl()); | ||||
|  | ||||
|         bool firstConnectionAttempt = true; | ||||
|  | ||||
|         while (true) | ||||
|         { | ||||
|             if (_stop) return; | ||||
|  | ||||
|             // 1. Make sure we are always connected | ||||
|             reconnectPerpetuallyIfDisconnected(); | ||||
|             checkConnection(firstConnectionAttempt); | ||||
|  | ||||
|             if (_stop) return; | ||||
|             firstConnectionAttempt = false; | ||||
|  | ||||
|             // if here we are closed then checkConnection was not able to connect | ||||
|             if (getReadyState() == ReadyState::Closed) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             // 2. Poll to see if there's any new data available | ||||
|             _ws.poll(); | ||||
|  | ||||
|             if (_stop) return; | ||||
|             WebSocketTransport::PollResult pollResult = _ws.poll(); | ||||
|  | ||||
|             // 3. Dispatch the incoming messages | ||||
|             _ws.dispatch( | ||||
|                 pollResult, | ||||
|                 [this](const std::string& msg, | ||||
|                        size_t wireSize, | ||||
|                        bool decompressionError, | ||||
| @@ -309,24 +306,25 @@ namespace ix | ||||
|                     WebSocketMessageType webSocketMessageType; | ||||
|                     switch (messageKind) | ||||
|                     { | ||||
|                         case WebSocketTransport::MSG: | ||||
|                         default: | ||||
|                         case WebSocketTransport::MessageKind::MSG: | ||||
|                         { | ||||
|                             webSocketMessageType = WebSocket_MessageType_Message; | ||||
|                             webSocketMessageType = WebSocketMessageType::Message; | ||||
|                         } break; | ||||
|  | ||||
|                         case WebSocketTransport::PING: | ||||
|                         case WebSocketTransport::MessageKind::PING: | ||||
|                         { | ||||
|                             webSocketMessageType = WebSocket_MessageType_Ping; | ||||
|                             webSocketMessageType = WebSocketMessageType::Ping; | ||||
|                         } break; | ||||
|  | ||||
|                         case WebSocketTransport::PONG: | ||||
|                         case WebSocketTransport::MessageKind::PONG: | ||||
|                         { | ||||
|                             webSocketMessageType = WebSocket_MessageType_Pong; | ||||
|                             webSocketMessageType = WebSocketMessageType::Pong; | ||||
|                         } break; | ||||
|  | ||||
|                         case WebSocketTransport::FRAGMENT: | ||||
|                         case WebSocketTransport::MessageKind::FRAGMENT: | ||||
|                         { | ||||
|                             webSocketMessageType = WebSocket_MessageType_Fragment; | ||||
|                             webSocketMessageType = WebSocketMessageType::Fragment; | ||||
|                         } break; | ||||
|                     } | ||||
|  | ||||
| @@ -339,11 +337,6 @@ namespace ix | ||||
|  | ||||
|                     WebSocket::invokeTrafficTrackerCallback(msg.size(), true); | ||||
|                 }); | ||||
|  | ||||
|             // 4. In blocking mode, getting out of this function is triggered by | ||||
|             //    an explicit disconnection from the callback, or by the remote end | ||||
|             //    closing the connection, ie isConnected() == false. | ||||
|             if (!isConnected() && !_automaticReconnection) return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -370,10 +363,10 @@ namespace ix | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     WebSocketSendInfo WebSocket::send(const std::string& text, | ||||
|     WebSocketSendInfo WebSocket::send(const std::string& data, | ||||
|                                       const OnProgressCallback& onProgressCallback) | ||||
|     { | ||||
|         return sendMessage(text, SendMessageKind::Binary, onProgressCallback); | ||||
|         return sendMessage(data, SendMessageKind::Binary, onProgressCallback); | ||||
|     } | ||||
|  | ||||
|     WebSocketSendInfo WebSocket::sendText(const std::string& text, | ||||
| @@ -436,11 +429,11 @@ namespace ix | ||||
|     { | ||||
|         switch (_ws.getReadyState()) | ||||
|         { | ||||
|             case ix::WebSocketTransport::OPEN: return WebSocket_ReadyState_Open; | ||||
|             case ix::WebSocketTransport::CONNECTING: return WebSocket_ReadyState_Connecting; | ||||
|             case ix::WebSocketTransport::CLOSING: return WebSocket_ReadyState_Closing; | ||||
|             case ix::WebSocketTransport::CLOSED: return WebSocket_ReadyState_Closed; | ||||
|             default: return WebSocket_ReadyState_Closed; | ||||
|             case ix::WebSocketTransport::ReadyState::OPEN      : return ReadyState::Open; | ||||
|             case ix::WebSocketTransport::ReadyState::CONNECTING: return ReadyState::Connecting; | ||||
|             case ix::WebSocketTransport::ReadyState::CLOSING   : return ReadyState::Closing; | ||||
|             case ix::WebSocketTransport::ReadyState::CLOSED    : return ReadyState::Closed; | ||||
|             default: return ReadyState::Closed; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -448,11 +441,11 @@ namespace ix | ||||
|     { | ||||
|         switch (readyState) | ||||
|         { | ||||
|             case WebSocket_ReadyState_Open: return "OPEN"; | ||||
|             case WebSocket_ReadyState_Connecting: return "CONNECTING"; | ||||
|             case WebSocket_ReadyState_Closing: return "CLOSING"; | ||||
|             case WebSocket_ReadyState_Closed: return "CLOSED"; | ||||
|             default: return "CLOSED"; | ||||
|             case ReadyState::Open      : return "OPEN"; | ||||
|             case ReadyState::Connecting: return "CONNECTING"; | ||||
|             case ReadyState::Closing   : return "CLOSING"; | ||||
|             case ReadyState::Closed    : return "CLOSED"; | ||||
|             default: return "UNKNOWN"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -24,23 +24,23 @@ | ||||
| namespace ix | ||||
| { | ||||
|     // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants | ||||
|     enum ReadyState | ||||
|     enum class ReadyState | ||||
|     { | ||||
|         WebSocket_ReadyState_Connecting = 0, | ||||
|         WebSocket_ReadyState_Open = 1, | ||||
|         WebSocket_ReadyState_Closing = 2, | ||||
|         WebSocket_ReadyState_Closed = 3 | ||||
|         Connecting     = 0, | ||||
|         Open           = 1, | ||||
|         Closing        = 2, | ||||
|         Closed         = 3 | ||||
|     }; | ||||
|  | ||||
|     enum WebSocketMessageType | ||||
|     enum class WebSocketMessageType | ||||
|     { | ||||
|         WebSocket_MessageType_Message = 0, | ||||
|         WebSocket_MessageType_Open = 1, | ||||
|         WebSocket_MessageType_Close = 2, | ||||
|         WebSocket_MessageType_Error = 3, | ||||
|         WebSocket_MessageType_Ping = 4, | ||||
|         WebSocket_MessageType_Pong = 5, | ||||
|         WebSocket_MessageType_Fragment = 6 | ||||
|         Message        = 0, | ||||
|         Open           = 1, | ||||
|         Close          = 2, | ||||
|         Error          = 3, | ||||
|         Ping           = 4, | ||||
|         Pong           = 5, | ||||
|         Fragment       = 6 | ||||
|     }; | ||||
|  | ||||
|     struct WebSocketOpenInfo | ||||
| @@ -91,7 +91,6 @@ namespace ix | ||||
|  | ||||
|         void setUrl(const std::string& url); | ||||
|         void setPerMessageDeflateOptions(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions); | ||||
|         void setHandshakeTimeout(int handshakeTimeoutSecs); | ||||
|         void setHeartBeatPeriod(int heartBeatPeriodSecs); | ||||
|         void setPingInterval(int pingIntervalSecs); // alias of setHeartBeatPeriod | ||||
|         void setPingTimeout(int pingTimeoutSecs); | ||||
| @@ -100,24 +99,32 @@ namespace ix | ||||
|  | ||||
|         // Run asynchronously, by calling start and stop. | ||||
|         void start(); | ||||
|         // stop is synchronous | ||||
|         void stop(); | ||||
|  | ||||
|         // Run in blocking mode, by connecting first manually, and then calling run. | ||||
|         WebSocketInitResult connect(int timeoutSecs); | ||||
|         void run(); | ||||
|  | ||||
|         WebSocketSendInfo send(const std::string& text, | ||||
|         // send binary data | ||||
|         WebSocketSendInfo send(const std::string& data, | ||||
|                                const OnProgressCallback& onProgressCallback = nullptr); | ||||
|         WebSocketSendInfo sendText(const std::string& text, | ||||
|                                    const OnProgressCallback& onProgressCallback = nullptr); | ||||
|         WebSocketSendInfo ping(const std::string& text); | ||||
|         void close(); | ||||
|  | ||||
|         // A close frame can provide a code and a reason | ||||
|         // FIXME: use constants | ||||
|         void close(uint16_t code = 1000, | ||||
|                    const std::string& reason = "Normal closure"); | ||||
|  | ||||
|         void setOnMessageCallback(const OnMessageCallback& callback); | ||||
|         static void setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback); | ||||
|         static void resetTrafficTrackerCallback(); | ||||
|  | ||||
|         ReadyState getReadyState() const; | ||||
|         static std::string readyStateToString(ReadyState readyState); | ||||
|  | ||||
|         const std::string& getUrl() const; | ||||
|         const WebSocketPerMessageDeflateOptions& getPerMessageDeflateOptions() const; | ||||
|         int getHeartBeatPeriod() const; | ||||
| @@ -136,12 +143,10 @@ namespace ix | ||||
|  | ||||
|         bool isConnected() const; | ||||
|         bool isClosing() const; | ||||
|         void reconnectPerpetuallyIfDisconnected(); | ||||
|         std::string readyStateToString(ReadyState readyState); | ||||
|         void checkConnection(bool firstConnectionAttempt); | ||||
|         static void invokeTrafficTrackerCallback(size_t size, bool incoming); | ||||
|  | ||||
|         // Server | ||||
|         void setSocketFileDescriptor(int fd); | ||||
|         WebSocketInitResult connectToSocket(int fd, int timeoutSecs); | ||||
|  | ||||
|         WebSocketTransport _ws; | ||||
| @@ -154,7 +159,6 @@ namespace ix | ||||
|         static OnTrafficTrackerCallback _onTrafficTrackerCallback; | ||||
|  | ||||
|         std::atomic<bool> _stop; | ||||
|         std::atomic<bool> _backgroundThreadRunning; | ||||
|         std::atomic<bool> _automaticReconnection; | ||||
|         std::thread _thread; | ||||
|         std::mutex _writeMutex; | ||||
|   | ||||
| @@ -12,10 +12,10 @@ namespace ix | ||||
| { | ||||
|     struct WebSocketErrorInfo | ||||
|     { | ||||
|         uint64_t retries; | ||||
|         double wait_time; | ||||
|         int http_status; | ||||
|         uint32_t retries = 0; | ||||
|         double wait_time = 0; | ||||
|         int http_status = 0; | ||||
|         std::string reason; | ||||
|         bool decompressionError; | ||||
|         bool decompressionError = false; | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -10,9 +10,7 @@ | ||||
|  | ||||
| #include "libwshandshake.hpp" | ||||
|  | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
| #include <regex> | ||||
| #include <random> | ||||
| #include <algorithm> | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
| #include "IXWebSocketPerMessageDeflateCodec.h" | ||||
| #include "IXWebSocketPerMessageDeflateOptions.h" | ||||
|  | ||||
| #include <iostream> | ||||
| #include <cassert> | ||||
| #include <string.h> | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
| #include "IXWebSocketPerMessageDeflateOptions.h" | ||||
|  | ||||
| #include <sstream> | ||||
| #include <iostream> | ||||
| #include <algorithm> | ||||
| #include <cctype> | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,6 @@ namespace ix | ||||
|         WebSocketPerMessageDeflateOptions(std::string extension); | ||||
|  | ||||
|         std::string generateHeader(); | ||||
|         std::string parseHeader(); | ||||
|         bool enabled() const; | ||||
|         bool getClientNoContextTakeover() const; | ||||
|         bool getServerNoContextTakeover() const; | ||||
|   | ||||
| @@ -6,9 +6,6 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <iostream> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     struct WebSocketSendInfo | ||||
|   | ||||
| @@ -23,7 +23,7 @@ namespace ix | ||||
|     using OnConnectionCallback = std::function<void(std::shared_ptr<WebSocket>, | ||||
|                                                     std::shared_ptr<ConnectionState>)>; | ||||
|  | ||||
|     class WebSocketServer : public SocketServer { | ||||
|     class WebSocketServer final : public SocketServer { | ||||
|     public: | ||||
|         WebSocketServer(int port = SocketServer::kDefaultPort, | ||||
|                         const std::string& host = SocketServer::kDefaultHost, | ||||
|   | ||||
| @@ -45,7 +45,6 @@ | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <cstdarg> | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
| #include <chrono> | ||||
| #include <thread> | ||||
| @@ -72,28 +71,34 @@ namespace ix | ||||
|     const int WebSocketTransport::kDefaultPingIntervalSecs(-1); | ||||
|     const int WebSocketTransport::kDefaultPingTimeoutSecs(-1); | ||||
|     const bool WebSocketTransport::kDefaultEnablePong(true); | ||||
|     const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(200); | ||||
|     constexpr size_t WebSocketTransport::kChunkSize; | ||||
|  | ||||
|     const uint16_t WebSocketTransport::kInternalErrorCode(1011); | ||||
|     const uint16_t WebSocketTransport::kAbnormalCloseCode(1006); | ||||
|     const uint16_t WebSocketTransport::kProtocolErrorCode(1002); | ||||
|     const uint16_t WebSocketTransport::kNoStatusCodeErrorCode(1005); | ||||
|     const std::string WebSocketTransport::kInternalErrorMessage("Internal error"); | ||||
|     const std::string WebSocketTransport::kAbnormalCloseMessage("Abnormal closure"); | ||||
|     const std::string WebSocketTransport::kPingTimeoutMessage("Ping timeout"); | ||||
|     const std::string WebSocketTransport::kProtocolErrorMessage("Protocol error"); | ||||
|     const std::string WebSocketTransport::kNoStatusCodeErrorMessage("No status code"); | ||||
|  | ||||
|     WebSocketTransport::WebSocketTransport() : | ||||
|         _useMask(true), | ||||
|         _readyState(CLOSED), | ||||
|         _readyState(ReadyState::CLOSED), | ||||
|         _closeCode(kInternalErrorCode), | ||||
|         _closeReason(kInternalErrorMessage), | ||||
|         _closeWireSize(0), | ||||
|         _closeRemote(false), | ||||
|         _enablePerMessageDeflate(false), | ||||
|         _requestInitCancellation(false), | ||||
|         _closingTimePoint(std::chrono::steady_clock::now()), | ||||
|         _enablePong(kDefaultEnablePong), | ||||
|         _pingIntervalSecs(kDefaultPingIntervalSecs), | ||||
|         _pingTimeoutSecs(kDefaultPingTimeoutSecs), | ||||
|         _pingIntervalOrTimeoutGCDSecs(-1), | ||||
|         _nextGCDTimePoint(std::chrono::steady_clock::now()), | ||||
|         _lastSendPingTimePoint(std::chrono::steady_clock::now()), | ||||
|         _lastReceivePongTimePoint(std::chrono::steady_clock::now()) | ||||
|     { | ||||
| @@ -137,9 +142,8 @@ namespace ix | ||||
|     { | ||||
|         std::string protocol, host, path, query; | ||||
|         int port; | ||||
|         bool websocket = true; | ||||
|  | ||||
|         if (!UrlParser::parse(url, protocol, host, path, query, port, websocket)) | ||||
|         if (!UrlParser::parse(url, protocol, host, path, query, port)) | ||||
|         { | ||||
|             return WebSocketInitResult(false, 0, | ||||
|                                        std::string("Could not parse URL ") + url); | ||||
| @@ -164,7 +168,7 @@ namespace ix | ||||
|                                                          timeoutSecs); | ||||
|         if (result.success) | ||||
|         { | ||||
|             setReadyState(OPEN); | ||||
|             setReadyState(ReadyState::OPEN); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| @@ -192,22 +196,22 @@ namespace ix | ||||
|         auto result = webSocketHandshake.serverHandshake(fd, timeoutSecs); | ||||
|         if (result.success) | ||||
|         { | ||||
|             setReadyState(OPEN); | ||||
|             setReadyState(ReadyState::OPEN); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     WebSocketTransport::ReadyStateValues WebSocketTransport::getReadyState() const | ||||
|     WebSocketTransport::ReadyState WebSocketTransport::getReadyState() const | ||||
|     { | ||||
|         return _readyState; | ||||
|     } | ||||
|  | ||||
|     void WebSocketTransport::setReadyState(ReadyStateValues readyStateValue) | ||||
|     void WebSocketTransport::setReadyState(ReadyState readyState) | ||||
|     { | ||||
|         // No state change, return | ||||
|         if (_readyState == readyStateValue) return; | ||||
|         if (_readyState == readyState) return; | ||||
|  | ||||
|         if (readyStateValue == CLOSED) | ||||
|         if (readyState == ReadyState::CLOSED) | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lock(_closeDataMutex); | ||||
|             _onCloseCallback(_closeCode, _closeReason, _closeWireSize, _closeRemote); | ||||
| @@ -216,8 +220,12 @@ namespace ix | ||||
|             _closeWireSize = 0; | ||||
|             _closeRemote = false; | ||||
|         } | ||||
|         else if (readyState == ReadyState::OPEN) | ||||
|         { | ||||
|             initTimePointsAndGCDAfterConnect(); | ||||
|         } | ||||
|  | ||||
|         _readyState = readyStateValue; | ||||
|         _readyState = readyState; | ||||
|     } | ||||
|  | ||||
|     void WebSocketTransport::setOnCloseCallback(const OnCloseCallback& onCloseCallback) | ||||
| @@ -225,6 +233,23 @@ namespace ix | ||||
|         _onCloseCallback = onCloseCallback; | ||||
|     } | ||||
|  | ||||
|     void WebSocketTransport::initTimePointsAndGCDAfterConnect() | ||||
|     { | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lock(_lastSendPingTimePointMutex); | ||||
|             _lastSendPingTimePoint = std::chrono::steady_clock::now(); | ||||
|         }  | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lock(_lastReceivePongTimePointMutex); | ||||
|             _lastReceivePongTimePoint = std::chrono::steady_clock::now(); | ||||
|         } | ||||
|  | ||||
|         if (_pingIntervalOrTimeoutGCDSecs > 0) | ||||
|         { | ||||
|             _nextGCDTimePoint = std::chrono::steady_clock::now() + std::chrono::seconds(_pingIntervalOrTimeoutGCDSecs); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Only consider send PING time points for that computation. | ||||
|     bool WebSocketTransport::pingIntervalExceeded() | ||||
|     { | ||||
| @@ -246,11 +271,16 @@ namespace ix | ||||
|         return now - _lastReceivePongTimePoint > std::chrono::seconds(_pingTimeoutSecs); | ||||
|     } | ||||
|  | ||||
|     void WebSocketTransport::poll() | ||||
|     bool WebSocketTransport::closingDelayExceeded() | ||||
|     { | ||||
|         PollResultType pollResult = _socket->poll(_pingIntervalOrTimeoutGCDSecs); | ||||
|         std::lock_guard<std::mutex> lock(_closingTimePointMutex); | ||||
|         auto now = std::chrono::steady_clock::now(); | ||||
|         return now - _closingTimePoint > std::chrono::milliseconds(kClosingMaximumWaitingDelayInMs); | ||||
|     } | ||||
|  | ||||
|         if (_readyState == OPEN) | ||||
|     WebSocketTransport::PollResult WebSocketTransport::poll() | ||||
|     { | ||||
|         if (_readyState == ReadyState::OPEN) | ||||
|         { | ||||
|             // if (1) ping timeout is enabled and (2) duration since last received | ||||
|             // ping response (PONG) exceeds the maximum delay, then close the connection | ||||
| @@ -267,6 +297,34 @@ namespace ix | ||||
|                 sendPing(ss.str()); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // No timeout if state is not OPEN, otherwise computed | ||||
|         // pingIntervalOrTimeoutGCD (equals to -1 if no ping and no ping timeout are set) | ||||
|         int lastingTimeoutDelayInMs = (_readyState != ReadyState::OPEN) ? 0 : _pingIntervalOrTimeoutGCDSecs; | ||||
|  | ||||
|         if (_pingIntervalOrTimeoutGCDSecs > 0) | ||||
|         { | ||||
|             // compute lasting delay to wait for next ping / timeout, if at least one set | ||||
|             auto now = std::chrono::steady_clock::now(); | ||||
|  | ||||
|             if (now >= _nextGCDTimePoint) | ||||
|             { | ||||
|                 _nextGCDTimePoint = now + std::chrono::seconds(_pingIntervalOrTimeoutGCDSecs); | ||||
|              | ||||
|                 lastingTimeoutDelayInMs = _pingIntervalOrTimeoutGCDSecs * 1000; | ||||
|             } | ||||
|             else  | ||||
|             { | ||||
|                 lastingTimeoutDelayInMs = (int)std::chrono::duration_cast<std::chrono::milliseconds>(_nextGCDTimePoint - now).count(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|         if (lastingTimeoutDelayInMs <= 0) lastingTimeoutDelayInMs = 20; | ||||
| #endif | ||||
|  | ||||
|         // poll the socket | ||||
|         PollResultType pollResult = _socket->poll(lastingTimeoutDelayInMs); | ||||
|  | ||||
|         // Make sure we send all the buffered data | ||||
|         // there can be a lot of it for large messages. | ||||
| @@ -281,7 +339,7 @@ namespace ix | ||||
|                 if (result == PollResultType::Error) | ||||
|                 { | ||||
|                     _socket->close(); | ||||
|                     setReadyState(CLOSED); | ||||
|                     setReadyState(ReadyState::CLOSED); | ||||
|                     break; | ||||
|                 } | ||||
|                 else if (result == PollResultType::ReadyForWrite) | ||||
| @@ -296,24 +354,18 @@ namespace ix | ||||
|             { | ||||
|                 ssize_t ret = _socket->recv((char*)&_readbuf[0], _readbuf.size()); | ||||
|  | ||||
|                 if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK || | ||||
|                                 _socket->getErrno() == EAGAIN)) | ||||
|                 if (ret < 0 && Socket::isWaitNeeded()) | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
|                 else if (ret <= 0) | ||||
|                 { | ||||
|                     _rxbuf.clear(); | ||||
|                     // if there are received data pending to be processed, then delay the abnormal closure | ||||
|                     // to after dispatch (other close code/reason could be read from the buffer) | ||||
|                      | ||||
|                     _socket->close(); | ||||
|                     { | ||||
|                         std::lock_guard<std::mutex> lock(_closeDataMutex); | ||||
|                         _closeCode = kAbnormalCloseCode; | ||||
|                         _closeReason = kAbnormalCloseMessage; | ||||
|                         _closeWireSize = 0; | ||||
|                         _closeRemote = true; | ||||
|                     } | ||||
|                     setReadyState(CLOSED); | ||||
|                     break; | ||||
|  | ||||
|                     return PollResult::AbnormalClose; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
| @@ -332,12 +384,15 @@ namespace ix | ||||
|             _socket->close(); | ||||
|         } | ||||
|  | ||||
|         // Avoid a race condition where we get stuck in select | ||||
|         // while closing. | ||||
|         if (_readyState == CLOSING) | ||||
|         if (_readyState == ReadyState::CLOSING && closingDelayExceeded()) | ||||
|         { | ||||
|             _rxbuf.clear(); | ||||
|             // close code and reason were set when calling close() | ||||
|             _socket->close(); | ||||
|             setReadyState(ReadyState::CLOSED); | ||||
|         } | ||||
|  | ||||
|         return PollResult::Succeeded; | ||||
|     } | ||||
|  | ||||
|     bool WebSocketTransport::isSendBufferEmpty() const | ||||
| @@ -399,12 +454,13 @@ namespace ix | ||||
|     // |                     Payload Data continued ...                | | ||||
|     // +---------------------------------------------------------------+ | ||||
|     // | ||||
|     void WebSocketTransport::dispatch(const OnMessageCallback& onMessageCallback) | ||||
|     void WebSocketTransport::dispatch(WebSocketTransport::PollResult pollResult, | ||||
|                                       const OnMessageCallback& onMessageCallback) | ||||
|     { | ||||
|         while (true) | ||||
|         { | ||||
|             wsheader_type ws; | ||||
|             if (_rxbuf.size() < 2) return; /* Need at least 2 */ | ||||
|             if (_rxbuf.size() < 2) break; /* Need at least 2 */ | ||||
|             const uint8_t * data = (uint8_t *) &_rxbuf[0]; // peek, but don't consume | ||||
|             ws.fin = (data[0] & 0x80) == 0x80; | ||||
|             ws.rsv1 = (data[0] & 0x40) == 0x40; | ||||
| @@ -412,7 +468,7 @@ namespace ix | ||||
|             ws.mask = (data[1] & 0x80) == 0x80; | ||||
|             ws.N0 = (data[1] & 0x7f); | ||||
|             ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0); | ||||
|             if (_rxbuf.size() < ws.header_size) return; /* Need: ws.header_size - _rxbuf.size() */ | ||||
|             if (_rxbuf.size() < ws.header_size) break; /* Need: ws.header_size - _rxbuf.size() */ | ||||
|  | ||||
|             // | ||||
|             // Calculate payload length: | ||||
| @@ -485,7 +541,7 @@ namespace ix | ||||
|                 // | ||||
|                 if (ws.fin && _chunks.empty()) | ||||
|                 { | ||||
|                     emitMessage(MSG, | ||||
|                     emitMessage(MessageKind::MSG, | ||||
|                                 std::string(_rxbuf.begin()+ws.header_size, | ||||
|                                             _rxbuf.begin()+ws.header_size+(size_t) ws.N), | ||||
|                                 ws, | ||||
| @@ -505,12 +561,12 @@ namespace ix | ||||
|                                              _rxbuf.begin()+ws.header_size+(size_t)ws.N)); | ||||
|                     if (ws.fin) | ||||
|                     { | ||||
|                         emitMessage(MSG, getMergedChunks(), ws, onMessageCallback); | ||||
|                         emitMessage(MessageKind::MSG, getMergedChunks(), ws, onMessageCallback); | ||||
|                         _chunks.clear(); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         emitMessage(FRAGMENT, std::string(), ws, onMessageCallback); | ||||
|                         emitMessage(MessageKind::FRAGMENT, std::string(), ws, onMessageCallback); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -528,7 +584,7 @@ namespace ix | ||||
|                     sendData(wsheader_type::PONG, pingData, compress); | ||||
|                 } | ||||
|  | ||||
|                 emitMessage(PING, pingData, ws, onMessageCallback); | ||||
|                 emitMessage(MessageKind::PING, pingData, ws, onMessageCallback); | ||||
|             } | ||||
|             else if (ws.opcode == wsheader_type::PONG) | ||||
|             { | ||||
| @@ -539,24 +595,62 @@ namespace ix | ||||
|                 std::lock_guard<std::mutex> lck(_lastReceivePongTimePointMutex); | ||||
|                 _lastReceivePongTimePoint = std::chrono::steady_clock::now(); | ||||
|  | ||||
|                 emitMessage(PONG, pongData, ws, onMessageCallback); | ||||
|                 emitMessage(MessageKind::PONG, pongData, ws, onMessageCallback); | ||||
|             } | ||||
|             else if (ws.opcode == wsheader_type::CLOSE) | ||||
|             { | ||||
|                 std::string reason; | ||||
|                 uint16_t code = 0; | ||||
|  | ||||
|                 unmaskReceiveBuffer(ws); | ||||
|  | ||||
|                 // Extract the close code first, available as the first 2 bytes | ||||
|                 uint16_t code = 0; | ||||
|                 code |= ((uint64_t) _rxbuf[ws.header_size])   << 8; | ||||
|                 code |= ((uint64_t) _rxbuf[ws.header_size+1]) << 0; | ||||
|                 if (ws.N >= 2) | ||||
|                 { | ||||
|                     // Extract the close code first, available as the first 2 bytes | ||||
|                     code |= ((uint64_t) _rxbuf[ws.header_size])   << 8; | ||||
|                     code |= ((uint64_t) _rxbuf[ws.header_size+1]) << 0; | ||||
|  | ||||
|                 // Get the reason. | ||||
|                 std::string reason(_rxbuf.begin()+ws.header_size + 2, | ||||
|                                    _rxbuf.begin()+ws.header_size + (size_t) ws.N); | ||||
|                  | ||||
|                 bool remote = true; | ||||
|                     // Get the reason. | ||||
|                     if (ws.N > 2) | ||||
|                     { | ||||
|                         reason.assign(_rxbuf.begin()+ws.header_size + 2, | ||||
|                                       _rxbuf.begin()+ws.header_size + (size_t) ws.N); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // no close code received | ||||
|                     code = kNoStatusCodeErrorCode; | ||||
|                     reason = kNoStatusCodeErrorMessage; | ||||
|                 } | ||||
|  | ||||
|                 close(code, reason, _rxbuf.size(), remote); | ||||
|                 // We receive a CLOSE frame from remote and are NOT the ones who triggered the close | ||||
|                 if (_readyState != ReadyState::CLOSING) | ||||
|                 { | ||||
|                     // send back the CLOSE frame | ||||
|                     sendCloseFrame(code, reason); | ||||
|  | ||||
|                     _socket->wakeUpFromPoll(Socket::kCloseRequest); | ||||
|  | ||||
|                     bool remote = true; | ||||
|                     closeSocketAndSwitchToClosedState(code, reason, _rxbuf.size(), remote); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     // we got the CLOSE frame answer from our close, so we can close the connection if | ||||
|                     // the code/reason are the same | ||||
|                     bool identicalReason; | ||||
|                     { | ||||
|                         std::lock_guard<std::mutex> lock(_closeDataMutex); | ||||
|                         identicalReason = _closeCode == code && _closeReason == reason; | ||||
|                     } | ||||
|  | ||||
|                     if (identicalReason) | ||||
|                     { | ||||
|                         bool remote = false; | ||||
|                         closeSocketAndSwitchToClosedState(code, reason, _rxbuf.size(), remote); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
| @@ -569,6 +663,25 @@ namespace ix | ||||
|             _rxbuf.erase(_rxbuf.begin(), | ||||
|                          _rxbuf.begin() + ws.header_size + (size_t) ws.N); | ||||
|         } | ||||
|  | ||||
|         // if an abnormal closure was raised in poll, and nothing else triggered a CLOSED state in | ||||
|         // the received and processed data then close the connection | ||||
|         if (pollResult == PollResult::AbnormalClose) | ||||
|         { | ||||
|             _rxbuf.clear(); | ||||
|  | ||||
|             // if we previously closed the connection (CLOSING state), then set state to CLOSED (code/reason were set before) | ||||
|             if (_readyState == ReadyState::CLOSING) | ||||
|             { | ||||
|                 _socket->close(); | ||||
|                 setReadyState(ReadyState::CLOSED); | ||||
|             } | ||||
|             // if we weren't closing, then close using abnormal close code and message  | ||||
|             else if (_readyState != ReadyState::CLOSED) | ||||
|             { | ||||
|                 closeSocketAndSwitchToClosedState(kAbnormalCloseCode, kAbnormalCloseMessage, 0, false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::string WebSocketTransport::getMergedChunks() const | ||||
| @@ -599,7 +712,7 @@ namespace ix | ||||
|         size_t wireSize = message.size(); | ||||
|  | ||||
|         // When the RSV1 bit is 1 it means the message is compressed | ||||
|         if (_enablePerMessageDeflate && ws.rsv1 && messageKind != FRAGMENT) | ||||
|         if (_enablePerMessageDeflate && ws.rsv1 && messageKind != MessageKind::FRAGMENT) | ||||
|         { | ||||
|             std::string decompressedMessage; | ||||
|             bool success = _perMessageDeflate.decompress(message, decompressedMessage); | ||||
| @@ -626,7 +739,7 @@ namespace ix | ||||
|         bool compress, | ||||
|         const OnProgressCallback& onProgressCallback) | ||||
|     { | ||||
|         if (_readyState == CLOSING || _readyState == CLOSED) | ||||
|         if (_readyState != ReadyState::OPEN) | ||||
|         { | ||||
|             return WebSocketSendInfo(); | ||||
|         } | ||||
| @@ -844,8 +957,7 @@ namespace ix | ||||
|         { | ||||
|             ssize_t ret = _socket->send((char*)&_txbuf[0], _txbuf.size()); | ||||
|  | ||||
|             if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK || | ||||
|                             _socket->getErrno() == EAGAIN)) | ||||
|             if (ret < 0 && Socket::isWaitNeeded()) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
| @@ -853,7 +965,7 @@ namespace ix | ||||
|             { | ||||
|                 _socket->close(); | ||||
|  | ||||
|                 setReadyState(CLOSED); | ||||
|                 setReadyState(ReadyState::CLOSED); | ||||
|                 break; | ||||
|             } | ||||
|             else | ||||
| @@ -863,29 +975,32 @@ namespace ix | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void WebSocketTransport::close(uint16_t code, const std::string& reason, size_t closeWireSize, bool remote) | ||||
|     void WebSocketTransport::sendCloseFrame(uint16_t code, const std::string& reason) | ||||
|     { | ||||
|         _requestInitCancellation = true; | ||||
|  | ||||
|         if (_readyState == CLOSING || _readyState == CLOSED) return; | ||||
|  | ||||
|         // See list of close events here: | ||||
|         // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent | ||||
|  | ||||
|         int codeLength = 2; | ||||
|         std::string closure{(char)(code >> 8), (char)(code & 0xff)}; | ||||
|         closure.resize(codeLength + reason.size()); | ||||
|  | ||||
|         // copy reason after code | ||||
|         closure.replace(codeLength, reason.size(), reason); | ||||
|  | ||||
|         bool compress = false; | ||||
|         sendData(wsheader_type::CLOSE, closure, compress); | ||||
|         setReadyState(CLOSING); | ||||
|  | ||||
|         _socket->wakeUpFromPoll(Socket::kCloseRequest); | ||||
|         // if a status is set/was read | ||||
|         if (code != kNoStatusCodeErrorCode) | ||||
|         { | ||||
|             // See list of close events here: | ||||
|             // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent | ||||
|             std::string closure{(char)(code >> 8), (char)(code & 0xff)}; | ||||
|  | ||||
|             // copy reason after code | ||||
|             closure.append(reason); | ||||
|  | ||||
|             sendData(wsheader_type::CLOSE, closure, compress); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // no close code/reason set | ||||
|             sendData(wsheader_type::CLOSE, "", compress); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void WebSocketTransport::closeSocketAndSwitchToClosedState(uint16_t code, const std::string& reason, size_t closeWireSize, bool remote) | ||||
|     { | ||||
|         _socket->close(); | ||||
|  | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lock(_closeDataMutex); | ||||
|             _closeCode = code; | ||||
| @@ -893,8 +1008,31 @@ namespace ix | ||||
|             _closeWireSize = closeWireSize; | ||||
|             _closeRemote = remote; | ||||
|         } | ||||
|         setReadyState(ReadyState::CLOSED); | ||||
|     } | ||||
|  | ||||
|         setReadyState(CLOSED); | ||||
|     void WebSocketTransport::close(uint16_t code, const std::string& reason, size_t closeWireSize, bool remote) | ||||
|     { | ||||
|         _requestInitCancellation = true; | ||||
|  | ||||
|         if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return; | ||||
|  | ||||
|         sendCloseFrame(code, reason); | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lock(_closeDataMutex); | ||||
|             _closeCode = code; | ||||
|             _closeReason = reason; | ||||
|             _closeWireSize = closeWireSize; | ||||
|             _closeRemote = remote; | ||||
|         } | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lock(_closingTimePointMutex); | ||||
|             _closingTimePoint = std::chrono::steady_clock::now(); | ||||
|         } | ||||
|         setReadyState(ReadyState::CLOSING); | ||||
|  | ||||
|         // wake up the poll, but do not close yet | ||||
|         _socket->wakeUpFromPoll(Socket::kSendRequest); | ||||
|     } | ||||
|  | ||||
|     size_t WebSocketTransport::bufferedAmount() const | ||||
|   | ||||
| @@ -40,7 +40,7 @@ namespace ix | ||||
|     class WebSocketTransport | ||||
|     { | ||||
|     public: | ||||
|         enum ReadyStateValues | ||||
|         enum class ReadyState | ||||
|         { | ||||
|             CLOSING, | ||||
|             CLOSED, | ||||
| @@ -48,7 +48,7 @@ namespace ix | ||||
|             OPEN | ||||
|         }; | ||||
|  | ||||
|         enum MessageKind | ||||
|         enum class MessageKind | ||||
|         { | ||||
|             MSG, | ||||
|             PING, | ||||
| @@ -56,6 +56,12 @@ namespace ix | ||||
|             FRAGMENT | ||||
|         }; | ||||
|  | ||||
|         enum class PollResult | ||||
|         { | ||||
|             Succeeded, | ||||
|             AbnormalClose | ||||
|         }; | ||||
|  | ||||
|         using OnMessageCallback = std::function<void(const std::string&, | ||||
|                                                      size_t, | ||||
|                                                      bool, | ||||
| @@ -78,7 +84,7 @@ namespace ix | ||||
|         WebSocketInitResult connectToSocket(int fd,              // Server | ||||
|                                             int timeoutSecs); | ||||
|  | ||||
|         void poll(); | ||||
|         PollResult poll(); | ||||
|         WebSocketSendInfo sendBinary(const std::string& message, | ||||
|                                      const OnProgressCallback& onProgressCallback); | ||||
|         WebSocketSendInfo sendText(const std::string& message, | ||||
| @@ -90,10 +96,11 @@ namespace ix | ||||
|                    size_t closeWireSize = 0, | ||||
|                    bool remote = false); | ||||
|  | ||||
|         ReadyStateValues getReadyState() const; | ||||
|         void setReadyState(ReadyStateValues readyStateValue); | ||||
|         ReadyState getReadyState() const; | ||||
|         void setReadyState(ReadyState readyState); | ||||
|         void setOnCloseCallback(const OnCloseCallback& onCloseCallback); | ||||
|         void dispatch(const OnMessageCallback& onMessageCallback); | ||||
|         void dispatch(PollResult pollResult, | ||||
|                       const OnMessageCallback& onMessageCallback); | ||||
|         size_t bufferedAmount() const; | ||||
|  | ||||
|     private: | ||||
| @@ -106,11 +113,11 @@ namespace ix | ||||
|             bool mask; | ||||
|             enum opcode_type { | ||||
|                 CONTINUATION = 0x0, | ||||
|                 TEXT_FRAME = 0x1, | ||||
|                 TEXT_FRAME   = 0x1, | ||||
|                 BINARY_FRAME = 0x2, | ||||
|                 CLOSE = 8, | ||||
|                 PING = 9, | ||||
|                 PONG = 0xa, | ||||
|                 CLOSE        = 8, | ||||
|                 PING         = 9, | ||||
|                 PONG         = 0xa, | ||||
|             } opcode; | ||||
|             int N0; | ||||
|             uint64_t N; | ||||
| @@ -146,7 +153,7 @@ namespace ix | ||||
|         std::shared_ptr<Socket> _socket; | ||||
|  | ||||
|         // Hold the state of the connection (OPEN, CLOSED, etc...) | ||||
|         std::atomic<ReadyStateValues> _readyState; | ||||
|         std::atomic<ReadyState> _readyState; | ||||
|  | ||||
|         OnCloseCallback _onCloseCallback; | ||||
|         uint16_t _closeCode; | ||||
| @@ -162,18 +169,24 @@ namespace ix | ||||
|  | ||||
|         // Used to cancel dns lookup + socket connect + http upgrade | ||||
|         std::atomic<bool> _requestInitCancellation; | ||||
|                | ||||
|         mutable std::mutex _closingTimePointMutex; | ||||
|         std::chrono::time_point<std::chrono::steady_clock>_closingTimePoint; | ||||
|         static const int kClosingMaximumWaitingDelayInMs; | ||||
|  | ||||
|         // Constants for dealing with closing conneections | ||||
|         static const uint16_t kInternalErrorCode; | ||||
|         static const uint16_t kAbnormalCloseCode; | ||||
|         static const uint16_t kProtocolErrorCode; | ||||
|         static const uint16_t kNoStatusCodeErrorCode; | ||||
|         static const std::string kInternalErrorMessage; | ||||
|         static const std::string kAbnormalCloseMessage; | ||||
|         static const std::string kPingTimeoutMessage; | ||||
|         static const std::string kProtocolErrorMessage; | ||||
|         static const std::string kNoStatusCodeErrorMessage; | ||||
|  | ||||
|         // enable auto response to ping | ||||
|         bool _enablePong; | ||||
|         std::atomic<bool> _enablePong; | ||||
|         static const bool kDefaultEnablePong; | ||||
|  | ||||
|         // Optional ping and pong timeout | ||||
| @@ -187,6 +200,9 @@ namespace ix | ||||
|         static const int kDefaultPingTimeoutSecs; | ||||
|         static const std::string kPingMessage; | ||||
|  | ||||
|         // Record time step for ping/ ping timeout to ensure we wait for the right left duration | ||||
|         std::chrono::time_point<std::chrono::steady_clock> _nextGCDTimePoint; | ||||
|  | ||||
|         // We record when ping are being sent so that we can know when to send the next one | ||||
|         // We also record when pong are being sent as a reply to pings, to close the connections | ||||
|         // if no pong were received sufficiently fast. | ||||
| @@ -201,6 +217,18 @@ namespace ix | ||||
|         // No PONG data was received through the socket for longer than ping timeout delay | ||||
|         bool pingTimeoutExceeded(); | ||||
|  | ||||
|         // after calling close(), if no CLOSE frame answer is received back from the remote, we should close the connexion | ||||
|         bool closingDelayExceeded(); | ||||
|  | ||||
|         void initTimePointsAndGCDAfterConnect(); | ||||
|  | ||||
|         void sendCloseFrame(uint16_t code, const std::string& reason); | ||||
|  | ||||
|         void closeSocketAndSwitchToClosedState(uint16_t code, | ||||
|                                                const std::string& reason, | ||||
|                                                size_t closeWireSize, | ||||
|                                                bool remote); | ||||
|  | ||||
|         void sendOnSocket(); | ||||
|         WebSocketSendInfo sendData(wsheader_type::opcode_type type, | ||||
|                                    const std::string& message, | ||||
|   | ||||
							
								
								
									
										263
									
								
								ixwebsocket/LUrlParser.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								ixwebsocket/LUrlParser.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | ||||
| /* | ||||
|  * Lightweight URL & URI parser (RFC 1738, RFC 3986) | ||||
|  * https://github.com/corporateshark/LUrlParser | ||||
|  *  | ||||
|  * The MIT License (MIT) | ||||
|  *  | ||||
|  * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com) | ||||
|  *  | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  *  | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  *  | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "LUrlParser.h" | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <cstring> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| // check if the scheme name is valid | ||||
| static bool IsSchemeValid( const std::string& SchemeName ) | ||||
| { | ||||
| 	for ( auto c : SchemeName  ) | ||||
| 	{ | ||||
| 		if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LUrlParser::clParseURL::GetPort( int* OutPort ) const | ||||
| { | ||||
| 	if ( !IsValid() ) { return false; } | ||||
|  | ||||
| 	int Port = atoi( m_Port.c_str() ); | ||||
|  | ||||
| 	if ( Port <= 0 || Port > 65535 ) { return false; } | ||||
|  | ||||
| 	if ( OutPort ) { *OutPort = Port; } | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| // based on RFC 1738 and RFC 3986 | ||||
| LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL ) | ||||
| { | ||||
| 	LUrlParser::clParseURL Result; | ||||
|  | ||||
| 	const char* CurrentString = URL.c_str(); | ||||
|  | ||||
| 	/* | ||||
| 	 *	<scheme>:<scheme-specific-part> | ||||
| 	 *	<scheme> := [a-z\+\-\.]+ | ||||
| 	 *	For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names | ||||
| 	 */ | ||||
|  | ||||
| 	// try to read scheme | ||||
| 	{ | ||||
| 		const char* LocalString = strchr( CurrentString, ':' ); | ||||
|  | ||||
| 		if ( !LocalString ) | ||||
| 		{ | ||||
| 			return clParseURL( LUrlParserError_NoUrlCharacter ); | ||||
| 		} | ||||
|  | ||||
| 		// save the scheme name | ||||
| 		Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString ); | ||||
|  | ||||
| 		if ( !IsSchemeValid( Result.m_Scheme ) ) | ||||
| 		{ | ||||
| 			return clParseURL( LUrlParserError_InvalidSchemeName ); | ||||
| 		} | ||||
|  | ||||
| 		// scheme should be lowercase | ||||
| 		std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower ); | ||||
|  | ||||
| 		// skip ':' | ||||
| 		CurrentString = LocalString+1; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 *	//<user>:<password>@<host>:<port>/<url-path> | ||||
| 	 *	any ":", "@" and "/" must be normalized | ||||
| 	 */ | ||||
|  | ||||
| 	// skip "//" | ||||
| 	if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash ); | ||||
| 	if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash ); | ||||
|  | ||||
| 	// check if the user name and password are specified | ||||
| 	bool bHasUserName = false; | ||||
|  | ||||
| 	const char* LocalString = CurrentString; | ||||
|  | ||||
| 	while ( *LocalString ) | ||||
| 	{ | ||||
| 		if ( *LocalString == '@' ) | ||||
| 		{ | ||||
| 			// user name and password are specified | ||||
| 			bHasUserName = true; | ||||
| 			break; | ||||
| 		} | ||||
| 		else if ( *LocalString == '/' ) | ||||
| 		{ | ||||
| 			// end of <host>:<port> specification | ||||
| 			bHasUserName = false; | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		LocalString++; | ||||
| 	} | ||||
|  | ||||
| 	// user name and password | ||||
| 	LocalString = CurrentString; | ||||
|  | ||||
| 	if ( bHasUserName ) | ||||
| 	{ | ||||
| 		// read user name | ||||
| 		while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++; | ||||
|  | ||||
| 		Result.m_UserName = std::string( CurrentString, LocalString - CurrentString ); | ||||
|  | ||||
| 		// proceed with the current pointer | ||||
| 		CurrentString = LocalString; | ||||
|  | ||||
| 		if ( *CurrentString == ':' ) | ||||
| 		{ | ||||
| 			// skip ':' | ||||
| 			CurrentString++; | ||||
|  | ||||
| 			// read password | ||||
| 			LocalString = CurrentString; | ||||
|  | ||||
| 			while ( *LocalString && *LocalString != '@' ) LocalString++; | ||||
|  | ||||
| 			Result.m_Password = std::string( CurrentString, LocalString - CurrentString ); | ||||
|  | ||||
| 			CurrentString = LocalString; | ||||
| 		} | ||||
|  | ||||
| 		// skip '@' | ||||
| 		if ( *CurrentString != '@' ) | ||||
| 		{ | ||||
| 			return clParseURL( LUrlParserError_NoAtSign ); | ||||
| 		} | ||||
|  | ||||
| 		CurrentString++; | ||||
| 	} | ||||
|  | ||||
| 	bool bHasBracket = ( *CurrentString == '[' ); | ||||
|  | ||||
| 	// go ahead, read the host name | ||||
| 	LocalString = CurrentString; | ||||
|  | ||||
| 	while ( *LocalString ) | ||||
| 	{ | ||||
| 		if ( bHasBracket && *LocalString == ']' ) | ||||
| 		{ | ||||
| 			// end of IPv6 address | ||||
| 			LocalString++; | ||||
| 			break; | ||||
| 		} | ||||
| 		else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) ) | ||||
| 		{ | ||||
| 			// port number is specified | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		LocalString++; | ||||
| 	} | ||||
|  | ||||
| 	Result.m_Host = std::string( CurrentString, LocalString - CurrentString ); | ||||
|  | ||||
| 	CurrentString = LocalString; | ||||
|  | ||||
| 	// is port number specified? | ||||
| 	if ( *CurrentString == ':' ) | ||||
| 	{ | ||||
| 		CurrentString++; | ||||
|  | ||||
| 		// read port number | ||||
| 		LocalString = CurrentString; | ||||
|  | ||||
| 		while ( *LocalString && *LocalString != '/' ) LocalString++; | ||||
|  | ||||
| 		Result.m_Port = std::string( CurrentString, LocalString - CurrentString ); | ||||
|  | ||||
| 		CurrentString = LocalString; | ||||
| 	} | ||||
|  | ||||
| 	// end of string | ||||
| 	if ( !*CurrentString ) | ||||
| 	{ | ||||
| 		Result.m_ErrorCode = LUrlParserError_Ok; | ||||
|  | ||||
| 		return Result; | ||||
| 	} | ||||
|  | ||||
| 	// skip '/' | ||||
| 	if ( *CurrentString != '/' ) | ||||
| 	{ | ||||
| 		return clParseURL( LUrlParserError_NoSlash ); | ||||
| 	} | ||||
|  | ||||
| 	CurrentString++; | ||||
|  | ||||
| 	// parse the path | ||||
| 	LocalString = CurrentString; | ||||
|  | ||||
| 	while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++; | ||||
|  | ||||
| 	Result.m_Path = std::string( CurrentString, LocalString - CurrentString ); | ||||
|  | ||||
| 	CurrentString = LocalString; | ||||
|  | ||||
| 	// check for query | ||||
| 	if ( *CurrentString == '?' ) | ||||
| 	{ | ||||
| 		// skip '?' | ||||
| 		CurrentString++; | ||||
|  | ||||
| 		// read query | ||||
| 		LocalString = CurrentString; | ||||
|  | ||||
| 		while ( *LocalString && *LocalString != '#' ) LocalString++; | ||||
|  | ||||
| 		Result.m_Query = std::string( CurrentString, LocalString - CurrentString ); | ||||
|  | ||||
| 		CurrentString = LocalString; | ||||
| 	} | ||||
|  | ||||
| 	// check for fragment | ||||
| 	if ( *CurrentString == '#' ) | ||||
| 	{ | ||||
| 		// skip '#' | ||||
| 		CurrentString++; | ||||
|  | ||||
| 		// read fragment | ||||
| 		LocalString = CurrentString; | ||||
|  | ||||
| 		while ( *LocalString ) LocalString++; | ||||
|  | ||||
| 		Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString ); | ||||
| 	} | ||||
|  | ||||
| 	Result.m_ErrorCode = LUrlParserError_Ok; | ||||
|  | ||||
| 	return Result; | ||||
| } | ||||
							
								
								
									
										78
									
								
								ixwebsocket/LUrlParser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								ixwebsocket/LUrlParser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| /* | ||||
|  * Lightweight URL & URI parser (RFC 1738, RFC 3986) | ||||
|  * https://github.com/corporateshark/LUrlParser | ||||
|  *  | ||||
|  * The MIT License (MIT) | ||||
|  *  | ||||
|  * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com) | ||||
|  *  | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  *  | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  *  | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| namespace LUrlParser | ||||
| { | ||||
| 	enum LUrlParserError | ||||
| 	{ | ||||
| 		LUrlParserError_Ok = 0, | ||||
| 		LUrlParserError_Uninitialized = 1, | ||||
| 		LUrlParserError_NoUrlCharacter = 2, | ||||
| 		LUrlParserError_InvalidSchemeName = 3, | ||||
| 		LUrlParserError_NoDoubleSlash = 4, | ||||
| 		LUrlParserError_NoAtSign = 5, | ||||
| 		LUrlParserError_UnexpectedEndOfLine = 6, | ||||
| 		LUrlParserError_NoSlash = 7, | ||||
| 	}; | ||||
|  | ||||
| 	class clParseURL | ||||
| 	{ | ||||
| 	public: | ||||
| 		LUrlParserError m_ErrorCode; | ||||
| 		std::string m_Scheme; | ||||
| 		std::string m_Host; | ||||
| 		std::string m_Port; | ||||
| 		std::string m_Path; | ||||
| 		std::string m_Query; | ||||
| 		std::string m_Fragment; | ||||
| 		std::string m_UserName; | ||||
| 		std::string m_Password; | ||||
|  | ||||
| 		clParseURL() | ||||
| 			: m_ErrorCode( LUrlParserError_Uninitialized ) | ||||
| 		{} | ||||
|  | ||||
| 		/// return 'true' if the parsing was successful | ||||
| 		bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; } | ||||
|  | ||||
| 		/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range) | ||||
| 		bool GetPort( int* OutPort ) const; | ||||
|  | ||||
| 		/// parse the URL | ||||
| 		static clParseURL ParseURL( const std::string& URL ); | ||||
|  | ||||
| 	private: | ||||
| 		explicit clParseURL( LUrlParserError ErrorCode ) | ||||
| 			: m_ErrorCode( ErrorCode ) | ||||
| 		{} | ||||
| 	}; | ||||
|  | ||||
| } // namespace LUrlParser | ||||
| @@ -4,13 +4,41 @@ | ||||
|  *  Copyright (c) 2019 Machine Zone, Inc. All rights reserved. | ||||
|  */ | ||||
| #include "../IXSetThreadName.h" | ||||
| #include <iostream> | ||||
| #include <Windows.h> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     const DWORD MS_VC_EXCEPTION = 0x406D1388; | ||||
|  | ||||
| #pragma pack(push,8) | ||||
|     typedef struct tagTHREADNAME_INFO | ||||
|     { | ||||
|         DWORD dwType;     // Must be 0x1000. | ||||
|         LPCSTR szName;    // Pointer to name (in user addr space). | ||||
|         DWORD dwThreadID; // Thread ID (-1=caller thread). | ||||
|         DWORD dwFlags;    // Reserved for future use, must be zero. | ||||
|     } THREADNAME_INFO; | ||||
| #pragma pack(pop) | ||||
|  | ||||
|     void SetThreadName(DWORD dwThreadID, const char* threadName) | ||||
|     { | ||||
|         THREADNAME_INFO info; | ||||
|         info.dwType = 0x1000; | ||||
|         info.szName = threadName; | ||||
|         info.dwThreadID = dwThreadID; | ||||
|         info.dwFlags = 0; | ||||
|  | ||||
|         __try | ||||
|         { | ||||
|             RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)& info); | ||||
|         } | ||||
|         __except (EXCEPTION_EXECUTE_HANDLER) | ||||
|         { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void setThreadName(const std::string& name) | ||||
|     { | ||||
|         // FIXME | ||||
|         std::cerr << "setThreadName not implemented on Windows yet" << std::endl; | ||||
|         SetThreadName(-1, name.c_str()); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										7
									
								
								test/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								test/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,9 +1,10 @@ | ||||
| CMakeCache.txt | ||||
| package-lock.json | ||||
| CMakeFiles		 | ||||
| ixwebsocket_unittest	 | ||||
| cmake_install.cmake	 | ||||
| CMakeFiles | ||||
| ixwebsocket_unittest | ||||
| cmake_install.cmake | ||||
| node_modules | ||||
| ixwebsocket | ||||
| Makefile | ||||
| build | ||||
| ixwebsocket_unittest.xml | ||||
|   | ||||
| @@ -5,15 +5,13 @@ | ||||
| cmake_minimum_required (VERSION 3.4.1) | ||||
| project (ixwebsocket_unittest) | ||||
|  | ||||
| set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) | ||||
| find_package(Sanitizers) | ||||
|  | ||||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") | ||||
| set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") | ||||
|  | ||||
| set (CMAKE_CXX_STANDARD 14) | ||||
|  | ||||
| if (NOT WIN32) | ||||
| if (UNIX) | ||||
|   set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) | ||||
|   find_package(Sanitizers) | ||||
|   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") | ||||
|   set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") | ||||
|   option(USE_TLS "Add TLS support" ON) | ||||
| endif() | ||||
|  | ||||
| @@ -22,6 +20,8 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/.. ixwebsocket) | ||||
| include_directories( | ||||
|   ${PROJECT_SOURCE_DIR}/Catch2/single_include | ||||
|   ../third_party/msgpack11 | ||||
|   ../third_party/spdlog/include | ||||
|   ../ws | ||||
| ) | ||||
|  | ||||
| # Shared sources | ||||
| @@ -29,25 +29,33 @@ set (SOURCES | ||||
|   test_runner.cpp | ||||
|   IXTest.cpp | ||||
|   ../third_party/msgpack11/msgpack11.cpp | ||||
|   ../ws/ixcore/utils/IXCoreLogger.cpp | ||||
|  | ||||
|   IXDNSLookupTest.cpp | ||||
|   IXSocketTest.cpp | ||||
|   IXSocketConnectTest.cpp | ||||
|   IXWebSocketServerTest.cpp | ||||
|   IXWebSocketPingTest.cpp | ||||
|   IXWebSocketTestConnectionDisconnection.cpp | ||||
|   IXUrlParserTest.cpp | ||||
|   IXWebSocketServerTest.cpp | ||||
|   IXWebSocketPingTest.cpp | ||||
| ) | ||||
|  | ||||
| # Some unittest don't work on windows yet | ||||
| if (NOT WIN32) | ||||
|   list(APPEND SOURCES  | ||||
|     IXWebSocketServerTest.cpp | ||||
|     IXWebSocketPingTest.cpp | ||||
|     IXWebSocketPingTimeoutTest.cpp | ||||
| if (UNIX) | ||||
|   list(APPEND SOURCES | ||||
|     # IXWebSocketPingTimeoutTest.cpp # This test isn't reliable # (multiple platforms), disabling in master | ||||
|     # IXWebSocketCloseTest.cpp       #  | ||||
|     cmd_websocket_chat.cpp | ||||
|     IXWebSocketTestConnectionDisconnection.cpp | ||||
|   ) | ||||
| endif() | ||||
|  | ||||
| add_executable(ixwebsocket_unittest ${SOURCES}) | ||||
| add_sanitizers(ixwebsocket_unittest) | ||||
|  | ||||
| if (UNIX) | ||||
|   add_sanitizers(ixwebsocket_unittest) | ||||
| endif() | ||||
|  | ||||
| if (APPLE AND USE_TLS) | ||||
|     target_link_libraries(ixwebsocket_unittest "-framework foundation" "-framework security") | ||||
|   | ||||
| @@ -108,7 +108,7 @@ namespace ix | ||||
|         { | ||||
|             log("Cannot compute a free port. bind error."); | ||||
|  | ||||
|             ::close(sockfd); | ||||
|             Socket::closeSocket(sockfd); | ||||
|             return getAnyFreePortRandom(); | ||||
|         } | ||||
|  | ||||
| @@ -118,12 +118,12 @@ namespace ix | ||||
|         { | ||||
|             log("Cannot compute a free port. getsockname error."); | ||||
|  | ||||
|             ::close(sockfd); | ||||
|             Socket::closeSocket(sockfd); | ||||
|             return getAnyFreePortRandom(); | ||||
|         } | ||||
|  | ||||
|         int port = ntohs(sa.sin_port); | ||||
|         ::close(sockfd); | ||||
|         Socket::closeSocket(sockfd); | ||||
|  | ||||
|         return port; | ||||
|     } | ||||
|   | ||||
| @@ -27,15 +27,6 @@ namespace ix | ||||
|     struct Logger | ||||
|     { | ||||
|         public: | ||||
|             Logger& operator<<(const std::string& msg) | ||||
|             { | ||||
|                 std::lock_guard<std::mutex> lock(_mutex); | ||||
|  | ||||
|                 std::cerr << msg; | ||||
|                 std::cerr << std::endl; | ||||
|                 return *this; | ||||
|             } | ||||
|  | ||||
|             template <typename T> | ||||
|             Logger& operator<<(T const& obj) | ||||
|             { | ||||
|   | ||||
							
								
								
									
										108
									
								
								test/IXUrlParserTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								test/IXUrlParserTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| /* | ||||
|  *  IXSocketTest.cpp | ||||
|  *  Author: Korchynskyi Dmytro | ||||
|  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #include <iostream> | ||||
| #include <ixwebsocket/IXUrlParser.h> | ||||
|  | ||||
| #include "IXTest.h" | ||||
| #include "catch.hpp" | ||||
| #include <string.h> | ||||
|  | ||||
| using namespace ix; | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|  | ||||
| TEST_CASE("urlParser", "[urlParser]") | ||||
| { | ||||
|     SECTION("http://google.com") | ||||
|     { | ||||
|         std::string url = "http://google.com"; | ||||
|         std::string protocol, host, path, query; | ||||
|         int port; | ||||
|         bool res; | ||||
|  | ||||
|         res = UrlParser::parse(url, protocol, host, path, query, port); | ||||
|  | ||||
|         REQUIRE(res); | ||||
|         REQUIRE(protocol == "http"); | ||||
|         REQUIRE(host == "google.com"); | ||||
|         REQUIRE(path == "/"); | ||||
|         REQUIRE(query == ""); | ||||
|         REQUIRE(port == 80); // default port for http | ||||
|     } | ||||
|  | ||||
|     SECTION("https://google.com") | ||||
|     { | ||||
|         std::string url = "https://google.com"; | ||||
|         std::string protocol, host, path, query; | ||||
|         int port; | ||||
|         bool res; | ||||
|  | ||||
|         res = UrlParser::parse(url, protocol, host, path, query, port); | ||||
|  | ||||
|         REQUIRE(res); | ||||
|         REQUIRE(protocol == "https"); | ||||
|         REQUIRE(host == "google.com"); | ||||
|         REQUIRE(path == "/"); | ||||
|         REQUIRE(query == ""); | ||||
|         REQUIRE(port == 443); // default port for https | ||||
|     } | ||||
|  | ||||
|     SECTION("ws://google.com") | ||||
|     { | ||||
|         std::string url = "ws://google.com"; | ||||
|         std::string protocol, host, path, query; | ||||
|         int port; | ||||
|         bool res; | ||||
|  | ||||
|         res = UrlParser::parse(url, protocol, host, path, query, port); | ||||
|  | ||||
|         REQUIRE(res); | ||||
|         REQUIRE(protocol == "ws"); | ||||
|         REQUIRE(host == "google.com"); | ||||
|         REQUIRE(path == "/"); | ||||
|         REQUIRE(query == ""); | ||||
|         REQUIRE(port == 80); // default port for ws | ||||
|     } | ||||
|  | ||||
|     SECTION("wss://google.com/ws?arg=value") | ||||
|     { | ||||
|         std::string url = "wss://google.com/ws?arg=value&arg2=value2"; | ||||
|         std::string protocol, host, path, query; | ||||
|         int port; | ||||
|         bool res; | ||||
|  | ||||
|         res = UrlParser::parse(url, protocol, host, path, query, port); | ||||
|  | ||||
|         REQUIRE(res); | ||||
|         REQUIRE(protocol == "wss"); | ||||
|         REQUIRE(host == "google.com"); | ||||
|         REQUIRE(path == "/ws?arg=value&arg2=value2"); | ||||
|         REQUIRE(query == "arg=value&arg2=value2"); | ||||
|         REQUIRE(port == 443); // default port for wss | ||||
|     } | ||||
|  | ||||
|     SECTION("real test") | ||||
|     { | ||||
|         std::string url = "ws://127.0.0.1:7350/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf"; | ||||
|         std::string protocol, host, path, query; | ||||
|         int port; | ||||
|         bool res; | ||||
|  | ||||
|         res = UrlParser::parse(url, protocol, host, path, query, port); | ||||
|  | ||||
|         REQUIRE(res); | ||||
|         REQUIRE(protocol == "ws"); | ||||
|         REQUIRE(host == "127.0.0.1"); | ||||
|         REQUIRE(path == "/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf"); | ||||
|         REQUIRE(query == "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf"); | ||||
|         REQUIRE(port == 7350); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| } | ||||
							
								
								
									
										407
									
								
								test/IXWebSocketCloseTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										407
									
								
								test/IXWebSocketCloseTest.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,407 @@ | ||||
| /* | ||||
|  *  IXWebSocketCloseTest.cpp | ||||
|  *  Author: Alexandre Konieczny | ||||
|  *  Copyright (c) 2019 Machine Zone. All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
| #include <queue> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| #include <ixwebsocket/IXWebSocketServer.h> | ||||
|  | ||||
| #include "IXTest.h" | ||||
|  | ||||
| #include "catch.hpp" | ||||
|  | ||||
| using namespace ix; | ||||
|  | ||||
| namespace | ||||
| { | ||||
|     class WebSocketClient | ||||
|     { | ||||
|         public: | ||||
|             WebSocketClient(int port); | ||||
|  | ||||
|             void subscribe(const std::string& channel); | ||||
|             void start(); | ||||
|             void stop(); | ||||
|             void stop(uint16_t code, const std::string& reason); | ||||
|             bool isReady() const; | ||||
|             void sendMessage(const std::string& text); | ||||
|  | ||||
|             uint16_t getCloseCode(); | ||||
|             const std::string& getCloseReason(); | ||||
|             bool getCloseRemote(); | ||||
|  | ||||
|         private: | ||||
|             ix::WebSocket _webSocket; | ||||
|             int _port; | ||||
|  | ||||
|             mutable std::mutex _mutexCloseData; | ||||
|             uint16_t _closeCode; | ||||
|             std::string _closeReason; | ||||
|             bool _closeRemote; | ||||
|     }; | ||||
|  | ||||
|     WebSocketClient::WebSocketClient(int port) | ||||
|         : _port(port) | ||||
|         , _closeCode(0) | ||||
|         , _closeReason(std::string("")) | ||||
|         , _closeRemote(false) | ||||
|     { | ||||
|         ; | ||||
|     } | ||||
|  | ||||
|     bool WebSocketClient::isReady() const | ||||
|     { | ||||
|         return _webSocket.getReadyState() == ix::ReadyState::Open; | ||||
|     } | ||||
|  | ||||
|     uint16_t WebSocketClient::getCloseCode() | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||
|          | ||||
|         return _closeCode; | ||||
|     } | ||||
|  | ||||
|     const std::string& WebSocketClient::getCloseReason() | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||
|  | ||||
|         return _closeReason; | ||||
|     } | ||||
|  | ||||
|     bool WebSocketClient::getCloseRemote() | ||||
|     { | ||||
|         std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||
|          | ||||
|         return _closeRemote; | ||||
|     } | ||||
|  | ||||
|     void WebSocketClient::stop() | ||||
|     { | ||||
|         _webSocket.stop(); | ||||
|     } | ||||
|  | ||||
|     void WebSocketClient::stop(uint16_t code, const std::string& reason) | ||||
|     { | ||||
|         _webSocket.close(code, reason); | ||||
|         _webSocket.stop(); | ||||
|     } | ||||
|  | ||||
|     void WebSocketClient::start() | ||||
|     { | ||||
|         std::string url; | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "ws://localhost:" | ||||
|                << _port | ||||
|                << "/"; | ||||
|  | ||||
|             url = ss.str(); | ||||
|         } | ||||
|  | ||||
|         _webSocket.setUrl(url); | ||||
|  | ||||
|         std::stringstream ss; | ||||
|         log(std::string("Connecting to url: ") + url); | ||||
|  | ||||
|         _webSocket.setOnMessageCallback( | ||||
|             [this](ix::WebSocketMessageType messageType, | ||||
|                const std::string& str, | ||||
|                size_t wireSize, | ||||
|                const ix::WebSocketErrorInfo& error, | ||||
|                const ix::WebSocketOpenInfo& openInfo, | ||||
|                    const ix::WebSocketCloseInfo& closeInfo) | ||||
|             { | ||||
|                 std::stringstream ss; | ||||
|                 if (messageType == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     log("client connected"); | ||||
|  | ||||
|                     _webSocket.disableAutomaticReconnection(); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                 { | ||||
|                     log("client disconnected"); | ||||
|  | ||||
|                     std::lock_guard<std::mutex> lck(_mutexCloseData); | ||||
|                      | ||||
|                     _closeCode = closeInfo.code; | ||||
|                     _closeReason = std::string(closeInfo.reason); | ||||
|                     _closeRemote = closeInfo.remote; | ||||
|                      | ||||
|                     _webSocket.disableAutomaticReconnection(); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocketMessageType::Error) | ||||
|                 { | ||||
|                     ss << "Error ! " << error.reason; | ||||
|                     log(ss.str()); | ||||
|  | ||||
|                     _webSocket.disableAutomaticReconnection(); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocketMessageType::Pong) | ||||
|                 { | ||||
|                     ss << "Received pong message " << str; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocketMessageType::Ping) | ||||
|                 { | ||||
|                     ss << "Received ping message " << str; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     ss << "Received message " << str; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ss << "Invalid ix::WebSocketMessageType"; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         _webSocket.start(); | ||||
|     } | ||||
|  | ||||
|     void WebSocketClient::sendMessage(const std::string& text) | ||||
|     { | ||||
|         _webSocket.send(text); | ||||
|     } | ||||
|  | ||||
|     bool startServer(ix::WebSocketServer& server, | ||||
|                      uint16_t& receivedCloseCode, | ||||
|                      std::string& receivedCloseReason, | ||||
|                      bool& receivedCloseRemote, | ||||
|                      std::mutex& mutexWrite) | ||||
|     { | ||||
|         // A dev/null server | ||||
|         server.setOnConnectionCallback( | ||||
|             [&server, &receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](std::shared_ptr<ix::WebSocket> webSocket, | ||||
|                                              std::shared_ptr<ConnectionState> connectionState) | ||||
|             { | ||||
|                 webSocket->setOnMessageCallback( | ||||
|                     [webSocket, connectionState, &server, &receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](ix::WebSocketMessageType messageType, | ||||
|                        const std::string& str, | ||||
|                        size_t wireSize, | ||||
|                        const ix::WebSocketErrorInfo& error, | ||||
|                        const ix::WebSocketOpenInfo& openInfo, | ||||
|                        const ix::WebSocketCloseInfo& closeInfo) | ||||
|                     { | ||||
|                         if (messageType == ix::WebSocketMessageType::Open) | ||||
|                         { | ||||
|                             Logger() << "New server connection"; | ||||
|                             Logger() << "id: " << connectionState->getId(); | ||||
|                             Logger() << "Uri: " << openInfo.uri; | ||||
|                             Logger() << "Headers:"; | ||||
|                             for (auto it : openInfo.headers) | ||||
|                             { | ||||
|                                 Logger() << it.first << ": " << it.second; | ||||
|                             } | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                         { | ||||
|                             log("Server closed connection"); | ||||
|  | ||||
|                             //Logger() << closeInfo.code; | ||||
|                             //Logger() << closeInfo.reason; | ||||
|                             //Logger() << closeInfo.remote; | ||||
|  | ||||
|                             std::lock_guard<std::mutex> lck(mutexWrite); | ||||
|                              | ||||
|                             receivedCloseCode = closeInfo.code; | ||||
|                             receivedCloseReason = std::string(closeInfo.reason); | ||||
|                             receivedCloseRemote = closeInfo.remote; | ||||
|                         } | ||||
|                     } | ||||
|                 ); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         auto res = server.listen(); | ||||
|         if (!res.first) | ||||
|         { | ||||
|             log(res.second); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         server.start(); | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_client_close_default", "[close]") | ||||
| { | ||||
|     SECTION("Make sure that close code and reason was used and sent to server.") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|          | ||||
|         uint16_t serverReceivedCloseCode(0); | ||||
|         bool serverReceivedCloseRemote(false); | ||||
|         std::string serverReceivedCloseReason(""); | ||||
|         std::mutex mutexWrite; | ||||
|  | ||||
|         REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         WebSocketClient webSocketClient(port); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         ix::msleep(100); | ||||
|  | ||||
|         webSocketClient.stop(); | ||||
|  | ||||
|         ix::msleep(200); | ||||
|  | ||||
|         // ensure client close is the same as values given | ||||
|         REQUIRE(webSocketClient.getCloseCode() == 1000); | ||||
|         REQUIRE(webSocketClient.getCloseReason() == "Normal closure"); | ||||
|         REQUIRE(webSocketClient.getCloseRemote() == false); | ||||
|  | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lck(mutexWrite); | ||||
|              | ||||
|             // Here we read the code/reason received by the server, and ensure that remote is true | ||||
|             REQUIRE(serverReceivedCloseCode == 1000); | ||||
|             REQUIRE(serverReceivedCloseReason == "Normal closure"); | ||||
|             REQUIRE(serverReceivedCloseRemote == true); | ||||
|         } | ||||
|  | ||||
|         // Give us 1000ms for the server to notice that clients went away | ||||
|         ix::msleep(1000); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_client_close_params_given", "[close]") | ||||
| { | ||||
|     SECTION("Make sure that close code and reason was used and sent to server.") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|          | ||||
|         uint16_t serverReceivedCloseCode(0); | ||||
|         bool serverReceivedCloseRemote(false); | ||||
|         std::string serverReceivedCloseReason(""); | ||||
|         std::mutex mutexWrite; | ||||
|  | ||||
|         REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         WebSocketClient webSocketClient(port); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         ix::msleep(100); | ||||
|  | ||||
|         webSocketClient.stop(4000, "My reason"); | ||||
|  | ||||
|         ix::msleep(500); | ||||
|  | ||||
|         // ensure client close is the same as values given | ||||
|         REQUIRE(webSocketClient.getCloseCode() == 4000); | ||||
|         REQUIRE(webSocketClient.getCloseReason() == "My reason"); | ||||
|         REQUIRE(webSocketClient.getCloseRemote() == false); | ||||
|  | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lck(mutexWrite); | ||||
|              | ||||
|             // Here we read the code/reason received by the server, and ensure that remote is true | ||||
|             REQUIRE(serverReceivedCloseCode == 4000); | ||||
|             REQUIRE(serverReceivedCloseReason == "My reason"); | ||||
|             REQUIRE(serverReceivedCloseRemote == true); | ||||
|         } | ||||
|  | ||||
|         // Give us 1000ms for the server to notice that clients went away | ||||
|         ix::msleep(1000); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_server_close", "[close]") | ||||
| { | ||||
|     SECTION("Make sure that close code and reason was read from server.") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|          | ||||
|         uint16_t serverReceivedCloseCode(0); | ||||
|         bool serverReceivedCloseRemote(false); | ||||
|         std::string serverReceivedCloseReason(""); | ||||
|         std::mutex mutexWrite; | ||||
|  | ||||
|         REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         WebSocketClient webSocketClient(port); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         ix::msleep(200); | ||||
|  | ||||
|         server.stop(); | ||||
|  | ||||
|         ix::msleep(500); | ||||
|  | ||||
|         // ensure client close is the same as values given | ||||
|         REQUIRE(webSocketClient.getCloseCode() == 1000); | ||||
|         REQUIRE(webSocketClient.getCloseReason() == "Normal closure"); | ||||
|         REQUIRE(webSocketClient.getCloseRemote() == true); | ||||
|  | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lck(mutexWrite); | ||||
|              | ||||
|             // Here we read the code/reason received by the server, and ensure that remote is true | ||||
|             REQUIRE(serverReceivedCloseCode == 1000); | ||||
|             REQUIRE(serverReceivedCloseReason == "Normal closure"); | ||||
|             REQUIRE(serverReceivedCloseRemote == false); | ||||
|         } | ||||
|  | ||||
|         // Give us 1000ms for the server to notice that clients went away | ||||
|         ix::msleep(1000); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
| @@ -23,7 +23,6 @@ namespace | ||||
|         public: | ||||
|             WebSocketClient(int port, bool useHeartBeatMethod); | ||||
|  | ||||
|             void subscribe(const std::string& channel); | ||||
|             void start(); | ||||
|             void stop(); | ||||
|             bool isReady() const; | ||||
| @@ -44,7 +43,7 @@ namespace | ||||
|  | ||||
|     bool WebSocketClient::isReady() const | ||||
|     { | ||||
|         return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Open; | ||||
|         return _webSocket.getReadyState() == ix::ReadyState::Open; | ||||
|     } | ||||
|  | ||||
|     void WebSocketClient::stop() | ||||
| @@ -57,7 +56,7 @@ namespace | ||||
|         std::string url; | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "ws://localhost:" | ||||
|             ss << "ws://127.0.0.1:" | ||||
|                << _port | ||||
|                << "/"; | ||||
|  | ||||
| @@ -69,9 +68,13 @@ namespace | ||||
|         // The important bit for this test. | ||||
|         // Set a 1 second heartbeat with the setter method to test | ||||
|         if (_useHeartBeatMethod) | ||||
|         { | ||||
|             _webSocket.setHeartBeatPeriod(1); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             _webSocket.setPingInterval(1); | ||||
|         } | ||||
|  | ||||
|         std::stringstream ss; | ||||
|         log(std::string("Connecting to url: ") + url); | ||||
| @@ -85,33 +88,32 @@ namespace | ||||
|                    const ix::WebSocketCloseInfo& closeInfo) | ||||
|             { | ||||
|                 std::stringstream ss; | ||||
|                 if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                 if (messageType == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     log("client connected"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                 { | ||||
|                     log("client disconnected"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Error) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Error) | ||||
|                 { | ||||
|                     ss << "Error ! " << error.reason; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Pong) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Pong) | ||||
|                 { | ||||
|                     ss << "Received pong message " << str; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Ping) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Ping) | ||||
|                 { | ||||
|                     ss << "Received ping message " << str; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Message) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     ss << "Received message " << str; | ||||
|                     log(ss.str()); | ||||
|                     // too many messages to log | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
| @@ -143,7 +145,7 @@ namespace | ||||
|                        const ix::WebSocketOpenInfo& openInfo, | ||||
|                        const ix::WebSocketCloseInfo& closeInfo) | ||||
|                     { | ||||
|                         if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                         if (messageType == ix::WebSocketMessageType::Open) | ||||
|                         { | ||||
|                             Logger() << "New server connection"; | ||||
|                             Logger() << "id: " << connectionState->getId(); | ||||
| @@ -154,15 +156,23 @@ namespace | ||||
|                                 Logger() << it.first << ": " << it.second; | ||||
|                             } | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                         { | ||||
|                             log("Server closed connection"); | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Ping) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Ping) | ||||
|                         { | ||||
|                             log("Server received a ping"); | ||||
|                             receivedPingMessages++; | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                         { | ||||
|                             // to many messages to log | ||||
|                             for(auto client: server.getClients()) | ||||
|                             { | ||||
|                                 client->sendText("reply"); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 ); | ||||
|             } | ||||
| @@ -180,96 +190,6 @@ namespace | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]") | ||||
| { | ||||
|     SECTION("Make sure that ping messages are sent when no other data are sent.") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|         std::atomic<int> serverReceivedPingMessages(0); | ||||
|         REQUIRE(startServer(server, serverReceivedPingMessages)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         bool useSetHeartBeatPeriodMethod = true; | ||||
|         WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         ix::msleep(1900); | ||||
|  | ||||
|         webSocketClient.stop(); | ||||
|  | ||||
|  | ||||
|         // Here we test ping interval | ||||
|         // -> expected ping messages == 1 as 1900 seconds, 1 ping sent every second | ||||
|         REQUIRE(serverReceivedPingMessages == 1); | ||||
|  | ||||
|         // Give us 500ms for the server to notice that clients went away | ||||
|         ix::msleep(500); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]") | ||||
| { | ||||
|     SECTION("Make sure that ping messages are sent, even if other messages are sent") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|         std::atomic<int> serverReceivedPingMessages(0); | ||||
|         REQUIRE(startServer(server, serverReceivedPingMessages)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         bool useSetHeartBeatPeriodMethod = true; | ||||
|         WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         ix::msleep(900); | ||||
|         webSocketClient.sendMessage("hello world"); | ||||
|         ix::msleep(900); | ||||
|         webSocketClient.sendMessage("hello world"); | ||||
|         ix::msleep(1100); | ||||
|  | ||||
|         webSocketClient.stop(); | ||||
|  | ||||
|         // Here we test ping interval | ||||
|         // client has sent data, but ping should have been sent no matter what | ||||
|         // -> expected ping messages == 2 as 900+900+1100 = 2900 seconds, 1 ping sent every second | ||||
|         REQUIRE(serverReceivedPingMessages == 2); | ||||
|  | ||||
|         // Give us 500ms for the server to notice that clients went away | ||||
|         ix::msleep(500); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_ping_no_data_sent_setPingInterval", "[setPingInterval]") | ||||
| { | ||||
|     SECTION("Make sure that ping messages are sent when no other data are sent.") | ||||
| @@ -359,3 +279,203 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]") | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterval]") | ||||
| { | ||||
|     SECTION("Make sure that ping messages are sent, even if other messages are sent continuously during a given time") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|         std::atomic<int> serverReceivedPingMessages(0); | ||||
|         REQUIRE(startServer(server, serverReceivedPingMessages)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval | ||||
|         WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(10); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         // send continuously for 1100ms | ||||
|         auto now = std::chrono::steady_clock::now(); | ||||
|  | ||||
|         while(std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(900)) | ||||
|         { | ||||
|             webSocketClient.sendMessage("message"); | ||||
|             ix::msleep(1); | ||||
|         } | ||||
|         ix::msleep(150); | ||||
|  | ||||
|         // Here we test ping interval | ||||
|         // client has sent data, but ping should have been sent no matter what | ||||
|         // -> expected ping messages == 1, as 900+150 = 1050ms, 1 ping sent every second | ||||
|         REQUIRE(serverReceivedPingMessages == 1); | ||||
|  | ||||
|         ix::msleep(100); | ||||
|  | ||||
|         webSocketClient.stop(); | ||||
|  | ||||
|         // Give us 500ms for the server to notice that clients went away | ||||
|         ix::msleep(500); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]") | ||||
| { | ||||
|     SECTION("Make sure that ping messages are sent, even if other messages are sent continuously for longer than ping interval") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|         std::atomic<int> serverReceivedPingMessages(0); | ||||
|         REQUIRE(startServer(server, serverReceivedPingMessages)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval | ||||
|         WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(1); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         // send continuously for 1100ms | ||||
|         auto now = std::chrono::steady_clock::now(); | ||||
|  | ||||
|         while(std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(1100)) | ||||
|         { | ||||
|             webSocketClient.sendMessage("message"); | ||||
|             ix::msleep(1); | ||||
|         } | ||||
|  | ||||
|         // Here we test ping interval | ||||
|         // client has sent data, but ping should have been sent no matter what | ||||
|         // -> expected ping messages == 1, 1 ping sent every second | ||||
|         REQUIRE(serverReceivedPingMessages == 1); | ||||
|  | ||||
|         ix::msleep(100); | ||||
|  | ||||
|         webSocketClient.stop(); | ||||
|  | ||||
|         // Give us 500ms for the server to notice that clients went away | ||||
|         ix::msleep(500); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Using setHeartBeatPeriod | ||||
|  | ||||
| TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]") | ||||
| { | ||||
|     SECTION("Make sure that ping messages are sent when no other data are sent.") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|         std::atomic<int> serverReceivedPingMessages(0); | ||||
|         REQUIRE(startServer(server, serverReceivedPingMessages)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         bool useSetHeartBeatPeriodMethod = true; | ||||
|         WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(1); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         ix::msleep(1900); | ||||
|  | ||||
|         webSocketClient.stop(); | ||||
|  | ||||
|  | ||||
|         // Here we test ping interval | ||||
|         // -> expected ping messages == 1 as 1900 seconds, 1 ping sent every second | ||||
|         REQUIRE(serverReceivedPingMessages == 1); | ||||
|  | ||||
|         // Give us 500ms for the server to notice that clients went away | ||||
|         ix::msleep(500); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]") | ||||
| { | ||||
|     SECTION("Make sure that ping messages are sent, even if other messages are sent") | ||||
|     { | ||||
|         ix::setupWebSocketTrafficTrackerCallback(); | ||||
|  | ||||
|         int port = getFreePort(); | ||||
|         ix::WebSocketServer server(port); | ||||
|         std::atomic<int> serverReceivedPingMessages(0); | ||||
|         REQUIRE(startServer(server, serverReceivedPingMessages)); | ||||
|  | ||||
|         std::string session = ix::generateSessionId(); | ||||
|         bool useSetHeartBeatPeriodMethod = true; | ||||
|         WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod); | ||||
|  | ||||
|         webSocketClient.start(); | ||||
|  | ||||
|         // Wait for all chat instance to be ready | ||||
|         while (true) | ||||
|         { | ||||
|             if (webSocketClient.isReady()) break; | ||||
|             ix::msleep(1); | ||||
|         } | ||||
|  | ||||
|         REQUIRE(server.getClients().size() == 1); | ||||
|  | ||||
|         ix::msleep(900); | ||||
|         webSocketClient.sendMessage("hello world"); | ||||
|         ix::msleep(900); | ||||
|         webSocketClient.sendMessage("hello world"); | ||||
|         ix::msleep(1100); | ||||
|  | ||||
|         webSocketClient.stop(); | ||||
|  | ||||
|         // without this sleep test fails on Windows | ||||
|         ix::msleep(100); | ||||
|  | ||||
|         // Here we test ping interval | ||||
|         // client has sent data, but ping should have been sent no matter what | ||||
|         // -> expected ping messages == 2 as 900+900+1100 = 2900 seconds, 1 ping sent every second | ||||
|         REQUIRE(serverReceivedPingMessages == 2); | ||||
|  | ||||
|         // Give us 500ms for the server to notice that clients went away | ||||
|         ix::msleep(500); | ||||
|         REQUIRE(server.getClients().size() == 0); | ||||
|  | ||||
|         ix::reportWebSocketTraffic(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,7 +23,6 @@ namespace | ||||
|         public: | ||||
|             WebSocketClient(int port, int pingInterval, int pingTimeout); | ||||
|  | ||||
|             void subscribe(const std::string& channel); | ||||
|             void start(); | ||||
|             void stop(); | ||||
|             bool isReady() const; | ||||
| @@ -53,12 +52,12 @@ namespace | ||||
|  | ||||
|     bool WebSocketClient::isReady() const | ||||
|     { | ||||
|         return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Open; | ||||
|         return _webSocket.getReadyState() == ix::ReadyState::Open; | ||||
|     } | ||||
|  | ||||
|     bool WebSocketClient::isClosed() const | ||||
|     { | ||||
|         return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Closed; | ||||
|         return _webSocket.getReadyState() == ix::ReadyState::Closed; | ||||
|     } | ||||
|  | ||||
|     void WebSocketClient::stop() | ||||
| @@ -71,7 +70,7 @@ namespace | ||||
|         std::string url; | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "ws://localhost:" | ||||
|             ss << "ws://127.0.0.1:" | ||||
|                << _port | ||||
|                << "/"; | ||||
|  | ||||
| @@ -79,6 +78,7 @@ namespace | ||||
|         } | ||||
|  | ||||
|         _webSocket.setUrl(url); | ||||
|         _webSocket.disableAutomaticReconnection(); | ||||
|  | ||||
|         // The important bit for this test. | ||||
|         // Set a ping interval, and a ping timeout | ||||
| @@ -97,13 +97,12 @@ namespace | ||||
|                    const ix::WebSocketCloseInfo& closeInfo) | ||||
|             { | ||||
|                 std::stringstream ss; | ||||
|                 if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                 if (messageType == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     log("client connected"); | ||||
|  | ||||
|                     _webSocket.disableAutomaticReconnection(); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                 { | ||||
|                     log("client disconnected"); | ||||
|  | ||||
| @@ -112,29 +111,25 @@ namespace | ||||
|                         _closedDueToPingTimeout = true; | ||||
|                     } | ||||
|  | ||||
|                     _webSocket.disableAutomaticReconnection(); | ||||
|  | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Error) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Error) | ||||
|                 { | ||||
|                     ss << "Error ! " << error.reason; | ||||
|                     log(ss.str()); | ||||
|  | ||||
|                     _webSocket.disableAutomaticReconnection(); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Pong) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Pong) | ||||
|                 { | ||||
|                     _receivedPongMessages++; | ||||
|  | ||||
|                     ss << "Received pong message " << str; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Ping) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Ping) | ||||
|                 { | ||||
|                     ss << "Received ping message " << str; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Message) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     ss << "Received message " << str; | ||||
|                     log(ss.str()); | ||||
| @@ -179,7 +174,7 @@ namespace | ||||
|                        const ix::WebSocketOpenInfo& openInfo, | ||||
|                        const ix::WebSocketCloseInfo& closeInfo) | ||||
|                     { | ||||
|                         if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                         if (messageType == ix::WebSocketMessageType::Open) | ||||
|                         { | ||||
|                             Logger() << "New server connection"; | ||||
|                             Logger() << "id: " << connectionState->getId(); | ||||
| @@ -190,11 +185,11 @@ namespace | ||||
|                                 Logger() << it.first << ": " << it.second; | ||||
|                             } | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                         { | ||||
|                             log("Server closed connection"); | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Ping) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Ping) | ||||
|                         { | ||||
|                             log("Server received a ping"); | ||||
|                             receivedPingMessages++; | ||||
| @@ -415,7 +410,7 @@ TEST_CASE("Websocket_ping_timeout", "[setPingTimeout]") | ||||
|         REQUIRE(serverReceivedPingMessages == 1); | ||||
|         REQUIRE(webSocketClient.getReceivedPongMessages() == 0); | ||||
|  | ||||
|         ix::msleep(1000); | ||||
|         ix::msleep(1100); | ||||
|  | ||||
|         // Here we test ping timeout, timeout | ||||
|         REQUIRE(serverReceivedPingMessages == 1); | ||||
|   | ||||
| @@ -50,7 +50,7 @@ namespace ix | ||||
|                        const ix::WebSocketOpenInfo& openInfo, | ||||
|                        const ix::WebSocketCloseInfo& closeInfo) | ||||
|                     { | ||||
|                         if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                         if (messageType == ix::WebSocketMessageType::Open) | ||||
|                         { | ||||
|                             Logger() << "New connection"; | ||||
|                             connectionState->computeId(); | ||||
| @@ -64,11 +64,11 @@ namespace ix | ||||
|  | ||||
|                             connectionId = connectionState->getId(); | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                         { | ||||
|                             Logger() << "Closed connection"; | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Message) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                         { | ||||
|                             for (auto&& client : server.getClients()) | ||||
|                             { | ||||
| @@ -107,7 +107,7 @@ TEST_CASE("Websocket_server", "[websocket_server]") | ||||
|         std::string errMsg; | ||||
|         bool tls = false; | ||||
|         std::shared_ptr<Socket> socket = createSocket(tls, errMsg); | ||||
|         std::string host("localhost"); | ||||
|         std::string host("127.0.0.1"); | ||||
|         auto isCancellationRequested = []() -> bool | ||||
|         { | ||||
|             return false; | ||||
| @@ -141,7 +141,7 @@ TEST_CASE("Websocket_server", "[websocket_server]") | ||||
|         std::string errMsg; | ||||
|         bool tls = false; | ||||
|         std::shared_ptr<Socket> socket = createSocket(tls, errMsg); | ||||
|         std::string host("localhost"); | ||||
|         std::string host("127.0.0.1"); | ||||
|         auto isCancellationRequested = []() -> bool | ||||
|         { | ||||
|             return false; | ||||
| @@ -178,7 +178,7 @@ TEST_CASE("Websocket_server", "[websocket_server]") | ||||
|         std::string errMsg; | ||||
|         bool tls = false; | ||||
|         std::shared_ptr<Socket> socket = createSocket(tls, errMsg); | ||||
|         std::string host("localhost"); | ||||
|         std::string host("127.0.0.1"); | ||||
|         auto isCancellationRequested = []() -> bool | ||||
|         { | ||||
|             return false; | ||||
|   | ||||
| @@ -60,30 +60,36 @@ namespace | ||||
|                const ix::WebSocketCloseInfo& closeInfo) | ||||
|             { | ||||
|                 std::stringstream ss; | ||||
|                 if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                 if (messageType == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     log("cmd_websocket_satori_chat: connected !"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                 { | ||||
|                     log("cmd_websocket_satori_chat: disconnected !"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Error) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Error) | ||||
|                 { | ||||
|                     log("cmd_websocket_satori_chat: Error!"); | ||||
|                     ss << "cmd_websocket_satori_chat: Error! "; | ||||
|                     ss << error.reason; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Message) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     log("cmd_websocket_satori_chat: received message.!"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Ping) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Ping) | ||||
|                 { | ||||
|                     log("cmd_websocket_satori_chat: received ping message.!"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Pong) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Pong) | ||||
|                 { | ||||
|                     log("cmd_websocket_satori_chat: received pong message.!"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocketMessageType::Fragment) | ||||
|                 { | ||||
|                     log("cmd_websocket_satori_chat: received fragment.!"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     log("Invalid ix::WebSocketMessageType"); | ||||
| @@ -103,26 +109,40 @@ TEST_CASE("websocket_connections", "[websocket]") | ||||
| { | ||||
|     SECTION("Try to connect to invalid servers.") | ||||
|     { | ||||
|         IXWebSocketTestConnectionDisconnection chatA; | ||||
|         IXWebSocketTestConnectionDisconnection test; | ||||
|  | ||||
|         chatA.start(GOOGLE_URL); | ||||
|         test.start(GOOGLE_URL); | ||||
|         ix::msleep(1000); | ||||
|         chatA.stop(); | ||||
|         test.stop(); | ||||
|  | ||||
|         chatA.start(UNKNOWN_URL); | ||||
|         test.start(UNKNOWN_URL); | ||||
|         ix::msleep(1000); | ||||
|         chatA.stop(); | ||||
|         test.stop(); | ||||
|     } | ||||
|  | ||||
|     SECTION("Try to connect and disconnect with different timing.") | ||||
|     SECTION("Try to connect and disconnect with different timing, not enough time to succesfully connect") | ||||
|     { | ||||
|         IXWebSocketTestConnectionDisconnection chatA; | ||||
|         IXWebSocketTestConnectionDisconnection test; | ||||
|         for (int i = 0; i < 50; ++i) | ||||
|         { | ||||
|             log(std::string("Run: ") + std::to_string(i)); | ||||
|             chatA.start(WEBSOCKET_DOT_ORG_URL); | ||||
|             test.start(WEBSOCKET_DOT_ORG_URL); | ||||
|             ix::msleep(i); | ||||
|             chatA.stop(); | ||||
|             test.stop(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // This test breaks on travis CI - Ubuntu Xenial + gcc + tsan | ||||
|     // We should fix this. | ||||
|     /*SECTION("Try to connect and disconnect with different timing, from not enough time to successfull connect") | ||||
|     { | ||||
|         IXWebSocketTestConnectionDisconnection test; | ||||
|         for (int i = 0; i < 20; ++i) | ||||
|         { | ||||
|             log(std::string("Run: ") + std::to_string(i)); | ||||
|             test.start(WEBSOCKET_DOT_ORG_URL); | ||||
|             ix::msleep(i*50); | ||||
|             test.stop(); | ||||
|         } | ||||
|     }*/ | ||||
| } | ||||
|   | ||||
| @@ -87,7 +87,7 @@ namespace | ||||
|  | ||||
|     bool WebSocketChat::isReady() const | ||||
|     { | ||||
|         return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Open; | ||||
|         return _webSocket.getReadyState() == ix::ReadyState::Open; | ||||
|     } | ||||
|  | ||||
|     void WebSocketChat::stop() | ||||
| @@ -100,7 +100,7 @@ namespace | ||||
|         std::string url; | ||||
|         { | ||||
|             std::stringstream ss; | ||||
|             ss << "ws://localhost:" | ||||
|             ss << "ws://127.0.0.1:" | ||||
|                << _port | ||||
|                << "/" | ||||
|                << _user; | ||||
| @@ -122,21 +122,21 @@ namespace | ||||
|                    const ix::WebSocketCloseInfo& closeInfo) | ||||
|             { | ||||
|                 std::stringstream ss; | ||||
|                 if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                 if (messageType == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     ss << "cmd_websocket_chat: user " | ||||
|                        << _user | ||||
|                        << " Connected !"; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                 { | ||||
|                     ss << "cmd_websocket_chat: user " | ||||
|                        << _user | ||||
|                        << " disconnected !"; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Message) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                 { | ||||
|                     auto result = decodeMessage(str); | ||||
|  | ||||
| @@ -159,20 +159,20 @@ namespace | ||||
|                        << _user << " > "; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Error) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Error) | ||||
|                 { | ||||
|                     ss << "cmd_websocket_chat: Error ! " << error.reason; | ||||
|                     log(ss.str()); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Ping) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Ping) | ||||
|                 { | ||||
|                     log("cmd_websocket_chat: received ping message"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Pong) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Pong) | ||||
|                 { | ||||
|                     log("cmd_websocket_chat: received pong message"); | ||||
|                 } | ||||
|                 else if (messageType == ix::WebSocket_MessageType_Fragment) | ||||
|                 else if (messageType == ix::WebSocketMessageType::Fragment) | ||||
|                 { | ||||
|                     log("cmd_websocket_chat: received message fragment"); | ||||
|                 } | ||||
| @@ -228,7 +228,7 @@ namespace | ||||
|                        const ix::WebSocketOpenInfo& openInfo, | ||||
|                        const ix::WebSocketCloseInfo& closeInfo) | ||||
|                     { | ||||
|                         if (messageType == ix::WebSocket_MessageType_Open) | ||||
|                         if (messageType == ix::WebSocketMessageType::Open) | ||||
|                         { | ||||
|                             Logger() << "New connection"; | ||||
|                             Logger() << "id: " << connectionState->getId(); | ||||
| @@ -239,11 +239,11 @@ namespace | ||||
|                                 Logger() << it.first << ": " << it.second; | ||||
|                             } | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Close) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Close) | ||||
|                         { | ||||
|                             log("Closed connection"); | ||||
|                         } | ||||
|                         else if (messageType == ix::WebSocket_MessageType_Message) | ||||
|                         else if (messageType == ix::WebSocketMessageType::Message) | ||||
|                         { | ||||
|                             for (auto&& client : server.getClients()) | ||||
|                             { | ||||
|   | ||||
							
								
								
									
										105
									
								
								test/run.py
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								test/run.py
									
									
									
									
									
								
							| @@ -28,9 +28,9 @@ try: | ||||
| except ImportError: | ||||
|     hasClick = False | ||||
|  | ||||
|  | ||||
| DEFAULT_EXE = 'ixwebsocket_unittest' | ||||
|  | ||||
| BUILD_TYPE = 'Debug' | ||||
| XML_OUTPUT_FILE = 'ixwebsocket_unittest.xml' | ||||
| TEST_EXE_PATH = None | ||||
|  | ||||
| class Command(object): | ||||
|     """Run system commands with timeout | ||||
| @@ -65,7 +65,7 @@ class Command(object): | ||||
|             return True, self.process.returncode | ||||
|  | ||||
|  | ||||
| def runCommand(cmd, assertOnFailure=True, timeout=None): | ||||
| def runCommand(cmd, abortOnFailure=True, timeout=None): | ||||
|     '''Small wrapper to run a command and make sure it succeed''' | ||||
|  | ||||
|     if timeout is None: | ||||
| @@ -73,16 +73,13 @@ def runCommand(cmd, assertOnFailure=True, timeout=None): | ||||
|  | ||||
|     print('\nRunning', cmd) | ||||
|     command = Command(cmd) | ||||
|     timedout, ret = command.run(timeout) | ||||
|     succeed, ret = command.run(timeout) | ||||
|  | ||||
|     if timedout: | ||||
|         print('Unittest timed out') | ||||
|  | ||||
|     msg = 'cmd {} failed with error code {}'.format(cmd, ret) | ||||
|     if ret != 0: | ||||
|     if not succeed or ret != 0: | ||||
|         msg = 'cmd {}\nfailed with error code {}'.format(cmd, ret) | ||||
|         print(msg) | ||||
|         if assertOnFailure: | ||||
|             assert False | ||||
|         if abortOnFailure: | ||||
|             sys.exit(-1) | ||||
|  | ||||
|  | ||||
| def runCMake(sanitizer, buildDir): | ||||
| @@ -91,12 +88,6 @@ def runCMake(sanitizer, buildDir): | ||||
|     (remove build sub-folder). | ||||
|     ''' | ||||
|  | ||||
|     # CMake installed via Self Service ends up here. | ||||
|     cmake_executable = '/Applications/CMake.app/Contents/bin/cmake' | ||||
|  | ||||
|     if not os.path.exists(cmake_executable): | ||||
|         cmake_executable = 'cmake' | ||||
|  | ||||
|     sanitizersFlags = { | ||||
|         'asan': '-DSANITIZE_ADDRESS=On', | ||||
|         'ubsan': '-DSANITIZE_UNDEFINED=On', | ||||
| @@ -110,19 +101,22 @@ def runCMake(sanitizer, buildDir): | ||||
|     if not os.path.exists(cmakeExecutable): | ||||
|         cmakeExecutable = 'cmake' | ||||
|  | ||||
|     generator = '"Unix Makefiles"' | ||||
|     if platform.system() == 'Windows': | ||||
|         generator = '"NMake Makefiles"' | ||||
|         #generator = '"NMake Makefiles"' | ||||
|         generator = '"Visual Studio 16 2019"' | ||||
|     else: | ||||
|         generator = '"Unix Makefiles"' | ||||
|  | ||||
|     fmt = ''' | ||||
| {cmakeExecutable} -H. \ | ||||
|     CMAKE_BUILD_TYPE = BUILD_TYPE | ||||
|  | ||||
|     fmt = '{cmakeExecutable} -H. \ | ||||
|     {sanitizerFlag} \ | ||||
|     -B{buildDir} \ | ||||
|     -DCMAKE_BUILD_TYPE=Debug \ | ||||
|     -B"{buildDir}" \ | ||||
|     -DCMAKE_BUILD_TYPE={CMAKE_BUILD_TYPE} \ | ||||
|     -DUSE_TLS=1 \ | ||||
|     -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ | ||||
|     -G{generator} | ||||
| ''' | ||||
|     -G{generator}' | ||||
|  | ||||
|     cmakeCmd = fmt.format(**locals()) | ||||
|     runCommand(cmakeCmd) | ||||
|  | ||||
| @@ -133,10 +127,10 @@ def runTest(args, buildDir, xmlOutput, testRunName): | ||||
|     if args is None: | ||||
|         args = '' | ||||
|  | ||||
|     fmt = '{buildDir}/{DEFAULT_EXE} -o {xmlOutput} -n "{testRunName}" -r junit "{args}"' | ||||
|     testCommand = fmt.format(**locals()) | ||||
|     testCommand = '{} -o {} -n "{}" -r junit "{}"'.format(TEST_EXE_PATH, xmlOutput, testRunName, args) | ||||
|  | ||||
|     runCommand(testCommand, | ||||
|                assertOnFailure=False) | ||||
|                abortOnFailure=False) | ||||
|  | ||||
|  | ||||
| def validateTestSuite(xmlOutput): | ||||
| @@ -296,8 +290,7 @@ def executeJobs(jobs): | ||||
| def computeAllTestNames(buildDir): | ||||
|     '''Compute all test case names, by executing the unittest in a custom mode''' | ||||
|  | ||||
|     executable = os.path.join(buildDir, DEFAULT_EXE) | ||||
|     cmd = '"{}" --list-test-names-only'.format(executable) | ||||
|     cmd = '"{}" --list-test-names-only'.format(TEST_EXE_PATH) | ||||
|     names = os.popen(cmd).read().splitlines() | ||||
|     names.sort()  # Sort test names for execution determinism | ||||
|     return names | ||||
| @@ -344,7 +337,7 @@ def generateXmlOutput(results, xmlOutput, testRunName, runTime): | ||||
|         }) | ||||
|  | ||||
|         systemOut = ET.Element('system-out') | ||||
|         systemOut.text = result['output'].decode('utf-8') | ||||
|         systemOut.text = result['output'].decode('utf-8', 'ignore') | ||||
|         testCase.append(systemOut) | ||||
|  | ||||
|         if not result['success']: | ||||
| @@ -365,16 +358,19 @@ def run(testName, buildDir, sanitizer, xmlOutput, testRunName, buildOnly, useLLD | ||||
|     runCMake(sanitizer, buildDir) | ||||
|  | ||||
|     # build with make | ||||
|     makeCmd = 'make' | ||||
|     jobs = '-j8' | ||||
|     #makeCmd = 'cmake --build ' | ||||
|     #jobs = '-j8' | ||||
|  | ||||
|     if platform.system() == 'Windows': | ||||
|         makeCmd = 'nmake' | ||||
|     #if platform.system() == 'Windows': | ||||
|     #    makeCmd = 'nmake' | ||||
|  | ||||
|         # nmake does not have a -j option | ||||
|         jobs = '' | ||||
|     #    jobs = '' | ||||
|  | ||||
|     runCommand('{} -C {} {}'.format(makeCmd, buildDir, jobs)) | ||||
|     #runCommand('{} -C {} {}'.format(makeCmd, buildDir, jobs)) | ||||
|  | ||||
|     # build with cmake | ||||
|     runCommand('cmake --build ' + buildDir) | ||||
|  | ||||
|     if buildOnly: | ||||
|         return | ||||
| @@ -409,12 +405,7 @@ def run(testName, buildDir, sanitizer, xmlOutput, testRunName, buildOnly, useLLD | ||||
|             continue | ||||
|  | ||||
|         # testName can contains spaces, so we enclose them in double quotes | ||||
|         executable = os.path.join(buildDir, DEFAULT_EXE) | ||||
|  | ||||
|         if platform.system() == 'Windows': | ||||
|             executable += '.exe' | ||||
|  | ||||
|         cmd = '{} "{}" "{}" > "{}" 2>&1'.format(lldb, executable, testName, outputPath) | ||||
|         cmd = '{} "{}" "{}" > "{}" 2>&1'.format(lldb, TEST_EXE_PATH, testName, outputPath) | ||||
|  | ||||
|         jobs.append({ | ||||
|             'name': testName, | ||||
| @@ -454,8 +445,6 @@ def main(): | ||||
|     if not os.path.exists(buildDir): | ||||
|         os.makedirs(buildDir) | ||||
|  | ||||
|     defaultOutput = DEFAULT_EXE + '.xml' | ||||
|  | ||||
|     parser = argparse.ArgumentParser(description='Build and Run the engine unittest') | ||||
|  | ||||
|     sanitizers = ['tsan', 'asan', 'ubsan', 'none'] | ||||
| @@ -481,14 +470,29 @@ def main(): | ||||
|  | ||||
|     # Default sanitizer is tsan | ||||
|     sanitizer = args.sanitizer | ||||
|     if args.sanitizer is None: | ||||
|  | ||||
|     if args.no_sanitizer: | ||||
|         sanitizer = 'none' | ||||
|     elif args.sanitizer is None: | ||||
|         sanitizer = 'tsan' | ||||
|  | ||||
|     # Sanitizers display lots of strange errors on Linux on CI, | ||||
|     # which looks like false positives | ||||
|     if platform.system() != 'Darwin': | ||||
|         sanitizer = 'none' | ||||
|  | ||||
|     defaultRunName = 'ixengine_{}_{}'.format(platform.system(), sanitizer) | ||||
|  | ||||
|     xmlOutput = args.output or defaultOutput | ||||
|     xmlOutput = args.output or XML_OUTPUT_FILE | ||||
|     testRunName = args.run_name or os.getenv('IXENGINE_TEST_RUN_NAME') or defaultRunName | ||||
|  | ||||
|     global TEST_EXE_PATH | ||||
|  | ||||
|     if platform.system() == 'Windows': | ||||
|         TEST_EXE_PATH = os.path.join(buildDir, BUILD_TYPE, 'ixwebsocket_unittest.exe') | ||||
|     else: | ||||
|         TEST_EXE_PATH = os.path.join(buildDir, 'ixwebsocket_unittest') | ||||
|  | ||||
|     if args.list: | ||||
|         # catch2 exit with a different error code when requesting the list of files | ||||
|         try: | ||||
| @@ -505,11 +509,6 @@ def main(): | ||||
|         print('LLDB is only supported on Apple at this point') | ||||
|         args.lldb = False | ||||
|  | ||||
|     # Sanitizers display lots of strange errors on Linux on CI, | ||||
|     # which looks like false positives | ||||
|     if platform.system() != 'Darwin': | ||||
|         sanitizer = 'none' | ||||
|  | ||||
|     return run(args.test, buildDir, sanitizer, xmlOutput,  | ||||
|                testRunName, args.build_only, args.lldb) | ||||
|  | ||||
|   | ||||
| @@ -7,12 +7,21 @@ | ||||
| #define CATCH_CONFIG_RUNNER | ||||
| #include "catch.hpp" | ||||
|  | ||||
| #include <spdlog/spdlog.h> | ||||
|  | ||||
| #include <ixwebsocket/IXNetSystem.h> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
|  | ||||
| int main(int argc, char* argv[]) | ||||
| { | ||||
|     ix::initNetSystem(); | ||||
|  | ||||
|     ix::IXCoreLogger::LogFunc logFunc = [](const char* msg) | ||||
|     { | ||||
|         spdlog::info(msg); | ||||
|     }; | ||||
|     ix::IXCoreLogger::setLogFunction(logFunc); | ||||
|  | ||||
|     int result = Catch::Session().run(argc, argv); | ||||
|  | ||||
|     ix::uninitNetSystem(); | ||||
|   | ||||
							
								
								
									
										4
									
								
								third_party/README.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								third_party/README.md
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,3 @@ | ||||
| Except ZLIB on Windows (whose port is currently broken...) all dependencies here are for the ws command line tools, not for the IXWebSockets library which is standalone. | ||||
| # Note | ||||
|  | ||||
| Except *zlib* on Windows, all dependencies here are for the ws command line tools, not for the IXWebSockets library which is standalone. | ||||
|   | ||||
							
								
								
									
										58
									
								
								third_party/msgpack11/msgpack11.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								third_party/msgpack11/msgpack11.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -119,62 +119,62 @@ static void verify_length(size_t len) | ||||
| } | ||||
|  | ||||
| static void dump(NullStruct, std::string &out) { | ||||
|     out.push_back(0xc0); | ||||
|     out.push_back((char) 0xc0); | ||||
| } | ||||
|  | ||||
| static void dump(float value, std::string &out) { | ||||
|     out.push_back(0xca); | ||||
|     out.push_back((char) 0xca); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| static void dump(double value, std::string &out) { | ||||
|     out.push_back(0xcb); | ||||
|     out.push_back((char) 0xcb); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| static void dump(int8_t value, std::string &out) { | ||||
|     if( value < -32 ) | ||||
|     { | ||||
|         out.push_back(0xd0); | ||||
|         out.push_back((char) 0xd0); | ||||
|     } | ||||
|     out.push_back(value); | ||||
| } | ||||
|  | ||||
| static void dump(int16_t value, std::string &out) { | ||||
|     out.push_back(0xd1); | ||||
|     out.push_back((char) 0xd1); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| static void dump(int32_t value, std::string &out) { | ||||
|     out.push_back(0xd2); | ||||
|     out.push_back((char) 0xd2); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| static void dump(int64_t value, std::string &out) { | ||||
|     out.push_back(0xd3); | ||||
|     out.push_back((char) 0xd3); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| static void dump(uint8_t value, std::string &out) { | ||||
|     if(128 <= value) | ||||
|     { | ||||
|         out.push_back(0xcc); | ||||
|         out.push_back((char) 0xcc); | ||||
|     } | ||||
|     out.push_back(value); | ||||
| } | ||||
|  | ||||
| static void dump(uint16_t value, std::string &out) { | ||||
|     out.push_back(0xcd); | ||||
|     out.push_back((char) 0xcd); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| static void dump(uint32_t value, std::string &out) { | ||||
|     out.push_back(0xce); | ||||
|     out.push_back((char) 0xce); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| static void dump(uint64_t value, std::string &out) { | ||||
|     out.push_back(0xcf); | ||||
|     out.push_back((char) 0xcf); | ||||
|     dump_data(value, out); | ||||
| } | ||||
|  | ||||
| @@ -194,19 +194,19 @@ static void dump(const std::string& value, std::string &out) { | ||||
|     else if(len <= 0xff) | ||||
|     { | ||||
|         uint8_t const length = static_cast<uint8_t>(len); | ||||
|         out.push_back(0xd9); | ||||
|         out.push_back((char) 0xd9); | ||||
|         out.push_back(length); | ||||
|     } | ||||
|     else if(len <= 0xffff) | ||||
|     { | ||||
|         uint16_t const length = static_cast<uint16_t>(len); | ||||
|         out.push_back(0xda); | ||||
|         out.push_back((char) 0xda); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         uint32_t const length = static_cast<uint32_t>(len); | ||||
|         out.push_back(0xdb); | ||||
|         out.push_back((char) 0xdb); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|  | ||||
| @@ -226,13 +226,13 @@ static void dump(const MsgPack::array& value, std::string &out) { | ||||
|     else if(len <= 0xffff) | ||||
|     { | ||||
|         uint16_t const length = static_cast<uint16_t>(len); | ||||
|         out.push_back(0xdc); | ||||
|         out.push_back((char) 0xdc); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         uint32_t const length = static_cast<uint32_t>(len); | ||||
|         out.push_back(0xdd); | ||||
|         out.push_back((char) 0xdd); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|  | ||||
| @@ -252,13 +252,13 @@ static void dump(const MsgPack::object& value, std::string &out) { | ||||
|     else if(len <= 0xffff) | ||||
|     { | ||||
|         uint16_t const length = static_cast<uint16_t>(len); | ||||
|         out.push_back(0xde); | ||||
|         out.push_back((char) 0xde); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         uint32_t const length = static_cast<uint32_t>(len); | ||||
|         out.push_back(0xdf); | ||||
|         out.push_back((char) 0xdf); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|  | ||||
| @@ -274,19 +274,19 @@ static void dump(const MsgPack::binary& value, std::string &out) { | ||||
|     if(len <= 0xff) | ||||
|     { | ||||
|         uint8_t const length = static_cast<uint8_t>(len); | ||||
|         out.push_back(0xc4); | ||||
|         out.push_back((char) 0xc4); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|     else if(len <= 0xffff) | ||||
|     { | ||||
|         uint16_t const length = static_cast<uint16_t>(len); | ||||
|         out.push_back(0xc5); | ||||
|         out.push_back((char) 0xc5); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         uint32_t const length = static_cast<uint32_t>(len); | ||||
|         out.push_back(0xc6); | ||||
|         out.push_back((char) 0xc6); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|  | ||||
| @@ -302,33 +302,33 @@ static void dump(const MsgPack::extension& value, std::string &out) { | ||||
|     verify_length(len); | ||||
|  | ||||
|     if(len == 0x01) { | ||||
|         out.push_back(0xd4); | ||||
|         out.push_back((char) 0xd4); | ||||
|     } | ||||
|     else if(len == 0x02) { | ||||
|         out.push_back(0xd5); | ||||
|         out.push_back((char) 0xd5); | ||||
|     } | ||||
|     else if(len == 0x04) { | ||||
|         out.push_back(0xd6); | ||||
|         out.push_back((char) 0xd6); | ||||
|     } | ||||
|     else if(len == 0x08) { | ||||
|         out.push_back(0xd7); | ||||
|         out.push_back((char) 0xd7); | ||||
|     } | ||||
|     else if(len == 0x10) { | ||||
|         out.push_back(0xd8); | ||||
|         out.push_back((char) 0xd8); | ||||
|     } | ||||
|     else if(len <= 0xff) { | ||||
|         uint8_t const length = static_cast<uint8_t>(len); | ||||
|         out.push_back(0xc7); | ||||
|         out.push_back((char) 0xc7); | ||||
|         out.push_back(length); | ||||
|     } | ||||
|     else if(len <= 0xffff) { | ||||
|         uint16_t const length = static_cast<uint16_t>(len); | ||||
|         out.push_back(0xc8); | ||||
|         out.push_back((char) 0xc8); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|     else { | ||||
|         uint32_t const length = static_cast<uint32_t>(len); | ||||
|         out.push_back(0xc9); | ||||
|         out.push_back((char) 0xc9); | ||||
|         dump_data(length, out); | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										108
									
								
								third_party/spdlog/.clang-format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								third_party/spdlog/.clang-format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| --- | ||||
| Language:        Cpp | ||||
| # BasedOnStyle:  LLVM | ||||
| AccessModifierOffset: -4 | ||||
| AlignAfterOpenBracket: DontAlign | ||||
| AlignConsecutiveAssignments: false | ||||
| AlignConsecutiveDeclarations: false | ||||
| AlignEscapedNewlines: Right | ||||
| AlignOperands:   true | ||||
| AlignTrailingComments: true | ||||
| AllowAllParametersOfDeclarationOnNextLine: true | ||||
| AllowShortBlocksOnASingleLine: true | ||||
| AllowShortCaseLabelsOnASingleLine: false | ||||
| AllowShortFunctionsOnASingleLine: Empty | ||||
| AllowShortIfStatementsOnASingleLine: false | ||||
| AllowShortLoopsOnASingleLine: false | ||||
| AlwaysBreakAfterDefinitionReturnType: None | ||||
| AlwaysBreakAfterReturnType: None | ||||
| AlwaysBreakBeforeMultilineStrings: false | ||||
| AlwaysBreakTemplateDeclarations: true | ||||
| BinPackArguments: true | ||||
| BinPackParameters: true | ||||
| BraceWrapping:      | ||||
|   AfterClass:      true | ||||
|   AfterControlStatement: true | ||||
|   AfterEnum:       true | ||||
|   AfterFunction:   true | ||||
|   AfterNamespace:  false | ||||
|   AfterObjCDeclaration: true | ||||
|   AfterStruct:     true | ||||
|   AfterUnion:      true | ||||
|   BeforeCatch:     true | ||||
|   BeforeElse:      true | ||||
|   IndentBraces:    false | ||||
|   SplitEmptyFunction: true | ||||
|   SplitEmptyRecord: true | ||||
|   SplitEmptyNamespace: true | ||||
| BreakBeforeBinaryOperators: None | ||||
| BreakBeforeBraces: Custom | ||||
| BreakBeforeInheritanceComma: false | ||||
| BreakBeforeTernaryOperators: true | ||||
| BreakConstructorInitializersBeforeComma: true | ||||
| BreakConstructorInitializers: BeforeColon | ||||
| BreakAfterJavaFieldAnnotations: false | ||||
| BreakStringLiterals: true | ||||
| ColumnLimit:     140 | ||||
| CommentPragmas:  '^ IWYU pragma:' | ||||
| CompactNamespaces:  false | ||||
| ConstructorInitializerAllOnOneLineOrOnePerLine: false | ||||
| ConstructorInitializerIndentWidth: 4 | ||||
| ContinuationIndentWidth: 4 | ||||
| Cpp11BracedListStyle: true | ||||
| DerivePointerAlignment: false | ||||
| DisableFormat:   false | ||||
| ExperimentalAutoDetectBinPacking: false | ||||
| FixNamespaceComments: true | ||||
| ForEachMacros:    | ||||
|   - foreach | ||||
|   - Q_FOREACH | ||||
|   - BOOST_FOREACH | ||||
| IncludeCategories:  | ||||
|   - Regex:           '^"(llvm|llvm-c|clang|clang-c)/' | ||||
|     Priority:        2 | ||||
|   - Regex:           '^(<|"(gtest|gmock|isl|json)/)' | ||||
|     Priority:        3 | ||||
|   - Regex:           '.*' | ||||
|     Priority:        1 | ||||
| IncludeIsMainRegex: '(Test)?$' | ||||
| IndentCaseLabels: false | ||||
| IndentWidth:     4 | ||||
| IndentWrappedFunctionNames: false | ||||
| JavaScriptQuotes: Leave | ||||
| JavaScriptWrapImports: true | ||||
| KeepEmptyLinesAtTheStartOfBlocks: true | ||||
| MacroBlockBegin: '' | ||||
| MacroBlockEnd:   '' | ||||
| MaxEmptyLinesToKeep: 1 | ||||
| NamespaceIndentation: None | ||||
| ObjCBlockIndentWidth: 2 | ||||
| ObjCSpaceAfterProperty: false | ||||
| ObjCSpaceBeforeProtocolList: true | ||||
| PenaltyBreakAssignment: 2 | ||||
| PenaltyBreakBeforeFirstCallParameter: 19 | ||||
| PenaltyBreakComment: 300 | ||||
| PenaltyBreakFirstLessLess: 120 | ||||
| PenaltyBreakString: 1000 | ||||
| PenaltyExcessCharacter: 1000000 | ||||
| PenaltyReturnTypeOnItsOwnLine: 60 | ||||
| PointerAlignment: Right | ||||
| ReflowComments:  true | ||||
| SortIncludes:    false | ||||
| SortUsingDeclarations: true | ||||
| SpaceAfterCStyleCast: false | ||||
| SpaceAfterTemplateKeyword: false | ||||
| SpaceBeforeAssignmentOperators: true | ||||
| SpaceBeforeParens: ControlStatements | ||||
| SpaceInEmptyParentheses: false | ||||
| SpacesBeforeTrailingComments: 1 | ||||
| SpacesInAngles:  false | ||||
| SpacesInContainerLiterals: true | ||||
| SpacesInCStyleCastParentheses: false | ||||
| SpacesInParentheses: false | ||||
| SpacesInSquareBrackets: false | ||||
| Standard:        Cpp11 | ||||
| TabWidth:        8 | ||||
| UseTab:          Never | ||||
| ... | ||||
|  | ||||
							
								
								
									
										28
									
								
								third_party/spdlog/.clang-tidy
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								third_party/spdlog/.clang-tidy
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| Checks: 'modernize-*,modernize-use-override,google-*,-google-runtime-references,misc-*,clang-analyzer-*' | ||||
| WarningsAsErrors: '' | ||||
| HeaderFilterRegex: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h' | ||||
| AnalyzeTemporaryDtors: false | ||||
| FormatStyle:     none | ||||
|  | ||||
| CheckOptions:     | ||||
|   - key:             google-readability-braces-around-statements.ShortStatementLines | ||||
|     value:           '1' | ||||
|   - key:             google-readability-function-size.StatementThreshold | ||||
|     value:           '800' | ||||
|   - key:             google-readability-namespace-comments.ShortNamespaceLines | ||||
|     value:           '10' | ||||
|   - key:             google-readability-namespace-comments.SpacesBeforeComments | ||||
|     value:           '2' | ||||
|   - key:             modernize-loop-convert.MaxCopySize | ||||
|     value:           '16' | ||||
|   - key:             modernize-loop-convert.MinConfidence | ||||
|     value:           reasonable | ||||
|   - key:             modernize-loop-convert.NamingStyle | ||||
|     value:           CamelCase | ||||
|   - key:             modernize-pass-by-value.IncludeStyle | ||||
|     value:           llvm | ||||
|   - key:             modernize-replace-auto-ptr.IncludeStyle | ||||
|     value:           llvm | ||||
|   - key:             modernize-use-nullptr.NullMacros | ||||
|     value:           'NULL' | ||||
|  | ||||
							
								
								
									
										68
									
								
								third_party/spdlog/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								third_party/spdlog/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| # Auto generated files | ||||
| build/* | ||||
| *.slo | ||||
| *.lo | ||||
| *.o | ||||
| *.obj | ||||
| *.suo | ||||
| *.tlog | ||||
| *.ilk | ||||
| *.log | ||||
| *.pdb | ||||
| *.idb | ||||
| *.iobj | ||||
| *.ipdb | ||||
| *.opensdf | ||||
| *.sdf | ||||
|  | ||||
| # Compiled Dynamic libraries | ||||
| *.so | ||||
| *.dylib | ||||
| *.dll | ||||
|  | ||||
| # Compiled Static libraries | ||||
| *.lai | ||||
| *.la | ||||
| *.a | ||||
| *.lib | ||||
|  | ||||
| # Executables | ||||
| *.exe | ||||
| *.out | ||||
| *.app | ||||
|  | ||||
| # Codelite | ||||
| .codelite | ||||
|  | ||||
| # .orig files | ||||
| *.orig | ||||
|  | ||||
| # example  files | ||||
| example/* | ||||
| !example/example.cpp | ||||
| !example/bench.cpp | ||||
| !example/utils.h | ||||
| !example/Makefile* | ||||
| !example/example.sln | ||||
| !example/example.vcxproj | ||||
| !example/CMakeLists.txt | ||||
| !example/multisink.cpp | ||||
| !example/jni | ||||
|  | ||||
| # generated files | ||||
| generated | ||||
|  | ||||
| # Cmake | ||||
| CMakeCache.txt | ||||
| CMakeFiles | ||||
| CMakeScripts | ||||
| Makefile | ||||
| cmake_install.cmake | ||||
| install_manifest.txt | ||||
| /tests/tests.VC.VC.opendb | ||||
| /tests/tests.VC.db | ||||
| /tests/tests | ||||
| /tests/logs/* | ||||
|  | ||||
| # idea | ||||
| .idea/ | ||||
							
								
								
									
										116
									
								
								third_party/spdlog/.travis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								third_party/spdlog/.travis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| # Adapted from various sources, including: | ||||
| # - Louis Dionne's Hana: https://github.com/ldionne/hana | ||||
| # - Paul Fultz II's FIT: https://github.com/pfultz2/Fit | ||||
| # - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3 | ||||
| sudo: required | ||||
| language: cpp | ||||
|  | ||||
| addons: &gcc48 | ||||
|   apt: | ||||
|     packages: | ||||
|       - g++-4.8 | ||||
|     sources: | ||||
|       - ubuntu-toolchain-r-test | ||||
|  | ||||
| addons: &gcc7 | ||||
|   apt: | ||||
|     packages: | ||||
|       - g++-7 | ||||
|     sources: | ||||
|       - ubuntu-toolchain-r-test | ||||
|  | ||||
| addons: &clang35 | ||||
|   apt: | ||||
|     packages: | ||||
|       - clang-3.5 | ||||
|     sources: | ||||
|       - ubuntu-toolchain-r-test | ||||
|       - llvm-toolchain-precise-3.5 | ||||
|  | ||||
| addons: &clang6 | ||||
|   apt: | ||||
|     packages: | ||||
|       - clang-6.0 | ||||
|     sources: | ||||
|       - ubuntu-toolchain-r-test | ||||
|       - llvm-toolchain-trusty-6.0 | ||||
|  | ||||
|  | ||||
| matrix: | ||||
|   include: | ||||
|     # Test gcc-4.8: C++11, Build=Debug/Release | ||||
|     - env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 | ||||
|       os: linux | ||||
|       addons: *gcc48 | ||||
|  | ||||
|     - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 | ||||
|       os: linux | ||||
|       addons: *gcc48 | ||||
|    | ||||
|     - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 | ||||
|       os: linux | ||||
|       addons: *gcc7 | ||||
|  | ||||
|     # Test clang-3.5: C++11, Build=Debug/Release | ||||
|     - env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11 | ||||
|       os: linux | ||||
|       addons: *clang35 | ||||
|  | ||||
|     - env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11 | ||||
|       os: linux | ||||
|       addons: *clang35 | ||||
|  | ||||
|       # Test clang-6.0: C++11, Build=Debug, ASAN=On | ||||
|     - env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off | ||||
|       os: linux | ||||
|       addons: *clang6 | ||||
|        | ||||
|     - env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off | ||||
|       os: linux | ||||
|       addons: *clang6 | ||||
|        | ||||
|       # Test clang-6.0: C++11, Build=Debug, TSAN=On | ||||
|     - env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=Off TSAN=On | ||||
|       os: linux | ||||
|       addons: *clang6 | ||||
|        | ||||
|     - env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=On | ||||
|       os: linux | ||||
|       addons: *clang6 | ||||
|  | ||||
|       # osx | ||||
|     - env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off | ||||
|       os: osx | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
| before_script: | ||||
|   - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi | ||||
|   - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi | ||||
|   - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi | ||||
|   - which $CXX | ||||
|   - which $CC | ||||
|   - $CXX --version | ||||
|   - cmake --version | ||||
|  | ||||
| script: | ||||
|   - cd ${TRAVIS_BUILD_DIR} | ||||
|   - mkdir -p build && cd build | ||||
|   - | | ||||
|     cmake .. \ | ||||
|       --warn-uninitialized \ | ||||
|       -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ | ||||
|       -DCMAKE_CXX_STANDARD=$CPP \ | ||||
|       -DSPDLOG_BUILD_EXAMPLES=ON \ | ||||
|       -DSPDLOG_BUILD_BENCH=OFF \ | ||||
|       -DSPDLOG_BUILD_TESTS=ON \ | ||||
|       -DSPDLOG_SANITIZE_ADDRESS=$ASAN \ | ||||
|       -DSPDLOG_SANITIZE_THREAD=$TSAN | ||||
|   - make VERBOSE=1 -j2 | ||||
|   - ctest -j2 --output-on-failure | ||||
|  | ||||
|  | ||||
|  | ||||
| notifications: | ||||
|   email: false | ||||
							
								
								
									
										157
									
								
								third_party/spdlog/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								third_party/spdlog/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| # | ||||
| # Copyright(c) 2015 Ruslan Baratov. | ||||
| # Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| # | ||||
|  | ||||
| cmake_minimum_required(VERSION 3.1) | ||||
| project(spdlog VERSION 1.3.1 LANGUAGES CXX) | ||||
| include(CMakeDependentOption) | ||||
| include(GNUInstallDirs) | ||||
|  | ||||
| #--------------------------------------------------------------------------------------- | ||||
| # set default build to release | ||||
| #--------------------------------------------------------------------------------------- | ||||
| if(NOT CMAKE_BUILD_TYPE) | ||||
|     set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) | ||||
| endif() | ||||
|  | ||||
| message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) | ||||
|  | ||||
| #--------------------------------------------------------------------------------------- | ||||
| # compiler config | ||||
| #--------------------------------------------------------------------------------------- | ||||
| set(CMAKE_CXX_STANDARD 11) | ||||
| set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||||
| set(CMAKE_CXX_EXTENSIONS OFF) | ||||
|  | ||||
| if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") | ||||
|     add_compile_options("-Wall") | ||||
|     add_compile_options("-Wextra") | ||||
|     add_compile_options("-Wconversion") | ||||
|     add_compile_options("-pedantic") | ||||
|     add_compile_options("-Wfatal-errors") | ||||
|      | ||||
| endif() | ||||
|  | ||||
| #--------------------------------------------------------------------------------------- | ||||
| # address sanitizers check | ||||
| #--------------------------------------------------------------------------------------- | ||||
| include(cmake/sanitizers.cmake) | ||||
|  | ||||
| #--------------------------------------------------------------------------------------- | ||||
| # spdlog target | ||||
| #--------------------------------------------------------------------------------------- | ||||
| add_library(spdlog INTERFACE) | ||||
| add_library(spdlog::spdlog ALIAS spdlog) | ||||
|  | ||||
| # Check if spdlog is being used directly or via add_subdirectory | ||||
| set(SPDLOG_MASTER_PROJECT OFF) | ||||
| if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) | ||||
|   set(SPDLOG_MASTER_PROJECT ON) | ||||
| endif() | ||||
|  | ||||
| option(SPDLOG_BUILD_EXAMPLES "Build examples" ${SPDLOG_MASTER_PROJECT}) | ||||
| option(SPDLOG_BUILD_BENCH "Build benchmarks" ${SPDLOG_MASTER_PROJECT}) | ||||
| option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT}) | ||||
| option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) | ||||
|  | ||||
| if(SPDLOG_FMT_EXTERNAL) | ||||
|     find_package(fmt REQUIRED CONFIG) | ||||
| endif() | ||||
|  | ||||
| target_include_directories( | ||||
|     spdlog | ||||
|     INTERFACE | ||||
|     "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>" | ||||
|     "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" | ||||
| ) | ||||
|  | ||||
| if(SPDLOG_FMT_EXTERNAL) | ||||
|     target_compile_definitions(spdlog INTERFACE SPDLOG_FMT_EXTERNAL) | ||||
|     target_link_libraries(spdlog INTERFACE fmt::fmt) | ||||
| endif() | ||||
|  | ||||
| set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include") | ||||
|  | ||||
| if(SPDLOG_BUILD_EXAMPLES) | ||||
|     add_subdirectory(example) | ||||
| endif() | ||||
|  | ||||
| if(SPDLOG_BUILD_TESTS) | ||||
|     include(CTest) | ||||
|     add_subdirectory(tests) | ||||
| endif() | ||||
|  | ||||
| if(SPDLOG_BUILD_BENCH) | ||||
|     add_subdirectory(bench) | ||||
| endif() | ||||
|  | ||||
| #--------------------------------------------------------------------------------------- | ||||
| # Install/export targets and files | ||||
| #--------------------------------------------------------------------------------------- | ||||
| # set files and directories | ||||
| set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") | ||||
| set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}") | ||||
| set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig") | ||||
| set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake") | ||||
| set(project_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake") | ||||
| set(targets_config "${PROJECT_NAME}Targets.cmake") | ||||
| set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc") | ||||
| set(targets_export_name "${PROJECT_NAME}Targets") | ||||
| set(namespace "${PROJECT_NAME}::") | ||||
|  | ||||
| # generate package version file | ||||
| include(CMakePackageConfigHelpers) | ||||
| write_basic_package_version_file( | ||||
|     "${version_config}" COMPATIBILITY SameMajorVersion | ||||
| ) | ||||
|  | ||||
| # configure pkg config file | ||||
| configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY) | ||||
| # configure spdlogConfig.cmake file | ||||
| configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY) | ||||
|  | ||||
| # install targets | ||||
| install( | ||||
|     TARGETS spdlog | ||||
|     EXPORT "${targets_export_name}" | ||||
| ) | ||||
|  | ||||
| # install headers | ||||
| install( | ||||
|     DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}" | ||||
|     DESTINATION "${include_install_dir}" | ||||
| ) | ||||
|  | ||||
| # install project config and version file | ||||
| install( | ||||
|     FILES "${project_config}" "${version_config}" | ||||
|     DESTINATION "${config_install_dir}" | ||||
| ) | ||||
|  | ||||
| # install pkg config file | ||||
| install( | ||||
|     FILES "${pkg_config}" | ||||
|     DESTINATION "${pkgconfig_install_dir}" | ||||
| ) | ||||
|  | ||||
| # install targets config file | ||||
| install( | ||||
|     EXPORT "${targets_export_name}" | ||||
|     NAMESPACE "${namespace}" | ||||
|     DESTINATION "${config_install_dir}" | ||||
|     FILE ${targets_config} | ||||
| ) | ||||
|  | ||||
| # export build directory targets file | ||||
| export( | ||||
|     EXPORT ${targets_export_name} | ||||
|     NAMESPACE "${namespace}" | ||||
|     FILE ${targets_config} | ||||
| ) | ||||
|  | ||||
| # register project in CMake user registry | ||||
| export(PACKAGE ${PROJECT_NAME}) | ||||
|  | ||||
| file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h") | ||||
| add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS}) | ||||
							
								
								
									
										13
									
								
								third_party/spdlog/INSTALL
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								third_party/spdlog/INSTALL
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| spdlog is header only library. | ||||
| Just copy the files to your build tree and use a C++11 compiler | ||||
|  | ||||
| Tested on: | ||||
| gcc 4.8.1 and above | ||||
| clang 3.5 | ||||
| Visual Studio 2013 | ||||
|  | ||||
| gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed | ||||
| gcc 4.9 flags: --std=c++11 -pthread -O3 -flto | ||||
|  | ||||
|  | ||||
| see the makefile in the example folder | ||||
							
								
								
									
										22
									
								
								third_party/spdlog/LICENSE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								third_party/spdlog/LICENSE
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2016 Gabi Melman.                                        | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
|  | ||||
							
								
								
									
										319
									
								
								third_party/spdlog/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								third_party/spdlog/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,319 @@ | ||||
| # spdlog | ||||
|  | ||||
| Very fast, header only, C++ logging library. [](https://travis-ci.org/gabime/spdlog)  [](https://ci.appveyor.com/project/gabime/spdlog) | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Install | ||||
| #### Just copy the headers: | ||||
|  | ||||
| * Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler. | ||||
|  | ||||
| #### Or use your favorite package manager: | ||||
|  | ||||
| * Ubuntu: `apt-get install libspdlog-dev` | ||||
| * Homebrew: `brew install spdlog` | ||||
| * FreeBSD:  `cd /usr/ports/devel/spdlog/ && make install clean` | ||||
| * Fedora: `yum install spdlog` | ||||
| * Gentoo: `emerge dev-libs/spdlog` | ||||
| * Arch Linux: `yaourt -S spdlog-git` | ||||
| * vcpkg: `vcpkg install spdlog` | ||||
|   | ||||
|  | ||||
| ## Platforms | ||||
|  * Linux, FreeBSD, OpenBSD, Solaris, AIX | ||||
|  * Windows (msvc 2013+, cygwin) | ||||
|  * macOS (clang 3.5+) | ||||
|  * Android | ||||
|  | ||||
| ## Features | ||||
| * Very fast (see [benchmarks](#benchmarks) below). | ||||
| * Headers only, just copy and use. | ||||
| * Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library. | ||||
| * Fast asynchronous mode (optional) | ||||
| * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. | ||||
| * Multi/Single threaded loggers. | ||||
| * Various log targets: | ||||
|     * Rotating log files. | ||||
|     * Daily log files. | ||||
|     * Console logging (colors supported). | ||||
|     * syslog. | ||||
|     * Windows debugger (```OutputDebugString(..)```) | ||||
|     * Easily extendable with custom log targets  (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). | ||||
| * Severity based filtering - threshold levels can be modified in runtime as well as in compile time. | ||||
| * Binary data logging. | ||||
|  | ||||
|  | ||||
| ## Benchmarks | ||||
|  | ||||
| Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz | ||||
|  | ||||
| #### Synchronous mode | ||||
| ``` | ||||
| ******************************************************************************* | ||||
| Single thread, 1,000,000 iterations | ||||
| ******************************************************************************* | ||||
| basic_st...             Elapsed: 0.181652       5,505,042/sec | ||||
| rotating_st...          Elapsed: 0.181781       5,501,117/sec | ||||
| daily_st...             Elapsed: 0.187595       5,330,630/sec | ||||
| null_st...              Elapsed: 0.0504704      19,813,602/sec | ||||
| ******************************************************************************* | ||||
| 10 threads sharing same logger, 1,000,000 iterations | ||||
| ******************************************************************************* | ||||
| basic_mt...             Elapsed: 0.616035       1,623,284/sec | ||||
| rotating_mt...          Elapsed: 0.620344       1,612,008/sec | ||||
| daily_mt...             Elapsed: 0.648353       1,542,369/sec | ||||
| null_mt...              Elapsed: 0.151972       6,580,166/sec | ||||
| ```  | ||||
| #### Asynchronous mode | ||||
| ``` | ||||
| ******************************************************************************* | ||||
| 10 threads sharing same logger, 1,000,000 iterations  | ||||
| ******************************************************************************* | ||||
| async...                Elapsed: 0.350066       2,856,606/sec | ||||
| async...                Elapsed: 0.314865       3,175,960/sec | ||||
| async...                Elapsed: 0.349851       2,858,358/sec | ||||
| ``` | ||||
|  | ||||
| ## Usage samples | ||||
|  | ||||
| #### Basic usage | ||||
| ```c++ | ||||
| #include "spdlog/spdlog.h" | ||||
| int main()  | ||||
| { | ||||
|     spdlog::info("Welcome to spdlog!"); | ||||
|     spdlog::error("Some error message with arg: {}", 1); | ||||
|      | ||||
|     spdlog::warn("Easy padding in numbers like {:08d}", 12); | ||||
|     spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42); | ||||
|     spdlog::info("Support for floats {:03.2f}", 1.23456); | ||||
|     spdlog::info("Positional args are {1} {0}..", "too", "supported"); | ||||
|     spdlog::info("{:<30}", "left aligned"); | ||||
|      | ||||
|     spdlog::set_level(spdlog::level::debug); // Set global log level to debug | ||||
|     spdlog::debug("This message should be displayed..");     | ||||
|      | ||||
|     // change log pattern | ||||
|     spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); | ||||
|      | ||||
|     // Compile time log levels | ||||
|     // define SPDLOG_ACTIVE_LEVEL to desired level | ||||
|     SPDLOG_TRACE("Some trace message with param {}", {}); | ||||
|     SPDLOG_DEBUG("Some debug message"); | ||||
|          | ||||
| } | ||||
| ``` | ||||
| #### create stdout/stderr logger object | ||||
| ```c++ | ||||
| #include "spdlog/spdlog.h" | ||||
| #include "spdlog/sinks/stdout_color_sinks.h" | ||||
| void stdout_example() | ||||
| { | ||||
|     // create color multi threaded logger | ||||
|     auto console = spdlog::stdout_color_mt("console");     | ||||
|     auto err_logger = spdlog::stderr_color_mt("stderr");     | ||||
|     spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)"); | ||||
| } | ||||
| ``` | ||||
| --- | ||||
| #### Basic file logger | ||||
| ```c++ | ||||
| #include "spdlog/sinks/basic_file_sink.h" | ||||
| void basic_logfile_example() | ||||
| { | ||||
|     try  | ||||
|     { | ||||
|         auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt"); | ||||
|     } | ||||
|     catch (const spdlog::spdlog_ex &ex) | ||||
|     { | ||||
|         std::cout << "Log init failed: " << ex.what() << std::endl; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| --- | ||||
| #### Rotating files | ||||
| ```c++ | ||||
| #include "spdlog/sinks/rotating_file_sink.h" | ||||
| void rotating_example() | ||||
| { | ||||
|     // Create a file rotating logger with 5mb size max and 3 rotated files | ||||
|     auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| --- | ||||
| #### Daily files | ||||
| ```c++ | ||||
|  | ||||
| #include "spdlog/sinks/daily_file_sink.h" | ||||
| void daily_example() | ||||
| { | ||||
|     // Create a daily logger - a new file is created every day on 2:30am | ||||
|     auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); | ||||
| } | ||||
|  | ||||
| ``` | ||||
|  | ||||
| --- | ||||
| #### Cloning loggers  | ||||
| ```c++ | ||||
| // clone a logger and give it new name. | ||||
| // Useful for creating subsystem loggers from some "root" logger | ||||
| void clone_example() | ||||
| { | ||||
|     auto network_logger = spdlog::get("root")->clone("network"); | ||||
|     network_logger->info("Logging network stuff.."); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| --- | ||||
| #### Periodic flush | ||||
| ```c++ | ||||
| // periodically flush all *registered* loggers every 3 seconds: | ||||
| // warning: only use if all your loggers are thread safe! | ||||
| spdlog::flush_every(std::chrono::seconds(3)); | ||||
|  | ||||
| ``` | ||||
|  | ||||
| --- | ||||
| #### Binary logging | ||||
| ```c++ | ||||
| // log binary data as hex. | ||||
| // many types of std::container<char> types can be used. | ||||
| // ranges are supported too. | ||||
| // format flags: | ||||
| // {:X} - print in uppercase. | ||||
| // {:s} - don't separate each byte with space. | ||||
| // {:p} - don't print the position on each line start. | ||||
| // {:n} - don't split the output to lines. | ||||
|  | ||||
| #include "spdlog/fmt/bin_to_hex.h" | ||||
|  | ||||
| void binary_example() | ||||
| { | ||||
|     auto console = spdlog::get("console"); | ||||
|     std::array<char, 80> buf; | ||||
|     console->info("Binary example: {}", spdlog::to_hex(buf)); | ||||
|     console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); | ||||
|     // more examples: | ||||
|     // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); | ||||
|     // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); | ||||
|     // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); | ||||
| } | ||||
|  | ||||
| ``` | ||||
|  | ||||
| --- | ||||
| #### Logger with multi sinks - each with different format and log level | ||||
| ```c++ | ||||
|  | ||||
| // create logger with 2 targets with different log levels and formats. | ||||
| // the console will show only warnings or errors, while the file will log all. | ||||
| void multi_sink_example() | ||||
| { | ||||
|     auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); | ||||
|     console_sink->set_level(spdlog::level::warn); | ||||
|     console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); | ||||
|  | ||||
|     auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true); | ||||
|     file_sink->set_level(spdlog::level::trace); | ||||
|  | ||||
|     spdlog::logger logger("multi_sink", {console_sink, file_sink}); | ||||
|     logger.set_level(spdlog::level::debug); | ||||
|     logger.warn("this should appear in both console and file"); | ||||
|     logger.info("this message should not appear in the console, only in the file"); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| --- | ||||
| #### Asynchronous logging | ||||
| ```c++ | ||||
| #include "spdlog/async.h" | ||||
| #include "spdlog/sinks/basic_file_sink.h" | ||||
| void async_example() | ||||
| { | ||||
|     // default thread pool settings can be modified *before* creating the async logger: | ||||
|     // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread. | ||||
|     auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt"); | ||||
|     // alternatively: | ||||
|     // auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");    | ||||
| } | ||||
|  | ||||
| ``` | ||||
|  | ||||
| --- | ||||
| #### Asynchronous logger with multi sinks   | ||||
| ```c++ | ||||
| #include "spdlog/sinks/stdout_color_sinks.h" | ||||
| #include "spdlog/sinks/rotating_file_sink.h" | ||||
|  | ||||
| void multi_sink_example2() | ||||
| { | ||||
|     spdlog::init_thread_pool(8192, 1); | ||||
|     auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >(); | ||||
|     auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3); | ||||
|     std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink}; | ||||
|     auto logger = std::make_shared<spdlog::async_logger>("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); | ||||
|     spdlog::register_logger(logger); | ||||
| } | ||||
| ``` | ||||
|   | ||||
| --- | ||||
| #### User defined types | ||||
| ```c++ | ||||
| // user defined types logging by implementing operator<< | ||||
| #include "spdlog/fmt/ostr.h" // must be included | ||||
| struct my_type | ||||
| { | ||||
|     int i; | ||||
|     template<typename OStream> | ||||
|     friend OStream &operator<<(OStream &os, const my_type &c) | ||||
|     { | ||||
|         return os << "[my_type i=" << c.i << "]"; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| void user_defined_example() | ||||
| { | ||||
|     spdlog::get("console")->info("user defined type: {}", my_type{14}); | ||||
| } | ||||
|  | ||||
| ``` | ||||
| --- | ||||
| #### Custom error handler | ||||
| ```c++ | ||||
| void err_handler_example() | ||||
| { | ||||
|     // can be set globally or per logger(logger->set_error_handler(..)) | ||||
|     spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); }); | ||||
|     spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); | ||||
| } | ||||
|  | ||||
| ``` | ||||
| --- | ||||
| #### syslog  | ||||
| ```c++ | ||||
| #include "spdlog/sinks/syslog_sink.h" | ||||
| void syslog_example() | ||||
| { | ||||
|     std::string ident = "spdlog-example"; | ||||
|     auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); | ||||
|     syslog_logger->warn("This is warning that will end up in syslog."); | ||||
| } | ||||
| ``` | ||||
| --- | ||||
| #### Android example  | ||||
| ```c++ | ||||
| #include "spdlog/sinks/android_sink.h" | ||||
| void android_example() | ||||
| { | ||||
|     std::string tag = "spdlog-android"; | ||||
|     auto android_logger = spdlog::android_logger("android", tag); | ||||
|     android_logger->critical("Use \"adb shell logcat\" to view this message."); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Documentation | ||||
| Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages. | ||||
							
								
								
									
										34
									
								
								third_party/spdlog/appveyor.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								third_party/spdlog/appveyor.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| version: 1.0.{build} | ||||
| image: Visual Studio 2015 | ||||
| environment: | ||||
|   matrix: | ||||
|   - GENERATOR: '"MinGW Makefiles"' | ||||
|     BUILD_TYPE: Debug | ||||
|   - GENERATOR: '"MinGW Makefiles"' | ||||
|     BUILD_TYPE: Release | ||||
|   - GENERATOR: '"Visual Studio 14 2015"' | ||||
|     BUILD_TYPE: Debug | ||||
|   - GENERATOR: '"Visual Studio 14 2015"' | ||||
|     BUILD_TYPE: Release | ||||
|   - GENERATOR: '"Visual Studio 14 2015 Win64"' | ||||
|     BUILD_TYPE: Debug | ||||
|   - GENERATOR: '"Visual Studio 14 2015 Win64"' | ||||
|     BUILD_TYPE: Release | ||||
| build_script: | ||||
| - cmd: >- | ||||
|     set | ||||
|  | ||||
|     mkdir build | ||||
|  | ||||
|     cd build | ||||
|  | ||||
|     set PATH=%PATH:C:\Program Files\Git\usr\bin;=% | ||||
|  | ||||
|     set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH% | ||||
|  | ||||
|     cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_BUILD_BENCH=OFF | ||||
|  | ||||
|     cmake --build . --config %BUILD_TYPE% | ||||
|  | ||||
| test_script: | ||||
| - ctest -VV -C "%BUILD_TYPE%" | ||||
							
								
								
									
										48
									
								
								third_party/spdlog/bench/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								third_party/spdlog/bench/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| # *************************************************************************/ | ||||
| # * Copyright (c) 2015 Ruslan Baratov.                                    */ | ||||
| # *                                                                       */ | ||||
| # * Permission is hereby granted, free of charge, to any person obtaining */ | ||||
| # * a copy of this software and associated documentation files (the       */ | ||||
| # * "Software"), to deal in the Software without restriction, including   */ | ||||
| # * without limitation the rights to use, copy, modify, merge, publish,   */ | ||||
| # * distribute, sublicense, and/or sell copies of the Software, and to    */ | ||||
| # * permit persons to whom the Software is furnished to do so, subject to */ | ||||
| # * the following conditions:                                             */ | ||||
| # *                                                                       */ | ||||
| # * The above copyright notice and this permission notice shall be        */ | ||||
| # * included in all copies or substantial portions of the Software.       */ | ||||
| # *                                                                       */ | ||||
| # * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ | ||||
| # * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ | ||||
| # * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ | ||||
| # * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ | ||||
| # * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ | ||||
| # * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ | ||||
| # * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | ||||
| # *************************************************************************/ | ||||
|  | ||||
| cmake_minimum_required(VERSION 3.1) | ||||
| project(SpdlogBench CXX) | ||||
|  | ||||
| if(NOT TARGET spdlog) | ||||
|   # Stand-alone build | ||||
|   find_package(spdlog CONFIG REQUIRED) | ||||
| endif() | ||||
|  | ||||
| find_package(Threads REQUIRED) | ||||
| find_package(benchmark CONFIG REQUIRED) | ||||
|  | ||||
| add_executable(bench bench.cpp) | ||||
| target_link_libraries(bench PRIVATE spdlog::spdlog Threads::Threads) | ||||
|  | ||||
| add_executable(async_bench async_bench.cpp) | ||||
| target_link_libraries(async_bench PRIVATE spdlog::spdlog Threads::Threads) | ||||
|  | ||||
| add_executable(latency latency.cpp) | ||||
| target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog Threads::Threads) | ||||
|  | ||||
|  | ||||
| add_executable(formatter-bench formatter-bench.cpp) | ||||
| target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog Threads::Threads) | ||||
|  | ||||
| file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") | ||||
							
								
								
									
										141
									
								
								third_party/spdlog/bench/async_bench.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								third_party/spdlog/bench/async_bench.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| // | ||||
| // bench.cpp : spdlog benchmarks | ||||
| // | ||||
| #include "spdlog/spdlog.h" | ||||
| #include "spdlog/async.h" | ||||
| #include "spdlog/sinks/basic_file_sink.h" | ||||
| #include "spdlog/sinks/stdout_color_sinks.h" | ||||
|  | ||||
| #include "utils.h" | ||||
| #include <atomic> | ||||
| #include <iostream> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <thread> | ||||
|  | ||||
| using namespace std; | ||||
| using namespace std::chrono; | ||||
| using namespace spdlog; | ||||
| using namespace spdlog::sinks; | ||||
| using namespace utils; | ||||
|  | ||||
| void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count); | ||||
|  | ||||
| int count_lines(const char *filename) | ||||
| { | ||||
|     int counter = 0; | ||||
|     auto *infile = fopen(filename, "r"); | ||||
|     int ch; | ||||
|     while (EOF != (ch = getc(infile))) | ||||
|     { | ||||
|         if ('\n' == ch) | ||||
|             counter++; | ||||
|     } | ||||
|     fclose(infile); | ||||
|  | ||||
|     return counter; | ||||
| } | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
|     int howmany = 1000000; | ||||
|     int queue_size = howmany + 2; | ||||
|     int threads = 10; | ||||
|     int iters = 3; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         if (argc == 1) | ||||
|         { | ||||
|             spdlog::set_pattern("%v"); | ||||
|             spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]); | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         if (argc > 1) | ||||
|             howmany = atoi(argv[1]); | ||||
|         if (argc > 2) | ||||
|             threads = atoi(argv[2]); | ||||
|         if (argc > 3) | ||||
|             queue_size = atoi(argv[3]); | ||||
|  | ||||
|         if (argc > 4) | ||||
|             iters = atoi(argv[4]); | ||||
|  | ||||
|         spdlog::info("-------------------------------------------------"); | ||||
|         spdlog::info("Messages: {:14n}", howmany); | ||||
|         spdlog::info("Threads : {:14n}", threads); | ||||
|         spdlog::info("Queue   : {:14n}", queue_size); | ||||
|         spdlog::info("Iters   : {:>14n}", iters); | ||||
|         spdlog::info("-------------------------------------------------"); | ||||
|  | ||||
|         const char *filename = "logs/basic_async.log"; | ||||
|  | ||||
|         for (int i = 0; i < iters; i++) | ||||
|         { | ||||
|             auto tp = std::make_shared<details::thread_pool>(queue_size, 1); | ||||
|             auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true); | ||||
|             auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block); | ||||
|             bench_mt(howmany, std::move(logger), threads); | ||||
|             auto count = count_lines(filename); | ||||
|  | ||||
|             if (count != howmany) | ||||
|             { | ||||
|                 spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany); | ||||
|                 exit(1); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 spdlog::info("Line count OK ({:n})\n", count); | ||||
|             } | ||||
|         } | ||||
|         spdlog::shutdown(); | ||||
|     } | ||||
|     catch (std::exception &ex) | ||||
|     { | ||||
|         std::cerr << "Error: " << ex.what() << std::endl; | ||||
|         perror("Last error"); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void thread_fun(std::shared_ptr<spdlog::logger> logger, int howmany) | ||||
| { | ||||
|     for (int i = 0; i < howmany; i++) | ||||
|     { | ||||
|         logger->info("Hello logger: msg number {}", i); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_count) | ||||
| { | ||||
|     using std::chrono::high_resolution_clock; | ||||
|     vector<thread> threads; | ||||
|     auto start = high_resolution_clock::now(); | ||||
|  | ||||
|     int msgs_per_thread = howmany / thread_count; | ||||
|     int msgs_per_thread_mod = howmany % thread_count; | ||||
|     for (int t = 0; t < thread_count; ++t) | ||||
|     { | ||||
|         if (t == 0 && msgs_per_thread_mod) | ||||
|             threads.push_back(std::thread(thread_fun, logger, msgs_per_thread + msgs_per_thread_mod)); | ||||
|         else | ||||
|             threads.push_back(std::thread(thread_fun, logger, msgs_per_thread)); | ||||
|     } | ||||
|  | ||||
|     for (auto &t : threads) | ||||
|     { | ||||
|         t.join(); | ||||
|     }; | ||||
|  | ||||
|     auto delta = high_resolution_clock::now() - start; | ||||
|     auto delta_d = duration_cast<duration<double>>(delta).count(); | ||||
|     spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d)); | ||||
| } | ||||
							
								
								
									
										199
									
								
								third_party/spdlog/bench/bench.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								third_party/spdlog/bench/bench.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| // | ||||
| // bench.cpp : spdlog benchmarks | ||||
| // | ||||
| #include "spdlog/spdlog.h" | ||||
| #include "spdlog/async.h" | ||||
| #include "spdlog/sinks/basic_file_sink.h" | ||||
| #include "spdlog/sinks/daily_file_sink.h" | ||||
| #include "spdlog/sinks/null_sink.h" | ||||
| #include "spdlog/sinks/rotating_file_sink.h" | ||||
|  | ||||
| #include "utils.h" | ||||
| #include <atomic> | ||||
| #include <cstdlib> // EXIT_FAILURE | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <thread> | ||||
|  | ||||
| using namespace std; | ||||
| using namespace std::chrono; | ||||
| using namespace spdlog; | ||||
| using namespace spdlog::sinks; | ||||
| using namespace utils; | ||||
|  | ||||
| void bench(int howmany, std::shared_ptr<spdlog::logger> log); | ||||
| void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count); | ||||
| void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log); | ||||
| void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log); | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
|     spdlog::default_logger()->set_pattern("[%^%l%$] %v"); | ||||
|     int howmany = 1000000; | ||||
|     int queue_size = howmany + 2; | ||||
|     int threads = 10; | ||||
|     size_t file_size = 30 * 1024 * 1024; | ||||
|     size_t rotating_files = 5; | ||||
|  | ||||
|     try | ||||
|     { | ||||
|  | ||||
|         if (argc > 1) | ||||
|             howmany = atoi(argv[1]); | ||||
|         if (argc > 2) | ||||
|             threads = atoi(argv[2]); | ||||
|         if (argc > 3) | ||||
|             queue_size = atoi(argv[3]); | ||||
|  | ||||
|         spdlog::info("**************************************************************"); | ||||
|         spdlog::info("Single thread, {:n} iterations", howmany); | ||||
|         spdlog::info("**************************************************************"); | ||||
|  | ||||
|         auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true); | ||||
|         bench(howmany, std::move(basic_st)); | ||||
|  | ||||
|         basic_st.reset(); | ||||
|         auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files); | ||||
|         bench(howmany, std::move(rotating_st)); | ||||
|  | ||||
|         auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log"); | ||||
|         bench(howmany, std::move(daily_st)); | ||||
|  | ||||
|         bench(howmany, spdlog::create<null_sink_st>("null_st")); | ||||
|  | ||||
|         spdlog::info("**************************************************************"); | ||||
|         spdlog::info("C-string (400 bytes). Single thread, {:n} iterations", howmany); | ||||
|         spdlog::info("**************************************************************"); | ||||
|  | ||||
|         basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_cs.log", true); | ||||
|         bench_c_string(howmany, std::move(basic_st)); | ||||
|  | ||||
|         rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_cs.log", file_size, rotating_files); | ||||
|         bench_c_string(howmany, std::move(rotating_st)); | ||||
|  | ||||
|         daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_cs.log"); | ||||
|         bench_c_string(howmany, std::move(daily_st)); | ||||
|  | ||||
|         bench_c_string(howmany, spdlog::create<null_sink_st>("null_st")); | ||||
|  | ||||
|         spdlog::info("**************************************************************"); | ||||
|         spdlog::info("{:n} threads sharing same logger, {:n} iterations", threads, howmany); | ||||
|         spdlog::info("**************************************************************"); | ||||
|  | ||||
|         auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true); | ||||
|         bench_mt(howmany, std::move(basic_mt), threads); | ||||
|  | ||||
|         auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files); | ||||
|         bench_mt(howmany, std::move(rotating_mt), threads); | ||||
|  | ||||
|         auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log"); | ||||
|         bench_mt(howmany, std::move(daily_mt), threads); | ||||
|         bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads); | ||||
|  | ||||
|         spdlog::info("**************************************************************"); | ||||
|         spdlog::info("Asyncronous.. {:n} threads sharing same logger, {:n} iterations", threads, howmany); | ||||
|         spdlog::info("**************************************************************"); | ||||
|  | ||||
|         for (int i = 0; i < 3; ++i) | ||||
|         { | ||||
|             spdlog::init_thread_pool(static_cast<size_t>(queue_size), 1); | ||||
|             auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true); | ||||
|             bench_mt(howmany, std::move(as), threads); | ||||
|         } | ||||
|     } | ||||
|     catch (std::exception &ex) | ||||
|     { | ||||
|         spdlog::error(ex.what()); | ||||
|         return EXIT_FAILURE; | ||||
|     } | ||||
|     return EXIT_SUCCESS; | ||||
| } | ||||
|  | ||||
| void bench(int howmany, std::shared_ptr<spdlog::logger> log) | ||||
| { | ||||
|     using std::chrono::high_resolution_clock; | ||||
|     auto start = high_resolution_clock::now(); | ||||
|     for (auto i = 0; i < howmany; ++i) | ||||
|     { | ||||
|         log->info("Hello logger: msg number {}", i); | ||||
|     } | ||||
|  | ||||
|     auto delta = high_resolution_clock::now() - start; | ||||
|     auto delta_d = duration_cast<duration<double>>(delta).count(); | ||||
|  | ||||
|     spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); | ||||
|     spdlog::drop(log->name()); | ||||
| } | ||||
|  | ||||
| void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count) | ||||
| { | ||||
|     using std::chrono::high_resolution_clock; | ||||
|     vector<thread> threads; | ||||
|     auto start = high_resolution_clock::now(); | ||||
|     for (int t = 0; t < thread_count; ++t) | ||||
|     { | ||||
|         threads.push_back(std::thread([&]() { | ||||
|             for (int j = 0; j < howmany / thread_count; j++) | ||||
|             { | ||||
|                 log->info("Hello logger: msg number {}", j); | ||||
|             } | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     for (auto &t : threads) | ||||
|     { | ||||
|         t.join(); | ||||
|     }; | ||||
|  | ||||
|     auto delta = high_resolution_clock::now() - start; | ||||
|     auto delta_d = duration_cast<duration<double>>(delta).count(); | ||||
|     spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); | ||||
|     spdlog::drop(log->name()); | ||||
| } | ||||
|  | ||||
| void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log) | ||||
| { | ||||
|     using std::chrono::high_resolution_clock; | ||||
|     auto orig_default = spdlog::default_logger(); | ||||
|     spdlog::set_default_logger(log); | ||||
|     auto start = high_resolution_clock::now(); | ||||
|     for (auto i = 0; i < howmany; ++i) | ||||
|     { | ||||
|         spdlog::info("Hello logger: msg number {}", i); | ||||
|     } | ||||
|  | ||||
|     auto delta = high_resolution_clock::now() - start; | ||||
|     auto delta_d = duration_cast<duration<double>>(delta).count(); | ||||
|     spdlog::drop(log->name()); | ||||
|     spdlog::set_default_logger(std::move(orig_default)); | ||||
|     spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); | ||||
| } | ||||
|  | ||||
| void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log) | ||||
| { | ||||
|     const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus " | ||||
|                       "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem " | ||||
|                       "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed " | ||||
|                       "augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare " | ||||
|                       "nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis."; | ||||
|     using std::chrono::high_resolution_clock; | ||||
|     auto orig_default = spdlog::default_logger(); | ||||
|     spdlog::set_default_logger(log); | ||||
|     auto start = high_resolution_clock::now(); | ||||
|     for (auto i = 0; i < howmany; ++i) | ||||
|     { | ||||
|         spdlog::log(level::info, msg); | ||||
|     } | ||||
|  | ||||
|     auto delta = high_resolution_clock::now() - start; | ||||
|     auto delta_d = duration_cast<duration<double>>(delta).count(); | ||||
|     spdlog::drop(log->name()); | ||||
|     spdlog::set_default_logger(std::move(orig_default)); | ||||
|     spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); | ||||
| } | ||||
							
								
								
									
										92
									
								
								third_party/spdlog/bench/formatter-bench.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								third_party/spdlog/bench/formatter-bench.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| // | ||||
| // Copyright(c) 2018 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #include "benchmark/benchmark.h" | ||||
|  | ||||
| #include "spdlog/spdlog.h" | ||||
| #include "spdlog/details/pattern_formatter.h" | ||||
|  | ||||
| void bench_scoped_pad(benchmark::State &state, size_t wrapped_size, spdlog::details::padding_info padinfo) | ||||
| { | ||||
|     fmt::memory_buffer dest; | ||||
|     for (auto _ : state) | ||||
|     { | ||||
|         { | ||||
|             spdlog::details::scoped_pad p(wrapped_size, padinfo, dest); | ||||
|             benchmark::DoNotOptimize(p); | ||||
|             dest.clear(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void bench_formatter(benchmark::State &state, std::string pattern) | ||||
| { | ||||
|     auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern); | ||||
|     fmt::memory_buffer dest; | ||||
|     std::string logger_name = "logger-name"; | ||||
|     const char *text = "Hello. This is some message with length of 80                                   "; | ||||
|  | ||||
|     spdlog::details::log_msg msg(&logger_name, spdlog::level::info, text); | ||||
|  | ||||
|     for (auto _ : state) | ||||
|     { | ||||
|         dest.clear(); | ||||
|         formatter->format(msg, dest); | ||||
|         benchmark::DoNotOptimize(dest); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void bench_formatters() | ||||
| { | ||||
|     // basic patterns(single flag) | ||||
|     std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEi%"; | ||||
|     std::vector<std::string> basic_patterns; | ||||
|     for (auto &flag : all_flags) | ||||
|     { | ||||
|         auto pattern = std::string("%") + flag; | ||||
|         benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); | ||||
|  | ||||
|         //        pattern = std::string("%16") + flag; | ||||
|         //        benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); | ||||
|         // | ||||
|         //        // bench center padding | ||||
|         //        pattern = std::string("%=16") + flag; | ||||
|         //        benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); | ||||
|     } | ||||
|  | ||||
|     // complex patterns | ||||
|     std::vector<std::string> patterns = { | ||||
|         "[%D %X] [%l] [%n] %v", | ||||
|         "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v", | ||||
|         "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v", | ||||
|     }; | ||||
|     for (auto &pattern : patterns) | ||||
|     { | ||||
|         benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
|     spdlog::set_pattern("[%^%l%$] %v"); | ||||
|     if (argc != 2) | ||||
|     { | ||||
|         spdlog::error("Usage: {} <pattern> (or \"all\" to bench all)", argv[0]); | ||||
|         exit(1); | ||||
|     } | ||||
|  | ||||
|     std::string pattern = argv[1]; | ||||
|     if (pattern == "all") | ||||
|     { | ||||
|         bench_formatters(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern); | ||||
|     } | ||||
|     benchmark::Initialize(&argc, argv); | ||||
|     benchmark::RunSpecifiedBenchmarks(); | ||||
| } | ||||
							
								
								
									
										143
									
								
								third_party/spdlog/bench/latency.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								third_party/spdlog/bench/latency.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| // | ||||
| // Copyright(c) 2018 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| // | ||||
| // latency.cpp : spdlog latency benchmarks | ||||
| // | ||||
|  | ||||
| #include "benchmark/benchmark.h" | ||||
|  | ||||
| #include "spdlog/spdlog.h" | ||||
| #include "spdlog/async.h" | ||||
| #include "spdlog/sinks/basic_file_sink.h" | ||||
| #include "spdlog/sinks/daily_file_sink.h" | ||||
| #include "spdlog/sinks/null_sink.h" | ||||
| #include "spdlog/sinks/rotating_file_sink.h" | ||||
|  | ||||
| void prepare_logdir() | ||||
| { | ||||
|     spdlog::info("Preparing latency_logs directory.."); | ||||
| #ifdef _WIN32 | ||||
|     system("if not exist logs mkdir latency_logs"); | ||||
|     system("del /F /Q logs\\*"); | ||||
| #else | ||||
|     auto rv = system("mkdir -p latency_logs"); | ||||
|     if (rv != 0) | ||||
|     { | ||||
|         throw std::runtime_error("Failed to mkdir -p latency_logs"); | ||||
|     } | ||||
|     rv = system("rm -f latency_logs/*"); | ||||
|     if (rv != 0) | ||||
|     { | ||||
|         throw std::runtime_error("Failed to rm -f latency_logs/*"); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void bench_c_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger) | ||||
| { | ||||
|     const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus " | ||||
|                       "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem " | ||||
|                       "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed " | ||||
|                       "augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare " | ||||
|                       "nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis."; | ||||
|  | ||||
|     for (auto _ : state) | ||||
|     { | ||||
|         logger->info(msg); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger) | ||||
| { | ||||
|     int i = 0; | ||||
|     for (auto _ : state) | ||||
|     { | ||||
|         logger->info("Hello logger: msg number {}...............", ++i); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger) | ||||
| { | ||||
|     int i = 0; | ||||
|     benchmark::DoNotOptimize(i);      // prevent unused warnings | ||||
|     benchmark::DoNotOptimize(logger); // prevent unused warnings | ||||
|     for (auto _ : state) | ||||
|     { | ||||
|         SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++); | ||||
|         SPDLOG_DEBUG("Hello logger: msg number {}...............", i++); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|  | ||||
|     using spdlog::sinks::basic_file_sink_mt; | ||||
|     using spdlog::sinks::basic_file_sink_st; | ||||
|     using spdlog::sinks::null_sink_mt; | ||||
|     using spdlog::sinks::null_sink_st; | ||||
|  | ||||
|     size_t file_size = 30 * 1024 * 1024; | ||||
|     size_t rotating_files = 5; | ||||
|     int n_threads = benchmark::CPUInfo::Get().num_cpus; | ||||
|  | ||||
|     prepare_logdir(); | ||||
|  | ||||
|     // disabled loggers | ||||
|     auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>()); | ||||
|     disabled_logger->set_level(spdlog::level::off); | ||||
|     benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger); | ||||
|     benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger); | ||||
|  | ||||
|     auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>()); | ||||
|     benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st)); | ||||
|     benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st); | ||||
|  | ||||
|     // basic_st | ||||
|     auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true); | ||||
|     benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime(); | ||||
|     spdlog::drop("basic_st"); | ||||
|  | ||||
|     // rotating st | ||||
|     auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files); | ||||
|     benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime(); | ||||
|     spdlog::drop("rotating_st"); | ||||
|  | ||||
|     // daily st | ||||
|     auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log"); | ||||
|     benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime(); | ||||
|     spdlog::drop("daily_st"); | ||||
|  | ||||
|     //    // | ||||
|     //    // Multi threaded bench, 10 loggers using same logger concurrently | ||||
|     //    // | ||||
|     auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>()); | ||||
|     benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime(); | ||||
|  | ||||
|     // basic_mt | ||||
|     auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true); | ||||
|     benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime(); | ||||
|     spdlog::drop("basic_mt"); | ||||
|  | ||||
|     // rotating mt | ||||
|     auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files); | ||||
|     benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime(); | ||||
|     spdlog::drop("rotating_mt"); | ||||
|  | ||||
|     // daily mt | ||||
|     auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log"); | ||||
|     benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime(); | ||||
|     spdlog::drop("daily_mt"); | ||||
|  | ||||
|     // async | ||||
|     auto queue_size = 1024 * 1024 * 3; | ||||
|     auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1); | ||||
|     auto async_logger = std::make_shared<spdlog::async_logger>( | ||||
|         "async_logger", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest); | ||||
|     benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime(); | ||||
|  | ||||
|     benchmark::Initialize(&argc, argv); | ||||
|     benchmark::RunSpecifiedBenchmarks(); | ||||
| } | ||||
							
								
								
									
										4
									
								
								third_party/spdlog/bench/logs/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								third_party/spdlog/bench/logs/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| # Ignore everything in this directory | ||||
| * | ||||
| # Except this file | ||||
| !.gitignore | ||||
							
								
								
									
										19
									
								
								third_party/spdlog/bench/mem
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										19
									
								
								third_party/spdlog/bench/mem
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| if [ $# -lt 1 ]; then | ||||
|   echo "usage: $0 <program>" | ||||
| fi | ||||
|  | ||||
| PROG=$1 | ||||
|  | ||||
| if [ ! -x "$PROG" ]; then | ||||
|   echo $PROG not found or not executable. | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| $* & | ||||
| PID=$! | ||||
|  | ||||
| while `kill -0 $PID 2>/dev/null`; do | ||||
|   ps -eo size,pid,user,pcpu,command --sort -size | awk '{ line=1 ; hr=$1/1024 ; printf("%13.2f Mb ",hr); } { for ( x=4 ; x<=NF ; x++ ) { printf("%s ",$x) } print "" }' | grep -v grep | grep -v $0 | grep $PROG | ||||
| done | ||||
							
								
								
									
										34
									
								
								third_party/spdlog/bench/utils.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								third_party/spdlog/bench/utils.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <iomanip> | ||||
| #include <locale> | ||||
| #include <sstream> | ||||
|  | ||||
| namespace utils { | ||||
|  | ||||
| template<typename T> | ||||
| inline std::string format(const T &value) | ||||
| { | ||||
|     static std::locale loc(""); | ||||
|     std::stringstream ss; | ||||
|     ss.imbue(loc); | ||||
|     ss << value; | ||||
|     return ss.str(); | ||||
| } | ||||
|  | ||||
| template<> | ||||
| inline std::string format(const double &value) | ||||
| { | ||||
|     static std::locale loc(""); | ||||
|     std::stringstream ss; | ||||
|     ss.imbue(loc); | ||||
|     ss << std::fixed << std::setprecision(1) << value; | ||||
|     return ss.str(); | ||||
| } | ||||
|  | ||||
| } // namespace utils | ||||
							
								
								
									
										2
									
								
								third_party/spdlog/clang_tidy.sh
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										2
									
								
								third_party/spdlog/clang_tidy.sh
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| #!/bin/bash | ||||
| clang-tidy example/example.cpp -- -I ./include  | ||||
							
								
								
									
										31
									
								
								third_party/spdlog/cmake/Config.cmake.in
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								third_party/spdlog/cmake/Config.cmake.in
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| # *************************************************************************/ | ||||
| # * Copyright (c) 2015 Ruslan Baratov.                                    */ | ||||
| # *                                                                       */ | ||||
| # * Permission is hereby granted, free of charge, to any person obtaining */ | ||||
| # * a copy of this software and associated documentation files (the       */ | ||||
| # * "Software"), to deal in the Software without restriction, including   */ | ||||
| # * without limitation the rights to use, copy, modify, merge, publish,   */ | ||||
| # * distribute, sublicense, and/or sell copies of the Software, and to    */ | ||||
| # * permit persons to whom the Software is furnished to do so, subject to */ | ||||
| # * the following conditions:                                             */ | ||||
| # *                                                                       */ | ||||
| # * The above copyright notice and this permission notice shall be        */ | ||||
| # * included in all copies or substantial portions of the Software.       */ | ||||
| # *                                                                       */ | ||||
| # * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ | ||||
| # * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ | ||||
| # * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ | ||||
| # * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ | ||||
| # * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ | ||||
| # * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ | ||||
| # * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | ||||
| # *************************************************************************/ | ||||
|  | ||||
| set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) | ||||
|  | ||||
| include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") | ||||
|  | ||||
| if(SPDLOG_FMT_EXTERNAL) | ||||
|     include(CMakeFindDependencyMacro) | ||||
|     find_dependency(fmt CONFIG) | ||||
| endif() | ||||
							
								
								
									
										21
									
								
								third_party/spdlog/cmake/sanitizers.cmake
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								third_party/spdlog/cmake/sanitizers.cmake
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| if(SPDLOG_SANITIZE_THREAD AND SPDLOG_SANITIZE_ADDRESS) | ||||
|     message(FATAL_ERROR "AddressSanitizer is not compatible with ThreadSanitizer.") | ||||
| endif() | ||||
|  | ||||
| if(SPDLOG_SANITIZE_ADDRESS) | ||||
|     message(STATUS "AddressSanitizer enabled") | ||||
|     set(SANITIZER_FLAGS "-fsanitize=address,undefined") | ||||
|     add_compile_options("-fno-sanitize=signed-integer-overflow") | ||||
| endif() | ||||
|  | ||||
| if(SPDLOG_SANITIZE_THREAD) | ||||
|     message(STATUS "ThreadSanitizer enabled") | ||||
|     set(SANITIZER_FLAGS "-fsanitize=thread") | ||||
| endif() | ||||
|  | ||||
| if(SPDLOG_SANITIZE_THREAD OR SPDLOG_SANITIZE_ADDRESS) | ||||
|     add_compile_options(${SANITIZER_FLAGS}) | ||||
|     add_compile_options("-fno-sanitize-recover=all") | ||||
|     add_compile_options("-fno-omit-frame-pointer") | ||||
|     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS} -fuse-ld=gold") | ||||
| endif() | ||||
							
								
								
									
										6
									
								
								third_party/spdlog/cmake/spdlog.pc.in
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								third_party/spdlog/cmake/spdlog.pc.in
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| prefix=@CMAKE_INSTALL_PREFIX@ | ||||
| includedir=${prefix}/include | ||||
|  | ||||
| Name: @PROJECT_NAME@ | ||||
| Description: Super fast C++ logging library.  | ||||
| Version: @PROJECT_VERSION@ | ||||
							
								
								
									
										49
									
								
								third_party/spdlog/example/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								third_party/spdlog/example/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| # *************************************************************************/ | ||||
| # * Copyright (c) 2015 Ruslan Baratov.                                    */ | ||||
| # *                                                                       */ | ||||
| # * Permission is hereby granted, free of charge, to any person obtaining */ | ||||
| # * a copy of this software and associated documentation files (the       */ | ||||
| # * "Software"), to deal in the Software without restriction, including   */ | ||||
| # * without limitation the rights to use, copy, modify, merge, publish,   */ | ||||
| # * distribute, sublicense, and/or sell copies of the Software, and to    */ | ||||
| # * permit persons to whom the Software is furnished to do so, subject to */ | ||||
| # * the following conditions:                                             */ | ||||
| # *                                                                       */ | ||||
| # * The above copyright notice and this permission notice shall be        */ | ||||
| # * included in all copies or substantial portions of the Software.       */ | ||||
| # *                                                                       */ | ||||
| # * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ | ||||
| # * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ | ||||
| # * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ | ||||
| # * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ | ||||
| # * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ | ||||
| # * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ | ||||
| # * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | ||||
| # *************************************************************************/ | ||||
|  | ||||
| cmake_minimum_required(VERSION 3.1) | ||||
| project(SpdlogExamples CXX) | ||||
|  | ||||
| if(NOT TARGET spdlog) | ||||
|   # Stand-alone build | ||||
|   find_package(spdlog CONFIG REQUIRED) | ||||
| endif() | ||||
|  | ||||
| find_package(Threads REQUIRED) | ||||
|  | ||||
| add_executable(example example.cpp) | ||||
| if(CMAKE_SYSTEM_NAME STREQUAL "Android") | ||||
|     find_library(log-lib log) | ||||
|     target_link_libraries(example spdlog::spdlog Threads::Threads log) | ||||
| else() | ||||
|     target_link_libraries(example spdlog::spdlog Threads::Threads) | ||||
| endif() | ||||
|  | ||||
|  | ||||
| add_executable(multisink multisink.cpp) | ||||
| target_link_libraries(multisink spdlog::spdlog Threads::Threads) | ||||
|  | ||||
| file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") | ||||
|  | ||||
| enable_testing() | ||||
| add_test(NAME example COMMAND example) | ||||
							
								
								
									
										22
									
								
								third_party/spdlog/example/Makefile-all-warn
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								third_party/spdlog/example/Makefile-all-warn
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| #-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded | ||||
| CXX	?= g++ | ||||
| CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-weak-vtables -Wno-global-constructors | ||||
| CXX_RELEASE_FLAGS = -O3 -march=native | ||||
| CXX_DEBUG_FLAGS= -g | ||||
|  | ||||
| all:	example  | ||||
| debug:	example-debug | ||||
|  | ||||
| example: example.cpp | ||||
| 	$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS) | ||||
|  | ||||
|  | ||||
| example-debug: example.cpp | ||||
| 	$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS) | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o logs/*.txt example example-debug | ||||
|  | ||||
|  | ||||
| rebuild: clean all | ||||
| rebuild-debug: clean debug | ||||
							
								
								
									
										26
									
								
								third_party/spdlog/example/Makefile.clang
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								third_party/spdlog/example/Makefile.clang
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| CXX	= clang++ | ||||
| CXXFLAGS	= -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include | ||||
| CXX_RELEASE_FLAGS = -O2 | ||||
| CXX_DEBUG_FLAGS= -g  | ||||
|  | ||||
|  | ||||
| all:	example  | ||||
| debug: example-debug  | ||||
|  | ||||
| example: example.cpp | ||||
| 	$(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS) | ||||
|  | ||||
| 	 | ||||
|  | ||||
| example-debug: example.cpp | ||||
| 	$(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) | ||||
|  | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o logs/*.txt example-clang example-clang-debug  | ||||
|  | ||||
|  | ||||
| rebuild: clean all | ||||
| rebuild-debug: clean debug | ||||
|  | ||||
|  | ||||
							
								
								
									
										25
									
								
								third_party/spdlog/example/Makefile.mingw
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								third_party/spdlog/example/Makefile.mingw
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| CXX	?= g++ | ||||
| CXXFLAGS	=  -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed  -I../include  | ||||
| CXX_RELEASE_FLAGS = -O3  | ||||
| CXX_DEBUG_FLAGS= -g  | ||||
|  | ||||
|  | ||||
| all:	example  | ||||
| debug: example-debug  | ||||
|  | ||||
| example: example.cpp | ||||
| 	$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS) | ||||
|  | ||||
| 	 | ||||
| example-debug: example.cpp | ||||
| 	$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) | ||||
|  | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o logs/*.txt example example-debug | ||||
|  | ||||
|  | ||||
| rebuild: clean all | ||||
| rebuild-debug: clean debug | ||||
|  | ||||
|  | ||||
							
								
								
									
										235
									
								
								third_party/spdlog/example/example.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								third_party/spdlog/example/example.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
| // | ||||
| // spdlog usage example | ||||
| // | ||||
| // | ||||
|  | ||||
| #include <cstdio> | ||||
|  | ||||
| void stdout_logger_example(); | ||||
| void basic_example(); | ||||
| void rotating_example(); | ||||
| void daily_example(); | ||||
| void async_example(); | ||||
| void binary_example(); | ||||
| void trace_example(); | ||||
| void multi_sink_example(); | ||||
| void user_defined_example(); | ||||
| void err_handler_example(); | ||||
| void syslog_example(); | ||||
| void clone_example(); | ||||
|  | ||||
| #include "spdlog/spdlog.h" | ||||
|  | ||||
| int main(int, char *[]) | ||||
| { | ||||
|     spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); | ||||
|     spdlog::warn("Easy padding in numbers like {:08d}", 12); | ||||
|     spdlog::critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42); | ||||
|     spdlog::info("Support for floats {:03.2f}", 1.23456); | ||||
|     spdlog::info("Positional args are {1} {0}..", "too", "supported"); | ||||
|     spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left"); | ||||
|  | ||||
|     // Runtime log levels | ||||
|     spdlog::set_level(spdlog::level::info); // Set global log level to info | ||||
|     spdlog::debug("This message should not be displayed!"); | ||||
|     spdlog::set_level(spdlog::level::trace); // Set specific logger's log level | ||||
|     spdlog::debug("This message should be displayed.."); | ||||
|  | ||||
|     // Customize msg format for all loggers | ||||
|     spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); | ||||
|     spdlog::info("This an info message with custom format"); | ||||
|     spdlog::set_pattern("%+"); // back to default format | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         stdout_logger_example(); | ||||
|         basic_example(); | ||||
|         rotating_example(); | ||||
|         daily_example(); | ||||
|         clone_example(); | ||||
|         async_example(); | ||||
|         binary_example(); | ||||
|         multi_sink_example(); | ||||
|         user_defined_example(); | ||||
|         err_handler_example(); | ||||
|         trace_example(); | ||||
|  | ||||
|         // Flush all *registered* loggers using a worker thread every 3 seconds. | ||||
|         // note: registered loggers *must* be thread safe for this to work correctly! | ||||
|         spdlog::flush_every(std::chrono::seconds(3)); | ||||
|  | ||||
|         // Apply some function on all registered loggers | ||||
|         spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); }); | ||||
|  | ||||
|         // Release all spdlog resources, and drop all loggers in the registry. | ||||
|         // This is optional (only mandatory if using windows + async log). | ||||
|         spdlog::shutdown(); | ||||
|     } | ||||
|  | ||||
|     // Exceptions will only be thrown upon failed logger or sink construction (not during logging). | ||||
|     catch (const spdlog::spdlog_ex &ex) | ||||
|     { | ||||
|         std::printf("Log initialization failed: %s\n", ex.what()); | ||||
|         return 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #include "spdlog/sinks/stdout_color_sinks.h" | ||||
| // or #include "spdlog/sinks/stdout_sinks.h" if no colors needed. | ||||
| void stdout_logger_example() | ||||
| { | ||||
|     // Create color multi threaded logger. | ||||
|     auto console = spdlog::stdout_color_mt("console"); | ||||
|     // or for stderr: | ||||
|     // auto console = spdlog::stderr_color_mt("error-logger"); | ||||
| } | ||||
|  | ||||
| #include "spdlog/sinks/basic_file_sink.h" | ||||
| void basic_example() | ||||
| { | ||||
|     // Create basic file logger (not rotated). | ||||
|     auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt"); | ||||
| } | ||||
|  | ||||
| #include "spdlog/sinks/rotating_file_sink.h" | ||||
| void rotating_example() | ||||
| { | ||||
|     // Create a file rotating logger with 5mb size max and 3 rotated files. | ||||
|     auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); | ||||
| } | ||||
|  | ||||
| #include "spdlog/sinks/daily_file_sink.h" | ||||
| void daily_example() | ||||
| { | ||||
|     // Create a daily logger - a new file is created every day on 2:30am. | ||||
|     auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); | ||||
| } | ||||
|  | ||||
| // Clone a logger and give it new name. | ||||
| // Useful for creating component/subsystem loggers from some "root" logger. | ||||
| void clone_example() | ||||
| { | ||||
|     auto network_logger = spdlog::default_logger()->clone("network"); | ||||
|     network_logger->info("Logging network stuff.."); | ||||
| } | ||||
|  | ||||
| #include "spdlog/async.h" | ||||
| void async_example() | ||||
| { | ||||
|     // Default thread pool settings can be modified *before* creating the async logger: | ||||
|     // spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread. | ||||
|     auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt"); | ||||
|     // alternatively: | ||||
|     // auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt"); | ||||
|  | ||||
|     for (int i = 1; i < 101; ++i) | ||||
|     { | ||||
|         async_file->info("Async message #{}", i); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Log binary data as hex. | ||||
| // Many types of std::container<char> types can be used. | ||||
| // Iterator ranges are supported too. | ||||
| // Format flags: | ||||
| // {:X} - print in uppercase. | ||||
| // {:s} - don't separate each byte with space. | ||||
| // {:p} - don't print the position on each line start. | ||||
| // {:n} - don't split the output to lines. | ||||
|  | ||||
| #include "spdlog/fmt/bin_to_hex.h" | ||||
| void binary_example() | ||||
| { | ||||
|     std::vector<char> buf; | ||||
|     for (int i = 0; i < 80; i++) | ||||
|     { | ||||
|         buf.push_back(static_cast<char>(i & 0xff)); | ||||
|     } | ||||
|     spdlog::info("Binary example: {}", spdlog::to_hex(buf)); | ||||
|     spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); | ||||
|     // more examples: | ||||
|     // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); | ||||
|     // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); | ||||
|     // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); | ||||
| } | ||||
|  | ||||
| // Compile time log levels. | ||||
| // define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE) | ||||
| void trace_example() | ||||
| { | ||||
|     // trace from default logger | ||||
|     SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23); | ||||
|     // debug from default logger | ||||
|     SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23); | ||||
|  | ||||
|     // trace from logger object | ||||
|     auto logger = spdlog::get("file_logger"); | ||||
|     SPDLOG_LOGGER_TRACE(logger, "another trace message"); | ||||
| } | ||||
|  | ||||
| // A logger with multiple sinks (stdout and file) - each with a different format and log level. | ||||
| void multi_sink_example() | ||||
| { | ||||
|     auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); | ||||
|     console_sink->set_level(spdlog::level::warn); | ||||
|     console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); | ||||
|  | ||||
|     auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true); | ||||
|     file_sink->set_level(spdlog::level::trace); | ||||
|  | ||||
|     spdlog::logger logger("multi_sink", {console_sink, file_sink}); | ||||
|     logger.set_level(spdlog::level::debug); | ||||
|     logger.warn("this should appear in both console and file"); | ||||
|     logger.info("this message should not appear in the console, only in the file"); | ||||
| } | ||||
|  | ||||
| // User defined types logging by implementing operator<< | ||||
| #include "spdlog/fmt/ostr.h" // must be included | ||||
| struct my_type | ||||
| { | ||||
|     int i; | ||||
|     template<typename OStream> | ||||
|     friend OStream &operator<<(OStream &os, const my_type &c) | ||||
|     { | ||||
|         return os << "[my_type i=" << c.i << "]"; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| void user_defined_example() | ||||
| { | ||||
|     spdlog::info("user defined type: {}", my_type{14}); | ||||
| } | ||||
|  | ||||
| // Custom error handler. Will be triggered on log failure. | ||||
| void err_handler_example() | ||||
| { | ||||
|     // can be set globally or per logger(logger->set_error_handler(..)) | ||||
|     spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); }); | ||||
| } | ||||
|  | ||||
| // syslog example (linux/osx/freebsd) | ||||
| #ifndef _WIN32 | ||||
| #include "spdlog/sinks/syslog_sink.h" | ||||
| void syslog_example() | ||||
| { | ||||
|     std::string ident = "spdlog-example"; | ||||
|     auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); | ||||
|     syslog_logger->warn("This is warning that will end up in syslog."); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| // Android example. | ||||
| #if defined(__ANDROID__) | ||||
| #include "spdlog/sinks/android_sink.h" | ||||
| void android_example() | ||||
| { | ||||
|     std::string tag = "spdlog-android"; | ||||
|     auto android_logger = spdlog::android_logger_mt("android", tag); | ||||
|     android_logger->critical("Use \"adb shell logcat\" to view this message."); | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										106
									
								
								third_party/spdlog/example/example.sln
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								third_party/spdlog/example/example.sln
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
|  | ||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio 15 | ||||
| VisualStudioVersion = 15.0.27703.2018 | ||||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{7FC6AB76-AD88-4135-888C-0568E81475AF}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		..\include\spdlog\async.h = ..\include\spdlog\async.h | ||||
| 		..\include\spdlog\async_logger.h = ..\include\spdlog\async_logger.h | ||||
| 		..\include\spdlog\common.h = ..\include\spdlog\common.h | ||||
| 		..\include\spdlog\formatter.h = ..\include\spdlog\formatter.h | ||||
| 		..\include\spdlog\logger.h = ..\include\spdlog\logger.h | ||||
| 		..\include\spdlog\spdlog.h = ..\include\spdlog\spdlog.h | ||||
| 		..\include\spdlog\tweakme.h = ..\include\spdlog\tweakme.h | ||||
| 		..\include\spdlog\version.h = ..\include\spdlog\version.h | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "details", "details", "{08E93803-E650-42D9-BBB4-3C16979F850E}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		..\include\spdlog\details\async_logger_impl.h = ..\include\spdlog\details\async_logger_impl.h | ||||
| 		..\include\spdlog\details\circular_q.h = ..\include\spdlog\details\circular_q.h | ||||
| 		..\include\spdlog\details\console_globals.h = ..\include\spdlog\details\console_globals.h | ||||
| 		..\include\spdlog\details\file_helper.h = ..\include\spdlog\details\file_helper.h | ||||
| 		..\include\spdlog\details\fmt_helper.h = ..\include\spdlog\details\fmt_helper.h | ||||
| 		..\include\spdlog\details\log_msg.h = ..\include\spdlog\details\log_msg.h | ||||
| 		..\include\spdlog\details\logger_impl.h = ..\include\spdlog\details\logger_impl.h | ||||
| 		..\include\spdlog\details\mpmc_blocking_q.h = ..\include\spdlog\details\mpmc_blocking_q.h | ||||
| 		..\include\spdlog\details\null_mutex.h = ..\include\spdlog\details\null_mutex.h | ||||
| 		..\include\spdlog\details\os.h = ..\include\spdlog\details\os.h | ||||
| 		..\include\spdlog\details\pattern_formatter.h = ..\include\spdlog\details\pattern_formatter.h | ||||
| 		..\include\spdlog\details\periodic_worker.h = ..\include\spdlog\details\periodic_worker.h | ||||
| 		..\include\spdlog\details\registry.h = ..\include\spdlog\details\registry.h | ||||
| 		..\include\spdlog\details\spdlog_impl.h = ..\include\spdlog\details\spdlog_impl.h | ||||
| 		..\include\spdlog\details\thread_pool.h = ..\include\spdlog\details\thread_pool.h | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fmt", "fmt", "{82378DE1-8463-4F91-91A0-C2C40E2AEA2A}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		..\include\spdlog\fmt\fmt.h = ..\include\spdlog\fmt\fmt.h | ||||
| 		..\include\spdlog\fmt\ostr.h = ..\include\spdlog\fmt\ostr.h | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bundled", "bundled", "{D9CA4494-80D1-48D1-A897-D3564F7B27FF}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		..\include\spdlog\fmt\bundled\format.cc = ..\include\spdlog\fmt\bundled\format.cc | ||||
| 		..\include\spdlog\fmt\bundled\format.h = ..\include\spdlog\fmt\bundled\format.h | ||||
| 		..\include\spdlog\fmt\bundled\LICENSE.rst = ..\include\spdlog\fmt\bundled\LICENSE.rst | ||||
| 		..\include\spdlog\fmt\bundled\ostream.cc = ..\include\spdlog\fmt\bundled\ostream.cc | ||||
| 		..\include\spdlog\fmt\bundled\ostream.h = ..\include\spdlog\fmt\bundled\ostream.h | ||||
| 		..\include\spdlog\fmt\bundled\posix.cc = ..\include\spdlog\fmt\bundled\posix.cc | ||||
| 		..\include\spdlog\fmt\bundled\posix.h = ..\include\spdlog\fmt\bundled\posix.h | ||||
| 		..\include\spdlog\fmt\bundled\printf.cc = ..\include\spdlog\fmt\bundled\printf.cc | ||||
| 		..\include\spdlog\fmt\bundled\printf.h = ..\include\spdlog\fmt\bundled\printf.h | ||||
| 		..\include\spdlog\fmt\bundled\time.h = ..\include\spdlog\fmt\bundled\time.h | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{27D16BB9-2B81-4F61-80EC-0C7A777248E4}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		..\include\spdlog\sinks\android_sink.h = ..\include\spdlog\sinks\android_sink.h | ||||
| 		..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h | ||||
| 		..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h | ||||
| 		..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h | ||||
| 		..\include\spdlog\sinks\file_sinks.h = ..\include\spdlog\sinks\file_sinks.h | ||||
| 		..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h | ||||
| 		..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h | ||||
| 		..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h | ||||
| 		..\include\spdlog\sinks\sink.h = ..\include\spdlog\sinks\sink.h | ||||
| 		..\include\spdlog\sinks\stdout_color_sinks.h = ..\include\spdlog\sinks\stdout_color_sinks.h | ||||
| 		..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h | ||||
| 		..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h | ||||
| 		..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h | ||||
| 		..\include\spdlog\sinks\windebug_sink.h = ..\include\spdlog\sinks\windebug_sink.h | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Win32 = Debug|Win32 | ||||
| 		Debug|x64 = Debug|x64 | ||||
| 		Release|Win32 = Release|Win32 | ||||
| 		Release|x64 = Release|x64 | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32 | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32 | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|x64 | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.Build.0 = Debug|x64 | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32 | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32 | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|x64 | ||||
| 		{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.Build.0 = Release|x64 | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(NestedProjects) = preSolution | ||||
| 		{08E93803-E650-42D9-BBB4-3C16979F850E} = {7FC6AB76-AD88-4135-888C-0568E81475AF} | ||||
| 		{82378DE1-8463-4F91-91A0-C2C40E2AEA2A} = {7FC6AB76-AD88-4135-888C-0568E81475AF} | ||||
| 		{D9CA4494-80D1-48D1-A897-D3564F7B27FF} = {82378DE1-8463-4F91-91A0-C2C40E2AEA2A} | ||||
| 		{27D16BB9-2B81-4F61-80EC-0C7A777248E4} = {7FC6AB76-AD88-4135-888C-0568E81475AF} | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {1BF53532-C5DC-4236-B195-9E17CBE40A48} | ||||
| 	EndGlobalSection | ||||
| EndGlobal | ||||
							
								
								
									
										167
									
								
								third_party/spdlog/example/example.vcxproj
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								third_party/spdlog/example/example.vcxproj
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <ItemGroup Label="ProjectConfigurations"> | ||||
|     <ProjectConfiguration Include="Debug|Win32"> | ||||
|       <Configuration>Debug</Configuration> | ||||
|       <Platform>Win32</Platform> | ||||
|     </ProjectConfiguration> | ||||
|     <ProjectConfiguration Include="Debug|x64"> | ||||
|       <Configuration>Debug</Configuration> | ||||
|       <Platform>x64</Platform> | ||||
|     </ProjectConfiguration> | ||||
|     <ProjectConfiguration Include="Release|Win32"> | ||||
|       <Configuration>Release</Configuration> | ||||
|       <Platform>Win32</Platform> | ||||
|     </ProjectConfiguration> | ||||
|     <ProjectConfiguration Include="Release|x64"> | ||||
|       <Configuration>Release</Configuration> | ||||
|       <Platform>x64</Platform> | ||||
|     </ProjectConfiguration> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="example.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <PropertyGroup Label="Globals"> | ||||
|     <ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid> | ||||
|     <Keyword>Win32Proj</Keyword> | ||||
|     <RootNamespace>.</RootNamespace> | ||||
|   </PropertyGroup> | ||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> | ||||
|     <ConfigurationType>Application</ConfigurationType> | ||||
|     <UseDebugLibraries>true</UseDebugLibraries> | ||||
|     <PlatformToolset>v141</PlatformToolset> | ||||
|     <CharacterSet>Unicode</CharacterSet> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> | ||||
|     <ConfigurationType>Application</ConfigurationType> | ||||
|     <UseDebugLibraries>true</UseDebugLibraries> | ||||
|     <PlatformToolset>v141</PlatformToolset> | ||||
|     <CharacterSet>Unicode</CharacterSet> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> | ||||
|     <ConfigurationType>Application</ConfigurationType> | ||||
|     <UseDebugLibraries>false</UseDebugLibraries> | ||||
|     <PlatformToolset>v141</PlatformToolset> | ||||
|     <WholeProgramOptimization>true</WholeProgramOptimization> | ||||
|     <CharacterSet>Unicode</CharacterSet> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> | ||||
|     <ConfigurationType>Application</ConfigurationType> | ||||
|     <UseDebugLibraries>false</UseDebugLibraries> | ||||
|     <PlatformToolset>v141</PlatformToolset> | ||||
|     <WholeProgramOptimization>true</WholeProgramOptimization> | ||||
|     <CharacterSet>Unicode</CharacterSet> | ||||
|   </PropertyGroup> | ||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | ||||
|   <ImportGroup Label="ExtensionSettings"> | ||||
|   </ImportGroup> | ||||
|   <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||
|     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | ||||
|   </ImportGroup> | ||||
|   <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> | ||||
|     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | ||||
|   </ImportGroup> | ||||
|   <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||
|     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | ||||
|   </ImportGroup> | ||||
|   <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> | ||||
|     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | ||||
|   </ImportGroup> | ||||
|   <PropertyGroup Label="UserMacros" /> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||
|     <LinkIncremental>true</LinkIncremental> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||
|     <LinkIncremental>true</LinkIncremental> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||
|     <LinkIncremental>false</LinkIncremental> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||
|     <LinkIncremental>false</LinkIncremental> | ||||
|   </PropertyGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||||
|     <ClCompile> | ||||
|       <PrecompiledHeader> | ||||
|       </PrecompiledHeader> | ||||
|       <WarningLevel>Level3</WarningLevel> | ||||
|       <Optimization>Disabled</Optimization> | ||||
|       <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||||
|       <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <PrecompiledHeaderFile /> | ||||
|       <PrecompiledHeaderOutputFile /> | ||||
|     </ClCompile> | ||||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||
|     <ClCompile> | ||||
|       <PrecompiledHeader> | ||||
|       </PrecompiledHeader> | ||||
|       <WarningLevel>Level3</WarningLevel> | ||||
|       <Optimization>Disabled</Optimization> | ||||
|       <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||||
|       <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <PrecompiledHeaderFile> | ||||
|       </PrecompiledHeaderFile> | ||||
|       <PrecompiledHeaderOutputFile> | ||||
|       </PrecompiledHeaderOutputFile> | ||||
|     </ClCompile> | ||||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||
|     <ClCompile> | ||||
|       <WarningLevel>Level3</WarningLevel> | ||||
|       <PrecompiledHeader> | ||||
|       </PrecompiledHeader> | ||||
|       <Optimization>MaxSpeed</Optimization> | ||||
|       <FunctionLevelLinking>true</FunctionLevelLinking> | ||||
|       <IntrinsicFunctions>true</IntrinsicFunctions> | ||||
|       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||||
|       <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <PrecompiledHeaderFile /> | ||||
|       <PrecompiledHeaderOutputFile /> | ||||
|     </ClCompile> | ||||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
|       <OptimizeReferences>true</OptimizeReferences> | ||||
|       <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|       <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||
|     <ClCompile> | ||||
|       <WarningLevel>Level3</WarningLevel> | ||||
|       <PrecompiledHeader> | ||||
|       </PrecompiledHeader> | ||||
|       <Optimization>MaxSpeed</Optimization> | ||||
|       <FunctionLevelLinking>true</FunctionLevelLinking> | ||||
|       <IntrinsicFunctions>true</IntrinsicFunctions> | ||||
|       <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||||
|       <AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | ||||
|       <PrecompiledHeaderFile> | ||||
|       </PrecompiledHeaderFile> | ||||
|       <PrecompiledHeaderOutputFile> | ||||
|       </PrecompiledHeaderOutputFile> | ||||
|     </ClCompile> | ||||
|     <Link> | ||||
|       <SubSystem>Console</SubSystem> | ||||
|       <GenerateDebugInformation>true</GenerateDebugInformation> | ||||
|       <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
|       <OptimizeReferences>true</OptimizeReferences> | ||||
|       <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | ||||
|       <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> | ||||
|     </Link> | ||||
|   </ItemDefinitionGroup> | ||||
|   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||
|   <ImportGroup Label="ExtensionTargets"> | ||||
|   </ImportGroup> | ||||
| </Project> | ||||
							
								
								
									
										15
									
								
								third_party/spdlog/example/jni/Android.mk
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								third_party/spdlog/example/jni/Android.mk
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # Setup a project | ||||
| LOCAL_PATH := $(call my-dir) | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| LOCAL_MODULE := example | ||||
| LOCAL_SRC_FILES := example.cpp | ||||
| LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie | ||||
| LOCAL_LDFLAGS +=  -fPIE -pie | ||||
|  | ||||
| # Add exception support and set path for spdlog's headers | ||||
| LOCAL_CPPFLAGS += -fexceptions -I../include | ||||
| # Use android's log library | ||||
| LOCAL_LDFLAGS += -llog | ||||
|  | ||||
| include $(BUILD_EXECUTABLE) | ||||
							
								
								
									
										2
									
								
								third_party/spdlog/example/jni/Application.mk
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								third_party/spdlog/example/jni/Application.mk
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| # Exceptions are used in spdlog. Link to an exception-ready C++ runtime. | ||||
| APP_STL = gnustl_static | ||||
							
								
								
									
										157
									
								
								third_party/spdlog/example/jni/example.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								third_party/spdlog/example/jni/example.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
| // | ||||
| // spdlog usage example | ||||
| // | ||||
| // | ||||
|  | ||||
| #define SPDLOG_TRACE_ON | ||||
| #define SPDLOG_DEBUG_ON | ||||
|  | ||||
| #include "spdlog/spdlog.h" | ||||
|  | ||||
| #include <iostream> | ||||
| #include <memory> | ||||
|  | ||||
| void async_example(); | ||||
| void syslog_example(); | ||||
| void android_example(); | ||||
| void user_defined_example(); | ||||
| void err_handler_example(); | ||||
|  | ||||
| namespace spd = spdlog; | ||||
| int main(int, char *[]) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         // Console logger with color | ||||
|         auto console = spd::stdout_color_mt("console"); | ||||
|         console->info("Welcome to spdlog!"); | ||||
|         console->error("Some error message with arg{}..", 1); | ||||
|  | ||||
|         // Formatting examples | ||||
|         console->warn("Easy padding in numbers like {:08d}", 12); | ||||
|         console->critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42); | ||||
|         console->info("Support for floats {:03.2f}", 1.23456); | ||||
|         console->info("Positional args are {1} {0}..", "too", "supported"); | ||||
|         console->info("{:<30}", "left aligned"); | ||||
|  | ||||
|         spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); | ||||
|  | ||||
|         // Create basic file logger (not rotated) | ||||
|         auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt"); | ||||
|         my_logger->info("Some log message"); | ||||
|  | ||||
|         // Create a file rotating logger with 5mb size max and 3 rotated files | ||||
|         auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); | ||||
|         for (int i = 0; i < 10; ++i) | ||||
|             rotating_logger->info("{} * {} equals {:>10}", i, i, i * i); | ||||
|  | ||||
|         // Create a daily logger - a new file is created every day on 2:30am | ||||
|         auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); | ||||
|         // trigger flush if the log severity is error or higher | ||||
|         daily_logger->flush_on(spd::level::err); | ||||
|         daily_logger->info(123.44); | ||||
|  | ||||
|         // Customize msg format for all messages | ||||
|         spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); | ||||
|         rotating_logger->info("This is another message with custom format"); | ||||
|  | ||||
|         // Runtime log levels | ||||
|         spd::set_level(spd::level::info); // Set global log level to info | ||||
|         console->debug("This message should not be displayed!"); | ||||
|         console->set_level(spd::level::debug); // Set specific logger's log level | ||||
|         console->debug("This message should be displayed.."); | ||||
|  | ||||
|         // Compile time log levels | ||||
|         // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON | ||||
|         SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); | ||||
|         SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); | ||||
|  | ||||
|         // Asynchronous logging is very fast.. | ||||
|         // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. | ||||
|         async_example(); | ||||
|  | ||||
|         // syslog example. linux/osx only | ||||
|         syslog_example(); | ||||
|  | ||||
|         // android example. compile with NDK | ||||
|         android_example(); | ||||
|  | ||||
|         // Log user-defined types example | ||||
|         user_defined_example(); | ||||
|  | ||||
|         // Change default log error handler | ||||
|         err_handler_example(); | ||||
|  | ||||
|         // Apply a function on all registered loggers | ||||
|         spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); }); | ||||
|  | ||||
|         // Release and close all loggers | ||||
|         spdlog::drop_all(); | ||||
|     } | ||||
|     // Exceptions will only be thrown upon failed logger or sink construction (not during logging) | ||||
|     catch (const spd::spdlog_ex &ex) | ||||
|     { | ||||
|         std::cout << "Log init failed: " << ex.what() << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void async_example() | ||||
| { | ||||
|     size_t q_size = 4096; // queue size must be power of 2 | ||||
|     spdlog::set_async_mode(q_size); | ||||
|     auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); | ||||
|     for (int i = 0; i < 100; ++i) | ||||
|         async_file->info("Async message #{}", i); | ||||
| } | ||||
|  | ||||
| // syslog example (linux/osx/freebsd) | ||||
| void syslog_example() | ||||
| { | ||||
| #ifdef SPDLOG_ENABLE_SYSLOG | ||||
|     std::string ident = "spdlog-example"; | ||||
|     auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); | ||||
|     syslog_logger->warn("This is warning that will end up in syslog."); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| // Android example | ||||
| void android_example() | ||||
| { | ||||
| #if defined(__ANDROID__) | ||||
|     std::string tag = "spdlog-android"; | ||||
|     auto android_logger = spd::android_logger("android", tag); | ||||
|     android_logger->critical("Use \"adb shell logcat\" to view this message."); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| // user defined types logging by implementing operator<< | ||||
| struct my_type | ||||
| { | ||||
|     int i; | ||||
|     template<typename OStream> | ||||
|     friend OStream &operator<<(OStream &os, const my_type &c) | ||||
|     { | ||||
|         return os << "[my_type i=" << c.i << "]"; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #include "spdlog/fmt/ostr.h" // must be included | ||||
| void user_defined_example() | ||||
| { | ||||
|     spd::get("console")->info("user defined type: {}", my_type{14}); | ||||
| } | ||||
|  | ||||
| // | ||||
| // custom error handler | ||||
| // | ||||
| void err_handler_example() | ||||
| { | ||||
|     // can be set globaly or per logger(logger->set_error_handler(..)) | ||||
|     spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; }); | ||||
|     spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); | ||||
| } | ||||
							
								
								
									
										47
									
								
								third_party/spdlog/example/multisink.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								third_party/spdlog/example/multisink.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| #include "spdlog/spdlog.h" | ||||
| #include "spdlog/sinks/basic_file_sink.h" | ||||
| #include "spdlog/sinks/stdout_sinks.h" | ||||
|  | ||||
| #include <iostream> | ||||
| #include <memory> | ||||
|  | ||||
| int main(int, char *[]) | ||||
| { | ||||
|     bool enable_debug = true; | ||||
|     try | ||||
|     { | ||||
|         // This other example use a single logger with multiple sinks. | ||||
|         // This means that the same log_msg is forwarded to multiple sinks; | ||||
|         // Each sink can have it's own log level and a message will be logged. | ||||
|         std::vector<spdlog::sink_ptr> sinks; | ||||
|         sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>()); | ||||
|         sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_regular_file.txt")); | ||||
|         sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_debug_file.txt")); | ||||
|  | ||||
|         spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end()); | ||||
|         console_multisink.set_level(spdlog::level::warn); | ||||
|  | ||||
|         sinks[0]->set_level(spdlog::level::trace); // console. Allow everything.  Default value | ||||
|         sinks[1]->set_level(spdlog::level::trace); //  regular file. Allow everything.  Default value | ||||
|         sinks[2]->set_level(spdlog::level::off);   //  regular file. Ignore everything. | ||||
|  | ||||
|         console_multisink.warn("warn: will print only on console and regular file"); | ||||
|  | ||||
|         if (enable_debug) | ||||
|         { | ||||
|             console_multisink.set_level(spdlog::level::debug); // level of the logger | ||||
|             sinks[1]->set_level(spdlog::level::debug);         // regular file | ||||
|             sinks[2]->set_level(spdlog::level::debug);         // debug file | ||||
|         } | ||||
|         console_multisink.debug("Debug: you should see this on console and both files"); | ||||
|  | ||||
|         // Release and close all loggers | ||||
|         spdlog::drop_all(); | ||||
|     } | ||||
|     // Exceptions will only be thrown upon failed logger or sink construction (not during logging) | ||||
|     catch (const spdlog::spdlog_ex &ex) | ||||
|     { | ||||
|         std::cout << "Log init failed: " << ex.what() << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								third_party/spdlog/example/utils.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								third_party/spdlog/example/utils.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <iomanip> | ||||
| #include <locale> | ||||
| #include <sstream> | ||||
|  | ||||
| namespace utils { | ||||
|  | ||||
| template<typename T> | ||||
| inline std::string format(const T &value) | ||||
| { | ||||
|     static std::locale loc(""); | ||||
|     std::stringstream ss; | ||||
|     ss.imbue(loc); | ||||
|     ss << value; | ||||
|     return ss.str(); | ||||
| } | ||||
|  | ||||
| template<> | ||||
| inline std::string format(const double &value) | ||||
| { | ||||
|     static std::locale loc(""); | ||||
|     std::stringstream ss; | ||||
|     ss.imbue(loc); | ||||
|     ss << std::fixed << std::setprecision(1) << value; | ||||
|     return ss.str(); | ||||
| } | ||||
|  | ||||
| } // namespace utils | ||||
							
								
								
									
										9
									
								
								third_party/spdlog/format.sh
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										9
									
								
								third_party/spdlog/format.sh
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| #!/bin/bash | ||||
| echo -n "Running dos2unix     " | ||||
| find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'" | ||||
| echo | ||||
| echo -n "Running clang-format " | ||||
| find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "clang-format -i {}; echo -n '.'" | ||||
| echo | ||||
|  | ||||
|  | ||||
							
								
								
									
										87
									
								
								third_party/spdlog/include/spdlog/async.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								third_party/spdlog/include/spdlog/async.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
|  | ||||
| // | ||||
| // Copyright(c) 2018 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| // | ||||
| // Async logging using global thread pool | ||||
| // All loggers created here share same global thread pool. | ||||
| // Each log message is pushed to a queue along withe a shared pointer to the | ||||
| // logger. | ||||
| // If a logger deleted while having pending messages in the queue, it's actual | ||||
| // destruction will defer | ||||
| // until all its messages are processed by the thread pool. | ||||
| // This is because each message in the queue holds a shared_ptr to the | ||||
| // originating logger. | ||||
|  | ||||
| #include "spdlog/async_logger.h" | ||||
| #include "spdlog/details/registry.h" | ||||
| #include "spdlog/details/thread_pool.h" | ||||
|  | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
|  | ||||
| namespace spdlog { | ||||
|  | ||||
| namespace details { | ||||
| static const size_t default_async_q_size = 8192; | ||||
| } | ||||
|  | ||||
| // async logger factory - creates async loggers backed with thread pool. | ||||
| // if a global thread pool doesn't already exist, create it with default queue | ||||
| // size of 8192 items and single thread. | ||||
| template<async_overflow_policy OverflowPolicy = async_overflow_policy::block> | ||||
| struct async_factory_impl | ||||
| { | ||||
|     template<typename Sink, typename... SinkArgs> | ||||
|     static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args) | ||||
|     { | ||||
|         auto ®istry_inst = details::registry::instance(); | ||||
|  | ||||
|         // create global thread pool if not already exists.. | ||||
|         std::lock_guard<std::recursive_mutex> tp_lock(registry_inst.tp_mutex()); | ||||
|         auto tp = registry_inst.get_tp(); | ||||
|         if (tp == nullptr) | ||||
|         { | ||||
|             tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1); | ||||
|             registry_inst.set_tp(tp); | ||||
|         } | ||||
|  | ||||
|         auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...); | ||||
|         auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy); | ||||
|         registry_inst.initialize_logger(new_logger); | ||||
|         return new_logger; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| using async_factory = async_factory_impl<async_overflow_policy::block>; | ||||
| using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>; | ||||
|  | ||||
| template<typename Sink, typename... SinkArgs> | ||||
| inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args) | ||||
| { | ||||
|     return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...); | ||||
| } | ||||
|  | ||||
| template<typename Sink, typename... SinkArgs> | ||||
| inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args) | ||||
| { | ||||
|     return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...); | ||||
| } | ||||
|  | ||||
| // set global thread pool. | ||||
| inline void init_thread_pool(size_t q_size, size_t thread_count) | ||||
| { | ||||
|     auto tp = std::make_shared<details::thread_pool>(q_size, thread_count); | ||||
|     details::registry::instance().set_tp(std::move(tp)); | ||||
| } | ||||
|  | ||||
| // get the global thread pool. | ||||
| inline std::shared_ptr<spdlog::details::thread_pool> thread_pool() | ||||
| { | ||||
|     return details::registry::instance().get_tp(); | ||||
| } | ||||
| } // namespace spdlog | ||||
							
								
								
									
										73
									
								
								third_party/spdlog/include/spdlog/async_logger.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								third_party/spdlog/include/spdlog/async_logger.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| // Very fast asynchronous logger (millions of logs per second on an average | ||||
| // desktop) | ||||
| // Uses pre allocated lockfree queue for maximum throughput even under large | ||||
| // number of threads. | ||||
| // Creates a single back thread to pop messages from the queue and log them. | ||||
| // | ||||
| // Upon each log write the logger: | ||||
| //    1. Checks if its log level is enough to log the message | ||||
| //    2. Push a new copy of the message to a queue (or block the caller until | ||||
| //    space is available in the queue) | ||||
| //    3. will throw spdlog_ex upon log exceptions | ||||
| // Upon destruction, logs all remaining messages in the queue before | ||||
| // destructing.. | ||||
|  | ||||
| #include "spdlog/common.h" | ||||
| #include "spdlog/logger.h" | ||||
|  | ||||
| #include <chrono> | ||||
| #include <memory> | ||||
| #include <string> | ||||
|  | ||||
| namespace spdlog { | ||||
|  | ||||
| // Async overflow policy - block by default. | ||||
| enum class async_overflow_policy | ||||
| { | ||||
|     block,         // Block until message can be enqueued | ||||
|     overrun_oldest // Discard oldest message in the queue if full when trying to | ||||
|                    // add new item. | ||||
| }; | ||||
|  | ||||
| namespace details { | ||||
| class thread_pool; | ||||
| } | ||||
|  | ||||
| class async_logger final : public std::enable_shared_from_this<async_logger>, public logger | ||||
| { | ||||
|     friend class details::thread_pool; | ||||
|  | ||||
| public: | ||||
|     template<typename It> | ||||
|     async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, | ||||
|         async_overflow_policy overflow_policy = async_overflow_policy::block); | ||||
|  | ||||
|     async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, | ||||
|         async_overflow_policy overflow_policy = async_overflow_policy::block); | ||||
|  | ||||
|     async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, | ||||
|         async_overflow_policy overflow_policy = async_overflow_policy::block); | ||||
|  | ||||
|     std::shared_ptr<logger> clone(std::string new_name) override; | ||||
|  | ||||
| protected: | ||||
|     void sink_it_(details::log_msg &msg) override; | ||||
|     void flush_() override; | ||||
|  | ||||
|     void backend_log_(const details::log_msg &incoming_log_msg); | ||||
|     void backend_flush_(); | ||||
|  | ||||
| private: | ||||
|     std::weak_ptr<details::thread_pool> thread_pool_; | ||||
|     async_overflow_policy overflow_policy_; | ||||
| }; | ||||
| } // namespace spdlog | ||||
|  | ||||
| #include "details/async_logger_impl.h" | ||||
							
								
								
									
										243
									
								
								third_party/spdlog/include/spdlog/common.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								third_party/spdlog/include/spdlog/common.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,243 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "spdlog/tweakme.h" | ||||
|  | ||||
| #include <atomic> | ||||
| #include <chrono> | ||||
| #include <functional> | ||||
| #include <initializer_list> | ||||
| #include <memory> | ||||
| #include <stdexcept> | ||||
| #include <string> | ||||
| #include <cstring> | ||||
| #include <type_traits> | ||||
| #include <unordered_map> | ||||
|  | ||||
| #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) | ||||
| #include <codecvt> | ||||
| #include <locale> | ||||
| #endif | ||||
|  | ||||
| #include "spdlog/details/null_mutex.h" | ||||
|  | ||||
| #include "spdlog/fmt/fmt.h" | ||||
|  | ||||
| // visual studio upto 2013 does not support noexcept nor constexpr | ||||
| #if defined(_MSC_VER) && (_MSC_VER < 1900) | ||||
| #define SPDLOG_NOEXCEPT throw() | ||||
| #define SPDLOG_CONSTEXPR | ||||
| #else | ||||
| #define SPDLOG_NOEXCEPT noexcept | ||||
| #define SPDLOG_CONSTEXPR constexpr | ||||
| #endif | ||||
|  | ||||
| #if defined(__GNUC__) || defined(__clang__) | ||||
| #define SPDLOG_DEPRECATED __attribute__((deprecated)) | ||||
| #elif defined(_MSC_VER) | ||||
| #define SPDLOG_DEPRECATED __declspec(deprecated) | ||||
| #else | ||||
| #define SPDLOG_DEPRECATED | ||||
| #endif | ||||
|  | ||||
| // disable thread local on msvc 2013 | ||||
| #ifndef SPDLOG_NO_TLS | ||||
| #if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) | ||||
| #define SPDLOG_NO_TLS 1 | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| // Get the basename of __FILE__ (at compile time if possible) | ||||
| #if FMT_HAS_FEATURE(__builtin_strrchr) | ||||
| #define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep) | ||||
| #else | ||||
| #define SPDLOG_STRRCHR(str, sep) strrchr(str, sep) | ||||
| #endif //__builtin_strrchr not defined | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1 | ||||
| #else | ||||
| #define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1 | ||||
| #endif | ||||
|  | ||||
| #ifndef SPDLOG_FUNCTION | ||||
| #define SPDLOG_FUNCTION __FUNCTION__ | ||||
| #endif | ||||
|  | ||||
| namespace spdlog { | ||||
|  | ||||
| class formatter; | ||||
|  | ||||
| namespace sinks { | ||||
| class sink; | ||||
| } | ||||
|  | ||||
| using log_clock = std::chrono::system_clock; | ||||
| using sink_ptr = std::shared_ptr<sinks::sink>; | ||||
| using sinks_init_list = std::initializer_list<sink_ptr>; | ||||
| using log_err_handler = std::function<void(const std::string &err_msg)>; | ||||
|  | ||||
| // string_view type - either std::string_view or fmt::string_view (pre c++17) | ||||
| #if defined(FMT_USE_STD_STRING_VIEW) | ||||
| using string_view_t = std::string_view; | ||||
| #else | ||||
| using string_view_t = fmt::string_view; | ||||
| #endif | ||||
|  | ||||
| #if defined(SPDLOG_NO_ATOMIC_LEVELS) | ||||
| using level_t = details::null_atomic_int; | ||||
| #else | ||||
| using level_t = std::atomic<int>; | ||||
| #endif | ||||
|  | ||||
| #define SPDLOG_LEVEL_TRACE 0 | ||||
| #define SPDLOG_LEVEL_DEBUG 1 | ||||
| #define SPDLOG_LEVEL_INFO 2 | ||||
| #define SPDLOG_LEVEL_WARN 3 | ||||
| #define SPDLOG_LEVEL_ERROR 4 | ||||
| #define SPDLOG_LEVEL_CRITICAL 5 | ||||
| #define SPDLOG_LEVEL_OFF 6 | ||||
|  | ||||
| #if !defined(SPDLOG_ACTIVE_LEVEL) | ||||
| #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO | ||||
| #endif | ||||
|  | ||||
| // Log level enum | ||||
| namespace level { | ||||
| enum level_enum | ||||
| { | ||||
|     trace = SPDLOG_LEVEL_TRACE, | ||||
|     debug = SPDLOG_LEVEL_DEBUG, | ||||
|     info = SPDLOG_LEVEL_INFO, | ||||
|     warn = SPDLOG_LEVEL_WARN, | ||||
|     err = SPDLOG_LEVEL_ERROR, | ||||
|     critical = SPDLOG_LEVEL_CRITICAL, | ||||
|     off = SPDLOG_LEVEL_OFF, | ||||
| }; | ||||
|  | ||||
| #if !defined(SPDLOG_LEVEL_NAMES) | ||||
| #define SPDLOG_LEVEL_NAMES                                                                                                                 \ | ||||
|     {                                                                                                                                      \ | ||||
|         "trace", "debug", "info", "warning", "error", "critical", "off"                                                                    \ | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; | ||||
| static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"}; | ||||
|  | ||||
| inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT | ||||
| { | ||||
|     return level_string_views[l]; | ||||
| } | ||||
|  | ||||
| inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT | ||||
| { | ||||
|     return short_level_names[l]; | ||||
| } | ||||
|  | ||||
| inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT | ||||
| { | ||||
|     int level = 0; | ||||
|     for (const auto &level_str : level_string_views) | ||||
|     { | ||||
|         if (level_str == name) | ||||
|         { | ||||
|             return static_cast<level::level_enum>(level); | ||||
|         } | ||||
|         level++; | ||||
|     } | ||||
|     return level::off; | ||||
| } | ||||
|  | ||||
| using level_hasher = std::hash<int>; | ||||
| } // namespace level | ||||
|  | ||||
| // | ||||
| // Pattern time - specific time getting to use for pattern_formatter. | ||||
| // local time by default | ||||
| // | ||||
| enum class pattern_time_type | ||||
| { | ||||
|     local, // log localtime | ||||
|     utc    // log utc | ||||
| }; | ||||
|  | ||||
| // | ||||
| // Log exception | ||||
| // | ||||
| class spdlog_ex : public std::exception | ||||
| { | ||||
| public: | ||||
|     explicit spdlog_ex(std::string msg) | ||||
|         : msg_(std::move(msg)) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     spdlog_ex(const std::string &msg, int last_errno) | ||||
|     { | ||||
|         fmt::memory_buffer outbuf; | ||||
|         fmt::format_system_error(outbuf, last_errno, msg); | ||||
|         msg_ = fmt::to_string(outbuf); | ||||
|     } | ||||
|  | ||||
|     const char *what() const SPDLOG_NOEXCEPT override | ||||
|     { | ||||
|         return msg_.c_str(); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::string msg_; | ||||
| }; | ||||
|  | ||||
| // | ||||
| // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) | ||||
| // | ||||
| #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) | ||||
| using filename_t = std::wstring; | ||||
| #else | ||||
| using filename_t = std::string; | ||||
| #endif | ||||
|  | ||||
| struct source_loc | ||||
| { | ||||
|     SPDLOG_CONSTEXPR source_loc() | ||||
|         : filename{""} | ||||
|         , line{0} | ||||
|         , funcname{""} | ||||
|     { | ||||
|     } | ||||
|     SPDLOG_CONSTEXPR source_loc(const char *filename, int line, const char *funcname) | ||||
|         : filename{filename} | ||||
|         , line{static_cast<uint32_t>(line)} | ||||
|         , funcname{funcname} | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT | ||||
|     { | ||||
|         return line == 0; | ||||
|     } | ||||
|     const char *filename; | ||||
|     uint32_t line; | ||||
|     const char *funcname; | ||||
| }; | ||||
|  | ||||
| namespace details { | ||||
| // make_unique support for pre c++14 | ||||
|  | ||||
| #if __cplusplus >= 201402L // C++14 and beyond | ||||
| using std::make_unique; | ||||
| #else | ||||
| template<typename T, typename... Args> | ||||
| std::unique_ptr<T> make_unique(Args &&... args) | ||||
| { | ||||
|     static_assert(!std::is_array<T>::value, "arrays not supported"); | ||||
|     return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | ||||
| } | ||||
| #endif | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
							
								
								
									
										110
									
								
								third_party/spdlog/include/spdlog/details/async_logger_impl.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								third_party/spdlog/include/spdlog/details/async_logger_impl.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| // async logger implementation | ||||
| // uses a thread pool to perform the actual logging | ||||
|  | ||||
| #include "spdlog/details/thread_pool.h" | ||||
|  | ||||
| #include <chrono> | ||||
| #include <memory> | ||||
| #include <string> | ||||
|  | ||||
| template<typename It> | ||||
| inline spdlog::async_logger::async_logger( | ||||
|     std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy) | ||||
|     : logger(std::move(logger_name), begin, end) | ||||
|     , thread_pool_(std::move(tp)) | ||||
|     , overflow_policy_(overflow_policy) | ||||
| { | ||||
| } | ||||
|  | ||||
| inline spdlog::async_logger::async_logger( | ||||
|     std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy) | ||||
|     : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy) | ||||
| { | ||||
| } | ||||
|  | ||||
| inline spdlog::async_logger::async_logger( | ||||
|     std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy) | ||||
|     : async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) | ||||
| { | ||||
| } | ||||
|  | ||||
| // send the log message to the thread pool | ||||
| inline void spdlog::async_logger::sink_it_(details::log_msg &msg) | ||||
| { | ||||
| #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) | ||||
|     incr_msg_counter_(msg); | ||||
| #endif | ||||
|     if (auto pool_ptr = thread_pool_.lock()) | ||||
|     { | ||||
|         pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         throw spdlog_ex("async log: thread pool doesn't exist anymore"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // send flush request to the thread pool | ||||
| inline void spdlog::async_logger::flush_() | ||||
| { | ||||
|     if (auto pool_ptr = thread_pool_.lock()) | ||||
|     { | ||||
|         pool_ptr->post_flush(shared_from_this(), overflow_policy_); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         throw spdlog_ex("async flush: thread pool doesn't exist anymore"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // | ||||
| // backend functions - called from the thread pool to do the actual job | ||||
| // | ||||
| inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         for (auto &s : sinks_) | ||||
|         { | ||||
|             if (s->should_log(incoming_log_msg.level)) | ||||
|             { | ||||
|                 s->log(incoming_log_msg); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
|  | ||||
|     if (should_flush_(incoming_log_msg)) | ||||
|     { | ||||
|         backend_flush_(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline void spdlog::async_logger::backend_flush_() | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         for (auto &sink : sinks_) | ||||
|         { | ||||
|             sink->flush(); | ||||
|         } | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
| } | ||||
|  | ||||
| inline std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name) | ||||
| { | ||||
|     auto cloned = std::make_shared<spdlog::async_logger>(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_); | ||||
|  | ||||
|     cloned->set_level(this->level()); | ||||
|     cloned->flush_on(this->flush_level()); | ||||
|     cloned->set_error_handler(this->error_handler()); | ||||
|     return std::move(cloned); | ||||
| } | ||||
							
								
								
									
										72
									
								
								third_party/spdlog/include/spdlog/details/circular_q.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								third_party/spdlog/include/spdlog/details/circular_q.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| // | ||||
| // Copyright(c) 2018 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| // cirucal q view of std::vector. | ||||
| #pragma once | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| namespace spdlog { | ||||
| namespace details { | ||||
| template<typename T> | ||||
| class circular_q | ||||
| { | ||||
| public: | ||||
|     using item_type = T; | ||||
|  | ||||
|     explicit circular_q(size_t max_items) | ||||
|         : max_items_(max_items + 1) // one item is reserved as marker for full q | ||||
|         , v_(max_items_) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     // push back, overrun (oldest) item if no room left | ||||
|     void push_back(T &&item) | ||||
|     { | ||||
|         v_[tail_] = std::move(item); | ||||
|         tail_ = (tail_ + 1) % max_items_; | ||||
|  | ||||
|         if (tail_ == head_) // overrun last item if full | ||||
|         { | ||||
|             head_ = (head_ + 1) % max_items_; | ||||
|             ++overrun_counter_; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Pop item from front. | ||||
|     // If there are no elements in the container, the behavior is undefined. | ||||
|     void pop_front(T &popped_item) | ||||
|     { | ||||
|         popped_item = std::move(v_[head_]); | ||||
|         head_ = (head_ + 1) % max_items_; | ||||
|     } | ||||
|  | ||||
|     bool empty() | ||||
|     { | ||||
|         return tail_ == head_; | ||||
|     } | ||||
|  | ||||
|     bool full() | ||||
|     { | ||||
|         // head is ahead of the tail by 1 | ||||
|         return ((tail_ + 1) % max_items_) == head_; | ||||
|     } | ||||
|  | ||||
|     size_t overrun_counter() const | ||||
|     { | ||||
|         return overrun_counter_; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     size_t max_items_; | ||||
|     typename std::vector<T>::size_type head_ = 0; | ||||
|     typename std::vector<T>::size_type tail_ = 0; | ||||
|  | ||||
|     std::vector<T> v_; | ||||
|  | ||||
|     size_t overrun_counter_ = 0; | ||||
| }; | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
							
								
								
									
										74
									
								
								third_party/spdlog/include/spdlog/details/console_globals.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								third_party/spdlog/include/spdlog/details/console_globals.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| #pragma once | ||||
| // | ||||
| // Copyright(c) 2018 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #include "spdlog/details/null_mutex.h" | ||||
| #include <cstdio> | ||||
| #include <mutex> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|  | ||||
| #ifndef NOMINMAX | ||||
| #define NOMINMAX // prevent windows redefining min/max | ||||
| #endif | ||||
|  | ||||
| #ifndef WIN32_LEAN_AND_MEAN | ||||
| #define WIN32_LEAN_AND_MEAN | ||||
| #endif | ||||
|  | ||||
| #include <windows.h> | ||||
| #endif | ||||
|  | ||||
| namespace spdlog { | ||||
| namespace details { | ||||
| struct console_stdout | ||||
| { | ||||
|     static std::FILE *stream() | ||||
|     { | ||||
|         return stdout; | ||||
|     } | ||||
| #ifdef _WIN32 | ||||
|     static HANDLE handle() | ||||
|     { | ||||
|         return ::GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|     } | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| struct console_stderr | ||||
| { | ||||
|     static std::FILE *stream() | ||||
|     { | ||||
|         return stderr; | ||||
|     } | ||||
| #ifdef _WIN32 | ||||
|     static HANDLE handle() | ||||
|     { | ||||
|         return ::GetStdHandle(STD_ERROR_HANDLE); | ||||
|     } | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| struct console_mutex | ||||
| { | ||||
|     using mutex_t = std::mutex; | ||||
|     static mutex_t &mutex() | ||||
|     { | ||||
|         static mutex_t s_mutex; | ||||
|         return s_mutex; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| struct console_nullmutex | ||||
| { | ||||
|     using mutex_t = null_mutex; | ||||
|     static mutex_t &mutex() | ||||
|     { | ||||
|         static mutex_t s_mutex; | ||||
|         return s_mutex; | ||||
|     } | ||||
| }; | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
							
								
								
									
										152
									
								
								third_party/spdlog/include/spdlog/details/file_helper.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								third_party/spdlog/include/spdlog/details/file_helper.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| // Helper class for file sinks. | ||||
| // When failing to open a file, retry several times(5) with a delay interval(10 ms). | ||||
| // Throw spdlog_ex exception on errors. | ||||
|  | ||||
| #include "spdlog/details/log_msg.h" | ||||
| #include "spdlog/details/os.h" | ||||
|  | ||||
| #include <cerrno> | ||||
| #include <chrono> | ||||
| #include <cstdio> | ||||
| #include <string> | ||||
| #include <thread> | ||||
| #include <tuple> | ||||
|  | ||||
| namespace spdlog { | ||||
| namespace details { | ||||
|  | ||||
| class file_helper | ||||
| { | ||||
|  | ||||
| public: | ||||
|     const int open_tries = 5; | ||||
|     const int open_interval = 10; | ||||
|  | ||||
|     explicit file_helper() = default; | ||||
|  | ||||
|     file_helper(const file_helper &) = delete; | ||||
|     file_helper &operator=(const file_helper &) = delete; | ||||
|  | ||||
|     ~file_helper() | ||||
|     { | ||||
|         close(); | ||||
|     } | ||||
|  | ||||
|     void open(const filename_t &fname, bool truncate = false) | ||||
|     { | ||||
|         close(); | ||||
|         auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); | ||||
|         _filename = fname; | ||||
|         for (int tries = 0; tries < open_tries; ++tries) | ||||
|         { | ||||
|             if (!os::fopen_s(&fd_, fname, mode)) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             details::os::sleep_for_millis(open_interval); | ||||
|         } | ||||
|  | ||||
|         throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); | ||||
|     } | ||||
|  | ||||
|     void reopen(bool truncate) | ||||
|     { | ||||
|         if (_filename.empty()) | ||||
|         { | ||||
|             throw spdlog_ex("Failed re opening file - was not opened before"); | ||||
|         } | ||||
|         open(_filename, truncate); | ||||
|     } | ||||
|  | ||||
|     void flush() | ||||
|     { | ||||
|         std::fflush(fd_); | ||||
|     } | ||||
|  | ||||
|     void close() | ||||
|     { | ||||
|         if (fd_ != nullptr) | ||||
|         { | ||||
|             std::fclose(fd_); | ||||
|             fd_ = nullptr; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void write(const fmt::memory_buffer &buf) | ||||
|     { | ||||
|         size_t msg_size = buf.size(); | ||||
|         auto data = buf.data(); | ||||
|         if (std::fwrite(data, 1, msg_size, fd_) != msg_size) | ||||
|         { | ||||
|             throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     size_t size() const | ||||
|     { | ||||
|         if (fd_ == nullptr) | ||||
|         { | ||||
|             throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); | ||||
|         } | ||||
|         return os::filesize(fd_); | ||||
|     } | ||||
|  | ||||
|     const filename_t &filename() const | ||||
|     { | ||||
|         return _filename; | ||||
|     } | ||||
|  | ||||
|     static bool file_exists(const filename_t &fname) | ||||
|     { | ||||
|         return os::file_exists(fname); | ||||
|     } | ||||
|  | ||||
|     // | ||||
|     // return file path and its extension: | ||||
|     // | ||||
|     // "mylog.txt" => ("mylog", ".txt") | ||||
|     // "mylog" => ("mylog", "") | ||||
|     // "mylog." => ("mylog.", "") | ||||
|     // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt") | ||||
|     // | ||||
|     // the starting dot in filenames is ignored (hidden files): | ||||
|     // | ||||
|     // ".mylog" => (".mylog". "") | ||||
|     // "my_folder/.mylog" => ("my_folder/.mylog", "") | ||||
|     // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") | ||||
|     static std::tuple<filename_t, filename_t> split_by_extension(const spdlog::filename_t &fname) | ||||
|     { | ||||
|         auto ext_index = fname.rfind('.'); | ||||
|  | ||||
|         // no valid extension found - return whole path and empty string as | ||||
|         // extension | ||||
|         if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) | ||||
|         { | ||||
|             return std::make_tuple(fname, spdlog::filename_t()); | ||||
|         } | ||||
|  | ||||
|         // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" | ||||
|         auto folder_index = fname.rfind(details::os::folder_sep); | ||||
|         if (folder_index != filename_t::npos && folder_index >= ext_index - 1) | ||||
|         { | ||||
|             return std::make_tuple(fname, spdlog::filename_t()); | ||||
|         } | ||||
|  | ||||
|         // finally - return a valid base and extension tuple | ||||
|         return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::FILE *fd_{nullptr}; | ||||
|     filename_t _filename; | ||||
| }; | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
							
								
								
									
										122
									
								
								third_party/spdlog/include/spdlog/details/fmt_helper.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								third_party/spdlog/include/spdlog/details/fmt_helper.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| // | ||||
| // Created by gabi on 6/15/18. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <chrono> | ||||
| #include <type_traits> | ||||
| #include "spdlog/fmt/fmt.h" | ||||
|  | ||||
| // Some fmt helpers to efficiently format and pad ints and strings | ||||
| namespace spdlog { | ||||
| namespace details { | ||||
| namespace fmt_helper { | ||||
|  | ||||
| template<size_t Buffer_Size> | ||||
| inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT | ||||
| { | ||||
|     return spdlog::string_view_t(buf.data(), buf.size()); | ||||
| } | ||||
|  | ||||
| template<size_t Buffer_Size1, size_t Buffer_Size2> | ||||
| inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest) | ||||
| { | ||||
|     auto *buf_ptr = buf.data(); | ||||
|     dest.append(buf_ptr, buf_ptr + buf.size()); | ||||
| } | ||||
|  | ||||
| template<size_t Buffer_Size> | ||||
| inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest) | ||||
| { | ||||
|     auto *buf_ptr = view.data(); | ||||
|     if (buf_ptr != nullptr) | ||||
|     { | ||||
|         dest.append(buf_ptr, buf_ptr + view.size()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| template<typename T, size_t Buffer_Size> | ||||
| inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) | ||||
| { | ||||
|     fmt::format_int i(n); | ||||
|     dest.append(i.data(), i.data() + i.size()); | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| inline unsigned count_digits(T n) | ||||
| { | ||||
|     using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type; | ||||
|     return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n))); | ||||
| } | ||||
|  | ||||
| template<size_t Buffer_Size> | ||||
| inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) | ||||
| { | ||||
|     if (n > 99) | ||||
|     { | ||||
|         append_int(n, dest); | ||||
|     } | ||||
|     else if (n > 9) // 10-99 | ||||
|     { | ||||
|         dest.push_back(static_cast<char>('0' + n / 10)); | ||||
|         dest.push_back(static_cast<char>('0' + n % 10)); | ||||
|     } | ||||
|     else if (n >= 0) // 0-9 | ||||
|     { | ||||
|         dest.push_back('0'); | ||||
|         dest.push_back(static_cast<char>('0' + n)); | ||||
|     } | ||||
|     else // negatives (unlikely, but just in case, let fmt deal with it) | ||||
|     { | ||||
|         fmt::format_to(dest, "{:02}", n); | ||||
|     } | ||||
| } | ||||
|  | ||||
| template<typename T, size_t Buffer_Size> | ||||
| inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buffer_Size> &dest) | ||||
| { | ||||
|     static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T"); | ||||
|     auto digits = count_digits(n); | ||||
|     if (width > digits) | ||||
|     { | ||||
|         const char *zeroes = "0000000000000000000"; | ||||
|         dest.append(zeroes, zeroes + width - digits); | ||||
|     } | ||||
|     append_int(n, dest); | ||||
| } | ||||
|  | ||||
| template<typename T, size_t Buffer_Size> | ||||
| inline void pad3(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) | ||||
| { | ||||
|     pad_uint(n, 3, dest); | ||||
| } | ||||
|  | ||||
| template<typename T, size_t Buffer_Size> | ||||
| inline void pad6(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) | ||||
| { | ||||
|     pad_uint(n, 6, dest); | ||||
| } | ||||
|  | ||||
| template<typename T, size_t Buffer_Size> | ||||
| inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) | ||||
| { | ||||
|     pad_uint(n, 9, dest); | ||||
| } | ||||
|  | ||||
| // return fraction of a second of the given time_point. | ||||
| // e.g. | ||||
| // fraction<std::milliseconds>(tp) -> will return the millis part of the second | ||||
| template<typename ToDuration> | ||||
| inline ToDuration time_fraction(const log_clock::time_point &tp) | ||||
| { | ||||
|     using std::chrono::duration_cast; | ||||
|     using std::chrono::seconds; | ||||
|     auto duration = tp.time_since_epoch(); | ||||
|     auto secs = duration_cast<seconds>(duration); | ||||
|     return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs); | ||||
| } | ||||
|  | ||||
| } // namespace fmt_helper | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
							
								
								
									
										55
									
								
								third_party/spdlog/include/spdlog/details/log_msg.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								third_party/spdlog/include/spdlog/details/log_msg.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "spdlog/common.h" | ||||
| #include "spdlog/details/os.h" | ||||
|  | ||||
| #include <string> | ||||
| #include <utility> | ||||
|  | ||||
| namespace spdlog { | ||||
| namespace details { | ||||
| struct log_msg | ||||
| { | ||||
|  | ||||
|     log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view) | ||||
|         : logger_name(loggers_name) | ||||
|         , level(lvl) | ||||
| #ifndef SPDLOG_NO_DATETIME | ||||
|         , time(os::now()) | ||||
| #endif | ||||
|  | ||||
| #ifndef SPDLOG_NO_THREAD_ID | ||||
|         , thread_id(os::thread_id()) | ||||
| #endif | ||||
|         , source(loc) | ||||
|         , payload(view) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view) | ||||
|         : log_msg(source_loc{}, loggers_name, lvl, view) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     log_msg(const log_msg &other) = default; | ||||
|  | ||||
|     const std::string *logger_name{nullptr}; | ||||
|     level::level_enum level{level::off}; | ||||
|     log_clock::time_point time; | ||||
|     size_t thread_id{0}; | ||||
|     size_t msg_id{0}; | ||||
|  | ||||
|     // wrapping the formatted text with color (updated by pattern_formatter). | ||||
|     mutable size_t color_range_start{0}; | ||||
|     mutable size_t color_range_end{0}; | ||||
|  | ||||
|     source_loc source; | ||||
|     const string_view_t payload; | ||||
| }; | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
							
								
								
									
										441
									
								
								third_party/spdlog/include/spdlog/details/logger_impl.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								third_party/spdlog/include/spdlog/details/logger_impl.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,441 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "spdlog/details/fmt_helper.h" | ||||
|  | ||||
| #include <memory> | ||||
| #include <string> | ||||
|  | ||||
| #define SPDLOG_CATCH_AND_HANDLE                                                                                                            \ | ||||
|     catch (const std::exception &ex)                                                                                                       \ | ||||
|     {                                                                                                                                      \ | ||||
|         err_handler_(ex.what());                                                                                                           \ | ||||
|     }                                                                                                                                      \ | ||||
|     catch (...)                                                                                                                            \ | ||||
|     {                                                                                                                                      \ | ||||
|         err_handler_("Unknown exception in logger");                                                                                       \ | ||||
|     } | ||||
|  | ||||
| // create logger with given name, sinks and the default pattern formatter | ||||
| // all other ctors will call this one | ||||
| template<typename It> | ||||
| inline spdlog::logger::logger(std::string logger_name, It begin, It end) | ||||
|     : name_(std::move(logger_name)) | ||||
|     , sinks_(begin, end) | ||||
| { | ||||
| } | ||||
|  | ||||
| // ctor with sinks as init list | ||||
| inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list) | ||||
|     : logger(std::move(logger_name), sinks_list.begin(), sinks_list.end()) | ||||
| { | ||||
| } | ||||
|  | ||||
| // ctor with single sink | ||||
| inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink) | ||||
|     : logger(std::move(logger_name), {std::move(single_sink)}) | ||||
| { | ||||
| } | ||||
|  | ||||
| inline spdlog::logger::~logger() = default; | ||||
|  | ||||
| inline void spdlog::logger::set_formatter(std::unique_ptr<spdlog::formatter> f) | ||||
| { | ||||
|     for (auto &sink : sinks_) | ||||
|     { | ||||
|         sink->set_formatter(f->clone()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type) | ||||
| { | ||||
|     auto new_formatter = details::make_unique<spdlog::pattern_formatter>(std::move(pattern), time_type); | ||||
|     set_formatter(std::move(new_formatter)); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args) | ||||
| { | ||||
|     if (!should_log(lvl)) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         using details::fmt_helper::to_string_view; | ||||
|         fmt::memory_buffer buf; | ||||
|         fmt::format_to(buf, fmt, args...); | ||||
|         details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); | ||||
|         sink_it_(log_msg); | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args) | ||||
| { | ||||
|     log(source_loc{}, lvl, fmt, args...); | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg) | ||||
| { | ||||
|     if (!should_log(lvl)) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg)); | ||||
|         sink_it_(log_msg); | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::log(level::level_enum lvl, const char *msg) | ||||
| { | ||||
|     log(source_loc{}, lvl, msg); | ||||
| } | ||||
|  | ||||
| template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *> | ||||
| inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg) | ||||
| { | ||||
|     if (!should_log(lvl)) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     try | ||||
|     { | ||||
|         details::log_msg log_msg(source, &name_, lvl, msg); | ||||
|         sink_it_(log_msg); | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
| } | ||||
|  | ||||
| template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *> | ||||
| inline void spdlog::logger::log(level::level_enum lvl, const T &msg) | ||||
| { | ||||
|     log(source_loc{}, lvl, msg); | ||||
| } | ||||
|  | ||||
| template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *> | ||||
| inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg) | ||||
| { | ||||
|     if (!should_log(lvl)) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     try | ||||
|     { | ||||
|         using details::fmt_helper::to_string_view; | ||||
|         fmt::memory_buffer buf; | ||||
|         fmt::format_to(buf, "{}", msg); | ||||
|         details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); | ||||
|         sink_it_(log_msg); | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
| } | ||||
|  | ||||
| template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *> | ||||
| inline void spdlog::logger::log(level::level_enum lvl, const T &msg) | ||||
| { | ||||
|     log(source_loc{}, lvl, msg); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::trace(const char *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::trace, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::debug(const char *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::debug, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::info(const char *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::info, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::warn(const char *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::warn, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::error(const char *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::err, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::critical(const char *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::critical, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| inline void spdlog::logger::trace(const T &msg) | ||||
| { | ||||
|     log(level::trace, msg); | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| inline void spdlog::logger::debug(const T &msg) | ||||
| { | ||||
|     log(level::debug, msg); | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| inline void spdlog::logger::info(const T &msg) | ||||
| { | ||||
|     log(level::info, msg); | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| inline void spdlog::logger::warn(const T &msg) | ||||
| { | ||||
|     log(level::warn, msg); | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| inline void spdlog::logger::error(const T &msg) | ||||
| { | ||||
|     log(level::err, msg); | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| inline void spdlog::logger::critical(const T &msg) | ||||
| { | ||||
|     log(level::critical, msg); | ||||
| } | ||||
|  | ||||
| #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT | ||||
|  | ||||
| inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target) | ||||
| { | ||||
|     int wbuf_size = static_cast<int>(wbuf.size()); | ||||
|     if (wbuf_size == 0) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL); | ||||
|  | ||||
|     if (result_size > 0) | ||||
|     { | ||||
|         target.resize(result_size); | ||||
|         ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); | ||||
|     } | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     if (!should_log(lvl)) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     try | ||||
|     { | ||||
|         // format to wmemory_buffer and convert to utf8 | ||||
|         using details::fmt_helper::to_string_view; | ||||
|         fmt::wmemory_buffer wbuf; | ||||
|         fmt::format_to(wbuf, fmt, args...); | ||||
|         fmt::memory_buffer buf; | ||||
|         wbuf_to_utf8buf(wbuf, buf); | ||||
|         details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); | ||||
|         sink_it_(log_msg); | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     log(source_loc{}, lvl, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::trace, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::debug, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::info, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::warn, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::err, fmt, args...); | ||||
| } | ||||
|  | ||||
| template<typename... Args> | ||||
| inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args) | ||||
| { | ||||
|     log(level::critical, fmt, args...); | ||||
| } | ||||
|  | ||||
| #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT | ||||
|  | ||||
| // | ||||
| // name and level | ||||
| // | ||||
| inline const std::string &spdlog::logger::name() const | ||||
| { | ||||
|     return name_; | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) | ||||
| { | ||||
|     level_.store(log_level); | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) | ||||
| { | ||||
|     err_handler_ = std::move(err_handler); | ||||
| } | ||||
|  | ||||
| inline spdlog::log_err_handler spdlog::logger::error_handler() const | ||||
| { | ||||
|     return err_handler_; | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::flush() | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         flush_(); | ||||
|     } | ||||
|     SPDLOG_CATCH_AND_HANDLE | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::flush_on(level::level_enum log_level) | ||||
| { | ||||
|     flush_level_.store(log_level); | ||||
| } | ||||
|  | ||||
| inline spdlog::level::level_enum spdlog::logger::flush_level() const | ||||
| { | ||||
|     return static_cast<spdlog::level::level_enum>(flush_level_.load(std::memory_order_relaxed)); | ||||
| } | ||||
|  | ||||
| inline bool spdlog::logger::should_flush_(const details::log_msg &msg) | ||||
| { | ||||
|     auto flush_level = flush_level_.load(std::memory_order_relaxed); | ||||
|     return (msg.level >= flush_level) && (msg.level != level::off); | ||||
| } | ||||
|  | ||||
| inline spdlog::level::level_enum spdlog::logger::default_level() | ||||
| { | ||||
|     return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL); | ||||
| } | ||||
|  | ||||
| inline spdlog::level::level_enum spdlog::logger::level() const | ||||
| { | ||||
|     return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed)); | ||||
| } | ||||
|  | ||||
| inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const | ||||
| { | ||||
|     return msg_level >= level_.load(std::memory_order_relaxed); | ||||
| } | ||||
|  | ||||
| // | ||||
| // protected virtual called at end of each user log call (if enabled) by the | ||||
| // line_logger | ||||
| // | ||||
| inline void spdlog::logger::sink_it_(details::log_msg &msg) | ||||
| { | ||||
| #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) | ||||
|     incr_msg_counter_(msg); | ||||
| #endif | ||||
|     for (auto &sink : sinks_) | ||||
|     { | ||||
|         if (sink->should_log(msg.level)) | ||||
|         { | ||||
|             sink->log(msg); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (should_flush_(msg)) | ||||
|     { | ||||
|         flush_(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::flush_() | ||||
| { | ||||
|     for (auto &sink : sinks_) | ||||
|     { | ||||
|         sink->flush(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::default_err_handler_(const std::string &msg) | ||||
| { | ||||
|     auto now = time(nullptr); | ||||
|     if (now - last_err_time_ < 60) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|     last_err_time_ = now; | ||||
|     auto tm_time = details::os::localtime(now); | ||||
|     char date_buf[100]; | ||||
|     std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); | ||||
|     fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); | ||||
| } | ||||
|  | ||||
| inline void spdlog::logger::incr_msg_counter_(details::log_msg &msg) | ||||
| { | ||||
|     msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed); | ||||
| } | ||||
|  | ||||
| inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const | ||||
| { | ||||
|     return sinks_; | ||||
| } | ||||
|  | ||||
| inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() | ||||
| { | ||||
|     return sinks_; | ||||
| } | ||||
|  | ||||
| inline std::shared_ptr<spdlog::logger> spdlog::logger::clone(std::string logger_name) | ||||
| { | ||||
|     auto cloned = std::make_shared<spdlog::logger>(std::move(logger_name), sinks_.begin(), sinks_.end()); | ||||
|     cloned->set_level(this->level()); | ||||
|     cloned->flush_on(this->flush_level()); | ||||
|     cloned->set_error_handler(this->error_handler()); | ||||
|     return cloned; | ||||
| } | ||||
							
								
								
									
										121
									
								
								third_party/spdlog/include/spdlog/details/mpmc_blocking_q.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								third_party/spdlog/include/spdlog/details/mpmc_blocking_q.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| #pragma once | ||||
|  | ||||
| // | ||||
| // Copyright(c) 2018 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| // multi producer-multi consumer blocking queue. | ||||
| // enqueue(..) - will block until room found to put the new message. | ||||
| // enqueue_nowait(..) - will return immediately with false if no room left in | ||||
| // the queue. | ||||
| // dequeue_for(..) - will block until the queue is not empty or timeout have | ||||
| // passed. | ||||
|  | ||||
| #include "spdlog/details/circular_q.h" | ||||
|  | ||||
| #include <condition_variable> | ||||
| #include <mutex> | ||||
|  | ||||
| namespace spdlog { | ||||
| namespace details { | ||||
|  | ||||
| template<typename T> | ||||
| class mpmc_blocking_queue | ||||
| { | ||||
| public: | ||||
|     using item_type = T; | ||||
|     explicit mpmc_blocking_queue(size_t max_items) | ||||
|         : q_(max_items) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| #ifndef __MINGW32__ | ||||
|     // try to enqueue and block if no room left | ||||
|     void enqueue(T &&item) | ||||
|     { | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lock(queue_mutex_); | ||||
|             pop_cv_.wait(lock, [this] { return !this->q_.full(); }); | ||||
|             q_.push_back(std::move(item)); | ||||
|         } | ||||
|         push_cv_.notify_one(); | ||||
|     } | ||||
|  | ||||
|     // enqueue immediately. overrun oldest message in the queue if no room left. | ||||
|     void enqueue_nowait(T &&item) | ||||
|     { | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lock(queue_mutex_); | ||||
|             q_.push_back(std::move(item)); | ||||
|         } | ||||
|         push_cv_.notify_one(); | ||||
|     } | ||||
|  | ||||
|     // try to dequeue item. if no item found. wait upto timeout and try again | ||||
|     // Return true, if succeeded dequeue item, false otherwise | ||||
|     bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) | ||||
|     { | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lock(queue_mutex_); | ||||
|             if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|             q_.pop_front(popped_item); | ||||
|         } | ||||
|         pop_cv_.notify_one(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| #else | ||||
|     // apparently mingw deadlocks if the mutex is released before cv.notify_one(), | ||||
|     // so release the mutex at the very end each function. | ||||
|  | ||||
|     // try to enqueue and block if no room left | ||||
|     void enqueue(T &&item) | ||||
|     { | ||||
|         std::unique_lock<std::mutex> lock(queue_mutex_); | ||||
|         pop_cv_.wait(lock, [this] { return !this->q_.full(); }); | ||||
|         q_.push_back(std::move(item)); | ||||
|         push_cv_.notify_one(); | ||||
|     } | ||||
|  | ||||
|     // enqueue immediately. overrun oldest message in the queue if no room left. | ||||
|     void enqueue_nowait(T &&item) | ||||
|     { | ||||
|         std::unique_lock<std::mutex> lock(queue_mutex_); | ||||
|         q_.push_back(std::move(item)); | ||||
|         push_cv_.notify_one(); | ||||
|     } | ||||
|  | ||||
|     // try to dequeue item. if no item found. wait upto timeout and try again | ||||
|     // Return true, if succeeded dequeue item, false otherwise | ||||
|     bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) | ||||
|     { | ||||
|         std::unique_lock<std::mutex> lock(queue_mutex_); | ||||
|         if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         q_.pop_front(popped_item); | ||||
|         pop_cv_.notify_one(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     size_t overrun_counter() | ||||
|     { | ||||
|         std::unique_lock<std::mutex> lock(queue_mutex_); | ||||
|         return q_.overrun_counter(); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::mutex queue_mutex_; | ||||
|     std::condition_variable push_cv_; | ||||
|     std::condition_variable pop_cv_; | ||||
|     spdlog::details::circular_q<T> q_; | ||||
| }; | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
							
								
								
									
										45
									
								
								third_party/spdlog/include/spdlog/details/null_mutex.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								third_party/spdlog/include/spdlog/details/null_mutex.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| // | ||||
| // Copyright(c) 2015 Gabi Melman. | ||||
| // Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <atomic> | ||||
| // null, no cost dummy "mutex" and dummy "atomic" int | ||||
|  | ||||
| namespace spdlog { | ||||
| namespace details { | ||||
| struct null_mutex | ||||
| { | ||||
|     void lock() {} | ||||
|     void unlock() {} | ||||
|     bool try_lock() | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| struct null_atomic_int | ||||
| { | ||||
|     int value; | ||||
|     null_atomic_int() = default; | ||||
|  | ||||
|     explicit null_atomic_int(int val) | ||||
|         : value(val) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     int load(std::memory_order) const | ||||
|     { | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     void store(int val) | ||||
|     { | ||||
|         value = val; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| } // namespace details | ||||
| } // namespace spdlog | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user