diff --git a/.clang-format b/.clang-format index 51d2aa3..18f4351 100644 --- a/.clang-format +++ b/.clang-format @@ -18,7 +18,7 @@ BreakBeforeBraces: Linux BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 80 -CommentPragmas: '^ IWYU pragma:' +CommentPragmas: "^ IWYU pragma:" ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 @@ -27,7 +27,7 @@ DerivePointerBinding: false ExperimentalAutoDetectBinPacking: false IndentCaseLabels: true IndentFunctionDeclarationAfterType: true -IndentWidth: 4 +IndentWidth: 2 MaxEmptyLinesToKeep: 2 NamespaceIndentation: All ObjCSpaceAfterProperty: true @@ -50,5 +50,5 @@ SpacesInCStyleCastParentheses: false SpacesInParentheses: false Standard: c++14 SortIncludes: true -TabWidth: 4 +TabWidth: 2 UseTab: Never diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 6abfb67..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -GermanAirlinesVA_GAConnector \ No newline at end of file diff --git a/.idea/GermanAirlinesVA-GAConnector.iml b/.idea/GermanAirlinesVA-GAConnector.iml deleted file mode 100644 index f08604b..0000000 --- a/.idea/GermanAirlinesVA-GAConnector.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 79b3c94..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index ff0f9b0..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 2378106..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": [], - "compilerPath": "/opt/llvm-mingw/bin/clang++", - "cStandard": "c17", - "intelliSenseMode": "windows-clang-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 0999cd5..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "clang++-10 - Build and debug active file", - "type": "cppdbg", - "request": "launch", - "program": "", - "args": [], - "stopAtEntry": false, - "cwd": "${fileDirname}", - "environment": [], - "externalConsole": false, - "MIMode": "lldb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - }, - { - "description": "Set Disassembly Flavor to Intel", - "text": "-gdb-set disassembly-flavor intel", - "ignoreFailures": true - } - ], - "preLaunchTask": "C/C++: clang++-10 build active file", - "miDebuggerPath": "/usr/bin/lldb-mi-10" - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index cd358a6..6ca437e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,67 +1,7 @@ { - "files.associations": { - "thread": "cpp", - "algorithm": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "*.tcc": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "codecvt": "cpp", - "condition_variable": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "forward_list": "cpp", - "list": "cpp", - "map": "cpp", - "set": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "exception": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "numeric": "cpp", - "optional": "cpp", - "random": "cpp", - "ratio": "cpp", - "regex": "cpp", - "string": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "fstream": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "ostream": "cpp", - "shared_mutex": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "cinttypes": "cpp", - "typeinfo": "cpp", - "valarray": "cpp" - } + "cmake.generator": "Unix Makefiles", + "cmake.configureArgs": [ + "-DIBM=ON" + ], + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 2fc6150..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "tasks": [ - { - "type": "cppbuild", - "label": "C/C++: clang++-10 build active file", - "command": "/usr/bin/clang++-10", - "args": [ - "-fdiagnostics-color=always", - "-g", - "${file}", - "-o", - "${fileDirname}/${fileBasenameNoExtension}" - ], - "options": { - "cwd": "${fileDirname}" - }, - "problemMatcher": [ - "$gcc" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "detail": "Task generated by Debugger." - } - ], - "version": "2.0.0" -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6047254..8473c7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,6 @@ set(PLUGIN_NAME GAConnector) option(DEBUG "Debug symbols" OFF) -add_subdirectory( - ixwebsocket -) add_subdirectory( xplugin ) diff --git a/file/config.hpp b/file/config.hpp index cc87d6c..a8cd004 100644 --- a/file/config.hpp +++ b/file/config.hpp @@ -12,36 +12,36 @@ namespace config { - static inline std::map - readConfig(const std::string &file) - { - std::ifstream config(file); - std::map settings; + static inline std::map + readConfig(const std::string &file) + { + std::ifstream config(file); + std::map settings; - std::string line; - while (std::getline(config, line)) { - std::vector fields = split(line, '='); - if (fields.size() >= 2) { - trim(fields[0]); - trim(fields[1]); - settings[fields[0]] = fields[1]; - } - } - - config.close(); - return settings; + std::string line; + while (std::getline(config, line)) { + std::vector fields = split(line, '='); + if (fields.size() >= 2) { + trim(fields[0]); + trim(fields[1]); + settings[fields[0]] = fields[1]; + } } + config.close(); + return settings; + } - static inline void - writeConfig(const std::map &config, - const std::string &file) - { - std::ofstream cfg(file); - for (const std::pair &entry : config) { - cfg << entry.first << '=' << entry.second << '\n'; - } - cfg.close(); + + static inline void + writeConfig(const std::map &config, + const std::string &file) + { + std::ofstream cfg(file); + for (const std::pair &entry : config) { + cfg << entry.first << '=' << entry.second << '\n'; } + cfg.close(); + } } // namespace config #endif diff --git a/file/gate.hpp b/file/gate.hpp index 9182544..59fe6a4 100644 --- a/file/gate.hpp +++ b/file/gate.hpp @@ -21,36 +21,35 @@ */ class Gate { -private: - std::string designator; - double latitude; - double longitude; - std::vector file; + private: + std::string designator; + double latitude; + double longitude; + std::vector file; -public: - Gate(const std::string &designator, double latitude, double longitude) - { + public: + Gate(const std::string &designator, double latitude, double longitude) + { - this->designator = designator; - this->latitude = latitude; - this->longitude = longitude; + this->designator = designator; + this->latitude = latitude; + this->longitude = longitude; - file = std::vector(18 + this->designator.length(), 0); - std::uint8_t *bufPtr = file.data(); - memset(bufPtr, - static_cast(this->designator.length()), - sizeof(std::uint8_t)); - bufPtr++; // Designator length - memcpy(bufPtr, this->designator.c_str(), this->designator.length()); - bufPtr += - this->designator.length() + 1; // Designator plus null termination - memcpy(bufPtr, &this->latitude, sizeof(this->latitude)); - bufPtr += 8; // Latitude - memcpy(bufPtr, &this->longitude, sizeof(this->longitude)); - } + file = std::vector(18 + this->designator.length(), 0); + std::uint8_t *bufPtr = file.data(); + memset(bufPtr, + static_cast(this->designator.length()), + sizeof(std::uint8_t)); + bufPtr++; // Designator length + memcpy(bufPtr, this->designator.c_str(), this->designator.length()); + bufPtr += this->designator.length() + 1; // Designator plus null termination + memcpy(bufPtr, &this->latitude, sizeof(this->latitude)); + bufPtr += 8; // Latitude + memcpy(bufPtr, &this->longitude, sizeof(this->longitude)); + } - std::uint8_t *getBinaryData() { return file.data(); } - std::size_t getBinaryLength() { return file.size(); } + std::uint8_t *getBinaryData() { return file.data(); } + std::size_t getBinaryLength() { return file.size(); } }; #endif \ No newline at end of file diff --git a/file/pathSegment.hpp b/file/pathSegment.hpp index 100db9e..75431cb 100644 --- a/file/pathSegment.hpp +++ b/file/pathSegment.hpp @@ -11,47 +11,46 @@ */ class PathSegment { -private: - std::uint16_t altitude = 0; - std::uint16_t groundSpeed = 0; - double latitude = 0; - double longitude = 0; - std::vector file; + private: + std::uint16_t altitude = 0; + std::uint16_t groundSpeed = 0; + double latitude = 0; + double longitude = 0; + std::vector file; -public: - PathSegment() = default; - PathSegment(std::uint16_t altitude, - std::uint16_t groundSpeed, - double latitude, - double longitude) - { - this->altitude = altitude; - this->groundSpeed = groundSpeed; - this->latitude = latitude; - this->longitude = longitude; + public: + PathSegment() = default; + PathSegment(std::uint16_t altitude, + std::uint16_t groundSpeed, + double latitude, + double longitude) + { + this->altitude = altitude; + this->groundSpeed = groundSpeed; + this->latitude = latitude; + this->longitude = longitude; - file = std::vector(20, 0); - std::uint8_t *bufPtr = file.data(); - memcpy(bufPtr, &this->altitude, sizeof(this->altitude)); - bufPtr += sizeof(this->altitude); - memcpy(bufPtr, &this->groundSpeed, sizeof(this->groundSpeed)); - bufPtr += sizeof(this->groundSpeed); - memcpy(bufPtr, &this->latitude, sizeof(this->latitude)); - bufPtr += sizeof(this->latitude); - memcpy(bufPtr, &this->longitude, sizeof(this->longitude)); - } + file = std::vector(20, 0); + std::uint8_t *bufPtr = file.data(); + memcpy(bufPtr, &this->altitude, sizeof(this->altitude)); + bufPtr += sizeof(this->altitude); + memcpy(bufPtr, &this->groundSpeed, sizeof(this->groundSpeed)); + bufPtr += sizeof(this->groundSpeed); + memcpy(bufPtr, &this->latitude, sizeof(this->latitude)); + bufPtr += sizeof(this->latitude); + memcpy(bufPtr, &this->longitude, sizeof(this->longitude)); + } - std::uint8_t *getBinaryData() { return file.data(); } - std::size_t getBinaryLength() { return file.size(); } + std::uint8_t *getBinaryData() { return file.data(); } + std::size_t getBinaryLength() { return file.size(); } - friend bool operator==(const PathSegment &lhs, const PathSegment &rhs) - { - return lhs.altitude == rhs.altitude && - lhs.groundSpeed == rhs.groundSpeed && - lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude; - } - friend bool operator!=(const PathSegment &lhs, const PathSegment &rhs) - { - return !(lhs == rhs); - } + friend bool operator==(const PathSegment &lhs, const PathSegment &rhs) + { + return lhs.altitude == rhs.altitude && lhs.groundSpeed == rhs.groundSpeed && + lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude; + } + friend bool operator!=(const PathSegment &lhs, const PathSegment &rhs) + { + return !(lhs == rhs); + } }; diff --git a/file/recordingPath.hpp b/file/recordingPath.hpp index 2e3d173..8740256 100644 --- a/file/recordingPath.hpp +++ b/file/recordingPath.hpp @@ -6,19 +6,19 @@ class Path { -private: - std::uint64_t count = 0; - std::vector file; + private: + std::uint64_t count = 0; + std::vector file; -public: - void addSegment(PathSegment segment) - { - file.resize(file.size() + segment.getBinaryLength()); - std::uint8_t *bufPtr = file.data() + count * segment.getBinaryLength(); - memcpy(bufPtr, segment.getBinaryData(), segment.getBinaryLength()); - count++; - } + public: + void addSegment(PathSegment segment) + { + file.resize(file.size() + segment.getBinaryLength()); + std::uint8_t *bufPtr = file.data() + count * segment.getBinaryLength(); + memcpy(bufPtr, segment.getBinaryData(), segment.getBinaryLength()); + count++; + } - std::uint8_t *getBinaryData() { return file.data(); } - std::size_t getBinaryLength() { return file.size(); } + std::uint8_t *getBinaryData() { return file.data(); } + std::size_t getBinaryLength() { return file.size(); } }; \ No newline at end of file diff --git a/file/runway.hpp b/file/runway.hpp index ec34bb8..5d22117 100644 --- a/file/runway.hpp +++ b/file/runway.hpp @@ -23,92 +23,91 @@ */ class Runway { -private: - std::string designator; - double latitudeStart; - double longitudeStart; - std::uint8_t width; - std::uint16_t length; - std::uint16_t trueHeading; - std::vector file; + private: + std::string designator; + double latitudeStart; + double longitudeStart; + std::uint8_t width; + std::uint16_t length; + std::uint16_t trueHeading; + std::vector file; -public: - Runway(std::string designator, - double latitudeStart, - double longitudeStart, - double latitudeEnd, - double longitudeEnd, - double width) - { - this->designator = std::move(designator); - this->latitudeStart = latitudeStart; - this->longitudeStart = longitudeStart; - this->width = (std::uint8_t)std::round(util::to_feet(width)); - double dist = util::distanceEarth(latitudeStart, - longitudeStart, - latitudeEnd, - longitudeEnd); - this->length = (std::uint16_t)std::round(util::to_feet(dist)); - this->trueHeading = - (std::uint16_t)std::round(util::bearing(latitudeStart, - longitudeStart, - latitudeEnd, - longitudeEnd)); + public: + Runway(std::string designator, + double latitudeStart, + double longitudeStart, + double latitudeEnd, + double longitudeEnd, + double width) + { + this->designator = std::move(designator); + this->latitudeStart = latitudeStart; + this->longitudeStart = longitudeStart; + this->width = (std::uint8_t)std::round(util::to_feet(width)); + double dist = util::distanceEarth(latitudeStart, + longitudeStart, + latitudeEnd, + longitudeEnd); + this->length = (std::uint16_t)std::round(util::to_feet(dist)); + this->trueHeading = (std::uint16_t)std::round(util::bearing(latitudeStart, + longitudeStart, + latitudeEnd, + longitudeEnd)); - file = std::vector(23 + this->designator.length(), 0); - std::uint8_t *bufPtr = file.data(); - memset(bufPtr, - static_cast(this->designator.length()), - sizeof(std::uint8_t)); - bufPtr++; - memcpy(bufPtr, this->designator.c_str(), this->designator.length()); - bufPtr += this->designator.length() + 1; - memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart)); - bufPtr += sizeof(this->latitudeStart); - memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart)); - bufPtr += sizeof(this->longitudeStart); - memcpy(bufPtr, &this->width, sizeof(this->width)); - bufPtr += sizeof(this->width); - memcpy(bufPtr, &this->length, sizeof(this->length)); - bufPtr += sizeof(this->length); - memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading)); - } + file = std::vector(23 + this->designator.length(), 0); + std::uint8_t *bufPtr = file.data(); + memset(bufPtr, + static_cast(this->designator.length()), + sizeof(std::uint8_t)); + bufPtr++; + memcpy(bufPtr, this->designator.c_str(), this->designator.length()); + bufPtr += this->designator.length() + 1; + memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart)); + bufPtr += sizeof(this->latitudeStart); + memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart)); + bufPtr += sizeof(this->longitudeStart); + memcpy(bufPtr, &this->width, sizeof(this->width)); + bufPtr += sizeof(this->width); + memcpy(bufPtr, &this->length, sizeof(this->length)); + bufPtr += sizeof(this->length); + memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading)); + } - Runway(std::string designator, - double latitudeStart, - double longitudeStart, - std::uint8_t width, - std::uint16_t length, - std::uint16_t trueHeading) - { - this->designator = std::move(designator); - this->latitudeStart = latitudeStart; - this->longitudeStart = longitudeStart; - this->width = width; - this->length = length; - this->trueHeading = trueHeading; + Runway(std::string designator, + double latitudeStart, + double longitudeStart, + std::uint8_t width, + std::uint16_t length, + std::uint16_t trueHeading) + { + this->designator = std::move(designator); + this->latitudeStart = latitudeStart; + this->longitudeStart = longitudeStart; + this->width = width; + this->length = length; + this->trueHeading = trueHeading; - file = std::vector(23 + this->designator.length(), 0); - std::uint8_t *bufPtr = file.data(); - memset(bufPtr, - static_cast(this->designator.length()), - sizeof(std::uint8_t)); - bufPtr++; - memcpy(bufPtr, this->designator.c_str(), this->designator.length()); - bufPtr += this->designator.length() + 1; - memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart)); - bufPtr += sizeof(this->latitudeStart); - memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart)); - bufPtr += sizeof(this->longitudeStart); - memcpy(bufPtr, &this->width, sizeof(this->width)); - bufPtr += sizeof(this->width); - memcpy(bufPtr, &this->length, sizeof(this->length)); - bufPtr += sizeof(this->length); - memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading)); - } + file = std::vector(23 + this->designator.length(), 0); + std::uint8_t *bufPtr = file.data(); + memset(bufPtr, + static_cast(this->designator.length()), + sizeof(std::uint8_t)); + bufPtr++; + memcpy(bufPtr, this->designator.c_str(), this->designator.length()); + bufPtr += this->designator.length() + 1; + memcpy(bufPtr, &this->latitudeStart, sizeof(this->latitudeStart)); + bufPtr += sizeof(this->latitudeStart); + memcpy(bufPtr, &this->longitudeStart, sizeof(this->longitudeStart)); + bufPtr += sizeof(this->longitudeStart); + memcpy(bufPtr, &this->width, sizeof(this->width)); + bufPtr += sizeof(this->width); + memcpy(bufPtr, &this->length, sizeof(this->length)); + bufPtr += sizeof(this->length); + memcpy(bufPtr, &this->trueHeading, sizeof(this->trueHeading)); + } - std::uint8_t *getBinaryData() { return file.data(); } - std::size_t getBinaryLength() { return file.size(); } + std::uint8_t *getBinaryData() { return file.data(); } + std::size_t getBinaryLength() { return file.size(); } }; #endif \ No newline at end of file diff --git a/file/simulatorDatabase.hpp b/file/simulatorDatabase.hpp index e92a5bb..9a951fc 100644 --- a/file/simulatorDatabase.hpp +++ b/file/simulatorDatabase.hpp @@ -32,160 +32,152 @@ namespace simulatorDatabase { - static inline void toFile( - std::map, std::vector>> - &airports, - const std::string &file) - { - std::uint8_t null = 0; - std::ofstream out(file, std::fstream::binary); + static inline void toFile( + std::map, std::vector>> + &airports, + const std::string &file) + { + std::uint8_t null = 0; + std::ofstream out(file, std::fstream::binary); - // File Header - std::uint8_t header[] = {'V', 'G', 'A', 'S', 0, CURRENT_VERSION}; - out.write(reinterpret_cast(header), 6); - // Num Airports - std::uint16_t numAirports = airports.size(); - out.write(reinterpret_cast(&numAirports), - sizeof(numAirports)); - // Airport - for (const std::pair, std::vector>> - &airport : airports) { - std::string icao = airport.first; - std::vector gates = airport.second.first; - std::vector runways = airport.second.second; - // ICAO - std::uint8_t icaoLength = icao.length(); - out.write(reinterpret_cast(&icaoLength), - sizeof(icaoLength)); - out.write(icao.c_str(), icaoLength); - out.write(reinterpret_cast(&null), sizeof(null)); - // Gates - std::uint16_t numGates = gates.size(); - out.write(reinterpret_cast(&numGates), - sizeof(numGates)); - for (Gate &gate : gates) { - out.write(reinterpret_cast(gate.getBinaryData()), - (std::streamsize)gate.getBinaryLength()); - } - // Runways - std::uint8_t numRunways = runways.size(); - out.write(reinterpret_cast(&numRunways), - sizeof(numRunways)); - for (Runway &runway : runways) { - out.write( - reinterpret_cast(runway.getBinaryData()), - (std::streamsize)runway.getBinaryLength()); - } - } - out.close(); + // File Header + std::uint8_t header[] = {'V', 'G', 'A', 'S', 0, CURRENT_VERSION}; + out.write(reinterpret_cast(header), 6); + // Num Airports + std::uint16_t numAirports = airports.size(); + out.write(reinterpret_cast(&numAirports), + sizeof(numAirports)); + // Airport + for (const std::pair, std::vector>> + &airport : airports) { + std::string icao = airport.first; + std::vector gates = airport.second.first; + std::vector runways = airport.second.second; + // ICAO + std::uint8_t icaoLength = icao.length(); + out.write(reinterpret_cast(&icaoLength), + sizeof(icaoLength)); + out.write(icao.c_str(), icaoLength); + out.write(reinterpret_cast(&null), sizeof(null)); + // Gates + std::uint16_t numGates = gates.size(); + out.write(reinterpret_cast(&numGates), sizeof(numGates)); + for (Gate &gate : gates) { + out.write(reinterpret_cast(gate.getBinaryData()), + (std::streamsize)gate.getBinaryLength()); + } + // Runways + std::uint8_t numRunways = runways.size(); + out.write(reinterpret_cast(&numRunways), + sizeof(numRunways)); + for (Runway &runway : runways) { + out.write(reinterpret_cast(runway.getBinaryData()), + (std::streamsize)runway.getBinaryLength()); + } + } + out.close(); + } + + static inline std::map, std::vector>> + readVersion1(std::ifstream &in) + { + std::map, std::vector>> + airports; + + std::uint16_t numAirports; + in.read(reinterpret_cast(&numAirports), sizeof(numAirports)); + + for (int i = 0; i < numAirports; i++) { + // ICAO + std::uint8_t icaoLength; + in.read(reinterpret_cast(&icaoLength), sizeof(icaoLength)); + char *icao = static_cast(calloc(icaoLength + 1, sizeof(char))); + in.read(icao, icaoLength + 1); + // Gates + std::uint16_t numGates; + in.read(reinterpret_cast(&numGates), sizeof(numGates)); + for (int j = 0; j < numGates; j++) { + // ICAO + std::uint8_t designatorLength; + in.read(reinterpret_cast(&designatorLength), + sizeof(designatorLength)); + char *designator = + static_cast(calloc(designatorLength + 1, sizeof(char))); + in.read(designator, designatorLength + 1); + // Latitude + double latitude; + in.read(reinterpret_cast(&latitude), sizeof(latitude)); + // Latitude + double longitude; + in.read(reinterpret_cast(&longitude), sizeof(longitude)); + + airports[icao].first.emplace_back(designator, latitude, longitude); + } + // Runways + std::uint8_t numRunways; + in.read(reinterpret_cast(&numRunways), sizeof(numRunways)); + for (int j = 0; j < numRunways; j++) { + // ICAO + std::uint8_t designatorLength; + in.read(reinterpret_cast(&designatorLength), + sizeof(designatorLength)); + char *designator = + static_cast(calloc(designatorLength + 1, sizeof(char))); + in.read(designator, designatorLength + 1); + // Latitude + double latitude; + in.read(reinterpret_cast(&latitude), sizeof(latitude)); + // Latitude + double longitude; + in.read(reinterpret_cast(&longitude), sizeof(longitude)); + // Width + std::uint8_t width; + in.read(reinterpret_cast(&width), sizeof(width)); + // Length + std::uint16_t length; + in.read(reinterpret_cast(&length), sizeof(length)); + // True Heading + std::uint16_t trueHeading; + in.read(reinterpret_cast(&trueHeading), sizeof(trueHeading)); + + airports[icao].second.emplace_back(designator, + latitude, + longitude, + width, + length, + trueHeading); + } } - static inline std::map, std::vector>> - readVersion1(std::ifstream &in) - { - std::map, std::vector>> - airports; + in.close(); - std::uint16_t numAirports; - in.read(reinterpret_cast(&numAirports), sizeof(numAirports)); + return airports; + } - for (int i = 0; i < numAirports; i++) { - // ICAO - std::uint8_t icaoLength; - in.read(reinterpret_cast(&icaoLength), sizeof(icaoLength)); - char *icao = - static_cast(calloc(icaoLength + 1, sizeof(char))); - in.read(icao, icaoLength + 1); - // Gates - std::uint16_t numGates; - in.read(reinterpret_cast(&numGates), sizeof(numGates)); - for (int j = 0; j < numGates; j++) { - // ICAO - std::uint8_t designatorLength; - in.read(reinterpret_cast(&designatorLength), - sizeof(designatorLength)); - char *designator = static_cast( - calloc(designatorLength + 1, sizeof(char))); - in.read(designator, designatorLength + 1); - // Latitude - double latitude; - in.read(reinterpret_cast(&latitude), sizeof(latitude)); - // Latitude - double longitude; - in.read(reinterpret_cast(&longitude), - sizeof(longitude)); + static inline std::map, std::vector>> + fromFile(const std::string &file) + { + std::map, std::vector>> + airports; + std::ifstream in(file); - airports[icao].first.emplace_back(designator, - latitude, - longitude); - } - // Runways - std::uint8_t numRunways; - in.read(reinterpret_cast(&numRunways), sizeof(numRunways)); - for (int j = 0; j < numRunways; j++) { - // ICAO - std::uint8_t designatorLength; - in.read(reinterpret_cast(&designatorLength), - sizeof(designatorLength)); - char *designator = static_cast( - calloc(designatorLength + 1, sizeof(char))); - in.read(designator, designatorLength + 1); - // Latitude - double latitude; - in.read(reinterpret_cast(&latitude), sizeof(latitude)); - // Latitude - double longitude; - in.read(reinterpret_cast(&longitude), - sizeof(longitude)); - // Width - std::uint8_t width; - in.read(reinterpret_cast(&width), sizeof(width)); - // Length - std::uint16_t length; - in.read(reinterpret_cast(&length), sizeof(length)); - // True Heading - std::uint16_t trueHeading; - in.read(reinterpret_cast(&trueHeading), - sizeof(trueHeading)); - - airports[icao].second.emplace_back(designator, - latitude, - longitude, - width, - length, - trueHeading); - } - } - - in.close(); - - return airports; + // File Header + char ident[5]; + in.read(ident, 5); + if (strcmp(ident, "VGAS") != 0) { + throw std::invalid_argument("Wrong file"); } + std::uint8_t version; + in.read(reinterpret_cast(&version), 1); - static inline std::map, std::vector>> - fromFile(const std::string &file) - { - std::map, std::vector>> - airports; - std::ifstream in(file); - - // File Header - char ident[5]; - in.read(ident, 5); - if (strcmp(ident, "VGAS") != 0) { - throw std::invalid_argument("Wrong file"); - } - std::uint8_t version; - in.read(reinterpret_cast(&version), 1); - - if (version == 1) { - return readVersion1(in); - } - return airports; + if (version == 1) { + return readVersion1(in); } + return airports; + } } // namespace simulatorDatabase #endif diff --git a/file/stringExtensions.hpp b/file/stringExtensions.hpp index 73cd052..ceea395 100644 --- a/file/stringExtensions.hpp +++ b/file/stringExtensions.hpp @@ -9,45 +9,45 @@ // trim from start (in place) static inline void ltrim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { - return !std::isspace(ch); - })); + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); } // trim from end (in place) static inline void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), - s.rend(), - [](unsigned char ch) { return !std::isspace(ch); }) - .base(), - s.end()); + s.erase(std::find_if(s.rbegin(), + s.rend(), + [](unsigned char ch) { return !std::isspace(ch); }) + .base(), + s.end()); } static inline std::string rtrim_copy(std::string s) { - rtrim(s); - return s; + rtrim(s); + return s; } // trim from both ends (in place) static inline void trim(std::string &s) { - ltrim(s); - rtrim(s); + ltrim(s); + rtrim(s); } static inline std::vector split(const std::string &s, char delim) { - std::vector result; - std::stringstream ss(s); - std::string item; + std::vector result; + std::stringstream ss(s); + std::string item; - while (getline(ss, item, delim)) { - result.push_back(item); - } + while (getline(ss, item, delim)) { + result.push_back(item); + } - return result; + return result; } #endif \ No newline at end of file diff --git a/file/util.hpp b/file/util.hpp index c3add92..592cec0 100644 --- a/file/util.hpp +++ b/file/util.hpp @@ -39,264 +39,258 @@ namespace util { - static inline double to_feet(double value) { return value * 3.280839895; } + static inline double to_feet(double value) { return value * 3.280839895; } - static inline double to_degrees(double value) { return value * 180 / M_PI; } + static inline double to_degrees(double value) { return value * 180 / M_PI; } - static inline double to_radians(double value) { return value * M_PI / 180; } + static inline double to_radians(double value) { return value * M_PI / 180; } - static inline double normalize(double value) - { - return fmod(value + 360, 360); - } + static inline double normalize(double value) + { + return fmod(value + 360, 360); + } - static inline double bearing(double fromLatitude, - double fromLongitude, - double toLatitude, - double toLongitude) - { - double y = sin(to_radians(toLongitude) - to_radians(fromLongitude)) * - cos(to_radians(toLatitude)); - double x = cos(to_radians(fromLatitude)) * sin(to_radians(toLatitude)) - - sin(to_radians(fromLatitude)) * cos(to_radians(toLatitude)) * - cos(to_radians(toLongitude) - to_radians(fromLongitude)); + static inline double bearing(double fromLatitude, + double fromLongitude, + double toLatitude, + double toLongitude) + { + double y = sin(to_radians(toLongitude) - to_radians(fromLongitude)) * + cos(to_radians(toLatitude)); + double x = cos(to_radians(fromLatitude)) * sin(to_radians(toLatitude)) - + sin(to_radians(fromLatitude)) * cos(to_radians(toLatitude)) * + cos(to_radians(toLongitude) - to_radians(fromLongitude)); - return normalize(to_degrees(atan2(y, x))); - } + return normalize(to_degrees(atan2(y, x))); + } - static inline double distanceEarth(double fromLatitude, - double fromLongitude, - double toLatitude, - double toLongitude) - { - double lat1r, lon1r, lat2r, lon2r, u, v; - lat1r = to_radians(fromLatitude); - lon1r = to_radians(fromLongitude); - lat2r = to_radians(toLatitude); - lon2r = to_radians(toLongitude); - u = sin((lat2r - lat1r) / 2); - v = sin((lon2r - lon1r) / 2); - return 2.0 * EARTH_M * - asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v)); - } + static inline double distanceEarth(double fromLatitude, + double fromLongitude, + double toLatitude, + double toLongitude) + { + double lat1r, lon1r, lat2r, lon2r, u, v; + lat1r = to_radians(fromLatitude); + lon1r = to_radians(fromLongitude); + lat2r = to_radians(toLatitude); + lon2r = to_radians(toLongitude); + u = sin((lat2r - lat1r) / 2); + v = sin((lon2r - lon1r) / 2); + return 2.0 * EARTH_M * asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v)); + } - template - static inline std::vector - select_T(const std::vector &inVec, - std::function predicate) - { - std::vector result; - copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate); - return result; - } + template + static inline std::vector + select_T(const std::vector &inVec, + std::function predicate) + { + std::vector result; + copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate); + return result; + } #if defined APL || defined LIN - static unsigned long get_size_by_fd(int fd) - { - struct stat buf { - }; - if (fstat(fd, &buf) < 0) - return 0; - return buf.st_size; - } + static unsigned long get_size_by_fd(int fd) + { + struct stat buf { + }; + if (fstat(fd, &buf) < 0) + return 0; + return buf.st_size; + } #endif - static void to_hex(const char *hash, char *buffer) - { - for (int i = 0; i < MD5LEN; i++) { - if (buffer != nullptr) { - sprintf(&buffer[2 * i], "%02x", hash[i] & 0xff); - } - } + static void to_hex(const char *hash, char *buffer) + { + for (int i = 0; i < MD5LEN; i++) { + if (buffer != nullptr) { + sprintf(&buffer[2 * i], "%02x", hash[i] & 0xff); + } } + } #ifdef IBM - static inline int - generateMD5(const char *filepath, - char *lastHash, - const std::function toLog) - { - BOOL bResult = FALSE; - HCRYPTPROV hProv = 0; - HCRYPTHASH hHash = 0; - HANDLE hFile; - BYTE rgbFile[BUFSIZE] = {0}; - DWORD cbRead = 0; - BYTE rgbHash[MD5LEN] = {0}; - DWORD cbHash = 0; + static inline int + generateMD5(const char *filepath, + char *lastHash, + const std::function toLog) + { + BOOL bResult = FALSE; + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + HANDLE hFile; + BYTE rgbFile[BUFSIZE] = {0}; + DWORD cbRead = 0; + BYTE rgbHash[MD5LEN] = {0}; + DWORD cbHash = 0; - // Logic to check usage goes here. - hFile = CreateFile(filepath, - GENERIC_READ, - FILE_SHARE_READ, - nullptr, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - nullptr); + // Logic to check usage goes here. + hFile = CreateFile(filepath, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + nullptr); - // Get handle to the crypto provider - if (!CryptAcquireContext(&hProv, - nullptr, - nullptr, - PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - std::stringstream debug_msg; - debug_msg << "CryptAcquireContext returned with error " - << GetLastError(); - toLog(debug_msg.str()); + // Get handle to the crypto provider + if (!CryptAcquireContext(&hProv, + nullptr, + nullptr, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + std::stringstream debug_msg; + debug_msg << "CryptAcquireContext returned with error " << GetLastError(); + toLog(debug_msg.str()); - CloseHandle(hFile); - return 1; - } + CloseHandle(hFile); + return 1; + } - if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { - std::stringstream debug_msg; - debug_msg << "CryptCreateHash returned with error " - << GetLastError(); - toLog(debug_msg.str()); + if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { + std::stringstream debug_msg; + debug_msg << "CryptCreateHash returned with error " << GetLastError(); + toLog(debug_msg.str()); - CloseHandle(hFile); - CryptReleaseContext(hProv, 0); - return 1; - } + CloseHandle(hFile); + CryptReleaseContext(hProv, 0); + return 1; + } - while ( - (bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, nullptr))) { - if (0 == cbRead) { - break; - } + while ((bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, nullptr))) { + if (0 == cbRead) { + break; + } - if (!CryptHashData(hHash, rgbFile, cbRead, 0)) { - std::stringstream debug_msg; - debug_msg << "CryptHashData returned with error " - << GetLastError(); - toLog(debug_msg.str()); + if (!CryptHashData(hHash, rgbFile, cbRead, 0)) { + std::stringstream debug_msg; + debug_msg << "CryptHashData returned with error " << GetLastError(); + toLog(debug_msg.str()); - CryptReleaseContext(hProv, 0); - CryptDestroyHash(hHash); - CloseHandle(hFile); - return 1; - } - } - - if (!bResult) { - std::stringstream debug_msg; - debug_msg << "ReadFile returned with error " << GetLastError(); - toLog(debug_msg.str()); - - CryptReleaseContext(hProv, 0); - CryptDestroyHash(hHash); - CloseHandle(hFile); - return 1; - } - - cbHash = MD5LEN; - if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) { - to_hex((char *)rgbHash, lastHash); - } else { - std::stringstream debug_msg; - debug_msg << "CryptGetHashParam returned with error " - << GetLastError(); - toLog(debug_msg.str()); - } - - CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); + CryptDestroyHash(hHash); CloseHandle(hFile); - - return 0; + return 1; + } } + + if (!bResult) { + std::stringstream debug_msg; + debug_msg << "ReadFile returned with error " << GetLastError(); + toLog(debug_msg.str()); + + CryptReleaseContext(hProv, 0); + CryptDestroyHash(hHash); + CloseHandle(hFile); + return 1; + } + + cbHash = MD5LEN; + if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) { + to_hex((char *)rgbHash, lastHash); + } else { + std::stringstream debug_msg; + debug_msg << "CryptGetHashParam returned with error " << GetLastError(); + toLog(debug_msg.str()); + } + + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + CloseHandle(hFile); + + return 0; + } #endif #ifdef APL #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" - static inline int - generateMD5(const char *filepath, - char *lastHash, - const std::function &toLog) - { - int file_descript; - unsigned long file_size; - char *file_buffer; - unsigned char result[MD5LEN]; + static inline int + generateMD5(const char *filepath, + char *lastHash, + const std::function &toLog) + { + int file_descript; + unsigned long file_size; + char *file_buffer; + unsigned char result[MD5LEN]; - file_descript = open(filepath, O_RDONLY); - if (file_descript < 0) - return 1; + file_descript = open(filepath, O_RDONLY); + if (file_descript < 0) + return 1; - file_size = get_size_by_fd(file_descript); + file_size = get_size_by_fd(file_descript); - file_buffer = - (char *)mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0); + file_buffer = + (char *)mmap(0, file_size, PROT_READ, MAP_SHARED, file_descript, 0); - CC_MD5_CTX context; - CC_MD5_Init(&context); - CC_MD5_Update(&context, file_buffer, (CC_LONG)file_size); - CC_MD5_Final(result, &context); + CC_MD5_CTX context; + CC_MD5_Init(&context); + CC_MD5_Update(&context, file_buffer, (CC_LONG)file_size); + CC_MD5_Final(result, &context); - munmap(file_buffer, file_size); - close(file_descript); + munmap(file_buffer, file_size); + close(file_descript); - to_hex((char *)result, lastHash); - return 0; - } + to_hex((char *)result, lastHash); + return 0; + } #pragma clang diagnostic pop #endif #ifdef LIN #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" - static inline int - generateMD5(const char *filepath, - char *buffer, - const std::function &toLog) - { - int file_descriptor; - unsigned long file_size; - char *file_buffer; - unsigned char result[MD5LEN]; + static inline int + generateMD5(const char *filepath, + char *buffer, + const std::function &toLog) + { + int file_descriptor; + unsigned long file_size; + char *file_buffer; + unsigned char result[MD5LEN]; - file_descriptor = open(filepath, O_RDONLY); - if (file_descriptor < 0) - return 1; + file_descriptor = open(filepath, O_RDONLY); + if (file_descriptor < 0) + return 1; - file_size = get_size_by_fd(file_descriptor); - if (file_size == 0) - return 1; + file_size = get_size_by_fd(file_descriptor); + if (file_size == 0) + return 1; - file_buffer = (char *) - mmap(nullptr, file_size, PROT_READ, MAP_SHARED, file_descriptor, 0); + file_buffer = (char *) + mmap(nullptr, file_size, PROT_READ, MAP_SHARED, file_descriptor, 0); - MD5((unsigned char *)file_buffer, file_size, result); + MD5((unsigned char *)file_buffer, file_size, result); - munmap(file_buffer, file_size); - close(file_descriptor); + munmap(file_buffer, file_size); + close(file_descriptor); - to_hex((char *)result, buffer); - return 0; - } + to_hex((char *)result, buffer); + return 0; + } #pragma clang diagnostic pop #endif #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" - static inline void setThreadName(const std::string &name) - { + static inline void setThreadName(const std::string &name) + { #ifdef APL - // - // Apple reserves 16 bytes for its thread names - // Notice that the Apple version of pthread_setname_np - // does not take a pthread_t argument - // - pthread_setname_np(name.substr(0, 63).c_str()); + // + // Apple reserves 16 bytes for its thread names + // Notice that the Apple version of pthread_setname_np + // does not take a pthread_t argument + // + pthread_setname_np(name.substr(0, 63).c_str()); #endif #ifdef LIN - // - // Linux only reserves 16 bytes for its thread names - // See prctl and PR_SET_NAME property in - // http://man7.org/linux/man-pages/man2/prctl.2.html - // - pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); + // + // Linux only reserves 16 bytes for its thread names + // See prctl and PR_SET_NAME property in + // http://man7.org/linux/man-pages/man2/prctl.2.html + // + pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); #endif - } + } #pragma clang diagnostic pop } // namespace util diff --git a/format.sh b/format.sh index 59aa6ed..3add914 100755 --- a/format.sh +++ b/format.sh @@ -2,7 +2,7 @@ shopt -s globstar -GLOBIGNORE='**/XPLM/**:XPLM/**:**/XPLM:**/ixwebsocket/**:ixwebsocket/**:**/ixwebsocket:**/nlohmann/**:nlohmann/**:**/nlohmann:**/XPSDK/**:XPSDK/**:**/XPSDK:**/build*/**:build*/**:**/build*' +GLOBIGNORE='**/XPLM/**:XPLM/**:**/XPLM:**/nlohmann/**:nlohmann/**:**/nlohmann:**/XPSDK/**:XPSDK/**:**/XPSDK:**/build*/**:build*/**:**/build*' clang-format -verbose -i **/*.cpp clang-format -verbose -i **/*.h diff --git a/ixwebsocket/CMakeLists.txt b/ixwebsocket/CMakeLists.txt deleted file mode 100644 index fd5bfb4..0000000 --- a/ixwebsocket/CMakeLists.txt +++ /dev/null @@ -1,99 +0,0 @@ - -file(GLOB ixwebsocket CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/ixwebsocket/*.cpp) - -add_library(ixwebsocket SHARED - ${ixwebsocket} -) - -target_include_directories(ixwebsocket PRIVATE - ${CMAKE_SOURCE_DIR}/ixwebsocket/include -) - -set_target_properties(ixwebsocket PROPERTIES - PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/ixwebsocket/include -) -target_compile_options(ixwebsocket PRIVATE - -Wall - -Wextra - -pedantic - #-fvisibility=hidden -) -if(DEBUG) - target_compile_options(ixwebsocket PRIVATE - -g - ) - target_link_options(ixwebsocket PRIVATE - -g - ) -else() - target_compile_options(ixwebsocket PRIVATE - -O2 - ) -endif() - -if(APPLE) - message("Building ixwebsocket for MacOSX Universal into ${PROJECT_BINARY_DIR}/${PLUGIN_NAME}/${BIT}") - - set_target_properties(ixwebsocket PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Plugin/${PLUGIN_NAME} - BUILD_WITH_INSTALL_NAME_DIR TRUE - ) - - target_compile_options(ixwebsocket PRIVATE - #"SHELL:-arch i386" - "SHELL:-arch x86_64" - ) - target_link_options(ixwebsocket PRIVATE - #"SHELL:-arch i386" - "SHELL:-arch x86_64" - ) - target_link_libraries(ixwebsocket PRIVATE - "-framework Security" - ) -elseif(UNIX) - message("Building ixwebsocket for Linux ${BIT} into ${PROJECT_BINARY_DIR}/Plugin/${PLUGIN_NAME}/${BIT}") - - set_target_properties(ixwebsocket PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Plugin/${PLUGIN_NAME}/${BIT} - ) - - target_compile_options(ixwebsocket PRIVATE - -nodefaultlibs - ) - if(BIT STREQUAL "32") - target_compile_options(ixwebsocket PRIVATE - -m32 - ) - target_link_options(ixwebsocket PRIVATE - -m32 - ) - endif() - target_link_libraries(ixwebsocket PRIVATE - crypto - pthread - ) -elseif(WIN32) - message("Building ixwebsocket for Windows ${BIT} into ${PROJECT_BINARY_DIR}/Plugin/${PLUGIN_NAME}/${BIT}") - - set_target_properties(ixwebsocket PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Plugin/${PLUGIN_NAME}/${BIT} - ) - - if(DEBUG) - target_compile_options(ixwebsocket PRIVATE - -gcodeview - ) - target_link_options(ixwebsocket PRIVATE - -Wl,-pdb= - ) - endif() - target_link_options(ixwebsocket PRIVATE - -static-libgcc - -static-libstdc++ - ) - target_link_libraries(ixwebsocket PRIVATE - ws2_32.lib - ) -endif() - -add_library(ixwebsocket::ixwebsocket ALIAS ixwebsocket) diff --git a/ixwebsocket/IXBench.cpp b/ixwebsocket/IXBench.cpp deleted file mode 100644 index 665f3fe..0000000 --- a/ixwebsocket/IXBench.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * IXBench.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. - */ - -#include "IXBench.h" - -#include - -namespace ix -{ -Bench::Bench(const std::string &description) : _description(description) -{ - reset(); -} - -Bench::~Bench() -{ - if (!_reported) { - report(); - } -} - -void Bench::reset() -{ - _start = std::chrono::high_resolution_clock::now(); - _reported = false; -} - -void Bench::report() -{ - auto now = std::chrono::high_resolution_clock::now(); - auto microseconds = - std::chrono::duration_cast(now - _start); - - _duration = microseconds.count(); - std::cerr << _description << " completed in " << _duration << " us" - << std::endl; - - setReported(); -} - -void Bench::record() -{ - auto now = std::chrono::high_resolution_clock::now(); - auto microseconds = - std::chrono::duration_cast(now - _start); - - _duration = microseconds.count(); -} - -void Bench::setReported() { _reported = true; } - -uint64_t Bench::getDuration() const { return _duration; } -} // namespace ix diff --git a/ixwebsocket/IXCancellationRequest.cpp b/ixwebsocket/IXCancellationRequest.cpp deleted file mode 100644 index ddc58e5..0000000 --- a/ixwebsocket/IXCancellationRequest.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * IXCancellationRequest.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXCancellationRequest.h" - -#include -#include - -namespace ix -{ -CancellationRequest makeCancellationRequestWithTimeout( - int secs, - std::atomic &requestInitCancellation) -{ - assert(secs > 0); - - auto start = std::chrono::system_clock::now(); - auto timeout = std::chrono::seconds(secs); - - auto isCancellationRequested = - [&requestInitCancellation, start, timeout]() -> bool { - // Was an explicit cancellation requested ? - if (requestInitCancellation) - return true; - - auto now = std::chrono::system_clock::now(); - if ((now - start) > timeout) - return true; - - // No cancellation request - return false; - }; - - return isCancellationRequested; -} -} // namespace ix diff --git a/ixwebsocket/IXConnectionState.cpp b/ixwebsocket/IXConnectionState.cpp deleted file mode 100644 index 43eb9e1..0000000 --- a/ixwebsocket/IXConnectionState.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * IXConnectionState.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXConnectionState.h" - -namespace ix -{ -std::atomic ConnectionState::_globalId(0); - -ConnectionState::ConnectionState() : _terminated(false) { computeId(); } - -void ConnectionState::computeId() { _id = std::to_string(_globalId++); } - -const std::string &ConnectionState::getId() const { return _id; } - -std::shared_ptr ConnectionState::createConnectionState() -{ - return std::make_shared(); -} - -void ConnectionState::setOnSetTerminatedCallback( - const OnSetTerminatedCallback &callback) -{ - _onSetTerminatedCallback = callback; -} - -bool ConnectionState::isTerminated() const { return _terminated; } - -void ConnectionState::setTerminated() -{ - _terminated = true; - - if (_onSetTerminatedCallback) { - _onSetTerminatedCallback(); - } -} - -const std::string &ConnectionState::getRemoteIp() { return _remoteIp; } - -int ConnectionState::getRemotePort() { return _remotePort; } - -void ConnectionState::setRemoteIp(const std::string &remoteIp) -{ - _remoteIp = remoteIp; -} - -void ConnectionState::setRemotePort(int remotePort) -{ - _remotePort = remotePort; -} -} // namespace ix diff --git a/ixwebsocket/IXDNSLookup.cpp b/ixwebsocket/IXDNSLookup.cpp deleted file mode 100644 index db0b8d1..0000000 --- a/ixwebsocket/IXDNSLookup.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * IXDNSLookup.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -// -// On Windows Universal Platform (uwp), gai_strerror defaults behavior is to -// returns wchar_t which is different from all other platforms. We want the non -// unicode version. See https://github.com/microsoft/vcpkg/pull/11030 We could -// do this in IXNetSystem.cpp but so far we are only using gai_strerror in here. -// -#ifdef _UNICODE -#undef _UNICODE -#endif -#ifdef UNICODE -#undef UNICODE -#endif - -#include "IXDNSLookup.h" - -#include "IXNetSystem.h" -#include -#include -#include - -// mingw build quirks -#if defined(_WIN32) && defined(__GNUC__) -#define AI_NUMERICSERV NI_NUMERICSERV -#define AI_ADDRCONFIG LUP_ADDRCONFIG -#endif - -namespace ix -{ -const int64_t DNSLookup::kDefaultWait = 1; // ms - -DNSLookup::DNSLookup(const std::string &hostname, int port, int64_t wait) - : _hostname(hostname), _port(port), _wait(wait), _res(nullptr), _done(false) -{ - ; -} - -struct addrinfo *DNSLookup::getAddrInfo(const std::string &hostname, - int port, - std::string &errMsg) -{ - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - std::string sport = std::to_string(port); - - struct addrinfo *res; - int getaddrinfo_result = - getaddrinfo(hostname.c_str(), sport.c_str(), &hints, &res); - if (getaddrinfo_result) { - errMsg = gai_strerror(getaddrinfo_result); - res = nullptr; - } - return res; -} - -struct addrinfo * - DNSLookup::resolve(std::string &errMsg, - const CancellationRequest &isCancellationRequested, - bool cancellable) -{ - return cancellable ? resolveCancellable(errMsg, isCancellationRequested) - : resolveUnCancellable(errMsg, isCancellationRequested); -} - -void DNSLookup::release(struct addrinfo *addr) { freeaddrinfo(addr); } - -struct addrinfo *DNSLookup::resolveUnCancellable( - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - errMsg = "no error"; - - // Maybe a cancellation request got in before the background thread - // terminated ? - if (isCancellationRequested()) { - errMsg = "cancellation requested"; - return nullptr; - } - - return getAddrInfo(_hostname, _port, errMsg); -} - -struct addrinfo *DNSLookup::resolveCancellable( - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - errMsg = "no error"; - - // Can only be called once, otherwise we would have to manage a pool - // of background thread which is overkill for our usage. - if (_done) { - return nullptr; // programming error, create a second DNSLookup instance - // if you need a second lookup. - } - - // - // Good resource on thread forced termination - // https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread - // - auto ptr = shared_from_this(); - std::weak_ptr self(ptr); - - int port = _port; - std::string hostname(_hostname); - - // We make the background thread doing the work a shared pointer - // instead of a member variable, because it can keep running when - // this object goes out of scope, in case of cancellation - auto t = std::make_shared(&DNSLookup::run, - this, - self, - hostname, - port); - t->detach(); - - while (!_done) { - // Wait for 1 milliseconds, to see if the bg thread has terminated. - // We do not use a condition variable to wait, as destroying this one - // if the bg thread is alive can cause undefined behavior. - std::this_thread::sleep_for(std::chrono::milliseconds(_wait)); - - // Were we cancelled ? - if (isCancellationRequested()) { - errMsg = "cancellation requested"; - return nullptr; - } - } - - // Maybe a cancellation request got in before the bg terminated ? - if (isCancellationRequested()) { - errMsg = "cancellation requested"; - return nullptr; - } - - errMsg = getErrMsg(); - return getRes(); -} - -void DNSLookup::run(std::weak_ptr self, - std::string hostname, - int port) // thread runner -{ - // We don't want to read or write into members variables of an object that - // could be gone, so we use temporary variables (res) or we pass in by copy - // everything that getAddrInfo needs to work. - std::string errMsg; - struct addrinfo *res = getAddrInfo(hostname, port, errMsg); - - if (auto lock = self.lock()) { - // Copy result into the member variables - setRes(res); - setErrMsg(errMsg); - - _done = true; - } -} - -void DNSLookup::setErrMsg(const std::string &errMsg) -{ - std::lock_guard lock(_errMsgMutex); - _errMsg = errMsg; -} - -const std::string &DNSLookup::getErrMsg() -{ - std::lock_guard lock(_errMsgMutex); - return _errMsg; -} - -void DNSLookup::setRes(struct addrinfo *addr) -{ - std::lock_guard lock(_resMutex); - _res = addr; -} - -struct addrinfo *DNSLookup::getRes() -{ - std::lock_guard lock(_resMutex); - return _res; -} -} // namespace ix diff --git a/ixwebsocket/IXExponentialBackoff.cpp b/ixwebsocket/IXExponentialBackoff.cpp deleted file mode 100644 index 9a40999..0000000 --- a/ixwebsocket/IXExponentialBackoff.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * IXExponentialBackoff.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXExponentialBackoff.h" - -#include - -namespace ix -{ -uint32_t - calculateRetryWaitMilliseconds(uint32_t retryCount, - uint32_t maxWaitBetweenReconnectionRetries, - uint32_t minWaitBetweenReconnectionRetries) -{ - uint32_t waitTime = (retryCount < 26) ? (std::pow(2, retryCount) * 100) : 0; - - if (waitTime < minWaitBetweenReconnectionRetries) { - waitTime = minWaitBetweenReconnectionRetries; - } - - if (waitTime > maxWaitBetweenReconnectionRetries || waitTime == 0) { - waitTime = maxWaitBetweenReconnectionRetries; - } - - return waitTime; -} -} // namespace ix diff --git a/ixwebsocket/IXGetFreePort.cpp b/ixwebsocket/IXGetFreePort.cpp deleted file mode 100644 index d5ac863..0000000 --- a/ixwebsocket/IXGetFreePort.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * IXGetFreePort.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone. All rights reserved. - */ - -// Using inet_addr will trigger an error on uwp without this -// FIXME: use a different api -#ifdef _WIN32 -#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif -#endif - -#include "IXGetFreePort.h" - -#include "IXNetSystem.h" -#include "IXSocket.h" -#include -#include - -namespace ix -{ -int getAnyFreePortRandom() -{ - std::random_device rd; - std::uniform_int_distribution dist(1024 + 1, 65535); - - return dist(rd); -} - -int getAnyFreePort() -{ - socket_t sockfd; - if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - return getAnyFreePortRandom(); - } - - int enable = 1; - if (setsockopt(sockfd, - SOL_SOCKET, - SO_REUSEADDR, - (char *)&enable, - sizeof(enable)) < 0) { - return getAnyFreePortRandom(); - } - - // Bind to port 0. This is the standard way to get a free port. - struct sockaddr_in server; // server address information - server.sin_family = AF_INET; - server.sin_port = htons(0); - server.sin_addr.s_addr = inet_addr("127.0.0.1"); - - if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) { - Socket::closeSocket(sockfd); - return getAnyFreePortRandom(); - } - - struct sockaddr_in sa; // server address information - socklen_t len = sizeof(sa); - if (getsockname(sockfd, (struct sockaddr *)&sa, &len) < 0) { - Socket::closeSocket(sockfd); - return getAnyFreePortRandom(); - } - - int port = ntohs(sa.sin_port); - Socket::closeSocket(sockfd); - - return port; -} - -int getFreePort() -{ - while (true) { -#if defined(__has_feature) -#if __has_feature(address_sanitizer) - int port = getAnyFreePortRandom(); -#else - int port = getAnyFreePort(); -#endif -#else - int port = getAnyFreePort(); -#endif - // - // Only port above 1024 can be used by non root users, but for some - // reason I got port 7 returned with macOS when binding on port 0... - // - if (port > 1024) { - return port; - } - } - - return -1; -} -} // namespace ix diff --git a/ixwebsocket/IXGzipCodec.cpp b/ixwebsocket/IXGzipCodec.cpp deleted file mode 100644 index 82251f5..0000000 --- a/ixwebsocket/IXGzipCodec.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * IXGzipCodec.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2020 Machine Zone, Inc. All rights reserved. - */ - -#include "IXGzipCodec.h" - -#include "IXBench.h" -#include -#include - -#ifdef IXWEBSOCKET_USE_ZLIB -#include -#endif - -#ifdef IXWEBSOCKET_USE_DEFLATE -#include -#endif - -namespace ix -{ -std::string gzipCompress(const std::string &str) -{ -#ifndef IXWEBSOCKET_USE_ZLIB - return std::string(); -#else -#ifdef IXWEBSOCKET_USE_DEFLATE - int compressionLevel = 6; - struct libdeflate_compressor *compressor; - - compressor = libdeflate_alloc_compressor(compressionLevel); - - const void *uncompressed_data = str.data(); - size_t uncompressed_size = str.size(); - void *compressed_data; - size_t actual_compressed_size; - size_t max_compressed_size; - - max_compressed_size = - libdeflate_gzip_compress_bound(compressor, uncompressed_size); - compressed_data = malloc(max_compressed_size); - - if (compressed_data == NULL) { - return std::string(); - } - - actual_compressed_size = libdeflate_gzip_compress(compressor, - uncompressed_data, - uncompressed_size, - compressed_data, - max_compressed_size); - - libdeflate_free_compressor(compressor); - - if (actual_compressed_size == 0) { - free(compressed_data); - return std::string(); - } - - std::string out; - out.assign(reinterpret_cast(compressed_data), - actual_compressed_size); - free(compressed_data); - - return out; -#else - z_stream zs; // z_stream is zlib's control structure - memset(&zs, 0, sizeof(zs)); - - // deflateInit2 configure the file format: request gzip instead of deflate - const int windowBits = 15; - const int GZIP_ENCODING = 16; - - deflateInit2(&zs, - Z_DEFAULT_COMPRESSION, - Z_DEFLATED, - windowBits | GZIP_ENCODING, - 8, - Z_DEFAULT_STRATEGY); - - zs.next_in = (Bytef *)str.data(); - zs.avail_in = (uInt)str.size(); // set the z_stream's input - - int ret; - char outbuffer[32768]; - std::string outstring; - - // retrieve the compressed bytes blockwise - do { - zs.next_out = reinterpret_cast(outbuffer); - zs.avail_out = sizeof(outbuffer); - - ret = deflate(&zs, Z_FINISH); - - if (outstring.size() < zs.total_out) { - // append the block to the output string - outstring.append(outbuffer, zs.total_out - outstring.size()); - } - } while (ret == Z_OK); - - deflateEnd(&zs); - - return outstring; -#endif // IXWEBSOCKET_USE_DEFLATE -#endif // IXWEBSOCKET_USE_ZLIB -} - -#ifdef IXWEBSOCKET_USE_DEFLATE -static uint32_t loadDecompressedGzipSize(const uint8_t *p) -{ - return ((uint32_t)p[0] << 0) | ((uint32_t)p[1] << 8) | - ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); -} -#endif - -bool gzipDecompress(const std::string &in, std::string &out) -{ -#ifndef IXWEBSOCKET_USE_ZLIB - return false; -#else -#ifdef IXWEBSOCKET_USE_DEFLATE - struct libdeflate_decompressor *decompressor; - decompressor = libdeflate_alloc_decompressor(); - - const void *compressed_data = in.data(); - size_t compressed_size = in.size(); - - // Retrieve uncompressed size from the trailer of the gziped data - const uint8_t *ptr = reinterpret_cast(&in.front()); - auto uncompressed_size = - loadDecompressedGzipSize(&ptr[compressed_size - 4]); - - // Use it to redimension our output buffer - out.resize(uncompressed_size); - - libdeflate_result result = libdeflate_gzip_decompress(decompressor, - compressed_data, - compressed_size, - &out.front(), - uncompressed_size, - NULL); - - libdeflate_free_decompressor(decompressor); - return result == LIBDEFLATE_SUCCESS; -#else - z_stream inflateState; - memset(&inflateState, 0, sizeof(inflateState)); - - inflateState.zalloc = Z_NULL; - inflateState.zfree = Z_NULL; - inflateState.opaque = Z_NULL; - inflateState.avail_in = 0; - inflateState.next_in = Z_NULL; - - if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK) { - return false; - } - - inflateState.avail_in = (uInt)in.size(); - inflateState.next_in = (unsigned char *)(const_cast(in.data())); - - const int kBufferSize = 1 << 14; - std::array compressBuffer; - - do { - inflateState.avail_out = (uInt)kBufferSize; - inflateState.next_out = &compressBuffer.front(); - - int ret = inflate(&inflateState, Z_SYNC_FLUSH); - - if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { - inflateEnd(&inflateState); - return false; - } - - out.append(reinterpret_cast(&compressBuffer.front()), - kBufferSize - inflateState.avail_out); - } while (inflateState.avail_out == 0); - - inflateEnd(&inflateState); - return true; -#endif // IXWEBSOCKET_USE_DEFLATE -#endif // IXWEBSOCKET_USE_ZLIB -} -} // namespace ix diff --git a/ixwebsocket/IXHttp.cpp b/ixwebsocket/IXHttp.cpp deleted file mode 100644 index ac87598..0000000 --- a/ixwebsocket/IXHttp.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * IXHttp.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXHttp.h" - -#include "IXCancellationRequest.h" -#include "IXGzipCodec.h" -#include "IXSocket.h" -#include -#include - -namespace ix -{ -std::string Http::trim(const std::string &str) -{ - std::string out; - for (auto c : str) { - if (c != ' ' && c != '\n' && c != '\r') { - out += c; - } - } - - return out; -} - -std::pair Http::parseStatusLine(const std::string &line) -{ - // Request-Line = Method SP Request-URI SP HTTP-Version CRLF - std::string token; - std::stringstream tokenStream(line); - std::vector tokens; - - // Split by ' ' - while (std::getline(tokenStream, token, ' ')) { - tokens.push_back(token); - } - - std::string httpVersion; - if (tokens.size() >= 1) { - httpVersion = trim(tokens[0]); - } - - int statusCode = -1; - if (tokens.size() >= 2) { - std::stringstream ss; - ss << trim(tokens[1]); - ss >> statusCode; - } - - return std::make_pair(httpVersion, statusCode); -} - -std::tuple - Http::parseRequestLine(const std::string &line) -{ - // Request-Line = Method SP Request-URI SP HTTP-Version CRLF - std::string token; - std::stringstream tokenStream(line); - std::vector tokens; - - // Split by ' ' - while (std::getline(tokenStream, token, ' ')) { - tokens.push_back(token); - } - - std::string method; - if (tokens.size() >= 1) { - method = trim(tokens[0]); - } - - std::string requestUri; - if (tokens.size() >= 2) { - requestUri = trim(tokens[1]); - } - - std::string httpVersion; - if (tokens.size() >= 3) { - httpVersion = trim(tokens[2]); - } - - return std::make_tuple(method, requestUri, httpVersion); -} - -std::tuple - Http::parseRequest(std::unique_ptr &socket, int timeoutSecs) -{ - HttpRequestPtr httpRequest; - - std::atomic requestInitCancellation(false); - - auto isCancellationRequested = - makeCancellationRequestWithTimeout(timeoutSecs, - requestInitCancellation); - - // Read first line - auto lineResult = socket->readLine(isCancellationRequested); - auto lineValid = lineResult.first; - auto line = lineResult.second; - - if (!lineValid) { - return std::make_tuple(false, - "Error reading HTTP request line", - httpRequest); - } - - // Parse request line (GET /foo HTTP/1.1\r\n) - auto requestLine = Http::parseRequestLine(line); - auto method = std::get<0>(requestLine); - auto uri = std::get<1>(requestLine); - auto httpVersion = std::get<2>(requestLine); - - // Retrieve and validate HTTP headers - auto result = parseHttpHeaders(socket, isCancellationRequested); - auto headersValid = result.first; - auto headers = result.second; - - if (!headersValid) { - return std::make_tuple(false, - "Error parsing HTTP headers", - httpRequest); - } - - std::string body; - if (headers.find("Content-Length") != headers.end()) { - int contentLength = 0; - try { - contentLength = std::stoi(headers["Content-Length"]); - } catch (const std::exception &) { - return std::make_tuple(false, - "Error parsing HTTP Header 'Content-Length'", - httpRequest); - } - - if (contentLength < 0) { - return std::make_tuple( - false, - "Error: 'Content-Length' should be a positive integer", - httpRequest); - } - - auto res = - socket->readBytes(contentLength, nullptr, isCancellationRequested); - if (!res.first) { - return std::make_tuple(false, - std::string("Error reading request: ") + - res.second, - httpRequest); - } - body = res.second; - } - - // If the content was compressed with gzip, decode it - if (headers["Content-Encoding"] == "gzip") { -#ifdef IXWEBSOCKET_USE_ZLIB - std::string decompressedPayload; - if (!gzipDecompress(body, decompressedPayload)) { - return std::make_tuple( - false, - std::string("Error during gzip decompression of the body"), - httpRequest); - } - body = decompressedPayload; -#else - std::string errorMsg( - "ixwebsocket was not compiled with gzip support on"); - return std::make_tuple(false, errorMsg, httpRequest); -#endif - } - - httpRequest = - std::make_shared(uri, method, httpVersion, body, headers); - return std::make_tuple(true, "", httpRequest); -} - -bool Http::sendResponse(HttpResponsePtr response, - std::unique_ptr &socket) -{ - // Write the response to the socket - std::stringstream ss; - ss << "HTTP/1.1 "; - ss << response->statusCode; - ss << " "; - ss << response->description; - ss << "\r\n"; - - if (!socket->writeBytes(ss.str(), nullptr)) { - return false; - } - - // Write headers - ss.str(""); - ss << "Content-Length: " << response->body.size() << "\r\n"; - for (auto &&it : response->headers) { - ss << it.first << ": " << it.second << "\r\n"; - } - ss << "\r\n"; - - if (!socket->writeBytes(ss.str(), nullptr)) { - return false; - } - - return response->body.empty() ? true - : socket->writeBytes(response->body, nullptr); -} -} // namespace ix diff --git a/ixwebsocket/IXHttpClient.cpp b/ixwebsocket/IXHttpClient.cpp deleted file mode 100644 index 7deb3d7..0000000 --- a/ixwebsocket/IXHttpClient.cpp +++ /dev/null @@ -1,724 +0,0 @@ -/* - * IXHttpClient.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXHttpClient.h" - -#include "IXGzipCodec.h" -#include "IXSocketFactory.h" -#include "IXUrlParser.h" -#include "IXUserAgent.h" -#include "IXWebSocketHttpHeaders.h" -#include -#include -#include -#include -#include -#include - -namespace ix -{ -// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods -const std::string HttpClient::kPost = "POST"; -const std::string HttpClient::kGet = "GET"; -const std::string HttpClient::kHead = "HEAD"; -const std::string HttpClient::kDelete = "DELETE"; -const std::string HttpClient::kPut = "PUT"; -const std::string HttpClient::kPatch = "PATCH"; - -HttpClient::HttpClient(bool async) - : _async(async), _stop(false), _forceBody(false) -{ - if (!_async) - return; - - _thread = std::thread(&HttpClient::run, this); -} - -HttpClient::~HttpClient() -{ - if (!_thread.joinable()) - return; - - _stop = true; - _condition.notify_one(); - _thread.join(); -} - -void HttpClient::setTLSOptions(const SocketTLSOptions &tlsOptions) -{ - _tlsOptions = tlsOptions; -} - -void HttpClient::setForceBody(bool value) { _forceBody = value; } - -HttpRequestArgsPtr HttpClient::createRequest(const std::string &url, - const std::string &verb) -{ - auto request = std::make_shared(); - request->url = url; - request->verb = verb; - return request; -} - -bool HttpClient::performRequest(HttpRequestArgsPtr args, - const OnResponseCallback &onResponseCallback) -{ - assert(_async && "HttpClient needs its async parameter set to true " - "in order to call performRequest"); - if (!_async) - return false; - - // Enqueue the task - { - // acquire lock - std::unique_lock lock(_queueMutex); - - // add the task - _queue.push(std::make_pair(args, onResponseCallback)); - } // release lock - - // wake up one thread - _condition.notify_one(); - - return true; -} - -void HttpClient::run() -{ - while (true) { - HttpRequestArgsPtr args; - OnResponseCallback onResponseCallback; - - { - std::unique_lock lock(_queueMutex); - - while (!_stop && _queue.empty()) { - _condition.wait(lock); - } - - if (_stop) - return; - - auto p = _queue.front(); - _queue.pop(); - - args = p.first; - onResponseCallback = p.second; - } - - if (_stop) - return; - - HttpResponsePtr response = - request(args->url, args->verb, args->body, args); - onResponseCallback(response); - - if (_stop) - return; - } -} - -HttpResponsePtr HttpClient::request(const std::string &url, - const std::string &verb, - const std::string &body, - HttpRequestArgsPtr args, - int redirects) -{ - // We only have one socket connection, so we cannot - // make multiple requests concurrently. - std::lock_guard lock(_mutex); - - uint64_t uploadSize = 0; - uint64_t downloadSize = 0; - int code = 0; - WebSocketHttpHeaders headers; - std::string payload; - std::string description; - - std::string protocol, host, path, query; - int port; - - if (!UrlParser::parse(url, protocol, host, path, query, port)) { - std::stringstream ss; - ss << "Cannot parse url: " << url; - return std::make_shared(code, - description, - HttpErrorCode::UrlMalformed, - headers, - payload, - ss.str(), - uploadSize, - downloadSize); - } - - bool tls = protocol == "https"; - std::string errorMsg; - _socket = createSocket(tls, -1, errorMsg, _tlsOptions); - - if (!_socket) { - return std::make_shared(code, - description, - HttpErrorCode::CannotCreateSocket, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - // Build request string - std::stringstream ss; - ss << verb << " " << path << " HTTP/1.1\r\n"; - ss << "Host: " << host << "\r\n"; - -#ifdef IXWEBSOCKET_USE_ZLIB - if (args->compress) { - ss << "Accept-Encoding: gzip" - << "\r\n"; - } -#endif - - // Append extra headers - for (auto &&it : args->extraHeaders) { - ss << it.first << ": " << it.second << "\r\n"; - } - - // Set a default Accept header if none is present - if (args->extraHeaders.find("Accept") == args->extraHeaders.end()) { - ss << "Accept: */*" - << "\r\n"; - } - - // Set a default User agent if none is present - if (args->extraHeaders.find("User-Agent") == args->extraHeaders.end()) { - ss << "User-Agent: " << userAgent() << "\r\n"; - } - - if (verb == kPost || verb == kPut || verb == kPatch || _forceBody) { - // Set request compression header -#ifdef IXWEBSOCKET_USE_ZLIB - if (args->compressRequest) { - ss << "Content-Encoding: gzip" - << "\r\n"; - } -#endif - - ss << "Content-Length: " << body.size() << "\r\n"; - - // Set default Content-Type if unspecified - if (args->extraHeaders.find("Content-Type") == - args->extraHeaders.end()) { - if (args->multipartBoundary.empty()) { - ss << "Content-Type: application/x-www-form-urlencoded" - << "\r\n"; - } else { - ss << "Content-Type: multipart/form-data; boundary=" - << args->multipartBoundary << "\r\n"; - } - } - ss << "\r\n"; - ss << body; - } else { - ss << "\r\n"; - } - - std::string req(ss.str()); - std::string errMsg; - - // Make a cancellation object dealing with connection timeout - auto isCancellationRequested = - makeCancellationRequestWithTimeout(args->connectTimeout, _stop); - - bool success = - _socket->connect(host, port, errMsg, isCancellationRequested); - if (!success) { - std::stringstream ss; - ss << "Cannot connect to url: " << url << " / error : " << errMsg; - return std::make_shared(code, - description, - HttpErrorCode::CannotConnect, - headers, - payload, - ss.str(), - uploadSize, - downloadSize); - } - - // Make a new cancellation object dealing with transfer timeout - isCancellationRequested = - makeCancellationRequestWithTimeout(args->transferTimeout, _stop); - - if (args->verbose) { - std::stringstream ss; - ss << "Sending " << verb << " request " - << "to " << host << ":" << port << std::endl - << "request size: " << req.size() << " bytes" << std::endl - << "=============" << std::endl - << req << "=============" << std::endl - << std::endl; - - log(ss.str(), args); - } - - if (!_socket->writeBytes(req, isCancellationRequested)) { - std::string errorMsg("Cannot send request"); - return std::make_shared(code, - description, - HttpErrorCode::SendError, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - uploadSize = req.size(); - - auto lineResult = _socket->readLine(isCancellationRequested); - auto lineValid = lineResult.first; - auto line = lineResult.second; - - if (!lineValid) { - std::string errorMsg("Cannot retrieve status line"); - return std::make_shared( - code, - description, - HttpErrorCode::CannotReadStatusLine, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - if (args->verbose) { - std::stringstream ss; - ss << "Status line " << line; - log(ss.str(), args); - } - - if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1) { - std::string errorMsg("Cannot parse response code from status line"); - return std::make_shared(code, - description, - HttpErrorCode::MissingStatus, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - auto result = parseHttpHeaders(_socket, isCancellationRequested); - auto headersValid = result.first; - headers = result.second; - - if (!headersValid) { - std::string errorMsg("Cannot parse http headers"); - return std::make_shared(code, - description, - HttpErrorCode::HeaderParsingError, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - // Redirect ? - if ((code >= 301 && code <= 308) && args->followRedirects) { - if (headers.find("Location") == headers.end()) { - std::string errorMsg("Missing location header for redirect"); - return std::make_shared( - code, - description, - HttpErrorCode::MissingLocation, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - if (redirects >= args->maxRedirects) { - std::stringstream ss; - ss << "Too many redirects: " << redirects; - return std::make_shared( - code, - description, - HttpErrorCode::TooManyRedirects, - headers, - payload, - ss.str(), - uploadSize, - downloadSize); - } - - // Recurse - std::string location = headers["Location"]; - return request(location, verb, body, args, redirects + 1); - } - - if (verb == "HEAD") { - return std::make_shared(code, - description, - HttpErrorCode::Ok, - headers, - payload, - std::string(), - uploadSize, - downloadSize); - } - - // Parse response: - if (headers.find("Content-Length") != headers.end()) { - ssize_t contentLength = -1; - ss.str(""); - ss << headers["Content-Length"]; - ss >> contentLength; - - payload.reserve(contentLength); - - auto chunkResult = _socket->readBytes(contentLength, - args->onProgressCallback, - isCancellationRequested); - if (!chunkResult.first) { - errorMsg = "Cannot read chunk"; - return std::make_shared(code, - description, - HttpErrorCode::ChunkReadError, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - payload += chunkResult.second; - } else if (headers.find("Transfer-Encoding") != headers.end() && - headers["Transfer-Encoding"] == "chunked") { - std::stringstream ss; - - while (true) { - lineResult = _socket->readLine(isCancellationRequested); - line = lineResult.second; - - if (!lineResult.first) { - return std::make_shared( - code, - description, - HttpErrorCode::ChunkReadError, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - uint64_t chunkSize; - ss.str(""); - ss << std::hex << line; - ss >> chunkSize; - - if (args->verbose) { - std::stringstream oss; - oss << "Reading " << chunkSize << " bytes" << std::endl; - log(oss.str(), args); - } - - payload.reserve(payload.size() + (size_t)chunkSize); - - // Read a chunk - auto chunkResult = _socket->readBytes((size_t)chunkSize, - args->onProgressCallback, - isCancellationRequested); - if (!chunkResult.first) { - errorMsg = "Cannot read chunk"; - return std::make_shared( - code, - description, - HttpErrorCode::ChunkReadError, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - payload += chunkResult.second; - - // Read the line that terminates the chunk (\r\n) - lineResult = _socket->readLine(isCancellationRequested); - - if (!lineResult.first) { - return std::make_shared( - code, - description, - HttpErrorCode::ChunkReadError, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - if (chunkSize == 0) - break; - } - } else if (code == 204) { - ; // 204 is NoContent response code - } else { - std::string errorMsg("Cannot read http body"); - return std::make_shared(code, - description, - HttpErrorCode::CannotReadBody, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - - downloadSize = payload.size(); - - // If the content was compressed with gzip, decode it - if (headers["Content-Encoding"] == "gzip") { -#ifdef IXWEBSOCKET_USE_ZLIB - std::string decompressedPayload; - if (!gzipDecompress(payload, decompressedPayload)) { - std::string errorMsg("Error decompressing payload"); - return std::make_shared(code, - description, - HttpErrorCode::Gzip, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); - } - payload = decompressedPayload; -#else - std::string errorMsg( - "ixwebsocket was not compiled with gzip support on"); - return std::make_shared(code, - description, - HttpErrorCode::Gzip, - headers, - payload, - errorMsg, - uploadSize, - downloadSize); -#endif - } - - return std::make_shared(code, - description, - HttpErrorCode::Ok, - headers, - payload, - std::string(), - uploadSize, - downloadSize); -} - -HttpResponsePtr HttpClient::get(const std::string &url, HttpRequestArgsPtr args) -{ - return request(url, kGet, std::string(), args); -} - -HttpResponsePtr HttpClient::head(const std::string &url, - HttpRequestArgsPtr args) -{ - return request(url, kHead, std::string(), args); -} - -HttpResponsePtr HttpClient::Delete(const std::string &url, - HttpRequestArgsPtr args) -{ - return request(url, kDelete, std::string(), args); -} - -HttpResponsePtr - HttpClient::request(const std::string &url, - const std::string &verb, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args) -{ - std::string body; - - if (httpFormDataParameters.empty()) { - body = serializeHttpParameters(httpParameters); - } else { - std::string multipartBoundary = generateMultipartBoundary(); - args->multipartBoundary = multipartBoundary; - body = serializeHttpFormDataParameters(multipartBoundary, - httpFormDataParameters, - httpParameters); - } - -#ifdef IXWEBSOCKET_USE_ZLIB - if (args->compressRequest) { - body = gzipCompress(body); - } -#endif - - return request(url, verb, body, args); -} - -HttpResponsePtr - HttpClient::post(const std::string &url, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args) -{ - return request(url, kPost, httpParameters, httpFormDataParameters, args); -} - -HttpResponsePtr HttpClient::post(const std::string &url, - const std::string &body, - HttpRequestArgsPtr args) -{ - return request(url, kPost, body, args); -} - -HttpResponsePtr - HttpClient::put(const std::string &url, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args) -{ - return request(url, kPut, httpParameters, httpFormDataParameters, args); -} - -HttpResponsePtr HttpClient::put(const std::string &url, - const std::string &body, - const HttpRequestArgsPtr args) -{ - return request(url, kPut, body, args); -} - -HttpResponsePtr - HttpClient::patch(const std::string &url, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args) -{ - return request(url, kPatch, httpParameters, httpFormDataParameters, args); -} - -HttpResponsePtr HttpClient::patch(const std::string &url, - const std::string &body, - const HttpRequestArgsPtr args) -{ - return request(url, kPatch, body, args); -} - -std::string HttpClient::urlEncode(const std::string &value) -{ - std::ostringstream escaped; - escaped.fill('0'); - escaped << std::hex; - - for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; - ++i) { - std::string::value_type c = (*i); - - // Keep alphanumeric and other accepted characters intact - if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { - escaped << c; - continue; - } - - // Any other characters are percent-encoded - escaped << std::uppercase; - escaped << '%' << std::setw(2) << int((unsigned char)c); - escaped << std::nouppercase; - } - - return escaped.str(); -} - -std::string - HttpClient::serializeHttpParameters(const HttpParameters &httpParameters) -{ - std::stringstream ss; - size_t count = httpParameters.size(); - size_t i = 0; - - for (auto &&it : httpParameters) { - ss << urlEncode(it.first) << "=" << urlEncode(it.second); - - if (i++ < (count - 1)) { - ss << "&"; - } - } - return ss.str(); -} - -std::string HttpClient::serializeHttpFormDataParameters( - const std::string &multipartBoundary, - const HttpFormDataParameters &httpFormDataParameters, - const HttpParameters &httpParameters) -{ - // - // --AaB03x - // Content-Disposition: form-data; name="submit-name" - - // Larry - // --AaB03x - // Content-Disposition: form-data; name="foo.txt"; filename="file1.txt" - // Content-Type: text/plain - - // ... contents of file1.txt ... - // --AaB03x-- - // - std::stringstream ss; - - for (auto &&it : httpFormDataParameters) { - ss << "--" << multipartBoundary << "\r\n" - << "Content-Disposition:" - << " form-data; name=\"" << it.first << "\";" - << " filename=\"" << it.first << "\"" - << "\r\n" - << "Content-Type: application/octet-stream" - << "\r\n" - << "\r\n" - << it.second << "\r\n"; - } - - for (auto &&it : httpParameters) { - ss << "--" << multipartBoundary << "\r\n" - << "Content-Disposition:" - << " form-data; name=\"" << it.first << "\";" - << "\r\n" - << "\r\n" - << it.second << "\r\n"; - } - - ss << "--" << multipartBoundary << "--\r\n"; - - return ss.str(); -} - -void HttpClient::log(const std::string &msg, HttpRequestArgsPtr args) -{ - if (args->logger) { - args->logger(msg); - } -} - -std::string HttpClient::generateMultipartBoundary() -{ - std::string str( - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); - - static std::random_device rd; - static std::mt19937 generator(rd()); - - std::shuffle(str.begin(), str.end(), generator); - - return str; -} -} // namespace ix diff --git a/ixwebsocket/IXHttpServer.cpp b/ixwebsocket/IXHttpServer.cpp deleted file mode 100644 index 743394c..0000000 --- a/ixwebsocket/IXHttpServer.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * IXHttpServer.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXHttpServer.h" - -#include "IXGzipCodec.h" -#include "IXNetSystem.h" -#include "IXSocketConnect.h" -#include "IXUserAgent.h" -#include -#include -#include -#include - -namespace -{ -std::pair> load(const std::string &path) -{ - std::vector memblock; - - std::ifstream file(path); - if (!file.is_open()) - return std::make_pair(false, memblock); - - file.seekg(0, file.end); - std::streamoff size = file.tellg(); - file.seekg(0, file.beg); - - memblock.resize((size_t)size); - file.read((char *)&memblock.front(), static_cast(size)); - - return std::make_pair(true, memblock); -} - -std::pair readAsString(const std::string &path) -{ - auto res = load(path); - auto vec = res.second; - return std::make_pair(res.first, std::string(vec.begin(), vec.end())); -} -} // namespace - -namespace ix -{ -const int HttpServer::kDefaultTimeoutSecs(30); - -HttpServer::HttpServer(int port, - const std::string &host, - int backlog, - size_t maxConnections, - int addressFamily, - int timeoutSecs) - : SocketServer(port, host, backlog, maxConnections, addressFamily), - _connectedClientsCount(0), _timeoutSecs(timeoutSecs) -{ - setDefaultConnectionCallback(); -} - -HttpServer::~HttpServer() { stop(); } - -void HttpServer::stop() -{ - stopAcceptingConnections(); - - // FIXME: cancelling / closing active clients ... - - SocketServer::stop(); -} - -void HttpServer::setOnConnectionCallback(const OnConnectionCallback &callback) -{ - _onConnectionCallback = callback; -} - -void HttpServer::handleConnection( - std::unique_ptr socket, - std::shared_ptr connectionState) -{ - _connectedClientsCount++; - - auto ret = Http::parseRequest(socket, _timeoutSecs); - // FIXME: handle errors in parseRequest - - if (std::get<0>(ret)) { - auto response = - _onConnectionCallback(std::get<2>(ret), connectionState); - if (!Http::sendResponse(response, socket)) { - logError("Cannot send response"); - } - } - connectionState->setTerminated(); - - _connectedClientsCount--; -} - -size_t HttpServer::getConnectedClientsCount() { return _connectedClientsCount; } - -void HttpServer::setDefaultConnectionCallback() -{ - setOnConnectionCallback( - [this](HttpRequestPtr request, - std::shared_ptr connectionState) - -> HttpResponsePtr { - std::string uri(request->uri); - if (uri.empty() || uri == "/") { - uri = "/index.html"; - } - - WebSocketHttpHeaders headers; - headers["Server"] = userAgent(); - - std::string path("." + uri); - auto res = readAsString(path); - bool found = res.first; - if (!found) { - return std::make_shared(404, - "Not Found", - HttpErrorCode::Ok, - WebSocketHttpHeaders(), - std::string()); - } - - std::string content = res.second; - -#ifdef IXWEBSOCKET_USE_ZLIB - std::string acceptEncoding = request->headers["Accept-encoding"]; - if (acceptEncoding == "*" || - acceptEncoding.find("gzip") != std::string::npos) { - content = gzipCompress(content); - headers["Content-Encoding"] = "gzip"; - } -#endif - - // Log request - std::stringstream ss; - ss << connectionState->getRemoteIp() << ":" - << connectionState->getRemotePort() << " " << request->method - << " " << request->headers["User-Agent"] << " " << request->uri - << " " << content.size(); - logInfo(ss.str()); - - // FIXME: check extensions to set the content type - // headers["Content-Type"] = "application/octet-stream"; - headers["Accept-Ranges"] = "none"; - - for (auto &&it : request->headers) { - headers[it.first] = it.second; - } - - return std::make_shared(200, - "OK", - HttpErrorCode::Ok, - headers, - content); - }); -} - -void HttpServer::makeRedirectServer(const std::string &redirectUrl) -{ - // - // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections - // - setOnConnectionCallback( - [this, redirectUrl](HttpRequestPtr request, - std::shared_ptr connectionState) - -> HttpResponsePtr { - WebSocketHttpHeaders headers; - headers["Server"] = userAgent(); - - // Log request - std::stringstream ss; - ss << connectionState->getRemoteIp() << ":" - << connectionState->getRemotePort() << " " << request->method - << " " << request->headers["User-Agent"] << " " << request->uri; - logInfo(ss.str()); - - if (request->method == "POST") { - return std::make_shared(200, - "OK", - HttpErrorCode::Ok, - headers, - std::string()); - } - - headers["Location"] = redirectUrl; - - return std::make_shared(301, - "OK", - HttpErrorCode::Ok, - headers, - std::string()); - }); -} - -// -// Display the client parameter and body on the console -// -void HttpServer::makeDebugServer() -{ - setOnConnectionCallback( - [this](HttpRequestPtr request, - std::shared_ptr connectionState) - -> HttpResponsePtr { - WebSocketHttpHeaders headers; - headers["Server"] = userAgent(); - - // Log request - std::stringstream ss; - ss << connectionState->getRemoteIp() << ":" - << connectionState->getRemotePort() << " " << request->method - << " " << request->headers["User-Agent"] << " " << request->uri; - logInfo(ss.str()); - - logInfo("== Headers == "); - for (auto &&it : request->headers) { - std::ostringstream oss; - oss << it.first << ": " << it.second; - logInfo(oss.str()); - } - logInfo(""); - - logInfo("== Body == "); - logInfo(request->body); - logInfo(""); - - return std::make_shared(200, - "OK", - HttpErrorCode::Ok, - headers, - std::string("OK")); - }); -} - -int HttpServer::getTimeoutSecs() { return _timeoutSecs; } - -} // namespace ix diff --git a/ixwebsocket/IXNetSystem.cpp b/ixwebsocket/IXNetSystem.cpp deleted file mode 100644 index ea5b105..0000000 --- a/ixwebsocket/IXNetSystem.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - * IXNetSystem.cpp - * Author: Korchynskyi Dmytro - * Copyright (c) 2019 Machine Zone. All rights reserved. - */ - -#include "IXNetSystem.h" -#include -#include - -namespace ix -{ -bool initNetSystem() -{ -#ifdef _WIN32 - WORD wVersionRequested; - WSADATA wsaData; - int err; - - // Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h - wVersionRequested = MAKEWORD(2, 2); - err = WSAStartup(wVersionRequested, &wsaData); - - return err == 0; -#else - return true; -#endif -} - -bool uninitNetSystem() -{ -#ifdef _WIN32 - int err = WSACleanup(); - return err == 0; -#else - return true; -#endif -} - -// -// That function could 'return WSAPoll(pfd, nfds, timeout);' -// but WSAPoll is said to have weird behaviors on the internet -// (the curl folks have had problems with it). -// -// So we make it a select wrapper -// -int poll(struct pollfd *fds, nfds_t nfds, int timeout) -{ -#ifdef _WIN32 - socket_t maxfd = 0; - fd_set readfds, writefds, errorfds; - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&errorfds); - - for (nfds_t i = 0; i < nfds; ++i) { - struct pollfd *fd = &fds[i]; - - if (fd->fd > maxfd) { - maxfd = fd->fd; - } - if ((fd->events & POLLIN)) { - FD_SET(fd->fd, &readfds); - } - if ((fd->events & POLLOUT)) { - FD_SET(fd->fd, &writefds); - } - if ((fd->events & POLLERR)) { - FD_SET(fd->fd, &errorfds); - } - } - - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - int ret = select(maxfd + 1, - &readfds, - &writefds, - &errorfds, - timeout != -1 ? &tv : NULL); - - if (ret < 0) { - return ret; - } - - for (nfds_t i = 0; i < nfds; ++i) { - struct pollfd *fd = &fds[i]; - fd->revents = 0; - - if (FD_ISSET(fd->fd, &readfds)) { - fd->revents |= POLLIN; - } - if (FD_ISSET(fd->fd, &writefds)) { - fd->revents |= POLLOUT; - } - if (FD_ISSET(fd->fd, &errorfds)) { - fd->revents |= POLLERR; - } - } - - return ret; -#else - // - // It was reported that on Android poll can fail and return -1 with - // errno == EINTR, which should be a temp error and should typically - // be handled by retrying in a loop. - // Maybe we need to put all syscall / C functions in - // a new IXSysCalls.cpp and wrap them all. - // - // The style from libuv is as such. - // - int ret = -1; - do { - ret = ::poll(fds, nfds, timeout); - } while (ret == -1 && errno == EINTR); - - return ret; -#endif -} - -// -// mingw does not have inet_ntop, which were taken as is from the musl C -// library. -// -const char *inet_ntop(int af, const void *a0, char *s, socklen_t l) -{ -#if defined(_WIN32) && defined(__GNUC__) - const unsigned char *a = (const unsigned char *)a0; - int i, j, max, best; - char buf[100]; - - switch (af) { - case AF_INET: - if (snprintf(s, l, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]) < l) - return s; - break; - case AF_INET6: - if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\377\377", 12)) - snprintf(buf, - sizeof buf, - "%x:%x:%x:%x:%x:%x:%x:%x", - 256 * a[0] + a[1], - 256 * a[2] + a[3], - 256 * a[4] + a[5], - 256 * a[6] + a[7], - 256 * a[8] + a[9], - 256 * a[10] + a[11], - 256 * a[12] + a[13], - 256 * a[14] + a[15]); - else - snprintf(buf, - sizeof buf, - "%x:%x:%x:%x:%x:%x:%d.%d.%d.%d", - 256 * a[0] + a[1], - 256 * a[2] + a[3], - 256 * a[4] + a[5], - 256 * a[6] + a[7], - 256 * a[8] + a[9], - 256 * a[10] + a[11], - a[12], - a[13], - a[14], - a[15]); - /* Replace longest /(^0|:)[:0]{2,}/ with "::" */ - for (i = best = 0, max = 2; buf[i]; i++) { - if (i && buf[i] != ':') - continue; - j = strspn(buf + i, ":0"); - if (j > max) - best = i, max = j; - } - if (max > 3) { - buf[best] = buf[best + 1] = ':'; - memmove(buf + best + 2, buf + best + max, i - best - max + 1); - } - if (strlen(buf) < l) { - strcpy(s, buf); - return s; - } - break; - default: - errno = EAFNOSUPPORT; - return 0; - } - errno = ENOSPC; - return 0; -#else - return ::inet_ntop(af, a0, s, l); -#endif -} - -#if defined(_WIN32) && defined(__GNUC__) -static int hexval(unsigned c) -{ - if (c - '0' < 10) - return c - '0'; - c |= 32; - if (c - 'a' < 6) - return c - 'a' + 10; - return -1; -} -#endif - -// -// mingw does not have inet_pton, which were taken as is from the musl C -// library. -// -int inet_pton(int af, const char *s, void *a0) -{ -#if defined(_WIN32) && defined(__GNUC__) - uint16_t ip[8]; - unsigned char *a = (unsigned char *)a0; - int i, j, v, d, brk = -1, need_v4 = 0; - - if (af == AF_INET) { - for (i = 0; i < 4; i++) { - for (v = j = 0; j < 3 && isdigit(s[j]); j++) - v = 10 * v + s[j] - '0'; - if (j == 0 || (j > 1 && s[0] == '0') || v > 255) - return 0; - a[i] = v; - if (s[j] == 0 && i == 3) - return 1; - if (s[j] != '.') - return 0; - s += j + 1; - } - return 0; - } else if (af != AF_INET6) { - errno = EAFNOSUPPORT; - return -1; - } - - if (*s == ':' && *++s != ':') - return 0; - - for (i = 0;; i++) { - if (s[0] == ':' && brk < 0) { - brk = i; - ip[i & 7] = 0; - if (!*++s) - break; - if (i == 7) - return 0; - continue; - } - for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++) - v = 16 * v + d; - if (j == 0) - return 0; - ip[i & 7] = v; - if (!s[j] && (brk >= 0 || i == 7)) - break; - if (i == 7) - return 0; - if (s[j] != ':') { - if (s[j] != '.' || (i < 6 && brk < 0)) - return 0; - need_v4 = 1; - i++; - break; - } - s += j + 1; - } - if (brk >= 0) { - memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk)); - for (j = 0; j < 7 - i; j++) - ip[brk + j] = 0; - } - for (j = 0; j < 8; j++) { - *a++ = ip[j] >> 8; - *a++ = ip[j]; - } - if (need_v4 && inet_pton(AF_INET, (const char *)s, a - 4) <= 0) - return 0; - return 1; -#else - return ::inet_pton(af, s, a0); -#endif -} - -// Convert network bytes to host bytes. Copied from the ASIO library -unsigned short network_to_host_short(unsigned short value) -{ -#if defined(_WIN32) - unsigned char *value_p = reinterpret_cast(&value); - unsigned short result = (static_cast(value_p[0]) << 8) | - static_cast(value_p[1]); - return result; -#else // defined(_WIN32) - return ntohs(value); -#endif // defined(_WIN32) -} - -} // namespace ix diff --git a/ixwebsocket/IXSelectInterrupt.cpp b/ixwebsocket/IXSelectInterrupt.cpp deleted file mode 100644 index f29a837..0000000 --- a/ixwebsocket/IXSelectInterrupt.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * IXSelectInterrupt.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXSelectInterrupt.h" - -namespace ix -{ -const uint64_t SelectInterrupt::kSendRequest = 1; -const uint64_t SelectInterrupt::kCloseRequest = 2; - -SelectInterrupt::SelectInterrupt() { ; } - -SelectInterrupt::~SelectInterrupt() { ; } - -bool SelectInterrupt::init(std::string & /*errorMsg*/) { return true; } - -bool SelectInterrupt::notify(uint64_t /*value*/) { return true; } - -uint64_t SelectInterrupt::read() { return 0; } - -bool SelectInterrupt::clear() { return true; } - -int SelectInterrupt::getFd() const { return -1; } -} // namespace ix diff --git a/ixwebsocket/IXSelectInterruptFactory.cpp b/ixwebsocket/IXSelectInterruptFactory.cpp deleted file mode 100644 index 9ab6347..0000000 --- a/ixwebsocket/IXSelectInterruptFactory.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * IXSelectInterruptFactory.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXSelectInterruptFactory.h" - -#include "IXUniquePtr.h" -#if defined(__linux__) || defined(__APPLE__) -#include "IXSelectInterruptPipe.h" -#else -#include "IXSelectInterrupt.h" -#endif - -namespace ix -{ -SelectInterruptPtr createSelectInterrupt() -{ -#if defined(__linux__) || defined(__APPLE__) - return ix::make_unique(); -#else - return ix::make_unique(); -#endif -} -} // namespace ix diff --git a/ixwebsocket/IXSelectInterruptPipe.cpp b/ixwebsocket/IXSelectInterruptPipe.cpp deleted file mode 100644 index def317f..0000000 --- a/ixwebsocket/IXSelectInterruptPipe.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * IXSelectInterruptPipe.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved. - */ - -// -// On UNIX we use pipes to wake up select. There is no way to do that -// on Windows so this file is compiled out on Windows. -// -#ifndef _WIN32 - -#include "IXSelectInterruptPipe.h" - -#include -#include -#include -#include -#include // for strerror -#include // for write - -namespace ix -{ -// File descriptor at index 0 in _fildes is the read end of the pipe -// File descriptor at index 1 in _fildes is the write end of the pipe -const int SelectInterruptPipe::kPipeReadIndex = 0; -const int SelectInterruptPipe::kPipeWriteIndex = 1; - -SelectInterruptPipe::SelectInterruptPipe() -{ - _fildes[kPipeReadIndex] = -1; - _fildes[kPipeWriteIndex] = -1; -} - -SelectInterruptPipe::~SelectInterruptPipe() -{ - ::close(_fildes[kPipeReadIndex]); - ::close(_fildes[kPipeWriteIndex]); - _fildes[kPipeReadIndex] = -1; - _fildes[kPipeWriteIndex] = -1; -} - -bool SelectInterruptPipe::init(std::string &errorMsg) -{ - std::lock_guard lock(_fildesMutex); - - // calling init twice is a programming error - assert(_fildes[kPipeReadIndex] == -1); - assert(_fildes[kPipeWriteIndex] == -1); - - if (pipe(_fildes) < 0) { - std::stringstream ss; - ss << "SelectInterruptPipe::init() failed in pipe() call" - << " : " << strerror(errno); - errorMsg = ss.str(); - return false; - } - - if (fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK) == -1) { - std::stringstream ss; - ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) " - "call" - << " : " << strerror(errno); - errorMsg = ss.str(); - - _fildes[kPipeReadIndex] = -1; - _fildes[kPipeWriteIndex] = -1; - return false; - } - - if (fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK) == -1) { - std::stringstream ss; - ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) " - "call" - << " : " << strerror(errno); - errorMsg = ss.str(); - - _fildes[kPipeReadIndex] = -1; - _fildes[kPipeWriteIndex] = -1; - return false; - } - -#ifdef F_SETNOSIGPIPE - if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1) { - std::stringstream ss; - ss << "SelectInterruptPipe::init() failed in fcntl(.... " - "F_SETNOSIGPIPE) call" - << " : " << strerror(errno); - errorMsg = ss.str(); - - _fildes[kPipeReadIndex] = -1; - _fildes[kPipeWriteIndex] = -1; - return false; - } - - if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1) { - std::stringstream ss; - ss << "SelectInterruptPipe::init() failed in fcntl(..., " - "F_SETNOSIGPIPE) call" - << " : " << strerror(errno); - errorMsg = ss.str(); - - _fildes[kPipeReadIndex] = -1; - _fildes[kPipeWriteIndex] = -1; - return false; - } -#endif - - return true; -} - -bool SelectInterruptPipe::notify(uint64_t value) -{ - std::lock_guard lock(_fildesMutex); - - int fd = _fildes[kPipeWriteIndex]; - if (fd == -1) - return false; - - ssize_t ret = -1; - do { - ret = ::write(fd, &value, sizeof(value)); - } while (ret == -1 && errno == EINTR); - - // we should write 8 bytes for an uint64_t - return ret == 8; -} - -// TODO: return max uint64_t for errors ? -uint64_t SelectInterruptPipe::read() -{ - std::lock_guard lock(_fildesMutex); - - int fd = _fildes[kPipeReadIndex]; - - uint64_t value = 0; - - ssize_t ret = -1; - do { - ret = ::read(fd, &value, sizeof(value)); - } while (ret == -1 && errno == EINTR); - - return value; -} - -bool SelectInterruptPipe::clear() { return true; } - -int SelectInterruptPipe::getFd() const -{ - std::lock_guard lock(_fildesMutex); - - return _fildes[kPipeReadIndex]; -} -} // namespace ix - -#endif // !_WIN32 diff --git a/ixwebsocket/IXSetThreadName.cpp b/ixwebsocket/IXSetThreadName.cpp deleted file mode 100644 index f4f83f8..0000000 --- a/ixwebsocket/IXSetThreadName.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * IXSetThreadName.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 2020 Machine Zone, Inc. All rights reserved. - */ -#include "IXSetThreadName.h" - -// unix systems -#if defined(__APPLE__) || defined(__linux__) || defined(BSD) -#include -#endif - -// freebsd needs this header as well -#if defined(BSD) -#include -#endif - -// Windows -#ifdef _WIN32 -#include -#endif - -namespace ix -{ -#ifdef _WIN32 -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) -{ -#ifndef __GNUC__ - 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) { - } -#endif -} -#endif - -void setThreadName(const std::string &name) -{ -#if defined(__APPLE__) - // - // Apple reserves 16 bytes for its thread names - // Notice that the Apple version of pthread_setname_np - // does not take a pthread_t argument - // - pthread_setname_np(name.substr(0, 63).c_str()); -#elif defined(__linux__) - // - // Linux only reserves 16 bytes for its thread names - // See prctl and PR_SET_NAME property in - // http://man7.org/linux/man-pages/man2/prctl.2.html - // - pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); -#elif defined(_WIN32) - SetThreadName(-1, name.c_str()); -#elif defined(BSD) - pthread_set_name_np(pthread_self(), name.substr(0, 15).c_str()); -#else - // ... assert here ? -#endif -} -} // namespace ix diff --git a/ixwebsocket/IXSocket.cpp b/ixwebsocket/IXSocket.cpp deleted file mode 100644 index 1f00d96..0000000 --- a/ixwebsocket/IXSocket.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* - * IXSocket.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXSocket.h" - -#include "IXNetSystem.h" -#include "IXSelectInterrupt.h" -#include "IXSelectInterruptFactory.h" -#include "IXSocketConnect.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef min -#undef min -#endif - -namespace ix -{ -const int Socket::kDefaultPollNoTimeout = -1; // No poll timeout by default -const int Socket::kDefaultPollTimeout = kDefaultPollNoTimeout; - -Socket::Socket(int fd) : _sockfd(fd), _selectInterrupt(createSelectInterrupt()) -{ - ; -} - -Socket::~Socket() { close(); } - -PollResultType Socket::poll(bool readyToRead, - int timeoutMs, - int sockfd, - const SelectInterruptPtr &selectInterrupt) -{ - // - // We used to use ::select to poll but on Android 9 we get large fds out of - // ::connect which crash in FD_SET as they are larger than FD_SETSIZE. - // Switching to ::poll does fix that. - // - // However poll isn't as portable as select and has bugs on Windows, so we - // have a shim to fallback to select on those platforms. See - // https://github.com/mpv-player/mpv/pull/5203/files for such a select - // wrapper. - // - nfds_t nfds = 1; - struct pollfd fds[2]; - memset(fds, 0, sizeof(fds)); - - fds[0].fd = sockfd; - fds[0].events = (readyToRead) ? POLLIN : POLLOUT; - - // this is ignored by poll, but our select based poll wrapper on Windows - // needs it - fds[0].events |= POLLERR; - - // File descriptor used to interrupt select when needed - int interruptFd = -1; - if (selectInterrupt) { - interruptFd = selectInterrupt->getFd(); - - if (interruptFd != -1) { - nfds = 2; - fds[1].fd = interruptFd; - fds[1].events = POLLIN; - } - } - - int ret = ix::poll(fds, nfds, timeoutMs); - - PollResultType pollResult = PollResultType::ReadyForRead; - if (ret < 0) { - pollResult = PollResultType::Error; - } else if (ret == 0) { - pollResult = PollResultType::Timeout; - } else if (interruptFd != -1 && fds[1].revents & POLLIN) { - uint64_t value = selectInterrupt->read(); - - if (value == SelectInterrupt::kSendRequest) { - pollResult = PollResultType::SendRequest; - } else if (value == SelectInterrupt::kCloseRequest) { - pollResult = PollResultType::CloseRequest; - } - } else if (sockfd != -1 && readyToRead && fds[0].revents & POLLIN) { - pollResult = PollResultType::ReadyForRead; - } else if (sockfd != -1 && !readyToRead && fds[0].revents & POLLOUT) { - pollResult = PollResultType::ReadyForWrite; - -#ifdef _WIN32 - // On connect error, in async mode, windows will write to the exceptions - // fds - if (fds[0].revents & POLLERR) { - pollResult = PollResultType::Error; - } -#else - int optval = -1; - socklen_t optlen = sizeof(optval); - - // getsockopt() puts the errno value for connect into optval so 0 - // means no-error. - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1 || - optval != 0) { - pollResult = PollResultType::Error; - - // set errno to optval so that external callers can have an - // appropriate error description when calling strerror - errno = optval; - } -#endif - } else if (sockfd != -1 && - (fds[0].revents & POLLERR || fds[0].revents & POLLHUP || - fds[0].revents & POLLNVAL)) { - pollResult = PollResultType::Error; - } - - return pollResult; -} - -PollResultType Socket::isReadyToRead(int timeoutMs) -{ - if (_sockfd == -1) { - return PollResultType::Error; - } - - bool readyToRead = true; - return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt); -} - -PollResultType Socket::isReadyToWrite(int timeoutMs) -{ - if (_sockfd == -1) { - return PollResultType::Error; - } - - bool readyToRead = false; - return poll(readyToRead, timeoutMs, _sockfd, _selectInterrupt); -} - -// Wake up from poll/select by writing to the pipe which is watched by select -bool Socket::wakeUpFromPoll(uint64_t wakeUpCode) -{ - return _selectInterrupt->notify(wakeUpCode); -} - -bool Socket::accept(std::string &errMsg) -{ - if (_sockfd == -1) { - errMsg = "Socket is uninitialized"; - return false; - } - return true; -} - -bool Socket::connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - std::lock_guard lock(_socketMutex); - - if (!_selectInterrupt->clear()) - return false; - - _sockfd = - SocketConnect::connect(host, port, errMsg, isCancellationRequested); - return _sockfd != -1; -} - -void Socket::close() -{ - std::lock_guard lock(_socketMutex); - - if (_sockfd == -1) - return; - - closeSocket(_sockfd); - _sockfd = -1; -} - -ssize_t Socket::send(char *buffer, size_t length) -{ - int flags = 0; -#ifdef MSG_NOSIGNAL - flags = MSG_NOSIGNAL; -#endif - - return ::send(_sockfd, buffer, length, flags); -} - -ssize_t Socket::send(const std::string &buffer) -{ - return send((char *)&buffer[0], buffer.size()); -} - -ssize_t Socket::recv(void *buffer, size_t length) -{ - int flags = 0; -#ifdef MSG_NOSIGNAL - flags = MSG_NOSIGNAL; -#endif - - return ::recv(_sockfd, (char *)buffer, length, flags); -} - -int Socket::getErrno() -{ - int err; - -#ifdef _WIN32 - err = WSAGetLastError(); -#else - 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) -{ -#ifdef _WIN32 - closesocket(fd); -#else - ::close(fd); -#endif -} - -bool Socket::init(std::string &errorMsg) -{ - return _selectInterrupt->init(errorMsg); -} - -bool Socket::writeBytes(const std::string &str, - const CancellationRequest &isCancellationRequested) -{ - int offset = 0; - int len = (int)str.size(); - - while (true) { - if (isCancellationRequested && isCancellationRequested()) - return false; - - ssize_t ret = send((char *)&str[offset], len); - - // We wrote some bytes, as needed, all good. - if (ret > 0) { - if (ret == len) { - return true; - } else { - offset += ret; - len -= ret; - continue; - } - } - // There is possibly something to be writen, try again - else if (ret < 0 && Socket::isWaitNeeded()) { - continue; - } - // There was an error during the write, abort - else { - return false; - } - } -} - -bool Socket::readByte(void *buffer, - const CancellationRequest &isCancellationRequested) -{ - while (true) { - if (isCancellationRequested && isCancellationRequested()) - return false; - - ssize_t ret; - ret = recv(buffer, 1); - - // We read one byte, as needed, all good. - if (ret == 1) { - return true; - } - // There is possibly something to be read, try again - 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 - if (isReadyToRead(1) == PollResultType::Error) { - return false; - } - } - // There was an error during the read, abort - else { - return false; - } - } -} - -std::pair - Socket::readLine(const CancellationRequest &isCancellationRequested) -{ - char c; - std::string line; - line.reserve(64); - - for (int i = 0; i < 2 || (line[i - 2] != '\r' && line[i - 1] != '\n'); - ++i) { - if (!readByte(&c, isCancellationRequested)) { - // Return what we were able to read - return std::make_pair(false, line); - } - - line += c; - } - - return std::make_pair(true, line); -} - -std::pair - Socket::readBytes(size_t length, - const OnProgressCallback &onProgressCallback, - const CancellationRequest &isCancellationRequested) -{ - std::array readBuffer; - - std::vector output; - while (output.size() != length) { - if (isCancellationRequested && isCancellationRequested()) { - const std::string errorMsg("Cancellation Requested"); - return std::make_pair(false, errorMsg); - } - - size_t size = std::min(readBuffer.size(), length - output.size()); - ssize_t ret = recv((char *)&readBuffer[0], size); - - if (ret > 0) { - output.insert(output.end(), - readBuffer.begin(), - readBuffer.begin() + ret); - } else if (ret <= 0 && !Socket::isWaitNeeded()) { - const std::string errorMsg("Recv Error"); - return std::make_pair(false, errorMsg); - } - - if (onProgressCallback) - onProgressCallback((int)output.size(), (int)length); - - // Wait with a 1ms timeout until the socket is ready to read. - // This way we are not busy looping - if (isReadyToRead(1) == PollResultType::Error) { - const std::string errorMsg("Poll Error"); - return std::make_pair(false, errorMsg); - } - } - - return std::make_pair(true, std::string(output.begin(), output.end())); -} -} // namespace ix diff --git a/ixwebsocket/IXSocketAppleSSL.cpp b/ixwebsocket/IXSocketAppleSSL.cpp deleted file mode 100644 index 81e14a0..0000000 --- a/ixwebsocket/IXSocketAppleSSL.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * IXSocketAppleSSL.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. - * - * Adapted from Satori SDK Apple SSL code. - */ -#ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT - -#include "IXSocketAppleSSL.h" - -#include "IXSocketConnect.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define socketerrno errno - -#include - -namespace ix -{ -SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions &tlsOptions, int fd) - : Socket(fd), _sslContext(nullptr), _tlsOptions(tlsOptions) -{ - ; -} - -SocketAppleSSL::~SocketAppleSSL() { SocketAppleSSL::close(); } - -std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status) -{ - std::string errMsg("Unknown SSL error."); - - CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, - kCFErrorDomainOSStatus, - status, - NULL); - if (error) { - CFStringRef message = CFErrorCopyDescription(error); - if (message) { - char localBuffer[128]; - Boolean success; - success = CFStringGetCString(message, - localBuffer, - 128, - kCFStringEncodingUTF8); - if (success) { - errMsg = localBuffer; - } - CFRelease(message); - } - CFRelease(error); - } - - return errMsg; -} - -OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection, - void *data, - size_t *len) -{ - int fd = (int)(long)connection; - if (fd < 0) - return errSSLInternal; - - assert(data != nullptr); - assert(len != nullptr); - - size_t requested_sz = *len; - - ssize_t status = read(fd, data, requested_sz); - - if (status > 0) { - *len = (size_t)status; - if (requested_sz > *len) { - return errSSLWouldBlock; - } else { - return noErr; - } - } else if (status == 0) { - *len = 0; - return errSSLClosedGraceful; - } else { - *len = 0; - switch (errno) { - case ENOENT: - return errSSLClosedGraceful; - - case EAGAIN: - return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN - // on osx - case EINPROGRESS: - return errSSLWouldBlock; - - case ECONNRESET: - return errSSLClosedAbort; - - default: - return errSecIO; - } - } -} - -OSStatus SocketAppleSSL::writeToSocket(SSLConnectionRef connection, - const void *data, - size_t *len) -{ - int fd = (int)(long)connection; - if (fd < 0) - return errSSLInternal; - - assert(data != nullptr); - assert(len != nullptr); - - size_t to_write_sz = *len; - ssize_t status = write(fd, data, to_write_sz); - - if (status > 0) { - *len = (size_t)status; - if (to_write_sz > *len) { - return errSSLWouldBlock; - } else { - return noErr; - } - } else if (status == 0) { - *len = 0; - return errSSLClosedGraceful; - } else { - *len = 0; - switch (errno) { - case ENOENT: - return errSSLClosedGraceful; - - case EAGAIN: - return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN - // on osx - case EINPROGRESS: - return errSSLWouldBlock; - - case ECONNRESET: - return errSSLClosedAbort; - - default: - return errSecIO; - } - } -} - - -bool SocketAppleSSL::accept(std::string &errMsg) -{ - errMsg = "TLS not supported yet in server mode with apple ssl backend"; - return false; -} - -OSStatus SocketAppleSSL::tlsHandShake( - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - OSStatus status; - - do { - status = SSLHandshake(_sslContext); - - // Interrupt the handshake - if (isCancellationRequested()) { - errMsg = "Cancellation requested"; - return errSSLInternal; - } - } while (status == errSSLWouldBlock || status == errSSLServerAuthCompleted); - - return status; -} - -// No wait support -bool SocketAppleSSL::connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - OSStatus status; - { - std::lock_guard lock(_mutex); - - _sockfd = - SocketConnect::connect(host, port, errMsg, isCancellationRequested); - if (_sockfd == -1) - return false; - - _sslContext = SSLCreateContext(kCFAllocatorDefault, - kSSLClientSide, - kSSLStreamType); - - SSLSetIOFuncs(_sslContext, - SocketAppleSSL::readFromSocket, - SocketAppleSSL::writeToSocket); - SSLSetConnection(_sslContext, (SSLConnectionRef)(long)_sockfd); - SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); - SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); - - if (_tlsOptions.isPeerVerifyDisabled()) { - Boolean option(1); - SSLSetSessionOption(_sslContext, - kSSLSessionOptionBreakOnServerAuth, - option); - - status = tlsHandShake(errMsg, isCancellationRequested); - - if (status == errSSLServerAuthCompleted) { - // proceed with the handshake - status = tlsHandShake(errMsg, isCancellationRequested); - } - } else { - status = tlsHandShake(errMsg, isCancellationRequested); - } - } - - if (status != noErr) { - errMsg = getSSLErrorDescription(status); - close(); - return false; - } - - return true; -} - -void SocketAppleSSL::close() -{ - std::lock_guard lock(_mutex); - - if (_sslContext == nullptr) - return; - - SSLClose(_sslContext); - CFRelease(_sslContext); - _sslContext = nullptr; - - Socket::close(); -} - -ssize_t SocketAppleSSL::send(char *buf, size_t nbyte) -{ - OSStatus status = errSSLWouldBlock; - while (status == errSSLWouldBlock) { - size_t processed = 0; - std::lock_guard lock(_mutex); - status = SSLWrite(_sslContext, buf, nbyte, &processed); - - if (processed > 0) - return (ssize_t)processed; - - // The connection was reset, inform the caller that this - // Socket should close - if (status == errSSLClosedGraceful || status == errSSLClosedNoNotify || - status == errSSLClosedAbort) { - errno = ECONNRESET; - return -1; - } - - if (status == errSSLWouldBlock) { - errno = EWOULDBLOCK; - return -1; - } - } - return -1; -} - -// No wait support -ssize_t SocketAppleSSL::recv(void *buf, size_t nbyte) -{ - OSStatus status = errSSLWouldBlock; - while (status == errSSLWouldBlock) { - size_t processed = 0; - std::lock_guard lock(_mutex); - status = SSLRead(_sslContext, buf, nbyte, &processed); - - if (processed > 0) - return (ssize_t)processed; - - // The connection was reset, inform the caller that this - // Socket should close - if (status == errSSLClosedGraceful || status == errSSLClosedNoNotify || - status == errSSLClosedAbort) { - errno = ECONNRESET; - return -1; - } - - if (status == errSSLWouldBlock) { - errno = EWOULDBLOCK; - return -1; - } - } - return -1; -} - -} // namespace ix - -#endif // IXWEBSOCKET_USE_SECURE_TRANSPORT diff --git a/ixwebsocket/IXSocketConnect.cpp b/ixwebsocket/IXSocketConnect.cpp deleted file mode 100644 index eb2d793..0000000 --- a/ixwebsocket/IXSocketConnect.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * IXSocketConnect.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXSocketConnect.h" - -#include "IXDNSLookup.h" -#include "IXNetSystem.h" -#include "IXSelectInterrupt.h" -#include "IXSocket.h" -#include "IXUniquePtr.h" -#include -#include -#include - -// Android needs extra headers for TCP_NODELAY and IPPROTO_TCP -#ifdef ANDROID -#include -#include -#endif - -namespace ix -{ -// -// This function can be cancelled every 50 ms -// This is important so that we don't block the main UI thread when shutting -// down a connection which is already trying to reconnect, and can be blocked -// waiting for -// ::connect to respond. -// -int SocketConnect::connectToAddress( - const struct addrinfo *address, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - errMsg = "no error"; - - socket_t fd = - socket(address->ai_family, address->ai_socktype, address->ai_protocol); - if (fd < 0) { - errMsg = "Cannot create a socket"; - return -1; - } - - // Set the socket to non blocking mode, so that slow responses cannot - // block us for too long - SocketConnect::configure(fd); - - int res = ::connect(fd, address->ai_addr, address->ai_addrlen); - - if (res == -1 && !Socket::isWaitNeeded()) { - errMsg = strerror(Socket::getErrno()); - Socket::closeSocket(fd); - return -1; - } - - for (;;) { - if (isCancellationRequested && - isCancellationRequested()) // Must handle timeout as well - { - Socket::closeSocket(fd); - errMsg = "Cancelled"; - return -1; - } - - int timeoutMs = 10; - bool readyToRead = false; - auto selectInterrupt = ix::make_unique(); - PollResultType pollResult = - Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt); - - if (pollResult == PollResultType::Timeout) { - continue; - } else if (pollResult == PollResultType::Error) { - Socket::closeSocket(fd); - errMsg = - std::string("Connect error: ") + strerror(Socket::getErrno()); - return -1; - } else if (pollResult == PollResultType::ReadyForWrite) { - return fd; - } else { - Socket::closeSocket(fd); - errMsg = - std::string("Connect error: ") + strerror(Socket::getErrno()); - return -1; - } - } - - Socket::closeSocket(fd); - errMsg = "connect timed out after 60 seconds"; - return -1; -} - -int SocketConnect::connect(const std::string &hostname, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - // - // First do DNS resolution - // - auto dnsLookup = std::make_shared(hostname, port); - struct addrinfo *res = dnsLookup->resolve(errMsg, isCancellationRequested); - if (res == nullptr) { - return -1; - } - - int sockfd = -1; - - // iterate through the records to find a working peer - struct addrinfo *address; - for (address = res; address != nullptr; address = address->ai_next) { - // - // Second try to connect to the remote host - // - sockfd = connectToAddress(address, errMsg, isCancellationRequested); - if (sockfd != -1) { - break; - } - } - - freeaddrinfo(res); - return sockfd; -} - -// FIXME: configure is a terrible name -void SocketConnect::configure(int sockfd) -{ - // 1. disable Nagle's algorithm - int flag = 1; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); - - // 2. make socket non blocking -#ifdef _WIN32 - unsigned long nonblocking = 1; - ioctlsocket(sockfd, FIONBIO, &nonblocking); -#else - fcntl(sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking -#endif - - // 3. (apple) prevent SIGPIPE from being emitted when the remote end - // disconnect -#ifdef SO_NOSIGPIPE - int value = 1; - setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&value, sizeof(value)); -#endif -} -} // namespace ix diff --git a/ixwebsocket/IXSocketFactory.cpp b/ixwebsocket/IXSocketFactory.cpp deleted file mode 100644 index 0e8434b..0000000 --- a/ixwebsocket/IXSocketFactory.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * IXSocketFactory.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXSocketFactory.h" - -#include "IXUniquePtr.h" -#ifdef IXWEBSOCKET_USE_TLS - -#ifdef IXWEBSOCKET_USE_MBED_TLS -#include "IXSocketMbedTLS.h" -#elif defined(IXWEBSOCKET_USE_OPEN_SSL) -#include "IXSocketOpenSSL.h" -#elif __APPLE__ -#include "IXSocketAppleSSL.h" -#endif - -#else - -#include "IXSocket.h" - -#endif - -namespace ix -{ -std::unique_ptr createSocket(bool tls, - int fd, - std::string &errorMsg, - const SocketTLSOptions &tlsOptions) -{ - (void)tlsOptions; - errorMsg.clear(); - std::unique_ptr socket; - - if (!tls) { - socket = ix::make_unique(fd); - } else { -#ifdef IXWEBSOCKET_USE_TLS -#if defined(IXWEBSOCKET_USE_MBED_TLS) - socket = ix::make_unique(tlsOptions, fd); -#elif defined(IXWEBSOCKET_USE_OPEN_SSL) - socket = ix::make_unique(tlsOptions, fd); -#elif defined(__APPLE__) - socket = ix::make_unique(tlsOptions, fd); -#endif -#else - errorMsg = "TLS support is not enabled on this platform."; - return nullptr; -#endif - } - - if (!socket->init(errorMsg)) { - socket.reset(); - } - - return socket; -} -} // namespace ix diff --git a/ixwebsocket/IXSocketMbedTLS.cpp b/ixwebsocket/IXSocketMbedTLS.cpp deleted file mode 100644 index 93fa6af..0000000 --- a/ixwebsocket/IXSocketMbedTLS.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* - * IXSocketMbedTLS.cpp - * Author: Benjamin Sergeant, Max Weisel - * Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. - * - * Some code taken from - * https://github.com/rottor12/WsClientLib/blob/master/lib/src/WsClientLib.cpp - * and mini_client.c example from mbedtls - */ -#ifdef IXWEBSOCKET_USE_MBED_TLS - -#include "IXSocketMbedTLS.h" - -#include "IXNetSystem.h" -#include "IXSocket.h" -#include "IXSocketConnect.h" -#include - -#ifdef _WIN32 -// For manipulating the certificate store -#include -#endif - -namespace ix -{ -SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions &tlsOptions, int fd) - : Socket(fd), _tlsOptions(tlsOptions) -{ - initMBedTLS(); -} - -SocketMbedTLS::~SocketMbedTLS() { SocketMbedTLS::close(); } - -void SocketMbedTLS::initMBedTLS() -{ - std::lock_guard lock(_mutex); - - mbedtls_ssl_init(&_ssl); - mbedtls_ssl_config_init(&_conf); - mbedtls_ctr_drbg_init(&_ctr_drbg); - mbedtls_entropy_init(&_entropy); - mbedtls_x509_crt_init(&_cacert); - mbedtls_x509_crt_init(&_cert); - mbedtls_pk_init(&_pkey); -} - -bool SocketMbedTLS::loadSystemCertificates(std::string &errorMsg) -{ -#ifdef _WIN32 - DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | - CERT_SYSTEM_STORE_CURRENT_USER; - HCERTSTORE systemStore = - CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root"); - - if (!systemStore) { - errorMsg = "CertOpenStore failed with "; - errorMsg += std::to_string(GetLastError()); - return false; - } - - PCCERT_CONTEXT certificateIterator = NULL; - - int certificateCount = 0; - while (certificateIterator = - CertEnumCertificatesInStore(systemStore, certificateIterator)) { - if (certificateIterator->dwCertEncodingType & X509_ASN_ENCODING) { - int ret = - mbedtls_x509_crt_parse(&_cacert, - certificateIterator->pbCertEncoded, - certificateIterator->cbCertEncoded); - if (ret == 0) { - ++certificateCount; - } - } - } - - CertFreeCertificateContext(certificateIterator); - CertCloseStore(systemStore, 0); - - if (certificateCount == 0) { - errorMsg = "No certificates found"; - return false; - } - - return true; -#else - // On macOS we can query the system cert location from the keychain - // On Linux we could try to fetch some local files based on the distribution - // On Android we could use JNI to get to the system certs - return false; -#endif -} - -bool SocketMbedTLS::init(const std::string &host, - bool isClient, - std::string &errMsg) -{ - initMBedTLS(); - std::lock_guard lock(_mutex); - - const char *pers = "IXSocketMbedTLS"; - - if (mbedtls_ctr_drbg_seed(&_ctr_drbg, - mbedtls_entropy_func, - &_entropy, - (const unsigned char *)pers, - strlen(pers)) != 0) { - errMsg = "Setting entropy seed failed"; - return false; - } - - if (mbedtls_ssl_config_defaults(&_conf, - (isClient) ? MBEDTLS_SSL_IS_CLIENT - : MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT) != 0) { - errMsg = "Setting config default failed"; - return false; - } - - mbedtls_ssl_conf_rng(&_conf, mbedtls_ctr_drbg_random, &_ctr_drbg); - - if (_tlsOptions.hasCertAndKey()) { - if (mbedtls_x509_crt_parse_file(&_cert, _tlsOptions.certFile.c_str()) < - 0) { - errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'"; - return false; - } -#ifdef IXWEBSOCKET_USE_MBED_TLS_MIN_VERSION_3 - if (mbedtls_pk_parse_keyfile(&_pkey, - _tlsOptions.keyFile.c_str(), - "", - mbedtls_ctr_drbg_random, - &_ctr_drbg) < 0) -#else - if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < - 0) -#endif - { - errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'"; - return false; - } - if (mbedtls_ssl_conf_own_cert(&_conf, &_cert, &_pkey) < 0) { - errMsg = "Problem configuring cert '" + _tlsOptions.certFile + "'"; - return false; - } - } - - if (_tlsOptions.isPeerVerifyDisabled()) { - mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_NONE); - } else { - // FIXME: should we call mbedtls_ssl_conf_verify ? - mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); - - if (_tlsOptions.isUsingSystemDefaults()) { - if (!loadSystemCertificates(errMsg)) { - return false; - } - } else { - if (_tlsOptions.isUsingInMemoryCAs()) { - const char *buffer = _tlsOptions.caFile.c_str(); - size_t bufferSize = _tlsOptions.caFile.size() + - 1; // Needs to include null terminating - // character otherwise mbedtls will fail. - if (mbedtls_x509_crt_parse(&_cacert, - (const unsigned char *)buffer, - bufferSize) < 0) { - errMsg = "Cannot parse CA from memory."; - return false; - } - } else if (mbedtls_x509_crt_parse_file(&_cacert, - _tlsOptions.caFile.c_str()) < - 0) { - errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; - return false; - } - } - - mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); - } - - if (mbedtls_ssl_setup(&_ssl, &_conf) != 0) { - errMsg = "SSL setup failed"; - return false; - } - - if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0) { - errMsg = "SNI setup failed"; - return false; - } - - return true; -} - -bool SocketMbedTLS::accept(std::string &errMsg) -{ - bool isClient = false; - bool initialized = init(std::string(), isClient, errMsg); - if (!initialized) { - close(); - return false; - } - - mbedtls_ssl_set_bio(&_ssl, - &_sockfd, - mbedtls_net_send, - mbedtls_net_recv, - NULL); - - int res; - do { - std::lock_guard lock(_mutex); - res = mbedtls_ssl_handshake(&_ssl); - } while (res == MBEDTLS_ERR_SSL_WANT_READ || - res == MBEDTLS_ERR_SSL_WANT_WRITE); - - if (res != 0) { - char buf[256]; - mbedtls_strerror(res, buf, sizeof(buf)); - - errMsg = "error in handshake : "; - errMsg += buf; - - if (res == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { - char verifyBuf[512]; - uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl); - - mbedtls_x509_crt_verify_info(verifyBuf, - sizeof(verifyBuf), - " ! ", - flags); - errMsg += " : "; - errMsg += verifyBuf; - } - - close(); - return false; - } - - return true; -} - -bool SocketMbedTLS::connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - { - std::lock_guard lock(_mutex); - _sockfd = - SocketConnect::connect(host, port, errMsg, isCancellationRequested); - if (_sockfd == -1) - return false; - } - - bool isClient = true; - bool initialized = init(host, isClient, errMsg); - if (!initialized) { - close(); - return false; - } - - mbedtls_ssl_set_bio(&_ssl, - &_sockfd, - mbedtls_net_send, - mbedtls_net_recv, - NULL); - - int res; - do { - { - std::lock_guard lock(_mutex); - res = mbedtls_ssl_handshake(&_ssl); - } - - if (isCancellationRequested()) { - errMsg = "Cancellation requested"; - close(); - return false; - } - } while (res == MBEDTLS_ERR_SSL_WANT_READ || - res == MBEDTLS_ERR_SSL_WANT_WRITE); - - if (res != 0) { - char buf[256]; - mbedtls_strerror(res, buf, sizeof(buf)); - - errMsg = "error in handshake : "; - errMsg += buf; - - close(); - return false; - } - - return true; -} - -void SocketMbedTLS::close() -{ - std::lock_guard lock(_mutex); - - mbedtls_ssl_free(&_ssl); - mbedtls_ssl_config_free(&_conf); - mbedtls_ctr_drbg_free(&_ctr_drbg); - mbedtls_entropy_free(&_entropy); - mbedtls_x509_crt_free(&_cacert); - mbedtls_x509_crt_free(&_cert); - - Socket::close(); -} - -ssize_t SocketMbedTLS::send(char *buf, size_t nbyte) -{ - std::lock_guard lock(_mutex); - - ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char *)buf, nbyte); - - if (res > 0) { - return res; - } else if (res == MBEDTLS_ERR_SSL_WANT_READ || - res == MBEDTLS_ERR_SSL_WANT_WRITE) { - errno = EWOULDBLOCK; - return -1; - } else { - return -1; - } -} - -ssize_t SocketMbedTLS::recv(void *buf, size_t nbyte) -{ - while (true) { - std::lock_guard lock(_mutex); - - ssize_t res = mbedtls_ssl_read(&_ssl, (unsigned char *)buf, (int)nbyte); - - if (res > 0) { - return res; - } - - if (res == MBEDTLS_ERR_SSL_WANT_READ || - res == MBEDTLS_ERR_SSL_WANT_WRITE) { - errno = EWOULDBLOCK; - } - return -1; - } -} - -} // namespace ix - -#endif // IXWEBSOCKET_USE_MBED_TLS diff --git a/ixwebsocket/IXSocketOpenSSL.cpp b/ixwebsocket/IXSocketOpenSSL.cpp deleted file mode 100644 index 4c18538..0000000 --- a/ixwebsocket/IXSocketOpenSSL.cpp +++ /dev/null @@ -1,761 +0,0 @@ -/* - * IXSocketOpenSSL.cpp - * Author: Benjamin Sergeant, Matt DeBoer, Max Weisel - * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. - * - * Adapted from Satori SDK OpenSSL code. - */ -#ifdef IXWEBSOCKET_USE_OPEN_SSL - -#include "IXSocketOpenSSL.h" - -#include "IXSocketConnect.h" -#include "IXUniquePtr.h" -#include -#include -#include -#ifdef _WIN32 -#include -#else -#include -#endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#include -#endif -#define socketerrno errno - -#ifdef _WIN32 -// For manipulating the certificate store -#include -#endif - -#ifdef _WIN32 -namespace -{ -bool loadWindowsSystemCertificates(SSL_CTX *ssl, std::string &errorMsg) -{ - DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG | - CERT_SYSTEM_STORE_CURRENT_USER; - HCERTSTORE systemStore = - CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root"); - - if (!systemStore) { - errorMsg = "CertOpenStore failed with "; - errorMsg += std::to_string(GetLastError()); - return false; - } - - PCCERT_CONTEXT certificateIterator = NULL; - X509_STORE *opensslStore = SSL_CTX_get_cert_store(ssl); - - int certificateCount = 0; - while (certificateIterator = - CertEnumCertificatesInStore(systemStore, certificateIterator)) { - X509 *x509 = d2i_X509( - NULL, - (const unsigned char **)&certificateIterator->pbCertEncoded, - certificateIterator->cbCertEncoded); - - if (x509) { - if (X509_STORE_add_cert(opensslStore, x509) == 1) { - ++certificateCount; - } - - X509_free(x509); - } - } - - CertFreeCertificateContext(certificateIterator); - CertCloseStore(systemStore, 0); - - if (certificateCount == 0) { - errorMsg = "No certificates found"; - return false; - } - - return true; -} -} // namespace -#endif - -namespace ix -{ -const std::string kDefaultCiphers = - "ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 " - "ECDHE-ECDSA-AES128-SHA " - "ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 " - "ECDHE-ECDSA-AES256-SHA384 " - "ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 " - "ECDHE-RSA-AES128-SHA " - "ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 " - "DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-SHA " - "DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256 AES128-SHA"; - -std::atomic SocketOpenSSL::_openSSLInitializationSuccessful(false); -std::once_flag SocketOpenSSL::_openSSLInitFlag; -std::vector> openSSLMutexes; - -SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions &tlsOptions, int fd) - : Socket(fd), _ssl_connection(nullptr), _ssl_context(nullptr), - _tlsOptions(tlsOptions) -{ - std::call_once(_openSSLInitFlag, &SocketOpenSSL::openSSLInitialize, this); -} - -SocketOpenSSL::~SocketOpenSSL() { SocketOpenSSL::close(); } - -void SocketOpenSSL::openSSLInitialize() -{ -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) - return; -#else - (void)OPENSSL_config(nullptr); - - if (CRYPTO_get_locking_callback() == nullptr) { - openSSLMutexes.clear(); - for (int i = 0; i < CRYPTO_num_locks(); ++i) { - openSSLMutexes.push_back(ix::make_unique()); - } - CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback); - } -#endif - - (void)OpenSSL_add_ssl_algorithms(); - (void)SSL_load_error_strings(); - - _openSSLInitializationSuccessful = true; -} - -void SocketOpenSSL::openSSLLockingCallback(int mode, - int type, - const char * /*file*/, - int /*line*/) -{ - if (mode & CRYPTO_LOCK) { - openSSLMutexes[type]->lock(); - } else { - openSSLMutexes[type]->unlock(); - } -} - -std::string SocketOpenSSL::getSSLError(int ret) -{ - unsigned long e; - - int err = SSL_get_error(_ssl_connection, ret); - - if (err == SSL_ERROR_WANT_CONNECT || err == SSL_ERROR_WANT_ACCEPT) { - return "OpenSSL failed - connection failure"; - } else if (err == SSL_ERROR_WANT_X509_LOOKUP) { - return "OpenSSL failed - x509 error"; - } else if (err == SSL_ERROR_SYSCALL) { - e = ERR_get_error(); - if (e > 0) { - std::string errMsg("OpenSSL failed - "); - errMsg += ERR_error_string(e, nullptr); - return errMsg; - } else if (e == 0 && ret == 0) { - return "OpenSSL failed - received early EOF"; - } else { - return "OpenSSL failed - underlying BIO reported an I/O error"; - } - } else if (err == SSL_ERROR_SSL) { - e = ERR_get_error(); - std::string errMsg("OpenSSL failed - "); - errMsg += ERR_error_string(e, nullptr); - return errMsg; - } else if (err == SSL_ERROR_NONE) { - return "OpenSSL failed - err none"; - } else if (err == SSL_ERROR_ZERO_RETURN) { - return "OpenSSL failed - err zero return"; - } else { - return "OpenSSL failed - unknown error"; - } -} - -SSL_CTX *SocketOpenSSL::openSSLCreateContext(std::string &errMsg) -{ - const SSL_METHOD *method = SSLv23_client_method(); - if (method == nullptr) { - errMsg = "SSLv23_client_method failure"; - return nullptr; - } - _ssl_method = method; - - SSL_CTX *ctx = SSL_CTX_new(_ssl_method); - if (ctx) { - SSL_CTX_set_mode(ctx, - SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - int options = - SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_CIPHER_SERVER_PREFERENCE; - -#ifdef SSL_OP_NO_TLSv1_3 - // (partially?) work around hang in openssl 1.1.1b, by disabling TLS - // V1.3 https://github.com/openssl/openssl/issues/7967 - options |= SSL_OP_NO_TLSv1_3; -#endif - SSL_CTX_set_options(ctx, options); - } - return ctx; -} - -bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots) -{ - // Create certificate store - X509_STORE *certificate_store = SSL_CTX_get_cert_store(_ssl_context); - if (certificate_store == nullptr) - return false; - - // Configure to allow intermediate certs - X509_STORE_set_flags(certificate_store, - X509_V_FLAG_TRUSTED_FIRST | X509_V_FLAG_PARTIAL_CHAIN); - - // Create a new buffer and populate it with the roots - BIO *buffer = BIO_new_mem_buf((void *)roots.c_str(), - static_cast(roots.length())); - if (buffer == nullptr) - return false; - - // Read each root in the buffer and add to the certificate store - bool success = true; - size_t number_of_roots = 0; - - while (true) { - // Read the next root in the buffer - X509 *root = - PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void *)""); - if (root == nullptr) { - // No more certs left in the buffer, we're done. - ERR_clear_error(); - break; - } - - // Try adding the root to the certificate store - ERR_clear_error(); - if (!X509_STORE_add_cert(certificate_store, root)) { - // Failed to add. If the error is unrelated to the x509 lib or the - // cert already exists, we're safe to continue. - unsigned long error = ERR_get_error(); - if (ERR_GET_LIB(error) != ERR_LIB_X509 || - ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { - // Failed. Clean up and bail. - success = false; - X509_free(root); - break; - } - } - - // Clean up and loop - X509_free(root); - number_of_roots++; - } - - // Clean up buffer - BIO_free(buffer); - - // Make sure we loaded at least one certificate. - if (number_of_roots == 0) - success = false; - - return success; -} - -/** - * Check whether a hostname matches a pattern - */ -bool SocketOpenSSL::checkHost(const std::string &host, const char *pattern) -{ -#ifdef _WIN32 - return PathMatchSpecA(host.c_str(), pattern); -#else - return fnmatch(pattern, host.c_str(), 0) != FNM_NOMATCH; -#endif -} - -bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl, - const std::string &hostname, - std::string &errMsg) -{ - X509 *server_cert = SSL_get_peer_certificate(ssl); - if (server_cert == nullptr) { - errMsg = "OpenSSL failed - peer didn't present a X509 certificate."; - return false; - } - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - // Check server name - bool hostname_verifies_ok = false; - STACK_OF(GENERAL_NAME) *san_names = (STACK_OF(GENERAL_NAME) *) - X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL); - if (san_names) { - for (int i = 0; i < sk_GENERAL_NAME_num(san_names); i++) { - const GENERAL_NAME *sk_name = sk_GENERAL_NAME_value(san_names, i); - if (sk_name->type == GEN_DNS) { - char *name = (char *)ASN1_STRING_data(sk_name->d.dNSName); - if ((size_t)ASN1_STRING_length(sk_name->d.dNSName) == - strlen(name) && - checkHost(hostname, name)) { - hostname_verifies_ok = true; - break; - } - } - } - } - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - - if (!hostname_verifies_ok) { - int cn_pos = X509_NAME_get_index_by_NID( - X509_get_subject_name((X509 *)server_cert), - NID_commonName, - -1); - if (cn_pos) { - X509_NAME_ENTRY *cn_entry = - X509_NAME_get_entry(X509_get_subject_name((X509 *)server_cert), - cn_pos); - - if (cn_entry) { - ASN1_STRING *cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); - char *cn = (char *)ASN1_STRING_data(cn_asn1); - - if ((size_t)ASN1_STRING_length(cn_asn1) == strlen(cn) && - checkHost(hostname, cn)) { - hostname_verifies_ok = true; - } - } - } - } - - if (!hostname_verifies_ok) { - errMsg = - "OpenSSL failed - certificate was issued for a different domain."; - return false; - } -#endif - - X509_free(server_cert); - return true; -} - -bool SocketOpenSSL::openSSLClientHandshake( - const std::string &host, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - while (true) { - if (_ssl_connection == nullptr || _ssl_context == nullptr) { - return false; - } - - if (isCancellationRequested()) { - errMsg = "Cancellation requested"; - return false; - } - - ERR_clear_error(); - int connect_result = SSL_connect(_ssl_connection); - if (connect_result == 1) { - return openSSLCheckServerCert(_ssl_connection, host, errMsg); - } - int reason = SSL_get_error(_ssl_connection, connect_result); - - bool rc = false; - if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) { - rc = true; - } else { - errMsg = getSSLError(connect_result); - rc = false; - } - - if (!rc) { - return false; - } - } -} - -bool SocketOpenSSL::openSSLServerHandshake(std::string &errMsg) -{ - while (true) { - if (_ssl_connection == nullptr || _ssl_context == nullptr) { - return false; - } - - ERR_clear_error(); - int accept_result = SSL_accept(_ssl_connection); - if (accept_result == 1) { - return true; - } - int reason = SSL_get_error(_ssl_connection, accept_result); - - bool rc = false; - if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) { - rc = true; - } else { - errMsg = getSSLError(accept_result); - rc = false; - } - - if (!rc) { - return false; - } - } -} - -bool SocketOpenSSL::handleTLSOptions(std::string &errMsg) -{ - ERR_clear_error(); - if (_tlsOptions.hasCertAndKey()) { - if (SSL_CTX_use_certificate_chain_file(_ssl_context, - _tlsOptions.certFile.c_str()) != - 1) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" + - _tlsOptions.certFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } else if (SSL_CTX_use_PrivateKey_file(_ssl_context, - _tlsOptions.keyFile.c_str(), - SSL_FILETYPE_PEM) != 1) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" + - _tlsOptions.keyFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } else if (!SSL_CTX_check_private_key(_ssl_context)) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - cert/key mismatch(\"" + - _tlsOptions.certFile + ", " + _tlsOptions.keyFile + "\")"; - errMsg += ERR_error_string(sslErr, nullptr); - } - } - - ERR_clear_error(); - if (!_tlsOptions.isPeerVerifyDisabled()) { - if (_tlsOptions.isUsingSystemDefaults()) { -#ifdef _WIN32 - if (!loadWindowsSystemCertificates(_ssl_context, errMsg)) { - return false; - } -#else - if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths " - "loading failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } -#endif - } else { - if (_tlsOptions.isUsingInMemoryCAs()) { - // Load from memory - openSSLAddCARootsFromString(_tlsOptions.caFile); - } else { - if (SSL_CTX_load_verify_locations(_ssl_context, - _tlsOptions.caFile.c_str(), - NULL) != 1) { - auto sslErr = ERR_get_error(); - errMsg = - "OpenSSL failed - SSL_CTX_load_verify_locations(\"" + - _tlsOptions.caFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } - } - } - - SSL_CTX_set_verify( - _ssl_context, - SSL_VERIFY_PEER, - [](int preverify, X509_STORE_CTX *) -> int { return preverify; }); - SSL_CTX_set_verify_depth(_ssl_context, 4); - } else { - SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr); - } - - if (_tlsOptions.isUsingDefaultCiphers()) { - if (SSL_CTX_set_cipher_list(_ssl_context, kDefaultCiphers.c_str()) != - 1) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + - kDefaultCiphers + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } - } else if (SSL_CTX_set_cipher_list(_ssl_context, - _tlsOptions.ciphers.c_str()) != 1) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + - _tlsOptions.ciphers + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - return false; - } - - return true; -} - -bool SocketOpenSSL::accept(std::string &errMsg) -{ - bool handshakeSuccessful = false; - { - std::lock_guard lock(_mutex); - - if (!_openSSLInitializationSuccessful) { - errMsg = "OPENSSL_init_ssl failure"; - return false; - } - - if (_sockfd == -1) { - return false; - } - - { - const SSL_METHOD *method = SSLv23_server_method(); - if (method == nullptr) { - errMsg = "SSLv23_server_method failure"; - _ssl_context = nullptr; - } else { - _ssl_method = method; - - _ssl_context = SSL_CTX_new(_ssl_method); - if (_ssl_context) { - SSL_CTX_set_mode(_ssl_context, - SSL_MODE_ENABLE_PARTIAL_WRITE); - SSL_CTX_set_mode(_ssl_context, - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_options(_ssl_context, - SSL_OP_ALL | SSL_OP_NO_SSLv2 | - SSL_OP_NO_SSLv3); - } - } - } - - if (_ssl_context == nullptr) { - return false; - } - - ERR_clear_error(); - if (_tlsOptions.hasCertAndKey()) { - if (SSL_CTX_use_certificate_chain_file( - _ssl_context, - _tlsOptions.certFile.c_str()) != 1) { - auto sslErr = ERR_get_error(); - errMsg = - "OpenSSL failed - SSL_CTX_use_certificate_chain_file(\"" + - _tlsOptions.certFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } else if (SSL_CTX_use_PrivateKey_file(_ssl_context, - _tlsOptions.keyFile.c_str(), - SSL_FILETYPE_PEM) != 1) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_use_PrivateKey_file(\"" + - _tlsOptions.keyFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - } - - - ERR_clear_error(); - if (!_tlsOptions.isPeerVerifyDisabled()) { - if (_tlsOptions.isUsingSystemDefaults()) { - if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_CTX_default_verify_paths " - "loading failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - } else { - if (_tlsOptions.isUsingInMemoryCAs()) { - // Load from memory - openSSLAddCARootsFromString(_tlsOptions.caFile); - } else { - const char *root_ca_file = _tlsOptions.caFile.c_str(); - STACK_OF(X509_NAME) * rootCAs; - rootCAs = SSL_load_client_CA_file(root_ca_file); - if (rootCAs == NULL) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - SSL_load_client_CA_file('" + - _tlsOptions.caFile + "') failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } else { - SSL_CTX_set_client_CA_list(_ssl_context, rootCAs); - if (SSL_CTX_load_verify_locations(_ssl_context, - root_ca_file, - nullptr) != 1) { - auto sslErr = ERR_get_error(); - errMsg = "OpenSSL failed - " - "SSL_CTX_load_verify_locations(\"" + - _tlsOptions.caFile + "\") failed: "; - errMsg += ERR_error_string(sslErr, nullptr); - } - } - } - } - - SSL_CTX_set_verify(_ssl_context, - SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - nullptr); - SSL_CTX_set_verify_depth(_ssl_context, 4); - } else { - SSL_CTX_set_verify(_ssl_context, SSL_VERIFY_NONE, nullptr); - } - if (_tlsOptions.isUsingDefaultCiphers()) { - if (SSL_CTX_set_cipher_list(_ssl_context, - kDefaultCiphers.c_str()) != 1) { - return false; - } - } else if (SSL_CTX_set_cipher_list(_ssl_context, - _tlsOptions.ciphers.c_str()) != 1) { - return false; - } - - _ssl_connection = SSL_new(_ssl_context); - if (_ssl_connection == nullptr) { - errMsg = "OpenSSL failed to connect"; - SSL_CTX_free(_ssl_context); - _ssl_context = nullptr; - return false; - } - - SSL_set_ecdh_auto(_ssl_connection, 1); - - SSL_set_fd(_ssl_connection, _sockfd); - - handshakeSuccessful = openSSLServerHandshake(errMsg); - } - - if (!handshakeSuccessful) { - close(); - return false; - } - - return true; -} - -bool SocketOpenSSL::connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) -{ - bool handshakeSuccessful = false; - { - std::lock_guard lock(_mutex); - - if (!_openSSLInitializationSuccessful) { - errMsg = "OPENSSL_init_ssl failure"; - return false; - } - - _sockfd = - SocketConnect::connect(host, port, errMsg, isCancellationRequested); - if (_sockfd == -1) - return false; - - _ssl_context = openSSLCreateContext(errMsg); - if (_ssl_context == nullptr) { - return false; - } - - if (!handleTLSOptions(errMsg)) { - return false; - } - - _ssl_connection = SSL_new(_ssl_context); - if (_ssl_connection == nullptr) { - errMsg = "OpenSSL failed to connect"; - SSL_CTX_free(_ssl_context); - _ssl_context = nullptr; - return false; - } - SSL_set_fd(_ssl_connection, _sockfd); - - // SNI support - SSL_set_tlsext_host_name(_ssl_connection, host.c_str()); - -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - // Support for server name verification - // (The docs say that this should work from 1.0.2, and is the default - // from 1.1.0, but it does not. To be on the safe side, the manual test - // below is enabled for all versions prior to 1.1.0.) - X509_VERIFY_PARAM *param = SSL_get0_param(_ssl_connection); - X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0); -#endif - handshakeSuccessful = - openSSLClientHandshake(host, errMsg, isCancellationRequested); - } - - if (!handshakeSuccessful) { - close(); - return false; - } - - return true; -} - -void SocketOpenSSL::close() -{ - std::lock_guard lock(_mutex); - - if (_ssl_connection != nullptr) { - SSL_free(_ssl_connection); - _ssl_connection = nullptr; - } - if (_ssl_context != nullptr) { - SSL_CTX_free(_ssl_context); - _ssl_context = nullptr; - } - - Socket::close(); -} - -ssize_t SocketOpenSSL::send(char *buf, size_t nbyte) -{ - std::lock_guard lock(_mutex); - - if (_ssl_connection == nullptr || _ssl_context == nullptr) { - return 0; - } - - ERR_clear_error(); - ssize_t write_result = SSL_write(_ssl_connection, buf, (int)nbyte); - int reason = SSL_get_error(_ssl_connection, (int)write_result); - - if (reason == SSL_ERROR_NONE) { - return write_result; - } else if (reason == SSL_ERROR_WANT_READ || - reason == SSL_ERROR_WANT_WRITE) { - errno = EWOULDBLOCK; - return -1; - } else { - return -1; - } -} - -ssize_t SocketOpenSSL::recv(void *buf, size_t nbyte) -{ - while (true) { - std::lock_guard lock(_mutex); - - if (_ssl_connection == nullptr || _ssl_context == nullptr) { - return 0; - } - - ERR_clear_error(); - ssize_t read_result = SSL_read(_ssl_connection, buf, (int)nbyte); - - if (read_result > 0) { - return read_result; - } - - int reason = SSL_get_error(_ssl_connection, (int)read_result); - - if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE) { - errno = EWOULDBLOCK; - } - return -1; - } -} - -} // namespace ix - -#endif // IXWEBSOCKET_USE_OPEN_SSL diff --git a/ixwebsocket/IXSocketServer.cpp b/ixwebsocket/IXSocketServer.cpp deleted file mode 100644 index 968b48a..0000000 --- a/ixwebsocket/IXSocketServer.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/* - * IXSocketServer.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXSocketServer.h" - -#include "IXNetSystem.h" -#include "IXSelectInterrupt.h" -#include "IXSelectInterruptFactory.h" -#include "IXSetThreadName.h" -#include "IXSocket.h" -#include "IXSocketConnect.h" -#include "IXSocketFactory.h" -#include -#include -#include -#include - -namespace ix -{ -const int SocketServer::kDefaultPort(8080); -const std::string SocketServer::kDefaultHost("127.0.0.1"); -const int SocketServer::kDefaultTcpBacklog(5); -const size_t SocketServer::kDefaultMaxConnections(128); -const int SocketServer::kDefaultAddressFamily(AF_INET); - -SocketServer::SocketServer(int port, - const std::string &host, - int backlog, - size_t maxConnections, - int addressFamily) - : _port(port), _host(host), _backlog(backlog), - _maxConnections(maxConnections), _addressFamily(addressFamily), - _serverFd(-1), _stop(false), _stopGc(false), - _connectionStateFactory(&ConnectionState::createConnectionState), - _acceptSelectInterrupt(createSelectInterrupt()) -{ -} - -SocketServer::~SocketServer() { stop(); } - -void SocketServer::logError(const std::string &str) -{ - std::lock_guard lock(_logMutex); - fprintf(stderr, "%s\n", str.c_str()); -} - -void SocketServer::logInfo(const std::string &str) -{ - std::lock_guard lock(_logMutex); - fprintf(stdout, "%s\n", str.c_str()); -} - -std::pair SocketServer::listen() -{ - std::string acceptSelectInterruptInitErrorMsg; - if (!_acceptSelectInterrupt->init(acceptSelectInterruptInitErrorMsg)) { - std::stringstream ss; - ss << "SocketServer::listen() error in SelectInterrupt::init: " - << acceptSelectInterruptInitErrorMsg; - - return std::make_pair(false, ss.str()); - } - - if (_addressFamily != AF_INET && _addressFamily != AF_INET6) { - std::string errMsg( - "SocketServer::listen() AF_INET and AF_INET6 are currently " - "the only supported address families"); - return std::make_pair(false, errMsg); - } - - // Get a socket for accepting connections. - if ((_serverFd = socket(_addressFamily, SOCK_STREAM, 0)) < 0) { - std::stringstream ss; - ss << "SocketServer::listen() error creating socket): " - << strerror(Socket::getErrno()); - - return std::make_pair(false, ss.str()); - } - - // Make that socket reusable. (allow restarting this server at will) - int enable = 1; - if (setsockopt(_serverFd, - SOL_SOCKET, - SO_REUSEADDR, - (char *)&enable, - sizeof(enable)) < 0) { - std::stringstream ss; - ss << "SocketServer::listen() error calling setsockopt(SO_REUSEADDR) " - << "at address " << _host << ":" << _port << " : " - << strerror(Socket::getErrno()); - - Socket::closeSocket(_serverFd); - return std::make_pair(false, ss.str()); - } - - if (_addressFamily == AF_INET) { - struct sockaddr_in server; - server.sin_family = _addressFamily; - server.sin_port = htons(_port); - - if (ix::inet_pton(_addressFamily, - _host.c_str(), - &server.sin_addr.s_addr) <= 0) { - std::stringstream ss; - ss << "SocketServer::listen() error calling inet_pton " - << "at address " << _host << ":" << _port << " : " - << strerror(Socket::getErrno()); - - Socket::closeSocket(_serverFd); - return std::make_pair(false, ss.str()); - } - - // Bind the socket to the server address. - if (bind(_serverFd, (struct sockaddr *)&server, sizeof(server)) < 0) { - std::stringstream ss; - ss << "SocketServer::listen() error calling bind " - << "at address " << _host << ":" << _port << " : " - << strerror(Socket::getErrno()); - - Socket::closeSocket(_serverFd); - return std::make_pair(false, ss.str()); - } - } else // AF_INET6 - { - struct sockaddr_in6 server; - server.sin6_family = _addressFamily; - server.sin6_port = htons(_port); - - if (ix::inet_pton(_addressFamily, _host.c_str(), &server.sin6_addr) <= - 0) { - std::stringstream ss; - ss << "SocketServer::listen() error calling inet_pton " - << "at address " << _host << ":" << _port << " : " - << strerror(Socket::getErrno()); - - Socket::closeSocket(_serverFd); - return std::make_pair(false, ss.str()); - } - - // Bind the socket to the server address. - if (bind(_serverFd, (struct sockaddr *)&server, sizeof(server)) < 0) { - std::stringstream ss; - ss << "SocketServer::listen() error calling bind " - << "at address " << _host << ":" << _port << " : " - << strerror(Socket::getErrno()); - - Socket::closeSocket(_serverFd); - return std::make_pair(false, ss.str()); - } - } - - // - // Listen for connections. Specify the tcp backlog. - // - if (::listen(_serverFd, _backlog) < 0) { - std::stringstream ss; - ss << "SocketServer::listen() error calling listen " - << "at address " << _host << ":" << _port << " : " - << strerror(Socket::getErrno()); - - Socket::closeSocket(_serverFd); - return std::make_pair(false, ss.str()); - } - - return std::make_pair(true, ""); -} - -void SocketServer::start() -{ - _stop = false; - - if (!_thread.joinable()) { - _thread = std::thread(&SocketServer::run, this); - } - - if (!_gcThread.joinable()) { - _gcThread = std::thread(&SocketServer::runGC, this); - } -} - -void SocketServer::wait() -{ - std::unique_lock lock(_conditionVariableMutex); - _conditionVariable.wait(lock); -} - -void SocketServer::stopAcceptingConnections() { _stop = true; } - -void SocketServer::stop() -{ - // Stop accepting connections, and close the 'accept' thread - if (_thread.joinable()) { - _stop = true; - // Wake up select - if (!_acceptSelectInterrupt->notify(SelectInterrupt::kCloseRequest)) { - logError("SocketServer::stop: Cannot wake up from select"); - } - - _thread.join(); - _stop = false; - } - - // Join all threads and make sure that all connections are terminated - if (_gcThread.joinable()) { - _stopGc = true; - _conditionVariableGC.notify_one(); - _gcThread.join(); - _stopGc = false; - } - - _conditionVariable.notify_one(); - Socket::closeSocket(_serverFd); -} - -void SocketServer::setConnectionStateFactory( - const ConnectionStateFactory &connectionStateFactory) -{ - _connectionStateFactory = connectionStateFactory; -} - -// -// join the threads for connections that have been closed -// -// When a connection is closed by a client, the connection state terminated -// 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). -// -void SocketServer::closeTerminatedThreads() -{ - std::lock_guard lock(_connectionsThreadsMutex); - auto it = _connectionsThreads.begin(); - auto itEnd = _connectionsThreads.end(); - - while (it != itEnd) { - auto &connectionState = it->first; - auto &thread = it->second; - - if (!connectionState->isTerminated()) { - ++it; - continue; - } - - if (thread.joinable()) - thread.join(); - it = _connectionsThreads.erase(it); - } -} - -void SocketServer::run() -{ - // Set the socket to non blocking mode, so that accept calls are not - // blocking - SocketConnect::configure(_serverFd); - - setThreadName("SocketServer::accept"); - - for (;;) { - if (_stop) - return; - - // Use poll to check whether a new connection is in progress - int timeoutMs = -1; -#ifdef _WIN32 - // select cannot be interrupted on Windows so we need to pass a small - // timeout - timeoutMs = 10; -#endif - - bool readyToRead = true; - PollResultType pollResult = Socket::poll(readyToRead, - timeoutMs, - _serverFd, - _acceptSelectInterrupt); - - if (pollResult == PollResultType::Error) { - std::stringstream ss; - ss << "SocketServer::run() error in select: " << -#ifndef _WIN32 - strerror(Socket::getErrno()); -#else - - Socket::getErrno(); -#endif - logError(ss.str()); - continue; - } - - if (pollResult != PollResultType::ReadyForRead) { - continue; - } - - // Accept a connection. - // FIXME: Is this working for ipv6 ? - struct sockaddr_in client; // client address information - int clientFd; // socket connected to client - socklen_t addressLen = sizeof(client); - memset(&client, 0, sizeof(client)); - - if ((clientFd = - accept(_serverFd, (struct sockaddr *)&client, &addressLen)) < - 0) { - if (!Socket::isWaitNeeded()) { - // FIXME: that error should be propagated - int err = Socket::getErrno(); - std::stringstream ss; - ss << "SocketServer::run() error accepting connection: " << err - << ", " << strerror(err); - logError(ss.str()); - } - continue; - } - - if (getConnectedClientsCount() >= _maxConnections) { - std::stringstream ss; - ss << "SocketServer::run() reached max connections = " - << _maxConnections << ". " - << "Not accepting connection"; - logError(ss.str()); - - Socket::closeSocket(clientFd); - - continue; - } - - // Retrieve connection info, the ip address of the remote peer/client) - std::string remoteIp; - int remotePort; - - if (_addressFamily == AF_INET) { - char remoteIp4[INET_ADDRSTRLEN]; - if (ix::inet_ntop(AF_INET, - &client.sin_addr, - remoteIp4, - INET_ADDRSTRLEN) == nullptr) { - int err = Socket::getErrno(); - std::stringstream ss; - ss << "SocketServer::run() error calling inet_ntop (ipv4): " - << err << ", " << strerror(err); - logError(ss.str()); - - Socket::closeSocket(clientFd); - - continue; - } - - remotePort = ix::network_to_host_short(client.sin_port); - remoteIp = remoteIp4; - } else // AF_INET6 - { - char remoteIp6[INET6_ADDRSTRLEN]; - if (ix::inet_ntop(AF_INET6, - &client.sin_addr, - remoteIp6, - INET6_ADDRSTRLEN) == nullptr) { - int err = Socket::getErrno(); - std::stringstream ss; - ss << "SocketServer::run() error calling inet_ntop (ipv6): " - << err << ", " << strerror(err); - logError(ss.str()); - - Socket::closeSocket(clientFd); - - continue; - } - - remotePort = ix::network_to_host_short(client.sin_port); - remoteIp = remoteIp6; - } - - std::shared_ptr connectionState; - if (_connectionStateFactory) { - connectionState = _connectionStateFactory(); - } - connectionState->setOnSetTerminatedCallback( - [this] { onSetTerminatedCallback(); }); - connectionState->setRemoteIp(remoteIp); - connectionState->setRemotePort(remotePort); - - if (_stop) - return; - - // create socket - std::string errorMsg; - bool tls = _socketTLSOptions.tls; - auto socket = createSocket(tls, clientFd, errorMsg, _socketTLSOptions); - - if (socket == nullptr) { - logError("SocketServer::run() cannot create socket: " + errorMsg); - Socket::closeSocket(clientFd); - continue; - } - - // Set the socket to non blocking mode + other tweaks - SocketConnect::configure(clientFd); - - if (!socket->accept(errorMsg)) { - logError("SocketServer::run() tls accept failed: " + errorMsg); - Socket::closeSocket(clientFd); - continue; - } - - // Launch the handleConnection work asynchronously in its own thread. - std::lock_guard lock(_connectionsThreadsMutex); - _connectionsThreads.push_back( - std::make_pair(connectionState, - std::thread(&SocketServer::handleConnection, - this, - std::move(socket), - connectionState))); - } -} - -size_t SocketServer::getConnectionsThreadsCount() -{ - std::lock_guard lock(_connectionsThreadsMutex); - return _connectionsThreads.size(); -} - -void SocketServer::runGC() -{ - setThreadName("SocketServer::GC"); - - 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; - } - - // Unless we are stopping the server, wait for a connection - // to be terminated to run the threads GC, instead of busy waiting - // with a sleep - if (!_stopGc) { - std::unique_lock lock(_conditionVariableMutexGC); - _conditionVariableGC.wait(lock); - } - } -} - -void SocketServer::setTLSOptions(const SocketTLSOptions &socketTLSOptions) -{ - _socketTLSOptions = socketTLSOptions; -} - -void SocketServer::onSetTerminatedCallback() -{ - // a connection got terminated, we can run the connection thread GC, - // so wake up the thread responsible for that - _conditionVariableGC.notify_one(); -} - -int SocketServer::getPort() { return _port; } - -std::string SocketServer::getHost() { return _host; } - -int SocketServer::getBacklog() { return _backlog; } - -std::size_t SocketServer::getMaxConnections() { return _maxConnections; } - -int SocketServer::getAddressFamily() { return _addressFamily; } -} // namespace ix diff --git a/ixwebsocket/IXSocketTLSOptions.cpp b/ixwebsocket/IXSocketTLSOptions.cpp deleted file mode 100644 index c0a84f2..0000000 --- a/ixwebsocket/IXSocketTLSOptions.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * IXSocketTLSOptions.h - * Author: Matt DeBoer - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXSocketTLSOptions.h" - -#include -#include -#include - -namespace ix -{ -const char *kTLSCAFileUseSystemDefaults = "SYSTEM"; -const char *kTLSCAFileDisableVerify = "NONE"; -const char *kTLSCiphersUseDefault = "DEFAULT"; -const char *kTLSInMemoryMarker = "-----BEGIN CERTIFICATE-----"; - -bool SocketTLSOptions::isValid() const -{ - if (!_validated) { - if (!certFile.empty() && !std::ifstream(certFile)) { - _errMsg = "certFile not found: " + certFile; - return false; - } - if (!keyFile.empty() && !std::ifstream(keyFile)) { - _errMsg = "keyFile not found: " + keyFile; - return false; - } - if (!caFile.empty() && caFile != kTLSCAFileDisableVerify && - caFile != kTLSCAFileUseSystemDefaults && !std::ifstream(caFile)) { - _errMsg = "caFile not found: " + caFile; - return false; - } - - if (certFile.empty() != keyFile.empty()) { - _errMsg = - "certFile and keyFile must be both present, or both absent"; - return false; - } - - _validated = true; - } - return true; -} - -bool SocketTLSOptions::hasCertAndKey() const -{ - return !certFile.empty() && !keyFile.empty(); -} - -bool SocketTLSOptions::isUsingSystemDefaults() const -{ - return caFile == kTLSCAFileUseSystemDefaults; -} - -bool SocketTLSOptions::isUsingInMemoryCAs() const -{ - return caFile.find(kTLSInMemoryMarker) != std::string::npos; -} - -bool SocketTLSOptions::isPeerVerifyDisabled() const -{ - return caFile == kTLSCAFileDisableVerify; -} - -bool SocketTLSOptions::isUsingDefaultCiphers() const -{ - return ciphers.empty() || ciphers == kTLSCiphersUseDefault; -} - -const std::string &SocketTLSOptions::getErrorMsg() const { return _errMsg; } - -std::string SocketTLSOptions::getDescription() const -{ - std::stringstream ss; - ss << "TLS Options:" << std::endl; - ss << " certFile = " << certFile << std::endl; - ss << " keyFile = " << keyFile << std::endl; - ss << " caFile = " << caFile << std::endl; - ss << " ciphers = " << ciphers << std::endl; - ss << " ciphers = " << ciphers << std::endl; - return ss.str(); -} -} // namespace ix diff --git a/ixwebsocket/IXStrCaseCompare.cpp b/ixwebsocket/IXStrCaseCompare.cpp deleted file mode 100644 index ca305e7..0000000 --- a/ixwebsocket/IXStrCaseCompare.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * IXStrCaseCompare.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2020 Machine Zone. All rights reserved. - */ - -#include "IXStrCaseCompare.h" - -#include -#include - -namespace ix -{ -bool CaseInsensitiveLess::NocaseCompare::operator()( - const unsigned char &c1, - const unsigned char &c2) const -{ -#if defined(_WIN32) && !defined(__GNUC__) - return std::tolower(c1, std::locale()) < std::tolower(c2, std::locale()); -#else - return std::tolower(c1) < std::tolower(c2); -#endif -} - -bool CaseInsensitiveLess::cmp(const std::string &s1, const std::string &s2) -{ - return std::lexicographical_compare(s1.begin(), - s1.end(), // source range - s2.begin(), - s2.end(), // dest range - NocaseCompare()); // comparison -} - -bool CaseInsensitiveLess::operator()(const std::string &s1, - const std::string &s2) const -{ - return CaseInsensitiveLess::cmp(s1, s2); -} -} // namespace ix diff --git a/ixwebsocket/IXUdpSocket.cpp b/ixwebsocket/IXUdpSocket.cpp deleted file mode 100644 index e64efdb..0000000 --- a/ixwebsocket/IXUdpSocket.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * IXUdpSocket.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2020 Machine Zone, Inc. All rights reserved. - */ - -#include "IXUdpSocket.h" - -#include "IXNetSystem.h" -#include -#include - -namespace ix -{ -UdpSocket::UdpSocket(int fd) : _sockfd(fd) { ; } - -UdpSocket::~UdpSocket() { close(); } - -void UdpSocket::close() -{ - if (_sockfd == -1) - return; - - closeSocket(_sockfd); - _sockfd = -1; -} - -int UdpSocket::getErrno() -{ - int err; - -#ifdef _WIN32 - err = WSAGetLastError(); -#else - err = errno; -#endif - - return err; -} - -bool UdpSocket::isWaitNeeded() -{ - int err = getErrno(); - - if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS) { - return true; - } - - return false; -} - -void UdpSocket::closeSocket(int fd) -{ -#ifdef _WIN32 - closesocket(fd); -#else - ::close(fd); -#endif -} - -bool UdpSocket::init(const std::string &host, int port, std::string &errMsg) -{ - _sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (_sockfd < 0) { - errMsg = "Could not create socket"; - return false; - } - -#ifdef _WIN32 - unsigned long nonblocking = 1; - ioctlsocket(_sockfd, FIONBIO, &nonblocking); -#else - fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking -#endif - - memset(&_server, 0, sizeof(_server)); - _server.sin_family = AF_INET; - _server.sin_port = htons(port); - - // DNS resolution. - struct addrinfo hints, *result = nullptr; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - - int ret = getaddrinfo(host.c_str(), nullptr, &hints, &result); - if (ret != 0) { - errMsg = strerror(UdpSocket::getErrno()); - freeaddrinfo(result); - close(); - return false; - } - - struct sockaddr_in *host_addr = (struct sockaddr_in *)result->ai_addr; - memcpy(&_server.sin_addr, &host_addr->sin_addr, sizeof(struct in_addr)); - freeaddrinfo(result); - - return true; -} - -ssize_t UdpSocket::sendto(const std::string &buffer) -{ - return (ssize_t)::sendto(_sockfd, - buffer.data(), - buffer.size(), - 0, - (struct sockaddr *)&_server, - sizeof(_server)); -} - -ssize_t UdpSocket::recvfrom(char *buffer, size_t length) -{ -#ifdef _WIN32 - int addressLen = (int)sizeof(_server); -#else - socklen_t addressLen = (socklen_t)sizeof(_server); -#endif - return (ssize_t)::recvfrom(_sockfd, - buffer, - length, - 0, - (struct sockaddr *)&_server, - &addressLen); -} -} // namespace ix diff --git a/ixwebsocket/IXUrlParser.cpp b/ixwebsocket/IXUrlParser.cpp deleted file mode 100644 index 6cccc5e..0000000 --- a/ixwebsocket/IXUrlParser.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* - * 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. - * - * IXUrlParser.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXUrlParser.h" - -#include -#include -#include - -namespace -{ -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) {} -}; - -static bool IsSchemeValid(const std::string &SchemeName) -{ - for (auto c : SchemeName) { - if (!isalpha(c) && c != '+' && c != '-' && c != '.') - return false; - } - - return true; -} - -bool 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 -clParseURL clParseURL::ParseURL(const std::string &URL) -{ - clParseURL Result; - - const char *CurrentString = URL.c_str(); - - /* - * : - * := [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; - } - - /* - * //:@:/ - * 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 : 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; -} -} // namespace - -namespace ix -{ -bool UrlParser::parse(const std::string &url, - std::string &protocol, - std::string &host, - std::string &path, - std::string &query, - int &port) -{ - clParseURL res = clParseURL::ParseURL(url); - - if (!res.IsValid()) { - return false; - } - - protocol = res.m_Scheme; - host = res.m_Host; - path = res.m_Path; - query = res.m_Query; - - if (!res.GetPort(&port)) { - if (protocol == "ws" || protocol == "http") { - port = 80; - } else if (protocol == "wss" || protocol == "https") { - port = 443; - } else { - // Invalid protocol. Should be caught by regex check - // but this missing branch trigger cpplint linter. - return false; - } - } - - if (path.empty()) { - path = "/"; - } else if (path[0] != '/') { - path = '/' + path; - } - - if (!query.empty()) { - path += "?"; - path += query; - } - - return true; -} - -} // namespace ix diff --git a/ixwebsocket/IXUserAgent.cpp b/ixwebsocket/IXUserAgent.cpp deleted file mode 100644 index aa9a37e..0000000 --- a/ixwebsocket/IXUserAgent.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * IXUserAgent.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXUserAgent.h" - -#include "IXWebSocketVersion.h" -#include -#ifdef IXWEBSOCKET_USE_ZLIB -#include -#endif - -// Platform name -#if defined(_WIN32) -#define PLATFORM_NAME "windows" // Windows -#elif defined(_WIN64) -#define PLATFORM_NAME "windows" // Windows -#elif defined(__CYGWIN__) && !defined(_WIN32) -#define PLATFORM_NAME "windows" // Windows (Cygwin POSIX under Microsoft Window) -#elif defined(__ANDROID__) -#define PLATFORM_NAME \ - "android" // Android (implies Linux, so it must come first) -#elif defined(__linux__) -#define PLATFORM_NAME \ - "linux" // Debian, Ubuntu, Gentoo, Fedora, openSUSE, RedHat, Centos and - // other -#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__) -#include -#if defined(BSD) -#define PLATFORM_NAME "bsd" // FreeBSD, NetBSD, OpenBSD, DragonFly BSD -#endif -#elif defined(__hpux) -#define PLATFORM_NAME "hp-ux" // HP-UX -#elif defined(_AIX) -#define PLATFORM_NAME "aix" // IBM AIX -#elif defined(__APPLE__) && defined(__MACH__) // Apple OSX and iOS (Darwin) -#include -#if TARGET_IPHONE_SIMULATOR == 1 -#define PLATFORM_NAME "ios" // Apple iOS -#elif TARGET_OS_IPHONE == 1 -#define PLATFORM_NAME "ios" // Apple iOS -#elif TARGET_OS_MAC == 1 -#define PLATFORM_NAME "macos" // Apple OSX -#endif -#elif defined(__sun) && defined(__SVR4) -#define PLATFORM_NAME "solaris" // Oracle Solaris, Open Indiana -#else -#define PLATFORM_NAME "unknown platform" -#endif - -// SSL -#ifdef IXWEBSOCKET_USE_MBED_TLS -#include -#elif defined(IXWEBSOCKET_USE_OPEN_SSL) -#include -#endif - -namespace ix -{ -std::string userAgent() -{ - std::stringstream ss; - - // IXWebSocket Version - ss << "ixwebsocket/" << IX_WEBSOCKET_VERSION; - - // Platform - ss << " " << PLATFORM_NAME; - - // TLS -#ifdef IXWEBSOCKET_USE_TLS -#ifdef IXWEBSOCKET_USE_MBED_TLS - ss << " ssl/mbedtls " << MBEDTLS_VERSION_STRING; -#elif defined(IXWEBSOCKET_USE_OPEN_SSL) - ss << " ssl/OpenSSL " << OPENSSL_VERSION_TEXT; -#elif __APPLE__ - ss << " ssl/SecureTransport"; -#endif -#else - ss << " nossl"; -#endif - -#ifdef IXWEBSOCKET_USE_ZLIB - // Zlib version - ss << " zlib " << ZLIB_VERSION; -#endif - - return ss.str(); -} -} // namespace ix diff --git a/ixwebsocket/IXUuid.cpp b/ixwebsocket/IXUuid.cpp deleted file mode 100644 index 103bc47..0000000 --- a/ixwebsocket/IXUuid.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * IXUuid.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone. All rights reserved. - */ - -/** - * Generate a random uuid similar to the uuid python module - * - * >>> import uuid - * >>> uuid.uuid4().hex - * 'bec08155b37d4050a1f3c3fa0276bf12' - * - * Code adapted from https://github.com/r-lyeh-archived/sole - */ - -#include "IXUuid.h" - -#include -#include -#include -#include - - -namespace ix -{ -class Uuid -{ -public: - Uuid(); - std::string toString() const; - -private: - uint64_t _ab; - uint64_t _cd; -}; - -Uuid::Uuid() -{ - static std::random_device rd; - static std::uniform_int_distribution dist(0, (uint64_t)(~0)); - - _ab = dist(rd); - _cd = dist(rd); - - _ab = (_ab & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL; - _cd = (_cd & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL; -} - -std::string Uuid::toString() const -{ - std::stringstream ss; - ss << std::hex << std::nouppercase << std::setfill('0'); - - uint32_t a = (_ab >> 32); - uint32_t b = (_ab & 0xFFFFFFFF); - uint32_t c = (_cd >> 32); - uint32_t d = (_cd & 0xFFFFFFFF); - - ss << std::setw(8) << (a); - ss << std::setw(4) << (b >> 16); - ss << std::setw(4) << (b & 0xFFFF); - ss << std::setw(4) << (c >> 16); - ss << std::setw(4) << (c & 0xFFFF); - ss << std::setw(8) << d; - - return ss.str(); -} - -std::string uuid4() -{ - Uuid id; - return id.toString(); -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocket.cpp b/ixwebsocket/IXWebSocket.cpp deleted file mode 100644 index efb8e5a..0000000 --- a/ixwebsocket/IXWebSocket.cpp +++ /dev/null @@ -1,588 +0,0 @@ -/* - * IXWebSocket.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocket.h" - -#include "IXExponentialBackoff.h" -#include "IXSetThreadName.h" -#include "IXUniquePtr.h" -#include "IXUtf8Validator.h" -#include "IXWebSocketHandshake.h" -#include -#include - - -namespace -{ -const std::string emptyMsg; -} // namespace - - -namespace ix -{ -OnTrafficTrackerCallback WebSocket::_onTrafficTrackerCallback = nullptr; -const int WebSocket::kDefaultHandShakeTimeoutSecs(60); -const int WebSocket::kDefaultPingIntervalSecs(-1); -const bool WebSocket::kDefaultEnablePong(true); -const uint32_t - WebSocket::kDefaultMaxWaitBetweenReconnectionRetries(10 * 1000); // 10s -const uint32_t WebSocket::kDefaultMinWaitBetweenReconnectionRetries(1); // 1 ms - -WebSocket::WebSocket() - : _onMessageCallback(OnMessageCallback()), _stop(false), - _automaticReconnection(true), - _maxWaitBetweenReconnectionRetries( - kDefaultMaxWaitBetweenReconnectionRetries), - _minWaitBetweenReconnectionRetries( - kDefaultMinWaitBetweenReconnectionRetries), - _handshakeTimeoutSecs(kDefaultHandShakeTimeoutSecs), - _enablePong(kDefaultEnablePong), - _pingIntervalSecs(kDefaultPingIntervalSecs) -{ - _ws.setOnCloseCallback([this](uint16_t code, - const std::string &reason, - size_t wireSize, - bool remote) { - _onMessageCallback(ix::make_unique( - WebSocketMessageType::Close, - emptyMsg, - wireSize, - WebSocketErrorInfo(), - WebSocketOpenInfo(), - WebSocketCloseInfo(code, reason, remote))); - }); -} - -WebSocket::~WebSocket() -{ - stop(); - _ws.setOnCloseCallback(nullptr); -} - -void WebSocket::setUrl(const std::string &url) -{ - std::lock_guard lock(_configMutex); - _url = url; -} - -void WebSocket::setHandshakeTimeout(int handshakeTimeoutSecs) -{ - _handshakeTimeoutSecs = handshakeTimeoutSecs; -} - -void WebSocket::setExtraHeaders(const WebSocketHttpHeaders &headers) -{ - std::lock_guard lock(_configMutex); - _extraHeaders = headers; -} - -const std::string WebSocket::getUrl() const -{ - std::lock_guard lock(_configMutex); - return _url; -} - -void WebSocket::setPerMessageDeflateOptions( - const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions) -{ - std::lock_guard lock(_configMutex); - _perMessageDeflateOptions = perMessageDeflateOptions; -} - -void WebSocket::setTLSOptions(const SocketTLSOptions &socketTLSOptions) -{ - std::lock_guard lock(_configMutex); - _socketTLSOptions = socketTLSOptions; -} - -const WebSocketPerMessageDeflateOptions - WebSocket::getPerMessageDeflateOptions() const -{ - std::lock_guard lock(_configMutex); - return _perMessageDeflateOptions; -} - -void WebSocket::setPingInterval(int pingIntervalSecs) -{ - std::lock_guard lock(_configMutex); - _pingIntervalSecs = pingIntervalSecs; -} - -int WebSocket::getPingInterval() const -{ - std::lock_guard lock(_configMutex); - return _pingIntervalSecs; -} - -void WebSocket::enablePong() -{ - std::lock_guard lock(_configMutex); - _enablePong = true; -} - -void WebSocket::disablePong() -{ - std::lock_guard lock(_configMutex); - _enablePong = false; -} - -void WebSocket::enablePerMessageDeflate() -{ - std::lock_guard lock(_configMutex); - WebSocketPerMessageDeflateOptions perMessageDeflateOptions(true); - _perMessageDeflateOptions = perMessageDeflateOptions; -} - -void WebSocket::disablePerMessageDeflate() -{ - std::lock_guard lock(_configMutex); - WebSocketPerMessageDeflateOptions perMessageDeflateOptions(false); - _perMessageDeflateOptions = perMessageDeflateOptions; -} - -void WebSocket::setMaxWaitBetweenReconnectionRetries( - uint32_t maxWaitBetweenReconnectionRetries) -{ - std::lock_guard lock(_configMutex); - _maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries; -} - -void WebSocket::setMinWaitBetweenReconnectionRetries( - uint32_t minWaitBetweenReconnectionRetries) -{ - std::lock_guard lock(_configMutex); - _minWaitBetweenReconnectionRetries = minWaitBetweenReconnectionRetries; -} - -uint32_t WebSocket::getMaxWaitBetweenReconnectionRetries() const -{ - std::lock_guard lock(_configMutex); - return _maxWaitBetweenReconnectionRetries; -} - -uint32_t WebSocket::getMinWaitBetweenReconnectionRetries() const -{ - std::lock_guard lock(_configMutex); - return _minWaitBetweenReconnectionRetries; -} - -void WebSocket::start() -{ - if (_thread.joinable()) - return; // we've already been started - - _thread = std::thread(&WebSocket::run, this); -} - -void WebSocket::stop(uint16_t code, const std::string &reason) -{ - close(code, reason); - - if (_thread.joinable()) { - // wait until working thread will exit - // it will exit after close operation is finished - _stop = true; - _sleepCondition.notify_one(); - _thread.join(); - _stop = false; - } -} - -WebSocketInitResult WebSocket::connect(int timeoutSecs) -{ - { - std::lock_guard lock(_configMutex); - _ws.configure(_perMessageDeflateOptions, - _socketTLSOptions, - _enablePong, - _pingIntervalSecs); - } - - WebSocketHttpHeaders headers(_extraHeaders); - std::string subProtocolsHeader; - auto subProtocols = getSubProtocols(); - if (!subProtocols.empty()) { - // - // Sub Protocol strings are comma separated. - // Python code to do that is: - // >>> ','.join(['json', 'msgpack']) - // 'json,msgpack' - // - int i = 0; - for (auto subProtocol : subProtocols) { - if (i++ != 0) { - subProtocolsHeader += ","; - } - subProtocolsHeader += subProtocol; - } - headers["Sec-WebSocket-Protocol"] = subProtocolsHeader; - } - - WebSocketInitResult status = _ws.connectToUrl(_url, headers, timeoutSecs); - if (!status.success) { - return status; - } - - _onMessageCallback(ix::make_unique( - WebSocketMessageType::Open, - emptyMsg, - 0, - WebSocketErrorInfo(), - WebSocketOpenInfo(status.uri, status.headers, status.protocol), - WebSocketCloseInfo())); - - if (_pingIntervalSecs > 0) { - // Send a heart beat right away - _ws.sendHeartBeat(); - } - - return status; -} - -WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr socket, - int timeoutSecs, - bool enablePerMessageDeflate) -{ - { - std::lock_guard lock(_configMutex); - _ws.configure(_perMessageDeflateOptions, - _socketTLSOptions, - _enablePong, - _pingIntervalSecs); - } - - WebSocketInitResult status = _ws.connectToSocket(std::move(socket), - timeoutSecs, - enablePerMessageDeflate); - if (!status.success) { - return status; - } - - _onMessageCallback(ix::make_unique( - WebSocketMessageType::Open, - emptyMsg, - 0, - WebSocketErrorInfo(), - WebSocketOpenInfo(status.uri, status.headers), - WebSocketCloseInfo())); - - if (_pingIntervalSecs > 0) { - // Send a heart beat right away - _ws.sendHeartBeat(); - } - - return status; -} - -bool WebSocket::isConnected() const -{ - return getReadyState() == ReadyState::Open; -} - -bool WebSocket::isClosing() const -{ - return getReadyState() == ReadyState::Closing; -} - -void WebSocket::close(uint16_t code, const std::string &reason) -{ - _ws.close(code, reason); -} - -void WebSocket::checkConnection(bool firstConnectionAttempt) -{ - using millis = std::chrono::duration; - - uint32_t retries = 0; - millis duration(0); - - // Try to connect perpertually - while (true) { - 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) { - std::unique_lock lock(_sleepMutex); - _sleepCondition.wait_for(lock, duration); - } - - if (_stop) { - break; - } - - // Try to connect synchronously - ix::WebSocketInitResult status = connect(_handshakeTimeoutSecs); - - if (!status.success) { - WebSocketErrorInfo connectErr; - - if (_automaticReconnection) { - duration = millis(calculateRetryWaitMilliseconds( - retries++, - _maxWaitBetweenReconnectionRetries, - _minWaitBetweenReconnectionRetries)); - - connectErr.wait_time = duration.count(); - connectErr.retries = retries; - } - - connectErr.reason = status.errorStr; - connectErr.http_status = status.http_status; - - _onMessageCallback( - ix::make_unique(WebSocketMessageType::Error, - emptyMsg, - 0, - connectErr, - WebSocketOpenInfo(), - WebSocketCloseInfo())); - } - } -} - -void WebSocket::run() -{ - setThreadName(getUrl()); - - bool firstConnectionAttempt = true; - - while (true) { - // 1. Make sure we are always connected - checkConnection(firstConnectionAttempt); - - firstConnectionAttempt = false; - - // if here we are closed then checkConnection was not able to connect - if (getReadyState() == ReadyState::Closed) { - break; - } - - // We can avoid to poll if we want to stop and are not closing - if (_stop && !isClosing()) - break; - - // 2. Poll to see if there's any new data available - WebSocketTransport::PollResult pollResult = _ws.poll(); - - // 3. Dispatch the incoming messages - _ws.dispatch( - pollResult, - [this](const std::string &msg, - size_t wireSize, - bool decompressionError, - WebSocketTransport::MessageKind messageKind) { - WebSocketMessageType webSocketMessageType{ - WebSocketMessageType::Error}; - switch (messageKind) { - case WebSocketTransport::MessageKind::MSG_TEXT: - case WebSocketTransport::MessageKind::MSG_BINARY: { - webSocketMessageType = WebSocketMessageType::Message; - } break; - - case WebSocketTransport::MessageKind::PING: { - webSocketMessageType = WebSocketMessageType::Ping; - } break; - - case WebSocketTransport::MessageKind::PONG: { - webSocketMessageType = WebSocketMessageType::Pong; - } break; - - case WebSocketTransport::MessageKind::FRAGMENT: { - webSocketMessageType = WebSocketMessageType::Fragment; - } break; - } - - WebSocketErrorInfo webSocketErrorInfo; - webSocketErrorInfo.decompressionError = decompressionError; - - bool binary = - messageKind == WebSocketTransport::MessageKind::MSG_BINARY; - - _onMessageCallback( - ix::make_unique(webSocketMessageType, - msg, - wireSize, - webSocketErrorInfo, - WebSocketOpenInfo(), - WebSocketCloseInfo(), - binary)); - - WebSocket::invokeTrafficTrackerCallback(wireSize, true); - }); - } -} - -void WebSocket::setOnMessageCallback(const OnMessageCallback &callback) -{ - _onMessageCallback = callback; -} - -bool WebSocket::isOnMessageCallbackRegistered() const -{ - return _onMessageCallback != nullptr; -} - -void WebSocket::setTrafficTrackerCallback( - const OnTrafficTrackerCallback &callback) -{ - _onTrafficTrackerCallback = callback; -} - -void WebSocket::resetTrafficTrackerCallback() -{ - setTrafficTrackerCallback(nullptr); -} - -void WebSocket::invokeTrafficTrackerCallback(size_t size, bool incoming) -{ - if (_onTrafficTrackerCallback) { - _onTrafficTrackerCallback(size, incoming); - } -} - -WebSocketSendInfo WebSocket::send(const std::string &data, - bool binary, - const OnProgressCallback &onProgressCallback) -{ - return (binary) ? sendBinary(data, onProgressCallback) - : sendText(data, onProgressCallback); -} - -WebSocketSendInfo - WebSocket::sendBinary(const std::string &text, - const OnProgressCallback &onProgressCallback) -{ - return sendMessage(text, SendMessageKind::Binary, onProgressCallback); -} - -WebSocketSendInfo - WebSocket::sendText(const std::string &text, - const OnProgressCallback &onProgressCallback) -{ - if (!validateUtf8(text)) { - close(WebSocketCloseConstants::kInvalidFramePayloadData, - WebSocketCloseConstants::kInvalidFramePayloadDataMessage); - return false; - } - return sendMessage(text, SendMessageKind::Text, onProgressCallback); -} - -WebSocketSendInfo WebSocket::ping(const std::string &text) -{ - // Standard limit ping message size - constexpr size_t pingMaxPayloadSize = 125; - if (text.size() > pingMaxPayloadSize) - return WebSocketSendInfo(false); - - return sendMessage(text, SendMessageKind::Ping); -} - -WebSocketSendInfo - WebSocket::sendMessage(const std::string &text, - SendMessageKind sendMessageKind, - const OnProgressCallback &onProgressCallback) -{ - if (!isConnected()) - return WebSocketSendInfo(false); - - // - // It is OK to read and write on the same socket in 2 different threads. - // https://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid - // - // This makes it so that messages are sent right away, and we dont need - // a timeout while we poll to keep wake ups to a minimum (which helps - // with battery life), and use the system select call to notify us when - // incoming messages are arriving / there's data to be received. - // - std::lock_guard lock(_writeMutex); - WebSocketSendInfo webSocketSendInfo; - - switch (sendMessageKind) { - case SendMessageKind::Text: { - webSocketSendInfo = _ws.sendText(text, onProgressCallback); - } break; - - case SendMessageKind::Binary: { - webSocketSendInfo = _ws.sendBinary(text, onProgressCallback); - } break; - - case SendMessageKind::Ping: { - webSocketSendInfo = _ws.sendPing(text); - } break; - } - - WebSocket::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false); - - return webSocketSendInfo; -} - -ReadyState WebSocket::getReadyState() const -{ - switch (_ws.getReadyState()) { - 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; - } -} - -std::string WebSocket::readyStateToString(ReadyState readyState) -{ - switch (readyState) { - case ReadyState::Open: - return "OPEN"; - case ReadyState::Connecting: - return "CONNECTING"; - case ReadyState::Closing: - return "CLOSING"; - case ReadyState::Closed: - return "CLOSED"; - default: - return "UNKNOWN"; - } -} - -void WebSocket::enableAutomaticReconnection() { _automaticReconnection = true; } - -void WebSocket::disableAutomaticReconnection() -{ - _automaticReconnection = false; -} - -bool WebSocket::isAutomaticReconnectionEnabled() const -{ - return _automaticReconnection; -} - -size_t WebSocket::bufferedAmount() const { return _ws.bufferedAmount(); } - -void WebSocket::addSubProtocol(const std::string &subProtocol) -{ - std::lock_guard lock(_configMutex); - _subProtocols.push_back(subProtocol); -} - -const std::vector &WebSocket::getSubProtocols() -{ - std::lock_guard lock(_configMutex); - return _subProtocols; -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocketCloseConstants.cpp b/ixwebsocket/IXWebSocketCloseConstants.cpp deleted file mode 100644 index 6c7159c..0000000 --- a/ixwebsocket/IXWebSocketCloseConstants.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * IXWebSocketCloseConstants.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocketCloseConstants.h" - -namespace ix -{ -const uint16_t WebSocketCloseConstants::kNormalClosureCode(1000); -const uint16_t WebSocketCloseConstants::kInternalErrorCode(1011); -const uint16_t WebSocketCloseConstants::kAbnormalCloseCode(1006); -const uint16_t WebSocketCloseConstants::kInvalidFramePayloadData(1007); -const uint16_t WebSocketCloseConstants::kProtocolErrorCode(1002); -const uint16_t WebSocketCloseConstants::kNoStatusCodeErrorCode(1005); - -const std::string - WebSocketCloseConstants::kNormalClosureMessage("Normal closure"); -const std::string - WebSocketCloseConstants::kInternalErrorMessage("Internal error"); -const std::string - WebSocketCloseConstants::kAbnormalCloseMessage("Abnormal closure"); -const std::string WebSocketCloseConstants::kPingTimeoutMessage("Ping timeout"); -const std::string - WebSocketCloseConstants::kProtocolErrorMessage("Protocol error"); -const std::string - WebSocketCloseConstants::kNoStatusCodeErrorMessage("No status code"); -const std::string - WebSocketCloseConstants::kProtocolErrorReservedBitUsed("Reserved bit used"); -const std::string WebSocketCloseConstants::kProtocolErrorPingPayloadOversized( - "Ping reason control frame with payload length > 125 octets"); -const std::string - WebSocketCloseConstants::kProtocolErrorCodeControlMessageFragmented( - "Control message fragmented"); -const std::string - WebSocketCloseConstants::kProtocolErrorCodeDataOpcodeOutOfSequence( - "Fragmentation: data message out of sequence"); -const std::string - WebSocketCloseConstants::kProtocolErrorCodeContinuationOpCodeOutOfSequence( - "Fragmentation: continuation opcode out of sequence"); -const std::string WebSocketCloseConstants::kInvalidFramePayloadDataMessage( - "Invalid frame payload data"); -const std::string - WebSocketCloseConstants::kInvalidCloseCodeMessage("Invalid close code"); -} // namespace ix diff --git a/ixwebsocket/IXWebSocketHandshake.cpp b/ixwebsocket/IXWebSocketHandshake.cpp deleted file mode 100644 index 5cd7874..0000000 --- a/ixwebsocket/IXWebSocketHandshake.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/* - * IXWebSocketHandshake.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocketHandshake.h" - -#include "IXHttp.h" -#include "IXSocketConnect.h" -#include "IXStrCaseCompare.h" -#include "IXUrlParser.h" -#include "IXUserAgent.h" -#include "IXWebSocketHandshakeKeyGen.h" -#include -#include -#include -#include - - -namespace ix -{ -WebSocketHandshake::WebSocketHandshake( - std::atomic &requestInitCancellation, - std::unique_ptr &socket, - WebSocketPerMessageDeflatePtr &perMessageDeflate, - WebSocketPerMessageDeflateOptions &perMessageDeflateOptions, - std::atomic &enablePerMessageDeflate) - : _requestInitCancellation(requestInitCancellation), _socket(socket), - _perMessageDeflate(perMessageDeflate), - _perMessageDeflateOptions(perMessageDeflateOptions), - _enablePerMessageDeflate(enablePerMessageDeflate) -{ -} - -bool WebSocketHandshake::insensitiveStringCompare(const std::string &a, - const std::string &b) -{ - return CaseInsensitiveLess::cmp(a, b) == 0; -} - -std::string WebSocketHandshake::genRandomString(const int len) -{ - std::string alphanum = "0123456789" - "ABCDEFGH" - "abcdefgh"; - - std::random_device r; - std::default_random_engine e1(r()); - std::uniform_int_distribution dist(0, (int)alphanum.size() - 1); - - std::string s; - s.resize(len); - - for (int i = 0; i < len; ++i) { - int x = dist(e1); - s[i] = alphanum[x]; - } - - return s; -} - -WebSocketInitResult - WebSocketHandshake::sendErrorResponse(int code, const std::string &reason) -{ - std::stringstream ss; - ss << "HTTP/1.1 "; - ss << code; - ss << " "; - ss << reason; - ss << "\r\n"; - ss << "Server: " << userAgent() << "\r\n"; - - // Socket write can only be cancelled through a timeout here, not manually. - static std::atomic requestInitCancellation(false); - auto isCancellationRequested = - makeCancellationRequestWithTimeout(1, requestInitCancellation); - - if (!_socket->writeBytes(ss.str(), isCancellationRequested)) { - return WebSocketInitResult(false, - 500, - "Timed out while sending error response"); - } - - return WebSocketInitResult(false, code, reason); -} - -WebSocketInitResult WebSocketHandshake::clientHandshake( - const std::string &url, - const WebSocketHttpHeaders &extraHeaders, - const std::string &host, - const std::string &path, - int port, - int timeoutSecs) -{ - _requestInitCancellation = false; - - auto isCancellationRequested = - makeCancellationRequestWithTimeout(timeoutSecs, - _requestInitCancellation); - - std::string errMsg; - bool success = - _socket->connect(host, port, errMsg, isCancellationRequested); - if (!success) { - std::stringstream ss; - ss << "Unable to connect to " << host << " on port " << port - << ", error: " << errMsg; - return WebSocketInitResult(false, 0, ss.str()); - } - - // - // Generate a random 24 bytes string which looks like it is base64 encoded - // y3JJHMbDL1EzLkh9GBhXDw== - // 0cb3Vd9HkbpVVumoS3Noka== - // - // See - // https://stackoverflow.com/questions/18265128/what-is-sec-websocket-key-for - // - std::string secWebSocketKey = genRandomString(22); - secWebSocketKey += "=="; - - std::stringstream ss; - ss << "GET " << path << " HTTP/1.1\r\n"; - ss << "Host: " << host << ":" << port << "\r\n"; - ss << "Upgrade: websocket\r\n"; - ss << "Connection: Upgrade\r\n"; - ss << "Sec-WebSocket-Version: 13\r\n"; - ss << "Sec-WebSocket-Key: " << secWebSocketKey << "\r\n"; - - // User-Agent can be customized by users - if (extraHeaders.find("User-Agent") == extraHeaders.end()) { - ss << "User-Agent: " << userAgent() << "\r\n"; - } - - for (auto &it : extraHeaders) { - ss << it.first << ": " << it.second << "\r\n"; - } - - if (_enablePerMessageDeflate) { - ss << _perMessageDeflateOptions.generateHeader(); - } - - ss << "\r\n"; - - if (!_socket->writeBytes(ss.str(), isCancellationRequested)) { - return WebSocketInitResult( - false, - 0, - std::string("Failed sending GET request to ") + url); - } - - // Read HTTP status line - auto lineResult = _socket->readLine(isCancellationRequested); - auto lineValid = lineResult.first; - auto line = lineResult.second; - - if (!lineValid) { - return WebSocketInitResult( - false, - 0, - std::string("Failed reading HTTP status line from ") + url); - } - - // Validate status - auto statusLine = Http::parseStatusLine(line); - std::string httpVersion = statusLine.first; - int status = statusLine.second; - - // HTTP/1.0 is too old. - if (httpVersion != "HTTP/1.1") { - std::stringstream ss; - ss << "Expecting HTTP/1.1, got " << httpVersion << ". " - << "Rejecting connection to " << url << ", status: " << status - << ", HTTP Status line: " << line; - return WebSocketInitResult(false, status, ss.str()); - } - - auto result = parseHttpHeaders(_socket, isCancellationRequested); - auto headersValid = result.first; - auto headers = result.second; - - if (!headersValid) { - return WebSocketInitResult(false, status, "Error parsing HTTP headers"); - } - - // We want an 101 HTTP status for websocket, otherwise it could be - // a redirection (like 301) - if (status != 101) { - std::stringstream ss; - ss << "Expecting status 101 (Switching Protocol), got " << status - << " status connecting to " << url << ", HTTP Status line: " << line; - - return WebSocketInitResult(false, status, ss.str(), headers, path); - } - - // Check the presence of the connection field - if (headers.find("connection") == headers.end()) { - std::string errorMsg("Missing connection value"); - return WebSocketInitResult(false, status, errorMsg); - } - - // Check the value of the connection field - // Some websocket servers (Go/Gorilla?) send lowercase values for the - // connection header, so do a case insensitive comparison - // - // See - // https://github.com/apache/thrift/commit/7c4bdf9914fcba6c89e0f69ae48b9675578f084a - // - if (!insensitiveStringCompare(headers["connection"], "Upgrade")) { - std::stringstream ss; - ss << "Invalid connection value: " << headers["connection"]; - return WebSocketInitResult(false, status, ss.str()); - } - - char output[29] = {}; - WebSocketHandshakeKeyGen::generate(secWebSocketKey, output); - if (std::string(output) != headers["sec-websocket-accept"]) { - std::string errorMsg("Invalid Sec-WebSocket-Accept value"); - return WebSocketInitResult(false, status, errorMsg); - } - - if (_enablePerMessageDeflate) { - // Parse the server response. Does it support deflate ? - std::string header = headers["sec-websocket-extensions"]; - WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions( - header); - - // If the server does not support that extension, disable it. - if (!webSocketPerMessageDeflateOptions.enabled()) { - _enablePerMessageDeflate = false; - } - // Otherwise try to initialize the deflate engine (zlib) - else if (!_perMessageDeflate->init(webSocketPerMessageDeflateOptions)) { - return WebSocketInitResult( - false, - 0, - "Failed to initialize per message deflate engine"); - } - } - - return WebSocketInitResult(true, status, "", headers, path); -} - -WebSocketInitResult - WebSocketHandshake::serverHandshake(int timeoutSecs, - bool enablePerMessageDeflate) -{ - _requestInitCancellation = false; - - auto isCancellationRequested = - makeCancellationRequestWithTimeout(timeoutSecs, - _requestInitCancellation); - - // Read first line - auto lineResult = _socket->readLine(isCancellationRequested); - auto lineValid = lineResult.first; - auto line = lineResult.second; - - if (!lineValid) { - return sendErrorResponse(400, "Error reading HTTP request line"); - } - - // Validate request line (GET /foo HTTP/1.1\r\n) - auto requestLine = Http::parseRequestLine(line); - auto method = std::get<0>(requestLine); - auto uri = std::get<1>(requestLine); - auto httpVersion = std::get<2>(requestLine); - - if (method != "GET") { - return sendErrorResponse(400, - "Invalid HTTP method, need GET, got " + - method); - } - - if (httpVersion != "HTTP/1.1") { - return sendErrorResponse(400, - "Invalid HTTP version, need HTTP/1.1, got: " + - httpVersion); - } - - // Retrieve and validate HTTP headers - auto result = parseHttpHeaders(_socket, isCancellationRequested); - auto headersValid = result.first; - auto headers = result.second; - - if (!headersValid) { - return sendErrorResponse(400, "Error parsing HTTP headers"); - } - - if (headers.find("sec-websocket-key") == headers.end()) { - return sendErrorResponse(400, "Missing Sec-WebSocket-Key value"); - } - - if (headers.find("upgrade") == headers.end()) { - return sendErrorResponse(400, "Missing Upgrade header"); - } - - if (!insensitiveStringCompare(headers["upgrade"], "WebSocket") && - headers["Upgrade"] != "keep-alive, Upgrade") // special case for firefox - { - return sendErrorResponse(400, - "Invalid Upgrade header, " - "need WebSocket, got " + - headers["upgrade"]); - } - - if (headers.find("sec-websocket-version") == headers.end()) { - return sendErrorResponse(400, "Missing Sec-WebSocket-Version value"); - } - - { - std::stringstream ss; - ss << headers["sec-websocket-version"]; - int version; - ss >> version; - - if (version != 13) { - return sendErrorResponse(400, - "Invalid Sec-WebSocket-Version, " - "need 13, got " + - ss.str()); - } - } - - char output[29] = {}; - WebSocketHandshakeKeyGen::generate(headers["sec-websocket-key"], output); - - std::stringstream ss; - ss << "HTTP/1.1 101 Switching Protocols\r\n"; - ss << "Sec-WebSocket-Accept: " << std::string(output) << "\r\n"; - ss << "Upgrade: websocket\r\n"; - ss << "Connection: Upgrade\r\n"; - ss << "Server: " << userAgent() << "\r\n"; - - // Parse the client headers. Does it support deflate ? - std::string header = headers["sec-websocket-extensions"]; - WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header); - - // If the client has requested that extension, - if (webSocketPerMessageDeflateOptions.enabled() && - enablePerMessageDeflate) { - _enablePerMessageDeflate = true; - - if (!_perMessageDeflate->init(webSocketPerMessageDeflateOptions)) { - return WebSocketInitResult( - false, - 0, - "Failed to initialize per message deflate engine"); - } - ss << webSocketPerMessageDeflateOptions.generateHeader(); - } - - ss << "\r\n"; - - if (!_socket->writeBytes(ss.str(), isCancellationRequested)) { - return WebSocketInitResult( - false, - 0, - std::string("Failed sending response to remote end")); - } - - return WebSocketInitResult(true, 200, "", headers, uri); -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocketHttpHeaders.cpp b/ixwebsocket/IXWebSocketHttpHeaders.cpp deleted file mode 100644 index 1100bf3..0000000 --- a/ixwebsocket/IXWebSocketHttpHeaders.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * IXWebSocketHttpHeaders.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocketHttpHeaders.h" - -#include "IXSocket.h" -#include -#include - -namespace ix -{ -std::pair - parseHttpHeaders(std::unique_ptr &socket, - const CancellationRequest &isCancellationRequested) -{ - WebSocketHttpHeaders headers; - - char line[1024]; - int i; - - while (true) { - int colon = 0; - - for (i = 0; - i < 2 || (i < 1023 && line[i - 2] != '\r' && line[i - 1] != '\n'); - ++i) { - if (!socket->readByte(line + i, isCancellationRequested)) { - return std::make_pair(false, headers); - } - - if (line[i] == ':' && colon == 0) { - colon = i; - } - } - if (line[0] == '\r' && line[1] == '\n') { - break; - } - - // line is a single header entry. split by ':', and add it to our - // header map. ignore lines with no colon. - if (colon > 0) { - line[i] = '\0'; - std::string lineStr(line); - // colon is ':', usually colon+1 is ' ', and colon+2 is the start of - // the value. some webservers do not put a space after the colon - // character, so the start of the value might be farther than - // colon+2. The spec says that space after the : should be - // discarded. i is end of string (\0), i-colon is length of string - // minus key; subtract 1 for '\0', 1 for '\n', 1 for '\r', 1 for the - // ' ' after the ':', and total is -4 since we use an std::string - // later on and don't account for '\0', plus the optional first - // space, total is -2 - int start = colon + 1; - while (lineStr[start] == ' ') { - start++; - } - - std::string name(lineStr.substr(0, colon)); - std::string value( - lineStr.substr(start, lineStr.size() - start - 2)); - - headers[name] = value; - } - } - - return std::make_pair(true, headers); -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocketPerMessageDeflate.cpp b/ixwebsocket/IXWebSocketPerMessageDeflate.cpp deleted file mode 100644 index 0106598..0000000 --- a/ixwebsocket/IXWebSocketPerMessageDeflate.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2015, Peter Thorson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the WebSocket++ Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - * - * Adapted from websocketpp/extensions/permessage_deflate/enabled.hpp - * (same license as MZ: https://opensource.org/licenses/BSD-3-Clause) - * - * - Reused zlib compression + decompression bits. - * - Refactored to have 2 class for compression and decompression, to allow - * multi-threading and make sure that _compressBuffer is not shared between - * threads. - * - Original code wasn't working for some reason, I had to add checks - * for the presence of the kEmptyUncompressedBlock at the end of buffer so - * that servers would start accepting receiving/decoding compressed messages. - * Original code was probably modifying the passed in buffers before processing - * in enabled.hpp ? - * - Added more documentation. - * - * Per message Deflate RFC: https://tools.ietf.org/html/rfc7692 - * Chrome websocket -> - * https://github.com/chromium/chromium/tree/2ca8c5037021c9d2ecc00b787d58a31ed8fc8bcb/net/websockets - * - */ - -#include "IXWebSocketPerMessageDeflate.h" - -#include "IXUniquePtr.h" -#include "IXWebSocketPerMessageDeflateCodec.h" -#include "IXWebSocketPerMessageDeflateOptions.h" - -namespace ix -{ -WebSocketPerMessageDeflate::WebSocketPerMessageDeflate() - : _compressor(ix::make_unique()), - _decompressor(ix::make_unique()) -{ - ; -} - -WebSocketPerMessageDeflate::~WebSocketPerMessageDeflate() { ; } - -bool WebSocketPerMessageDeflate::init( - const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions) -{ - bool clientNoContextTakeover = - perMessageDeflateOptions.getClientNoContextTakeover(); - - uint8_t deflateBits = perMessageDeflateOptions.getClientMaxWindowBits(); - uint8_t inflateBits = perMessageDeflateOptions.getServerMaxWindowBits(); - - return _compressor->init(deflateBits, clientNoContextTakeover) && - _decompressor->init(inflateBits, clientNoContextTakeover); -} - -bool WebSocketPerMessageDeflate::compress(const std::string &in, - std::string &out) -{ - return _compressor->compress(in, out); -} - -bool WebSocketPerMessageDeflate::decompress(const std::string &in, - std::string &out) -{ - return _decompressor->decompress(in, out); -} - -} // namespace ix diff --git a/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp b/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp deleted file mode 100644 index dc41d33..0000000 --- a/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - * IXWebSocketPerMessageDeflateCodec.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocketPerMessageDeflateCodec.h" - -#include "IXWebSocketPerMessageDeflateOptions.h" -#include -#include - -namespace -{ -// The passed in size (4) is important, without it the string litteral -// is treated as a char* and the null termination (\x00) makes it -// look like an empty string. -const std::string kEmptyUncompressedBlock = std::string("\x00\x00\xff\xff", 4); -} // namespace - -namespace ix -{ -// -// Compressor -// -WebSocketPerMessageDeflateCompressor::WebSocketPerMessageDeflateCompressor() -{ -#ifdef IXWEBSOCKET_USE_ZLIB - memset(&_deflateState, 0, sizeof(_deflateState)); - - _deflateState.zalloc = Z_NULL; - _deflateState.zfree = Z_NULL; - _deflateState.opaque = Z_NULL; -#endif -} - -WebSocketPerMessageDeflateCompressor::~WebSocketPerMessageDeflateCompressor() -{ -#ifdef IXWEBSOCKET_USE_ZLIB - deflateEnd(&_deflateState); -#endif -} - -bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits, - bool clientNoContextTakeOver) -{ -#ifdef IXWEBSOCKET_USE_ZLIB - int ret = deflateInit2(&_deflateState, - Z_DEFAULT_COMPRESSION, - Z_DEFLATED, - -1 * deflateBits, - 4, // memory level 1-9 - Z_DEFAULT_STRATEGY); - - if (ret != Z_OK) - return false; - - _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH; - - return true; -#else - return false; -#endif -} - -template -bool WebSocketPerMessageDeflateCompressor::endsWithEmptyUnCompressedBlock( - const T &value) -{ - if (kEmptyUncompressedBlock.size() > value.size()) - return false; - auto N = value.size(); - return value[N - 1] == kEmptyUncompressedBlock[3] && - value[N - 2] == kEmptyUncompressedBlock[2] && - value[N - 3] == kEmptyUncompressedBlock[1] && - value[N - 4] == kEmptyUncompressedBlock[0]; -} - -bool WebSocketPerMessageDeflateCompressor::compress(const std::string &in, - std::string &out) -{ - return compressData(in, out); -} - -bool WebSocketPerMessageDeflateCompressor::compress(const std::string &in, - std::vector &out) -{ - return compressData(in, out); -} - -bool WebSocketPerMessageDeflateCompressor::compress( - const std::vector &in, - std::string &out) -{ - return compressData(in, out); -} - -bool WebSocketPerMessageDeflateCompressor::compress( - const std::vector &in, - std::vector &out) -{ - return compressData(in, out); -} - -template -bool WebSocketPerMessageDeflateCompressor::compressData(const T &in, S &out) -{ -#ifdef IXWEBSOCKET_USE_ZLIB - // - // 7.2.1. Compression - // - // An endpoint uses the following algorithm to compress a message. - // - // 1. Compress all the octets of the payload of the message using - // DEFLATE. - // - // 2. If the resulting data does not end with an empty DEFLATE block - // with no compression (the "BTYPE" bits are set to 00), append an - // empty DEFLATE block with no compression to the tail end. - // - // 3. Remove 4 octets (that are 0x00 0x00 0xff 0xff) from the tail end. - // After this step, the last octet of the compressed data contains - // (possibly part of) the DEFLATE header bits with the "BTYPE" bits - // set to 00. - // - size_t output; - - // Clear output - out.clear(); - - if (in.empty()) { - // See issue #167 - // The normal buffer size should be 6 but - // we remove the 4 octets from the tail (#4) - uint8_t buf[2] = {0x02, 0x00}; - out.push_back(buf[0]); - out.push_back(buf[1]); - - return true; - } - - _deflateState.avail_in = (uInt)in.size(); - _deflateState.next_in = (Bytef *)in.data(); - - do { - // Output to local buffer - _deflateState.avail_out = (uInt)_compressBuffer.size(); - _deflateState.next_out = &_compressBuffer.front(); - - deflate(&_deflateState, _flush); - - output = _compressBuffer.size() - _deflateState.avail_out; - - out.insert(out.end(), - _compressBuffer.begin(), - _compressBuffer.begin() + output); - } while (_deflateState.avail_out == 0); - - if (endsWithEmptyUnCompressedBlock(out)) { - out.resize(out.size() - 4); - } - - return true; -#else - return false; -#endif -} - -// -// Decompressor -// -WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor() -{ -#ifdef IXWEBSOCKET_USE_ZLIB - memset(&_inflateState, 0, sizeof(_inflateState)); - - _inflateState.zalloc = Z_NULL; - _inflateState.zfree = Z_NULL; - _inflateState.opaque = Z_NULL; - _inflateState.avail_in = 0; - _inflateState.next_in = Z_NULL; -#endif -} - -WebSocketPerMessageDeflateDecompressor:: - ~WebSocketPerMessageDeflateDecompressor() -{ -#ifdef IXWEBSOCKET_USE_ZLIB - inflateEnd(&_inflateState); -#endif -} - -bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits, - bool clientNoContextTakeOver) -{ -#ifdef IXWEBSOCKET_USE_ZLIB - int ret = inflateInit2(&_inflateState, -1 * inflateBits); - - if (ret != Z_OK) - return false; - - _flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH; - - return true; -#else - return false; -#endif -} - -bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string &in, - std::string &out) -{ -#ifdef IXWEBSOCKET_USE_ZLIB - // - // 7.2.2. Decompression - // - // An endpoint uses the following algorithm to decompress a message. - // - // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the - // payload of the message. - // - // 2. Decompress the resulting data using DEFLATE. - // - std::string inFixed(in); - inFixed += kEmptyUncompressedBlock; - - _inflateState.avail_in = (uInt)inFixed.size(); - _inflateState.next_in = - (unsigned char *)(const_cast(inFixed.data())); - - // Clear output - out.clear(); - - do { - _inflateState.avail_out = (uInt)_compressBuffer.size(); - _inflateState.next_out = &_compressBuffer.front(); - - int ret = inflate(&_inflateState, Z_SYNC_FLUSH); - - if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) { - return false; // zlib error - } - - out.append(reinterpret_cast(&_compressBuffer.front()), - _compressBuffer.size() - _inflateState.avail_out); - } while (_inflateState.avail_out == 0); - - return true; -#else - return false; -#endif -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp b/ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp deleted file mode 100644 index 3f2b4cb..0000000 --- a/ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * IXWebSocketPerMessageDeflateOptions.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocketPerMessageDeflateOptions.h" - -#include -#include -#include - -namespace ix -{ -/// Default values as defined in the RFC -const uint8_t WebSocketPerMessageDeflateOptions::kDefaultServerMaxWindowBits = - 15; -static const uint8_t minServerMaxWindowBits = 8; -static const uint8_t maxServerMaxWindowBits = 15; - -const uint8_t WebSocketPerMessageDeflateOptions::kDefaultClientMaxWindowBits = - 15; -static const uint8_t minClientMaxWindowBits = 8; -static const uint8_t maxClientMaxWindowBits = 15; - -WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions( - bool enabled, - bool clientNoContextTakeover, - bool serverNoContextTakeover, - uint8_t clientMaxWindowBits, - uint8_t serverMaxWindowBits) -{ - _enabled = enabled; - _clientNoContextTakeover = clientNoContextTakeover; - _serverNoContextTakeover = serverNoContextTakeover; - _clientMaxWindowBits = clientMaxWindowBits; - _serverMaxWindowBits = serverMaxWindowBits; - - sanitizeClientMaxWindowBits(); -} - -// -// Four extension parameters are defined for "permessage-deflate" to -// help endpoints manage per-connection resource usage. -// -// - "server_no_context_takeover" -// - "client_no_context_takeover" -// - "server_max_window_bits" -// - "client_max_window_bits" -// -// Server response could look like that: -// -// Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; -// server_no_context_takeover -// -WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions( - std::string extension) -{ - extension = removeSpaces(extension); - - _enabled = false; - _clientNoContextTakeover = false; - _serverNoContextTakeover = false; - _clientMaxWindowBits = kDefaultClientMaxWindowBits; - _serverMaxWindowBits = kDefaultServerMaxWindowBits; - -#ifdef IXWEBSOCKET_USE_ZLIB - // Split by ; - std::string token; - std::stringstream tokenStream(extension); - - while (std::getline(tokenStream, token, ';')) { - if (token == "permessage-deflate") { - _enabled = true; - } - - if (token == "server_no_context_takeover") { - _serverNoContextTakeover = true; - } - - if (token == "client_no_context_takeover") { - _clientNoContextTakeover = true; - } - - if (startsWith(token, "server_max_window_bits=")) { - uint8_t x = - strtol(token.substr(token.find_last_of("=") + 1).c_str(), - nullptr, - 10); - - // Sanitize values to be in the proper range [8, 15] in - // case a server would give us bogus values - _serverMaxWindowBits = - std::min(maxServerMaxWindowBits, - std::max(x, minServerMaxWindowBits)); - } - - if (startsWith(token, "client_max_window_bits=")) { - uint8_t x = - strtol(token.substr(token.find_last_of("=") + 1).c_str(), - nullptr, - 10); - - // Sanitize values to be in the proper range [8, 15] in - // case a server would give us bogus values - _clientMaxWindowBits = - std::min(maxClientMaxWindowBits, - std::max(x, minClientMaxWindowBits)); - - sanitizeClientMaxWindowBits(); - } - } -#endif -} - -void WebSocketPerMessageDeflateOptions::sanitizeClientMaxWindowBits() -{ - // zlib/deflate has a bug with windowsbits == 8, so we silently upgrade it - // to 9 See https://bugs.chromium.org/p/chromium/issues/detail?id=691074 - if (_clientMaxWindowBits == 8) { - _clientMaxWindowBits = 9; - } -} - -std::string WebSocketPerMessageDeflateOptions::generateHeader() -{ -#ifdef IXWEBSOCKET_USE_ZLIB - std::stringstream ss; - ss << "Sec-WebSocket-Extensions: permessage-deflate"; - - if (_clientNoContextTakeover) - ss << "; client_no_context_takeover"; - if (_serverNoContextTakeover) - ss << "; server_no_context_takeover"; - - ss << "; server_max_window_bits=" << _serverMaxWindowBits; - ss << "; client_max_window_bits=" << _clientMaxWindowBits; - - ss << "\r\n"; - - return ss.str(); -#else - return std::string(); -#endif -} - -bool WebSocketPerMessageDeflateOptions::enabled() const -{ -#ifdef IXWEBSOCKET_USE_ZLIB - return _enabled; -#else - return false; -#endif -} - -bool WebSocketPerMessageDeflateOptions::getClientNoContextTakeover() const -{ - return _clientNoContextTakeover; -} - -bool WebSocketPerMessageDeflateOptions::getServerNoContextTakeover() const -{ - return _serverNoContextTakeover; -} - -uint8_t WebSocketPerMessageDeflateOptions::getClientMaxWindowBits() const -{ - return _clientMaxWindowBits; -} - -uint8_t WebSocketPerMessageDeflateOptions::getServerMaxWindowBits() const -{ - return _serverMaxWindowBits; -} - -bool WebSocketPerMessageDeflateOptions::startsWith(const std::string &str, - const std::string &start) -{ - return str.compare(0, start.length(), start) == 0; -} - -std::string - WebSocketPerMessageDeflateOptions::removeSpaces(const std::string &str) -{ - std::string out(str); - out.erase(std::remove_if(out.begin(), - out.end(), - [](unsigned char x) { return std::isspace(x); }), - out.end()); - - return out; -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocketProxyServer.cpp b/ixwebsocket/IXWebSocketProxyServer.cpp deleted file mode 100644 index a4ac231..0000000 --- a/ixwebsocket/IXWebSocketProxyServer.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * IXWebSocketProxyServer.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocketProxyServer.h" - -#include "IXWebSocketServer.h" -#include - -namespace ix -{ -class ProxyConnectionState : public ix::ConnectionState -{ -public: - ProxyConnectionState() : _connected(false) {} - - ix::WebSocket &webSocket() { return _serverWebSocket; } - - bool isConnected() { return _connected; } - - void setConnected() { _connected = true; } - -private: - ix::WebSocket _serverWebSocket; - bool _connected; -}; - -int websocket_proxy_server_main(int port, - const std::string &hostname, - const ix::SocketTLSOptions &tlsOptions, - const std::string &remoteUrl, - const RemoteUrlsMapping &remoteUrlsMapping, - bool /*verbose*/) -{ - ix::WebSocketServer server(port, hostname); - server.setTLSOptions(tlsOptions); - - auto factory = []() -> std::shared_ptr { - return std::make_shared(); - }; - server.setConnectionStateFactory(factory); - - server.setOnConnectionCallback( - [remoteUrl, - remoteUrlsMapping](std::weak_ptr webSocket, - std::shared_ptr connectionState) { - auto state = std::dynamic_pointer_cast( - connectionState); - auto remoteIp = connectionState->getRemoteIp(); - - // Server connection - state->webSocket().setOnMessageCallback( - [webSocket, state, remoteIp](const WebSocketMessagePtr &msg) { - if (msg->type == ix::WebSocketMessageType::Close) { - state->setTerminated(); - } else if (msg->type == ix::WebSocketMessageType::Message) { - auto ws = webSocket.lock(); - if (ws) { - ws->send(msg->str, msg->binary); - } - } - }); - - // Client connection - auto ws = webSocket.lock(); - if (ws) { - ws->setOnMessageCallback([state, remoteUrl, remoteUrlsMapping]( - const WebSocketMessagePtr &msg) { - if (msg->type == ix::WebSocketMessageType::Open) { - // Connect to the 'real' server - std::string url(remoteUrl); - - // maybe we want a different url based on the mapping - std::string host = msg->openInfo.headers["Host"]; - auto it = remoteUrlsMapping.find(host); - if (it != remoteUrlsMapping.end()) { - url = it->second; - } - - // append the uri to form the full url - // (say ws://localhost:1234/foo/?bar=baz) - url += msg->openInfo.uri; - - state->webSocket().setUrl(url); - state->webSocket().disableAutomaticReconnection(); - state->webSocket().start(); - - // we should sleep here for a bit until we've - // established the connection with the remote server - while (state->webSocket().getReadyState() != - ReadyState::Open) { - std::this_thread::sleep_for( - std::chrono::milliseconds(10)); - } - } else if (msg->type == ix::WebSocketMessageType::Close) { - state->webSocket().close(msg->closeInfo.code, - msg->closeInfo.reason); - } else if (msg->type == ix::WebSocketMessageType::Message) { - state->webSocket().send(msg->str, msg->binary); - } - }); - } - }); - - auto res = server.listen(); - if (!res.first) { - return 1; - } - - server.start(); - server.wait(); - - return 0; -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocketServer.cpp b/ixwebsocket/IXWebSocketServer.cpp deleted file mode 100644 index dca3a18..0000000 --- a/ixwebsocket/IXWebSocketServer.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * IXWebSocketServer.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#include "IXWebSocketServer.h" - -#include "IXNetSystem.h" -#include "IXSetThreadName.h" -#include "IXSocketConnect.h" -#include "IXWebSocket.h" -#include "IXWebSocketTransport.h" -#include -#include -#include - -namespace ix -{ -const int WebSocketServer::kDefaultHandShakeTimeoutSecs(3); // 3 seconds -const bool WebSocketServer::kDefaultEnablePong(true); - -WebSocketServer::WebSocketServer(int port, - const std::string &host, - int backlog, - size_t maxConnections, - int handshakeTimeoutSecs, - int addressFamily) - : SocketServer(port, host, backlog, maxConnections, addressFamily), - _handshakeTimeoutSecs(handshakeTimeoutSecs), - _enablePong(kDefaultEnablePong), _enablePerMessageDeflate(true) -{ -} - -WebSocketServer::~WebSocketServer() { stop(); } - -void WebSocketServer::stop() -{ - stopAcceptingConnections(); - - auto clients = getClients(); - for (auto client : clients) { - client->close(); - } - - SocketServer::stop(); -} - -void WebSocketServer::enablePong() { _enablePong = true; } - -void WebSocketServer::disablePong() { _enablePong = false; } - -void WebSocketServer::disablePerMessageDeflate() -{ - _enablePerMessageDeflate = false; -} - -void WebSocketServer::setOnConnectionCallback( - const OnConnectionCallback &callback) -{ - _onConnectionCallback = callback; -} - -void WebSocketServer::setOnClientMessageCallback( - const OnClientMessageCallback &callback) -{ - _onClientMessageCallback = callback; -} - -void WebSocketServer::handleConnection( - std::unique_ptr socket, - std::shared_ptr connectionState) -{ - setThreadName("WebSocketServer::" + connectionState->getId()); - - auto webSocket = std::make_shared(); - if (_onConnectionCallback) { - _onConnectionCallback(webSocket, connectionState); - - if (!webSocket->isOnMessageCallbackRegistered()) { - logError("WebSocketServer Application developer error: Server " - "callback improperly " - "registerered."); - logError("Missing call to setOnMessageCallback inside " - "setOnConnectionCallback."); - connectionState->setTerminated(); - return; - } - } else if (_onClientMessageCallback) { - WebSocket *webSocketRawPtr = webSocket.get(); - webSocket->setOnMessageCallback([this, - webSocketRawPtr, - connectionState]( - const WebSocketMessagePtr &msg) { - _onClientMessageCallback(connectionState, *webSocketRawPtr, msg); - }); - } else { - logError("WebSocketServer Application developer error: No server " - "callback is registerered."); - logError("Missing call to setOnConnectionCallback or " - "setOnClientMessageCallback."); - connectionState->setTerminated(); - return; - } - - webSocket->disableAutomaticReconnection(); - - if (_enablePong) { - webSocket->enablePong(); - } else { - webSocket->disablePong(); - } - - // Add this client to our client set - { - std::lock_guard lock(_clientsMutex); - _clients.insert(webSocket); - } - - auto status = webSocket->connectToSocket(std::move(socket), - _handshakeTimeoutSecs, - _enablePerMessageDeflate); - if (status.success) { - // Process incoming messages and execute callbacks - // until the connection is closed - webSocket->run(); - } else { - std::stringstream ss; - ss << "WebSocketServer::handleConnection() HTTP status: " - << status.http_status << " error: " << status.errorStr; - logError(ss.str()); - } - - webSocket->setOnMessageCallback(nullptr); - - // Remove this client from our client set - { - std::lock_guard lock(_clientsMutex); - if (_clients.erase(webSocket) != 1) { - logError("Cannot delete client"); - } - } - - connectionState->setTerminated(); -} - -std::set> WebSocketServer::getClients() -{ - std::lock_guard lock(_clientsMutex); - return _clients; -} - -size_t WebSocketServer::getConnectedClientsCount() -{ - std::lock_guard lock(_clientsMutex); - return _clients.size(); -} - -// -// Classic servers -// -void WebSocketServer::makeBroadcastServer() -{ - setOnClientMessageCallback( - [this](std::shared_ptr connectionState, - WebSocket &webSocket, - const WebSocketMessagePtr &msg) { - auto remoteIp = connectionState->getRemoteIp(); - if (msg->type == ix::WebSocketMessageType::Message) { - for (auto &&client : getClients()) { - if (client.get() != &webSocket) { - client->send(msg->str, msg->binary); - - // Make sure the OS send buffer is flushed before moving - // on - do { - std::chrono::duration duration( - 500); - std::this_thread::sleep_for(duration); - } while (client->bufferedAmount() != 0); - } - } - } - }); -} - -bool WebSocketServer::listenAndStart() -{ - auto res = listen(); - if (!res.first) { - return false; - } - - start(); - return true; -} - -int WebSocketServer::getHandshakeTimeoutSecs() { return _handshakeTimeoutSecs; } - -bool WebSocketServer::isPongEnabled() { return _enablePong; } - -bool WebSocketServer::isPerMessageDeflateEnabled() -{ - return _enablePerMessageDeflate; -} -} // namespace ix diff --git a/ixwebsocket/IXWebSocketTransport.cpp b/ixwebsocket/IXWebSocketTransport.cpp deleted file mode 100644 index e35848b..0000000 --- a/ixwebsocket/IXWebSocketTransport.cpp +++ /dev/null @@ -1,1130 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2012, 2013 - * - * 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. - */ - -/* - * IXWebSocketTransport.cpp - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -// -// Adapted from https://github.com/dhbaird/easywsclient -// - -#include "IXWebSocketTransport.h" - -#include "IXSocketFactory.h" -#include "IXSocketTLSOptions.h" -#include "IXUniquePtr.h" -#include "IXUrlParser.h" -#include "IXUtf8Validator.h" -#include "IXWebSocketHandshake.h" -#include "IXWebSocketHttpHeaders.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace ix -{ -const std::string WebSocketTransport::kPingMessage("ixwebsocket::heartbeat"); -const int WebSocketTransport::kDefaultPingIntervalSecs(-1); -const bool WebSocketTransport::kDefaultEnablePong(true); -const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300); -constexpr size_t WebSocketTransport::kChunkSize; - -WebSocketTransport::WebSocketTransport() - : _useMask(true), _blockingSend(false), _receivedMessageCompressed(false), - _readyState(ReadyState::CLOSED), - _closeCode(WebSocketCloseConstants::kInternalErrorCode), - _closeWireSize(0), _closeRemote(false), _enablePerMessageDeflate(false), - _requestInitCancellation(false), - _closingTimePoint(std::chrono::steady_clock::now()), - _enablePong(kDefaultEnablePong), - _pingIntervalSecs(kDefaultPingIntervalSecs), _pongReceived(false), - _pingCount(0), _lastSendPingTimePoint(std::chrono::steady_clock::now()) -{ - setCloseReason(WebSocketCloseConstants::kInternalErrorMessage); - _readbuf.resize(kChunkSize); -} - -WebSocketTransport::~WebSocketTransport() { ; } - -void WebSocketTransport::configure( - const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions, - const SocketTLSOptions &socketTLSOptions, - bool enablePong, - int pingIntervalSecs) -{ - _perMessageDeflateOptions = perMessageDeflateOptions; - _enablePerMessageDeflate = _perMessageDeflateOptions.enabled(); - _socketTLSOptions = socketTLSOptions; - _enablePong = enablePong; - _pingIntervalSecs = pingIntervalSecs; -} - -// Client -WebSocketInitResult - WebSocketTransport::connectToUrl(const std::string &url, - const WebSocketHttpHeaders &headers, - int timeoutSecs) -{ - std::lock_guard lock(_socketMutex); - - std::string protocol, host, path, query; - int port; - std::string remoteUrl(url); - - WebSocketInitResult result; - const int maxRedirections = 10; - - for (int i = 0; i < maxRedirections; ++i) { - if (!UrlParser::parse(remoteUrl, protocol, host, path, query, port)) { - std::stringstream ss; - ss << "Could not parse url: '" << url << "'"; - return WebSocketInitResult(false, 0, ss.str()); - } - - std::string errorMsg; - bool tls = protocol == "wss"; - _socket = createSocket(tls, -1, errorMsg, _socketTLSOptions); - _perMessageDeflate = ix::make_unique(); - - if (!_socket) { - return WebSocketInitResult(false, 0, errorMsg); - } - - WebSocketHandshake webSocketHandshake(_requestInitCancellation, - _socket, - _perMessageDeflate, - _perMessageDeflateOptions, - _enablePerMessageDeflate); - - result = webSocketHandshake.clientHandshake(remoteUrl, - headers, - host, - path, - port, - timeoutSecs); - - if (result.http_status >= 300 && result.http_status < 400) { - auto it = result.headers.find("Location"); - if (it == result.headers.end()) { - std::stringstream ss; - ss << "Missing Location Header for HTTP Redirect response. " - << "Rejecting connection to " << url - << ", status: " << result.http_status; - result.errorStr = ss.str(); - break; - } - - remoteUrl = it->second; - continue; - } - - if (result.success) { - setReadyState(ReadyState::OPEN); - } - return result; - } - - return result; -} - -// Server -WebSocketInitResult - WebSocketTransport::connectToSocket(std::unique_ptr socket, - int timeoutSecs, - bool enablePerMessageDeflate) -{ - std::lock_guard lock(_socketMutex); - - // Server should not mask the data it sends to the client - _useMask = false; - _blockingSend = true; - - _socket = std::move(socket); - _perMessageDeflate = ix::make_unique(); - - WebSocketHandshake webSocketHandshake(_requestInitCancellation, - _socket, - _perMessageDeflate, - _perMessageDeflateOptions, - _enablePerMessageDeflate); - - auto result = webSocketHandshake.serverHandshake(timeoutSecs, - enablePerMessageDeflate); - if (result.success) { - setReadyState(ReadyState::OPEN); - } - return result; -} - -WebSocketTransport::ReadyState WebSocketTransport::getReadyState() const -{ - return _readyState; -} - -void WebSocketTransport::setReadyState(ReadyState readyState) -{ - // No state change, return - if (_readyState == readyState) - return; - - if (readyState == ReadyState::CLOSED) { - if (_onCloseCallback) { - _onCloseCallback(_closeCode, - getCloseReason(), - _closeWireSize, - _closeRemote); - } - setCloseReason(WebSocketCloseConstants::kInternalErrorMessage); - _closeCode = WebSocketCloseConstants::kInternalErrorCode; - _closeWireSize = 0; - _closeRemote = false; - } else if (readyState == ReadyState::OPEN) { - initTimePointsAfterConnect(); - _pongReceived = false; - } - - _readyState = readyState; -} - -void WebSocketTransport::setOnCloseCallback( - const OnCloseCallback &onCloseCallback) -{ - _onCloseCallback = onCloseCallback; -} - -void WebSocketTransport::initTimePointsAfterConnect() -{ - { - std::lock_guard lock(_lastSendPingTimePointMutex); - _lastSendPingTimePoint = std::chrono::steady_clock::now(); - } -} - -// Only consider send PING time points for that computation. -bool WebSocketTransport::pingIntervalExceeded() -{ - if (_pingIntervalSecs <= 0) - return false; - - std::lock_guard lock(_lastSendPingTimePointMutex); - auto now = std::chrono::steady_clock::now(); - return now - _lastSendPingTimePoint > - std::chrono::seconds(_pingIntervalSecs); -} - -WebSocketSendInfo WebSocketTransport::sendHeartBeat() -{ - _pongReceived = false; - std::stringstream ss; - ss << kPingMessage << "::" << _pingIntervalSecs << "s" - << "::" << _pingCount++; - return sendPing(ss.str()); -} - -bool WebSocketTransport::closingDelayExceeded() -{ - std::lock_guard lock(_closingTimePointMutex); - auto now = std::chrono::steady_clock::now(); - return now - _closingTimePoint > - std::chrono::milliseconds(kClosingMaximumWaitingDelayInMs); -} - -WebSocketTransport::PollResult WebSocketTransport::poll() -{ - if (_readyState == ReadyState::OPEN) { - if (pingIntervalExceeded()) { - if (!_pongReceived) { - // ping response (PONG) exceeds the maximum delay, close the - // connection - close(WebSocketCloseConstants::kInternalErrorCode, - WebSocketCloseConstants::kPingTimeoutMessage); - } else { - sendHeartBeat(); - } - } - } - - // 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 : _pingIntervalSecs; - - if (_pingIntervalSecs > 0) { - // compute lasting delay to wait for next ping / timeout, if at least - // one set - auto now = std::chrono::steady_clock::now(); - int timeSinceLastPingMs = - (int)std::chrono::duration_cast( - now - _lastSendPingTimePoint) - .count(); - lastingTimeoutDelayInMs = - (1000 * _pingIntervalSecs) - timeSinceLastPingMs; - } - -#ifdef _WIN32 - // Windows does not have select interrupt capabilities, so wait with a small - // timeout - if (lastingTimeoutDelayInMs <= 0) { - lastingTimeoutDelayInMs = 20; - } -#endif - - // If we are requesting a cancellation, pass in a positive and small timeout - // to never poll forever without a timeout. - if (_requestInitCancellation) { - lastingTimeoutDelayInMs = 100; - } - - // poll the socket - PollResultType pollResult = _socket->isReadyToRead(lastingTimeoutDelayInMs); - - // Make sure we send all the buffered data - // there can be a lot of it for large messages. - if (pollResult == PollResultType::SendRequest) { - if (!flushSendBuffer()) { - return PollResult::CannotFlushSendBuffer; - } - } else if (pollResult == PollResultType::ReadyForRead) { - if (!receiveFromSocket()) { - return PollResult::AbnormalClose; - } - } else if (pollResult == PollResultType::Error) { - closeSocket(); - } else if (pollResult == PollResultType::CloseRequest) { - closeSocket(); - } - - if (_readyState == ReadyState::CLOSING && closingDelayExceeded()) { - _rxbuf.clear(); - // close code and reason were set when calling close() - closeSocket(); - setReadyState(ReadyState::CLOSED); - } - - return PollResult::Succeeded; -} - -bool WebSocketTransport::isSendBufferEmpty() const -{ - std::lock_guard lock(_txbufMutex); - return _txbuf.empty(); -} - -template -void WebSocketTransport::appendToSendBuffer(const std::vector &header, - Iterator begin, - Iterator end, - uint64_t message_size, - uint8_t masking_key[4]) -{ - std::lock_guard lock(_txbufMutex); - - _txbuf.insert(_txbuf.end(), header.begin(), header.end()); - _txbuf.insert(_txbuf.end(), begin, end); - - if (_useMask) { - for (size_t i = 0; i != (size_t)message_size; ++i) { - *(_txbuf.end() - (size_t)message_size + i) ^= masking_key[i & 0x3]; - } - } -} - -void WebSocketTransport::unmaskReceiveBuffer(const wsheader_type &ws) -{ - if (ws.mask) { - for (size_t j = 0; j != ws.N; ++j) { - _rxbuf[j + ws.header_size] ^= ws.masking_key[j & 0x3]; - } - } -} - -// -// http://tools.ietf.org/html/rfc6455#section-5.2 Base Framing Protocol -// -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// +-+-+-+-+-------+-+-------------+-------------------------------+ -// |F|R|R|R| opcode|M| Payload len | Extended payload length | -// |I|S|S|S| (4) |A| (7) | (16/64) | -// |N|V|V|V| |S| | (if payload len==126/127) | -// | |1|2|3| |K| | | -// +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + -// | Extended payload length continued, if payload len == 127 | -// + - - - - - - - - - - - - - - - +-------------------------------+ -// | |Masking-key, if MASK set to 1 | -// +-------------------------------+-------------------------------+ -// | Masking-key (continued) | Payload Data | -// +-------------------------------- - - - - - - - - - - - - - - - + -// : Payload Data continued ... : -// + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + -// | Payload Data continued ... | -// +---------------------------------------------------------------+ -// -void WebSocketTransport::dispatch(WebSocketTransport::PollResult pollResult, - const OnMessageCallback &onMessageCallback) -{ - while (true) { - wsheader_type ws; - 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; - ws.rsv2 = (data[0] & 0x20) == 0x20; - ws.rsv3 = (data[0] & 0x10) == 0x10; - ws.opcode = (wsheader_type::opcode_type)(data[0] & 0x0f); - 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) - break; /* Need: ws.header_size - _rxbuf.size() */ - - if ((ws.rsv1 && !_enablePerMessageDeflate) || ws.rsv2 || ws.rsv3) { - close(WebSocketCloseConstants::kProtocolErrorCode, - WebSocketCloseConstants::kProtocolErrorReservedBitUsed, - _rxbuf.size()); - return; - } - - // - // Calculate payload length: - // 0-125 mean the payload is that long. - // 126 means that the following two bytes indicate the length, - // 127 means the next 8 bytes indicate the length. - // - int i = 0; - if (ws.N0 < 126) { - ws.N = ws.N0; - i = 2; - } else if (ws.N0 == 126) { - ws.N = 0; - ws.N |= ((uint64_t)data[2]) << 8; - ws.N |= ((uint64_t)data[3]) << 0; - i = 4; - } else if (ws.N0 == 127) { - ws.N = 0; - ws.N |= ((uint64_t)data[2]) << 56; - ws.N |= ((uint64_t)data[3]) << 48; - ws.N |= ((uint64_t)data[4]) << 40; - ws.N |= ((uint64_t)data[5]) << 32; - ws.N |= ((uint64_t)data[6]) << 24; - ws.N |= ((uint64_t)data[7]) << 16; - ws.N |= ((uint64_t)data[8]) << 8; - ws.N |= ((uint64_t)data[9]) << 0; - i = 10; - } else { - // invalid payload length according to the spec. bail out - return; - } - - if (ws.mask) { - ws.masking_key[0] = ((uint8_t)data[i + 0]) << 0; - ws.masking_key[1] = ((uint8_t)data[i + 1]) << 0; - ws.masking_key[2] = ((uint8_t)data[i + 2]) << 0; - ws.masking_key[3] = ((uint8_t)data[i + 3]) << 0; - } else { - ws.masking_key[0] = 0; - ws.masking_key[1] = 0; - ws.masking_key[2] = 0; - ws.masking_key[3] = 0; - } - - // Prevent integer overflow in the next conditional - const uint64_t maxFrameSize(1ULL << 63); - if (ws.N > maxFrameSize) { - return; - } - - if (_rxbuf.size() < ws.header_size + ws.N) { - return; /* Need: ws.header_size+ws.N - _rxbuf.size() */ - } - - if (!ws.fin && (ws.opcode == wsheader_type::PING || - ws.opcode == wsheader_type::PONG || - ws.opcode == wsheader_type::CLOSE)) { - // Control messages should not be fragmented - close(WebSocketCloseConstants::kProtocolErrorCode, - WebSocketCloseConstants:: - kProtocolErrorCodeControlMessageFragmented); - return; - } - - unmaskReceiveBuffer(ws); - std::string frameData(_rxbuf.begin() + ws.header_size, - _rxbuf.begin() + ws.header_size + (size_t)ws.N); - - // We got a whole message, now do something with it: - if (ws.opcode == wsheader_type::TEXT_FRAME || - ws.opcode == wsheader_type::BINARY_FRAME || - ws.opcode == wsheader_type::CONTINUATION) { - if (ws.opcode != wsheader_type::CONTINUATION) { - _fragmentedMessageKind = - (ws.opcode == wsheader_type::TEXT_FRAME) - ? MessageKind::MSG_TEXT - : MessageKind::MSG_BINARY; - - _receivedMessageCompressed = - _enablePerMessageDeflate && ws.rsv1; - - // Continuation message needs to follow a non-fin TEXT or BINARY - // message - if (!_chunks.empty()) { - close(WebSocketCloseConstants::kProtocolErrorCode, - WebSocketCloseConstants:: - kProtocolErrorCodeDataOpcodeOutOfSequence); - } - } else if (_chunks.empty()) { - // Continuation message need to follow a non-fin TEXT or BINARY - // message - close(WebSocketCloseConstants::kProtocolErrorCode, - WebSocketCloseConstants:: - kProtocolErrorCodeContinuationOpCodeOutOfSequence); - } - - // - // Usual case. Small unfragmented messages - // - if (ws.fin && _chunks.empty()) { - emitMessage(_fragmentedMessageKind, - frameData, - _receivedMessageCompressed, - onMessageCallback); - - _receivedMessageCompressed = false; - } else { - // - // Add intermediary message to our chunk list. - // We use a chunk list instead of a big buffer because resizing - // large buffer can be very costly when we need to re-allocate - // the internal buffer which is slow and can let the internal OS - // receive buffer fill out. - // - _chunks.emplace_back(frameData); - - if (ws.fin) { - emitMessage(_fragmentedMessageKind, - getMergedChunks(), - _receivedMessageCompressed, - onMessageCallback); - - _chunks.clear(); - _receivedMessageCompressed = false; - } else { - emitMessage(MessageKind::FRAGMENT, - std::string(), - false, - onMessageCallback); - } - } - } else if (ws.opcode == wsheader_type::PING) { - // too large - if (frameData.size() > 125) { - // Unexpected frame type - close(WebSocketCloseConstants::kProtocolErrorCode, - WebSocketCloseConstants:: - kProtocolErrorPingPayloadOversized); - return; - } - - if (_enablePong) { - // Reply back right away - bool compress = false; - sendData(wsheader_type::PONG, frameData, compress); - } - - emitMessage(MessageKind::PING, frameData, false, onMessageCallback); - } else if (ws.opcode == wsheader_type::PONG) { - _pongReceived = true; - emitMessage(MessageKind::PONG, frameData, false, onMessageCallback); - } else if (ws.opcode == wsheader_type::CLOSE) { - std::string reason; - uint16_t code = 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. - if (ws.N > 2) { - reason = frameData.substr(2, frameData.size()); - } - - // Validate that the reason is proper utf-8. Autobahn 7.5.1 - if (!validateUtf8(reason)) { - code = WebSocketCloseConstants::kInvalidFramePayloadData; - reason = WebSocketCloseConstants:: - kInvalidFramePayloadDataMessage; - } - - // - // Validate close codes. Autobahn 7.9.* - // 1014, 1015 are debattable. The firefox MSDN has a description - // for them. Full list of status code and status range is - // defined in the dedicated RFC section at - // https://tools.ietf.org/html/rfc6455#page-45 - // - if (code < 1000 || code == 1004 || code == 1006 || - (code > 1013 && code < 3000)) { - // build up an error message containing the bad error code - std::stringstream ss; - ss << WebSocketCloseConstants::kInvalidCloseCodeMessage - << ": " << code; - reason = ss.str(); - - code = WebSocketCloseConstants::kProtocolErrorCode; - } - } else { - // no close code received - code = WebSocketCloseConstants::kNoStatusCodeErrorCode; - reason = WebSocketCloseConstants::kNoStatusCodeErrorMessage; - } - - // 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); - - wakeUpFromPoll(SelectInterrupt::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 = - _closeCode == code && getCloseReason() == reason; - - if (identicalReason) { - bool remote = false; - closeSocketAndSwitchToClosedState(code, - reason, - _rxbuf.size(), - remote); - } - } - } else { - // Unexpected frame type - close(WebSocketCloseConstants::kProtocolErrorCode, - WebSocketCloseConstants::kProtocolErrorMessage, - _rxbuf.size()); - } - - // Erase the message that has been processed from the input/read buffer - _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::Succeeded) { - _rxbuf.clear(); - - // if we previously closed the connection (CLOSING state), then set - // state to CLOSED (code/reason were set before) - if (_readyState == ReadyState::CLOSING) { - closeSocket(); - setReadyState(ReadyState::CLOSED); - } - // if we weren't closing, then close using abnormal close code and - // message - else if (_readyState != ReadyState::CLOSED) { - closeSocketAndSwitchToClosedState( - WebSocketCloseConstants::kAbnormalCloseCode, - WebSocketCloseConstants::kAbnormalCloseMessage, - 0, - false); - } - } -} - -std::string WebSocketTransport::getMergedChunks() const -{ - size_t length = 0; - for (auto &&chunk : _chunks) { - length += chunk.size(); - } - - std::string msg; - msg.reserve(length); - - for (auto &&chunk : _chunks) { - msg += chunk; - } - - return msg; -} - -void WebSocketTransport::emitMessage(MessageKind messageKind, - const std::string &message, - bool compressedMessage, - const OnMessageCallback &onMessageCallback) -{ - size_t wireSize = message.size(); - - // When the RSV1 bit is 1 it means the message is compressed - if (compressedMessage && messageKind != MessageKind::FRAGMENT) { - bool success = - _perMessageDeflate->decompress(message, _decompressedMessage); - - if (messageKind == MessageKind::MSG_TEXT && - !validateUtf8(_decompressedMessage)) { - close(WebSocketCloseConstants::kInvalidFramePayloadData, - WebSocketCloseConstants::kInvalidFramePayloadDataMessage); - } else { - onMessageCallback(_decompressedMessage, - wireSize, - !success, - messageKind); - } - } else { - if (messageKind == MessageKind::MSG_TEXT && !validateUtf8(message)) { - close(WebSocketCloseConstants::kInvalidFramePayloadData, - WebSocketCloseConstants::kInvalidFramePayloadDataMessage); - } else { - onMessageCallback(message, wireSize, false, messageKind); - } - } -} - -unsigned WebSocketTransport::getRandomUnsigned() -{ - auto now = std::chrono::system_clock::now(); - auto seconds = - std::chrono::duration_cast(now.time_since_epoch()) - .count(); - return static_cast(seconds); -} - -template -WebSocketSendInfo - WebSocketTransport::sendData(wsheader_type::opcode_type type, - const T &message, - bool compress, - const OnProgressCallback &onProgressCallback) -{ - if (_readyState != ReadyState::OPEN && _readyState != ReadyState::CLOSING) { - return WebSocketSendInfo(false); - } - - size_t payloadSize = message.size(); - size_t wireSize = message.size(); - bool compressionError = false; - - auto message_begin = message.cbegin(); - auto message_end = message.cend(); - - if (compress) { - if (!_perMessageDeflate->compress(message, _compressedMessage)) { - bool success = false; - compressionError = true; - payloadSize = 0; - wireSize = 0; - return WebSocketSendInfo(success, - compressionError, - payloadSize, - wireSize); - } - compressionError = false; - wireSize = _compressedMessage.size(); - - message_begin = _compressedMessage.cbegin(); - message_end = _compressedMessage.cend(); - } - - { - std::lock_guard lock(_txbufMutex); - _txbuf.reserve(wireSize); - } - - bool success = true; - - // Common case for most message. No fragmentation required. - if (wireSize < kChunkSize) { - success = - sendFragment(type, true, message_begin, message_end, compress); - - if (onProgressCallback) { - onProgressCallback(0, 1); - } - } else { - // - // Large messages need to be fragmented - // - // Rules: - // First message needs to specify a proper type (BINARY or TEXT) - // Intermediary and last messages need to be of type CONTINUATION - // Last message must set the fin byte. - // - auto steps = wireSize / kChunkSize; - - std::string::const_iterator begin = message_begin; - std::string::const_iterator end = message_end; - - for (uint64_t i = 0; i < steps; ++i) { - bool firstStep = i == 0; - bool lastStep = (i + 1) == steps; - bool fin = lastStep; - - end = begin + kChunkSize; - if (lastStep) { - end = message_end; - } - - auto opcodeType = type; - if (!firstStep) { - opcodeType = wsheader_type::CONTINUATION; - } - - // Send message - if (!sendFragment(opcodeType, fin, begin, end, compress)) { - return WebSocketSendInfo(false); - } - - if (onProgressCallback && !onProgressCallback((int)i, (int)steps)) { - break; - } - - begin += kChunkSize; - } - } - - // Request to flush the send buffer on the background thread if it isn't - // empty - if (!isSendBufferEmpty()) { - wakeUpFromPoll(SelectInterrupt::kSendRequest); - - // FIXME: we should have a timeout when sending large messages: see #131 - if (_blockingSend && !flushSendBuffer()) { - success = false; - } - } - - return WebSocketSendInfo(success, compressionError, payloadSize, wireSize); -} - -template -bool WebSocketTransport::sendFragment(wsheader_type::opcode_type type, - bool fin, - Iterator message_begin, - Iterator message_end, - bool compress) -{ - uint64_t message_size = static_cast(message_end - message_begin); - - unsigned x = getRandomUnsigned(); - uint8_t masking_key[4] = {}; - masking_key[0] = (x >> 24); - masking_key[1] = (x >> 16) & 0xff; - masking_key[2] = (x >> 8) & 0xff; - masking_key[3] = (x)&0xff; - - std::vector header; - header.assign(2 + (message_size >= 126 ? 2 : 0) + - (message_size >= 65536 ? 6 : 0) + (_useMask ? 4 : 0), - 0); - header[0] = type; - - // The fin bit indicate that this is the last fragment. Fin is French for - // end. - if (fin) { - header[0] |= 0x80; - } - - // The rsv1 bit indicate that the frame is compressed - // continuation opcodes should not set it. Autobahn 12.2.10 and others 12.X - if (compress && type != wsheader_type::CONTINUATION) { - header[0] |= 0x40; - } - - if (message_size < 126) { - header[1] = (message_size & 0xff) | (_useMask ? 0x80 : 0); - - if (_useMask) { - header[2] = masking_key[0]; - header[3] = masking_key[1]; - header[4] = masking_key[2]; - header[5] = masking_key[3]; - } - } else if (message_size < 65536) { - header[1] = 126 | (_useMask ? 0x80 : 0); - header[2] = (message_size >> 8) & 0xff; - header[3] = (message_size >> 0) & 0xff; - - if (_useMask) { - header[4] = masking_key[0]; - header[5] = masking_key[1]; - header[6] = masking_key[2]; - header[7] = masking_key[3]; - } - } else { // TODO: run coverage testing here - header[1] = 127 | (_useMask ? 0x80 : 0); - header[2] = (message_size >> 56) & 0xff; - header[3] = (message_size >> 48) & 0xff; - header[4] = (message_size >> 40) & 0xff; - header[5] = (message_size >> 32) & 0xff; - header[6] = (message_size >> 24) & 0xff; - header[7] = (message_size >> 16) & 0xff; - header[8] = (message_size >> 8) & 0xff; - header[9] = (message_size >> 0) & 0xff; - - if (_useMask) { - header[10] = masking_key[0]; - header[11] = masking_key[1]; - header[12] = masking_key[2]; - header[13] = masking_key[3]; - } - } - - // _txbuf will keep growing until it can be transmitted over the socket: - appendToSendBuffer(header, - message_begin, - message_end, - message_size, - masking_key); - - // Now actually send this data - return sendOnSocket(); -} - -WebSocketSendInfo WebSocketTransport::sendPing(const std::string &message) -{ - bool compress = false; - WebSocketSendInfo info = sendData(wsheader_type::PING, message, compress); - - if (info.success) { - std::lock_guard lck(_lastSendPingTimePointMutex); - _lastSendPingTimePoint = std::chrono::steady_clock::now(); - } - - return info; -} - -WebSocketSendInfo - WebSocketTransport::sendBinary(const std::string &message, - const OnProgressCallback &onProgressCallback) - -{ - return sendData(wsheader_type::BINARY_FRAME, - message, - _enablePerMessageDeflate, - onProgressCallback); -} - -WebSocketSendInfo - WebSocketTransport::sendText(const std::string &message, - const OnProgressCallback &onProgressCallback) - -{ - return sendData(wsheader_type::TEXT_FRAME, - message, - _enablePerMessageDeflate, - onProgressCallback); -} - -bool WebSocketTransport::sendOnSocket() -{ - std::lock_guard lock(_txbufMutex); - - while (_txbuf.size()) { - ssize_t ret = 0; - { - std::lock_guard lock(_socketMutex); - ret = _socket->send((char *)&_txbuf[0], _txbuf.size()); - } - - if (ret < 0 && Socket::isWaitNeeded()) { - break; - } else if (ret <= 0) { - closeSocket(); - setReadyState(ReadyState::CLOSED); - return false; - } else { - _txbuf.erase(_txbuf.begin(), _txbuf.begin() + ret); - } - } - - return true; -} - -bool WebSocketTransport::receiveFromSocket() -{ - while (true) { - ssize_t ret = _socket->recv((char *)&_readbuf[0], _readbuf.size()); - - if (ret < 0 && Socket::isWaitNeeded()) { - break; - } else if (ret <= 0) { - // 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) - - closeSocket(); - return false; - } else { - _rxbuf.insert(_rxbuf.end(), - _readbuf.begin(), - _readbuf.begin() + ret); - } - } - - return true; -} - -void WebSocketTransport::sendCloseFrame(uint16_t code, - const std::string &reason) -{ - bool compress = false; - - // if a status is set/was read - if (code != WebSocketCloseConstants::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, std::string(""), compress); - } -} - -void WebSocketTransport::closeSocket() -{ - std::lock_guard lock(_socketMutex); - _socket->close(); -} - -bool WebSocketTransport::wakeUpFromPoll(uint64_t wakeUpCode) -{ - std::lock_guard lock(_socketMutex); - return _socket->wakeUpFromPoll(wakeUpCode); -} - -void WebSocketTransport::closeSocketAndSwitchToClosedState( - uint16_t code, - const std::string &reason, - size_t closeWireSize, - bool remote) -{ - closeSocket(); - - setCloseReason(reason); - _closeCode = code; - _closeWireSize = closeWireSize; - _closeRemote = remote; - - setReadyState(ReadyState::CLOSED); - _requestInitCancellation = false; -} - -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; - - if (closeWireSize == 0) { - closeWireSize = reason.size(); - } - - setCloseReason(reason); - _closeCode = code; - _closeWireSize = closeWireSize; - _closeRemote = remote; - - { - std::lock_guard lock(_closingTimePointMutex); - _closingTimePoint = std::chrono::steady_clock::now(); - } - setReadyState(ReadyState::CLOSING); - - sendCloseFrame(code, reason); - - // wake up the poll, but do not close yet - wakeUpFromPoll(SelectInterrupt::kSendRequest); -} - -size_t WebSocketTransport::bufferedAmount() const -{ - std::lock_guard lock(_txbufMutex); - return _txbuf.size(); -} - -bool WebSocketTransport::flushSendBuffer() -{ - while (!isSendBufferEmpty() && !_requestInitCancellation) { - // Wait with a 10ms timeout until the socket is ready to write. - // This way we are not busy looping - PollResultType result = _socket->isReadyToWrite(10); - - if (result == PollResultType::Error) { - closeSocket(); - setReadyState(ReadyState::CLOSED); - return false; - } else if (result == PollResultType::ReadyForWrite) { - if (!sendOnSocket()) { - return false; - } - } - } - - return true; -} - -void WebSocketTransport::setCloseReason(const std::string &reason) -{ - std::lock_guard lock(_closeReasonMutex); - _closeReason = reason; -} - -const std::string &WebSocketTransport::getCloseReason() const -{ - std::lock_guard lock(_closeReasonMutex); - return _closeReason; -} -} // namespace ix diff --git a/ixwebsocket/License.txt b/ixwebsocket/License.txt deleted file mode 100644 index dffb40e..0000000 --- a/ixwebsocket/License.txt +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/ixwebsocket/include/IXBench.h b/ixwebsocket/include/IXBench.h deleted file mode 100644 index 873e016..0000000 --- a/ixwebsocket/include/IXBench.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * IXBench.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. - */ -#pragma once - -#include -#include -#include - -namespace ix -{ -class Bench -{ -public: - Bench(const std::string &description); - ~Bench(); - - void reset(); - void record(); - void report(); - void setReported(); - uint64_t getDuration() const; - -private: - std::string _description; - std::chrono::time_point _start; - uint64_t _duration; - bool _reported; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXCancellationRequest.h b/ixwebsocket/include/IXCancellationRequest.h deleted file mode 100644 index 2a4f048..0000000 --- a/ixwebsocket/include/IXCancellationRequest.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * IXCancellationRequest.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include - -namespace ix -{ -using CancellationRequest = std::function; - -CancellationRequest makeCancellationRequestWithTimeout( - int seconds, - std::atomic &requestInitCancellation); -} // namespace ix diff --git a/ixwebsocket/include/IXConnectionState.h b/ixwebsocket/include/IXConnectionState.h deleted file mode 100644 index c561318..0000000 --- a/ixwebsocket/include/IXConnectionState.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * IXConnectionState.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace ix -{ -using OnSetTerminatedCallback = std::function; - -class ConnectionState -{ -public: - ConnectionState(); - virtual ~ConnectionState() = default; - - virtual void computeId(); - virtual const std::string &getId() const; - - void setTerminated(); - bool isTerminated() const; - - const std::string &getRemoteIp(); - int getRemotePort(); - - static std::shared_ptr createConnectionState(); - -private: - void setOnSetTerminatedCallback(const OnSetTerminatedCallback &callback); - - void setRemoteIp(const std::string &remoteIp); - void setRemotePort(int remotePort); - -protected: - std::atomic _terminated; - std::string _id; - OnSetTerminatedCallback _onSetTerminatedCallback; - - static std::atomic _globalId; - - std::string _remoteIp; - int _remotePort; - - friend class SocketServer; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXDNSLookup.h b/ixwebsocket/include/IXDNSLookup.h deleted file mode 100644 index dea9301..0000000 --- a/ixwebsocket/include/IXDNSLookup.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * IXDNSLookup.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - * - * Resolve a hostname+port to a struct addrinfo obtained with getaddrinfo - * Does this in a background thread so that it can be cancelled, since - * getaddrinfo is a blocking call, and we don't want to block the main thread - * on Mobile. - */ - -#pragma once - -#include "IXCancellationRequest.h" -#include -#include -#include -#include -#include - -struct addrinfo; - -namespace ix -{ -class DNSLookup : public std::enable_shared_from_this -{ -public: - DNSLookup(const std::string &hostname, - int port, - int64_t wait = DNSLookup::kDefaultWait); - ~DNSLookup() = default; - - struct addrinfo *resolve(std::string &errMsg, - const CancellationRequest &isCancellationRequested, - bool cancellable = true); - - void release(struct addrinfo *addr); - -private: - struct addrinfo * - resolveCancellable(std::string &errMsg, - const CancellationRequest &isCancellationRequested); - struct addrinfo *resolveUnCancellable( - std::string &errMsg, - const CancellationRequest &isCancellationRequested); - - static struct addrinfo * - getAddrInfo(const std::string &hostname, int port, std::string &errMsg); - - void run(std::weak_ptr self, - std::string hostname, - int port); // thread runner - - void setErrMsg(const std::string &errMsg); - const std::string &getErrMsg(); - - void setRes(struct addrinfo *addr); - struct addrinfo *getRes(); - - std::string _hostname; - int _port; - int64_t _wait; - const static int64_t kDefaultWait; - - struct addrinfo *_res; - std::mutex _resMutex; - - std::string _errMsg; - std::mutex _errMsgMutex; - - std::atomic _done; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXExponentialBackoff.h b/ixwebsocket/include/IXExponentialBackoff.h deleted file mode 100644 index 4df2387..0000000 --- a/ixwebsocket/include/IXExponentialBackoff.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * IXExponentialBackoff.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -uint32_t - calculateRetryWaitMilliseconds(uint32_t retryCount, - uint32_t maxWaitBetweenReconnectionRetries, - uint32_t minWaitBetweenReconnectionRetries); -} // namespace ix diff --git a/ixwebsocket/include/IXGetFreePort.h b/ixwebsocket/include/IXGetFreePort.h deleted file mode 100644 index 7632922..0000000 --- a/ixwebsocket/include/IXGetFreePort.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * IXGetFreePort.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone. All rights reserved. - */ - -#pragma once - -namespace ix -{ -int getFreePort(); -} // namespace ix diff --git a/ixwebsocket/include/IXGzipCodec.h b/ixwebsocket/include/IXGzipCodec.h deleted file mode 100644 index 38e3580..0000000 --- a/ixwebsocket/include/IXGzipCodec.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * IXGzipCodec.h - * Author: Benjamin Sergeant - * Copyright (c) 2020 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -std::string gzipCompress(const std::string &str); -bool gzipDecompress(const std::string &in, std::string &out); -} // namespace ix diff --git a/ixwebsocket/include/IXHttp.h b/ixwebsocket/include/IXHttp.h deleted file mode 100644 index 942fc7e..0000000 --- a/ixwebsocket/include/IXHttp.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * IXHttp.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXProgressCallback.h" -#include "IXWebSocketHttpHeaders.h" -#include -#include - -namespace ix -{ -enum class HttpErrorCode : int { - 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, - Invalid = 100 -}; - -struct HttpResponse { - int statusCode; - std::string description; - HttpErrorCode errorCode; - WebSocketHttpHeaders headers; - std::string body; - std::string errorMsg; - uint64_t uploadSize; - uint64_t downloadSize; - - HttpResponse(int s = 0, - const std::string &des = std::string(), - const HttpErrorCode &c = HttpErrorCode::Ok, - const WebSocketHttpHeaders &h = WebSocketHttpHeaders(), - const std::string &b = std::string(), - const std::string &e = std::string(), - uint64_t u = 0, - uint64_t d = 0) - : statusCode(s), description(des), errorCode(c), headers(h), body(b), - errorMsg(e), uploadSize(u), downloadSize(d) - { - ; - } -}; - -using HttpResponsePtr = std::shared_ptr; -using HttpParameters = std::unordered_map; -using HttpFormDataParameters = std::unordered_map; -using Logger = std::function; -using OnResponseCallback = std::function; - -struct HttpRequestArgs { - std::string url; - std::string verb; - WebSocketHttpHeaders extraHeaders; - std::string body; - std::string multipartBoundary; - int connectTimeout = 60; - int transferTimeout = 1800; - bool followRedirects = true; - int maxRedirects = 5; - bool verbose = false; - bool compress = true; - bool compressRequest = false; - Logger logger; - OnProgressCallback onProgressCallback; -}; - -using HttpRequestArgsPtr = std::shared_ptr; - -struct HttpRequest { - std::string uri; - std::string method; - std::string version; - std::string body; - WebSocketHttpHeaders headers; - - HttpRequest(const std::string &u, - const std::string &m, - const std::string &v, - const std::string &b, - const WebSocketHttpHeaders &h = WebSocketHttpHeaders()) - : uri(u), method(m), version(v), body(b), headers(h) - { - } -}; - -using HttpRequestPtr = std::shared_ptr; - -class Http -{ -public: - static std::tuple - parseRequest(std::unique_ptr &socket, int timeoutSecs); - static bool sendResponse(HttpResponsePtr response, - std::unique_ptr &socket); - - static std::pair parseStatusLine(const std::string &line); - static std::tuple - parseRequestLine(const std::string &line); - static std::string trim(const std::string &str); -}; -} // namespace ix diff --git a/ixwebsocket/include/IXHttpClient.h b/ixwebsocket/include/IXHttpClient.h deleted file mode 100644 index 98f47e8..0000000 --- a/ixwebsocket/include/IXHttpClient.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * IXHttpClient.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXHttp.h" -#include "IXSocket.h" -#include "IXSocketTLSOptions.h" -#include "IXWebSocketHttpHeaders.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ix -{ -class HttpClient -{ -public: - HttpClient(bool async = false); - ~HttpClient(); - - HttpResponsePtr get(const std::string &url, HttpRequestArgsPtr args); - HttpResponsePtr head(const std::string &url, HttpRequestArgsPtr args); - HttpResponsePtr Delete(const std::string &url, HttpRequestArgsPtr args); - - HttpResponsePtr post(const std::string &url, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args); - HttpResponsePtr post(const std::string &url, - const std::string &body, - HttpRequestArgsPtr args); - - HttpResponsePtr put(const std::string &url, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args); - HttpResponsePtr put(const std::string &url, - const std::string &body, - HttpRequestArgsPtr args); - - HttpResponsePtr patch(const std::string &url, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args); - HttpResponsePtr patch(const std::string &url, - const std::string &body, - HttpRequestArgsPtr args); - - HttpResponsePtr request(const std::string &url, - const std::string &verb, - const std::string &body, - HttpRequestArgsPtr args, - int redirects = 0); - - HttpResponsePtr - request(const std::string &url, - const std::string &verb, - const HttpParameters &httpParameters, - const HttpFormDataParameters &httpFormDataParameters, - HttpRequestArgsPtr args); - - void setForceBody(bool value); - - // Async API - HttpRequestArgsPtr - createRequest(const std::string &url = std::string(), - const std::string &verb = HttpClient::kGet); - - bool performRequest(HttpRequestArgsPtr request, - const OnResponseCallback &onResponseCallback); - - // TLS - void setTLSOptions(const SocketTLSOptions &tlsOptions); - - std::string serializeHttpParameters(const HttpParameters &httpParameters); - - std::string serializeHttpFormDataParameters( - const std::string &multipartBoundary, - const HttpFormDataParameters &httpFormDataParameters, - const HttpParameters &httpParameters = HttpParameters()); - - std::string generateMultipartBoundary(); - - std::string urlEncode(const std::string &value); - - const static std::string kPost; - const static std::string kGet; - const static std::string kHead; - const static std::string kDelete; - const static std::string kPut; - const static std::string kPatch; - -private: - void log(const std::string &msg, HttpRequestArgsPtr args); - - // Async API background thread runner - void run(); - // Async API - bool _async; - std::queue> _queue; - mutable std::mutex _queueMutex; - std::condition_variable _condition; - std::atomic _stop; - std::thread _thread; - - std::unique_ptr _socket; - std::recursive_mutex - _mutex; // to protect accessing the _socket (only one socket per - // client) the mutex needs to be recursive as this function - // might be called recursively to follow HTTP redirections - - SocketTLSOptions _tlsOptions; - - bool _forceBody; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXHttpServer.h b/ixwebsocket/include/IXHttpServer.h deleted file mode 100644 index f8a76d6..0000000 --- a/ixwebsocket/include/IXHttpServer.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * IXHttpServer.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXHttp.h" -#include "IXSocketServer.h" -#include "IXWebSocket.h" -#include -#include -#include -#include -#include -#include -#include // pair - -namespace ix -{ -class HttpServer final : public SocketServer -{ -public: - using OnConnectionCallback = - std::function)>; - - HttpServer(int port = SocketServer::kDefaultPort, - const std::string &host = SocketServer::kDefaultHost, - int backlog = SocketServer::kDefaultTcpBacklog, - size_t maxConnections = SocketServer::kDefaultMaxConnections, - int addressFamily = SocketServer::kDefaultAddressFamily, - int timeoutSecs = HttpServer::kDefaultTimeoutSecs); - virtual ~HttpServer(); - virtual void stop() final; - - void setOnConnectionCallback(const OnConnectionCallback &callback); - - void makeRedirectServer(const std::string &redirectUrl); - - void makeDebugServer(); - - int getTimeoutSecs(); - -private: - // Member variables - OnConnectionCallback _onConnectionCallback; - std::atomic _connectedClientsCount; - - const static int kDefaultTimeoutSecs; - int _timeoutSecs; - - // Methods - virtual void handleConnection( - std::unique_ptr, - std::shared_ptr connectionState) final; - virtual size_t getConnectedClientsCount() final; - - void setDefaultConnectionCallback(); -}; -} // namespace ix diff --git a/ixwebsocket/include/IXNetSystem.h b/ixwebsocket/include/IXNetSystem.h deleted file mode 100644 index 3b1258d..0000000 --- a/ixwebsocket/include/IXNetSystem.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * IXNetSystem.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone. All rights reserved. - */ - -#pragma once - -#ifdef _WIN32 - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include -#include -#include -#include -#include - -#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 - -// Define our own poll on Windows, as a wrapper on top of select -typedef unsigned long int nfds_t; - -// pollfd is not defined by some versions of mingw64 since _WIN32_WINNT is too -// low -#if _WIN32_WINNT < 0x0600 -struct pollfd { - int fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ -}; - -#define POLLIN 0x001 /* There is data to read. */ -#define POLLOUT 0x004 /* Writing now will not block. */ -#define POLLERR 0x008 /* Error condition. */ -#define POLLHUP 0x010 /* Hung up. */ -#define POLLNVAL 0x020 /* Invalid polling request. */ -#endif - -#else -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include - -namespace ix -{ -#ifdef _WIN32 -typedef SOCKET socket_t; -#else -typedef int socket_t; -#endif - -bool initNetSystem(); -bool uninitNetSystem(); - -int poll(struct pollfd *fds, nfds_t nfds, int timeout); - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); -int inet_pton(int af, const char *src, void *dst); - -unsigned short network_to_host_short(unsigned short value); -} // namespace ix diff --git a/ixwebsocket/include/IXProgressCallback.h b/ixwebsocket/include/IXProgressCallback.h deleted file mode 100644 index c8b992a..0000000 --- a/ixwebsocket/include/IXProgressCallback.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * IXProgressCallback.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -using OnProgressCallback = std::function; -} diff --git a/ixwebsocket/include/IXSelectInterrupt.h b/ixwebsocket/include/IXSelectInterrupt.h deleted file mode 100644 index d1d2350..0000000 --- a/ixwebsocket/include/IXSelectInterrupt.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * IXSelectInterrupt.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include -#include - -namespace ix -{ -class SelectInterrupt -{ -public: - SelectInterrupt(); - virtual ~SelectInterrupt(); - - virtual bool init(std::string &errorMsg); - - virtual bool notify(uint64_t value); - virtual bool clear(); - virtual uint64_t read(); - virtual int getFd() const; - - // Used as special codes for pipe communication - static const uint64_t kSendRequest; - static const uint64_t kCloseRequest; -}; - -using SelectInterruptPtr = std::unique_ptr; -} // namespace ix diff --git a/ixwebsocket/include/IXSelectInterruptFactory.h b/ixwebsocket/include/IXSelectInterruptFactory.h deleted file mode 100644 index cf9a9d2..0000000 --- a/ixwebsocket/include/IXSelectInterruptFactory.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * IXSelectInterruptFactory.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -class SelectInterrupt; -using SelectInterruptPtr = std::unique_ptr; -SelectInterruptPtr createSelectInterrupt(); -} // namespace ix diff --git a/ixwebsocket/include/IXSelectInterruptPipe.h b/ixwebsocket/include/IXSelectInterruptPipe.h deleted file mode 100644 index 6eaacff..0000000 --- a/ixwebsocket/include/IXSelectInterruptPipe.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * IXSelectInterruptPipe.h - * Author: Benjamin Sergeant - * Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXSelectInterrupt.h" -#include -#include -#include - -namespace ix -{ -class SelectInterruptPipe final : public SelectInterrupt -{ -public: - SelectInterruptPipe(); - virtual ~SelectInterruptPipe(); - - bool init(std::string &errorMsg) final; - - bool notify(uint64_t value) final; - bool clear() final; - uint64_t read() final; - int getFd() const final; - -private: - // Store file descriptors used by the communication pipe. Communication - // 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; - static const int kPipeWriteIndex; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXSetThreadName.h b/ixwebsocket/include/IXSetThreadName.h deleted file mode 100644 index 1a60bb3..0000000 --- a/ixwebsocket/include/IXSetThreadName.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * IXSetThreadName.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ -#pragma once -#include - -namespace ix -{ -void setThreadName(const std::string &name); -} diff --git a/ixwebsocket/include/IXSocket.h b/ixwebsocket/include/IXSocket.h deleted file mode 100644 index da94916..0000000 --- a/ixwebsocket/include/IXSocket.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * IXSocket.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#ifndef _SSIZE_T_DEFINED -#include -typedef SSIZE_T ssize_t; -#endif -#endif - -#include "IXCancellationRequest.h" -#include "IXProgressCallback.h" -#include "IXSelectInterrupt.h" - -namespace ix -{ -enum class PollResultType { - ReadyForRead = 0, - ReadyForWrite = 1, - Timeout = 2, - Error = 3, - SendRequest = 4, - CloseRequest = 5 -}; - -class Socket -{ -public: - Socket(int fd = -1); - virtual ~Socket(); - bool init(std::string &errorMsg); - - // Functions to check whether there is activity on the socket - PollResultType poll(int timeoutMs = kDefaultPollTimeout); - bool wakeUpFromPoll(uint64_t wakeUpCode); - - PollResultType isReadyToWrite(int timeoutMs); - PollResultType isReadyToRead(int timeoutMs); - - // Virtual methods - virtual bool accept(std::string &errMsg); - - virtual bool connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested); - virtual void close(); - - virtual ssize_t send(char *buffer, size_t length); - ssize_t send(const std::string &buffer); - virtual ssize_t recv(void *buffer, size_t length); - - // Blocking and cancellable versions, working with socket that can be set - // to non blocking mode. Used during HTTP upgrade. - bool readByte(void *buffer, - const CancellationRequest &isCancellationRequested); - bool writeBytes(const std::string &str, - const CancellationRequest &isCancellationRequested); - - std::pair - readLine(const CancellationRequest &isCancellationRequested); - std::pair - readBytes(size_t length, - const OnProgressCallback &onProgressCallback, - const CancellationRequest &isCancellationRequested); - - static int getErrno(); - static bool isWaitNeeded(); - static void closeSocket(int fd); - - static PollResultType poll(bool readyToRead, - int timeoutMs, - int sockfd, - const SelectInterruptPtr &selectInterrupt); - -protected: - std::atomic _sockfd; - std::mutex _socketMutex; - -private: - static const int kDefaultPollTimeout; - static const int kDefaultPollNoTimeout; - - SelectInterruptPtr _selectInterrupt; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXSocketAppleSSL.h b/ixwebsocket/include/IXSocketAppleSSL.h deleted file mode 100644 index 47ef8d6..0000000 --- a/ixwebsocket/include/IXSocketAppleSSL.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * IXSocketAppleSSL.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. - */ -#ifdef IXWEBSOCKET_USE_SECURE_TRANSPORT - -#pragma once - -#include "IXCancellationRequest.h" -#include "IXSocket.h" -#include "IXSocketTLSOptions.h" -#include -#include -#include - -namespace ix -{ -class SocketAppleSSL final : public Socket -{ -public: - SocketAppleSSL(const SocketTLSOptions &tlsOptions, int fd = -1); - ~SocketAppleSSL(); - - virtual bool accept(std::string &errMsg) final; - - virtual bool - connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) final; - virtual void close() final; - - virtual ssize_t send(char *buffer, size_t length) final; - virtual ssize_t recv(void *buffer, size_t length) final; - -private: - static std::string getSSLErrorDescription(OSStatus status); - static OSStatus writeToSocket(SSLConnectionRef connection, - const void *data, - size_t *len); - static OSStatus - readFromSocket(SSLConnectionRef connection, void *data, size_t *len); - - OSStatus tlsHandShake(std::string &errMsg, - const CancellationRequest &isCancellationRequested); - - SSLContextRef _sslContext; - mutable std::mutex _mutex; // AppleSSL routines are not thread-safe - - SocketTLSOptions _tlsOptions; -}; - -} // namespace ix - -#endif // IXWEBSOCKET_USE_SECURE_TRANSPORT diff --git a/ixwebsocket/include/IXSocketConnect.h b/ixwebsocket/include/IXSocketConnect.h deleted file mode 100644 index 71aac82..0000000 --- a/ixwebsocket/include/IXSocketConnect.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * IXSocketConnect.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXCancellationRequest.h" -#include - -struct addrinfo; - -namespace ix -{ -class SocketConnect -{ -public: - static int connect(const std::string &hostname, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested); - - static void configure(int sockfd); - -private: - static int - connectToAddress(const struct addrinfo *address, - std::string &errMsg, - const CancellationRequest &isCancellationRequested); -}; -} // namespace ix diff --git a/ixwebsocket/include/IXSocketFactory.h b/ixwebsocket/include/IXSocketFactory.h deleted file mode 100644 index 5e92bc6..0000000 --- a/ixwebsocket/include/IXSocketFactory.h +++ /dev/null @@ -1,21 +0,0 @@ - -/* - * IXSocketFactory.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXSocketTLSOptions.h" -#include -#include - -namespace ix -{ -class Socket; -std::unique_ptr createSocket(bool tls, - int fd, - std::string &errorMsg, - const SocketTLSOptions &tlsOptions); -} // namespace ix diff --git a/ixwebsocket/include/IXSocketMbedTLS.h b/ixwebsocket/include/IXSocketMbedTLS.h deleted file mode 100644 index 8af50fc..0000000 --- a/ixwebsocket/include/IXSocketMbedTLS.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * IXSocketMbedTLS.h - * Author: Benjamin Sergeant - * Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. - */ -#ifdef IXWEBSOCKET_USE_MBED_TLS - -#pragma once - -#include "IXSocket.h" -#include "IXSocketTLSOptions.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ix -{ -class SocketMbedTLS final : public Socket -{ -public: - SocketMbedTLS(const SocketTLSOptions &tlsOptions, int fd = -1); - ~SocketMbedTLS(); - - virtual bool accept(std::string &errMsg) final; - - virtual bool - connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) final; - virtual void close() final; - - virtual ssize_t send(char *buffer, size_t length) final; - virtual ssize_t recv(void *buffer, size_t length) final; - -private: - mbedtls_ssl_context _ssl; - mbedtls_ssl_config _conf; - mbedtls_entropy_context _entropy; - mbedtls_ctr_drbg_context _ctr_drbg; - mbedtls_x509_crt _cacert; - mbedtls_x509_crt _cert; - mbedtls_pk_context _pkey; - - std::mutex _mutex; - SocketTLSOptions _tlsOptions; - - bool init(const std::string &host, bool isClient, std::string &errMsg); - void initMBedTLS(); - bool loadSystemCertificates(std::string &errMsg); -}; - -} // namespace ix - -#endif // IXWEBSOCKET_USE_MBED_TLS diff --git a/ixwebsocket/include/IXSocketOpenSSL.h b/ixwebsocket/include/IXSocketOpenSSL.h deleted file mode 100644 index 977fbc6..0000000 --- a/ixwebsocket/include/IXSocketOpenSSL.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * IXSocketOpenSSL.h - * Author: Benjamin Sergeant, Matt DeBoer - * Copyright (c) 2017-2020 Machine Zone, Inc. All rights reserved. - */ -#ifdef IXWEBSOCKET_USE_OPEN_SSL - -#pragma once - -#include "IXCancellationRequest.h" -#include "IXSocket.h" -#include "IXSocketTLSOptions.h" -#include -#include -#include -#include -#include -#include - -namespace ix -{ -class SocketOpenSSL final : public Socket -{ -public: - SocketOpenSSL(const SocketTLSOptions &tlsOptions, int fd = -1); - ~SocketOpenSSL(); - - virtual bool accept(std::string &errMsg) final; - - virtual bool - connect(const std::string &host, - int port, - std::string &errMsg, - const CancellationRequest &isCancellationRequested) final; - virtual void close() final; - - virtual ssize_t send(char *buffer, size_t length) final; - virtual ssize_t recv(void *buffer, size_t length) final; - -private: - void openSSLInitialize(); - std::string getSSLError(int ret); - SSL_CTX *openSSLCreateContext(std::string &errMsg); - bool openSSLAddCARootsFromString(const std::string roots); - bool openSSLClientHandshake( - const std::string &hostname, - std::string &errMsg, - const CancellationRequest &isCancellationRequested); - bool openSSLCheckServerCert(SSL *ssl, - const std::string &hostname, - std::string &errMsg); - bool checkHost(const std::string &host, const char *pattern); - bool handleTLSOptions(std::string &errMsg); - bool openSSLServerHandshake(std::string &errMsg); - - // Required for OpenSSL < 1.1 - static void openSSLLockingCallback(int mode, - int type, - const char * /*file*/, - int /*line*/); - - SSL *_ssl_connection; - SSL_CTX *_ssl_context; - const SSL_METHOD *_ssl_method; - SocketTLSOptions _tlsOptions; - - mutable std::mutex _mutex; // OpenSSL routines are not thread-safe - - static std::once_flag _openSSLInitFlag; - static std::atomic _openSSLInitializationSuccessful; -}; - -} // namespace ix - -#endif // IXWEBSOCKET_USE_OPEN_SSL diff --git a/ixwebsocket/include/IXSocketServer.h b/ixwebsocket/include/IXSocketServer.h deleted file mode 100644 index 2ec6a4e..0000000 --- a/ixwebsocket/include/IXSocketServer.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * IXSocketServer.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXConnectionState.h" -#include "IXNetSystem.h" -#include "IXSelectInterrupt.h" -#include "IXSocketTLSOptions.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // pair - -namespace ix -{ -class Socket; - -class SocketServer -{ -public: - using ConnectionStateFactory = - std::function()>; - - // Each connection is handled by its own worker thread. - // We use a list as we only care about remove and append operations. - using ConnectionThreads = - std::list, std::thread>>; - - SocketServer(int port = SocketServer::kDefaultPort, - const std::string &host = SocketServer::kDefaultHost, - int backlog = SocketServer::kDefaultTcpBacklog, - size_t maxConnections = SocketServer::kDefaultMaxConnections, - int addressFamily = SocketServer::kDefaultAddressFamily); - virtual ~SocketServer(); - virtual void stop(); - - // It is possible to override ConnectionState through inheritance - // this method allows user to change the factory by returning an object - // that inherits from ConnectionState but has its own methods. - void setConnectionStateFactory( - const ConnectionStateFactory &connectionStateFactory); - - const static int kDefaultPort; - const static std::string kDefaultHost; - const static int kDefaultTcpBacklog; - const static size_t kDefaultMaxConnections; - const static int kDefaultAddressFamily; - - void start(); - std::pair listen(); - void wait(); - - void setTLSOptions(const SocketTLSOptions &socketTLSOptions); - - int getPort(); - std::string getHost(); - int getBacklog(); - std::size_t getMaxConnections(); - int getAddressFamily(); - -protected: - // Logging - void logError(const std::string &str); - void logInfo(const std::string &str); - - void stopAcceptingConnections(); - -private: - // Member variables - int _port; - std::string _host; - int _backlog; - size_t _maxConnections; - int _addressFamily; - - // socket for accepting connections - socket_t _serverFd; - - std::atomic _stop; - - std::mutex _logMutex; - - // background thread to wait for incoming connections - std::thread _thread; - void run(); - void onSetTerminatedCallback(); - - // background thread to cleanup (join) terminated threads - std::atomic _stopGc; - std::thread _gcThread; - void runGC(); - - // the list of (connectionState, threads) for each connections - ConnectionThreads _connectionsThreads; - std::mutex _connectionsThreadsMutex; - - // used to have the main control thread for a server - // wait for a 'terminate' notification without busy polling - std::condition_variable _conditionVariable; - std::mutex _conditionVariableMutex; - - // the factory to create ConnectionState objects - ConnectionStateFactory _connectionStateFactory; - - virtual void - handleConnection(std::unique_ptr, - std::shared_ptr connectionState) = 0; - virtual size_t getConnectedClientsCount() = 0; - - // Returns true if all connection threads are joined - void closeTerminatedThreads(); - size_t getConnectionsThreadsCount(); - - SocketTLSOptions _socketTLSOptions; - - // to wake up from select - SelectInterruptPtr _acceptSelectInterrupt; - - // used by the gc thread, to know that a thread needs to be garbage - // collected as a connection - std::condition_variable _conditionVariableGC; - std::mutex _conditionVariableMutexGC; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXSocketTLSOptions.h b/ixwebsocket/include/IXSocketTLSOptions.h deleted file mode 100644 index b2ae968..0000000 --- a/ixwebsocket/include/IXSocketTLSOptions.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * IXSocketTLSOptions.h - * Author: Matt DeBoer - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -struct SocketTLSOptions { -public: - // check validity of the object - bool isValid() const; - - // the certificate presented to peers - std::string certFile; - - // the key used for signing/encryption - std::string keyFile; - - // the ca certificate (or certificate bundle) file containing - // certificates to be trusted by peers; use 'SYSTEM' to - // leverage the system defaults, use 'NONE' to disable peer verification - std::string caFile = "SYSTEM"; - - // list of ciphers (rsa, etc...) - std::string ciphers = "DEFAULT"; - - // whether tls is enabled, used for server code - bool tls = false; - - bool hasCertAndKey() const; - - bool isUsingSystemDefaults() const; - - bool isUsingInMemoryCAs() const; - - bool isPeerVerifyDisabled() const; - - bool isUsingDefaultCiphers() const; - - const std::string &getErrorMsg() const; - - std::string getDescription() const; - -private: - mutable std::string _errMsg; - mutable bool _validated = false; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXStrCaseCompare.h b/ixwebsocket/include/IXStrCaseCompare.h deleted file mode 100644 index 5c3d9a4..0000000 --- a/ixwebsocket/include/IXStrCaseCompare.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * IXStrCaseCompare.h - * Author: Benjamin Sergeant - * Copyright (c) 2020 Machine Zone. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -struct CaseInsensitiveLess { - // Case Insensitive compare_less binary function - struct NocaseCompare { - bool operator()(const unsigned char &c1, const unsigned char &c2) const; - }; - - static bool cmp(const std::string &s1, const std::string &s2); - - bool operator()(const std::string &s1, const std::string &s2) const; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXUdpSocket.h b/ixwebsocket/include/IXUdpSocket.h deleted file mode 100644 index 75cef02..0000000 --- a/ixwebsocket/include/IXUdpSocket.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * IXUdpSocket.h - * Author: Benjamin Sergeant - * Copyright (c) 2020 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include -#include - -#ifdef _WIN32 -#ifndef _SSIZE_T_DEFINED -#include -typedef SSIZE_T ssize_t; -#endif -#endif - -#include "IXNetSystem.h" - -namespace ix -{ -class UdpSocket -{ -public: - UdpSocket(int fd = -1); - ~UdpSocket(); - - // Virtual methods - bool init(const std::string &host, int port, std::string &errMsg); - ssize_t sendto(const std::string &buffer); - ssize_t recvfrom(char *buffer, size_t length); - - void close(); - - static int getErrno(); - static bool isWaitNeeded(); - static void closeSocket(int fd); - -private: - std::atomic _sockfd; - struct sockaddr_in _server; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXUniquePtr.h b/ixwebsocket/include/IXUniquePtr.h deleted file mode 100644 index 0fb530a..0000000 --- a/ixwebsocket/include/IXUniquePtr.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * IXUniquePtr.h - * Author: Benjamin Sergeant - * Copyright (c) 2020 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -template -std::unique_ptr make_unique(Args &&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} -} // namespace ix diff --git a/ixwebsocket/include/IXUrlParser.h b/ixwebsocket/include/IXUrlParser.h deleted file mode 100644 index e7ed1cc..0000000 --- a/ixwebsocket/include/IXUrlParser.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * IXUrlParser.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -class UrlParser -{ -public: - static bool parse(const std::string &url, - std::string &protocol, - std::string &host, - std::string &path, - std::string &query, - int &port); -}; -} // namespace ix diff --git a/ixwebsocket/include/IXUserAgent.h b/ixwebsocket/include/IXUserAgent.h deleted file mode 100644 index 7278494..0000000 --- a/ixwebsocket/include/IXUserAgent.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * IXUserAgent.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -std::string userAgent(); -} // namespace ix diff --git a/ixwebsocket/include/IXUtf8Validator.h b/ixwebsocket/include/IXUtf8Validator.h deleted file mode 100644 index 8e8fb87..0000000 --- a/ixwebsocket/include/IXUtf8Validator.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * The following code is adapted from code originally written by Bjoern - * Hoehrmann . See - * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. - * - * The original license: - * - * Copyright (c) 2008-2009 Bjoern Hoehrmann - * - * 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. - */ - -/* - * IXUtf8Validator.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - * - * From websocketpp. Tiny modifications made for code style, function names - * etc... - */ - -#pragma once - -#include -#include - -namespace ix -{ -/// State that represents a valid utf8 input sequence -static unsigned int const utf8_accept = 0; -/// State that represents an invalid utf8 input sequence -static unsigned int const utf8_reject = 1; - -/// Lookup table for the UTF8 decode state machine -static uint8_t const utf8d[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf - 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df - 0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, - 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef - 0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, - 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff - 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, - 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, - 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 - 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 - 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 - 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, - 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8 -}; - -/// Decode the next byte of a UTF8 sequence -/** - * @param [out] state The decoder state to advance - * @param [out] codep The codepoint to fill in - * @param [in] byte The byte to input - * @return The ending state of the decode operation - */ -inline uint32_t decodeNextByte(uint32_t *state, uint32_t *codep, uint8_t byte) -{ - uint32_t type = utf8d[byte]; - - *codep = (*state != utf8_accept) ? (byte & 0x3fu) | (*codep << 6) - : (0xff >> type) & (byte); - - *state = utf8d[256 + *state * 16 + type]; - return *state; -} - -/// Provides streaming UTF8 validation functionality -class Utf8Validator -{ -public: - /// Construct and initialize the validator - Utf8Validator() : m_state(utf8_accept), m_codepoint(0) {} - - /// Advance the state of the validator with the next input byte - /** - * @param byte The byte to advance the validation state with - * @return Whether or not the byte resulted in a validation error. - */ - bool consume(uint8_t byte) - { - if (decodeNextByte(&m_state, &m_codepoint, byte) == utf8_reject) { - return false; - } - return true; - } - - /// Advance Validator state with input from an iterator pair - /** - * @param begin Input iterator to the start of the input range - * @param end Input iterator to the end of the input range - * @return Whether or not decoding the bytes resulted in a validation error. - */ - template - bool decode(iterator_type begin, iterator_type end) - { - for (iterator_type it = begin; it != end; ++it) { - unsigned int result = decodeNextByte(&m_state, - &m_codepoint, - static_cast(*it)); - - if (result == utf8_reject) { - return false; - } - } - return true; - } - - /// Return whether the input sequence ended on a valid utf8 codepoint - /** - * @return Whether or not the input sequence ended on a valid codepoint. - */ - bool complete() { return m_state == utf8_accept; } - - /// Reset the Validator to decode another message - void reset() - { - m_state = utf8_accept; - m_codepoint = 0; - } - -private: - uint32_t m_state; - uint32_t m_codepoint; -}; - -/// Validate a UTF8 string -/** - * convenience function that creates a Validator, validates a complete string - * and returns the result. - */ -inline bool validateUtf8(std::string const &s) -{ - Utf8Validator v; - if (!v.decode(s.begin(), s.end())) { - return false; - } - return v.complete(); -} - -} // namespace ix diff --git a/ixwebsocket/include/IXUuid.h b/ixwebsocket/include/IXUuid.h deleted file mode 100644 index f7c70b8..0000000 --- a/ixwebsocket/include/IXUuid.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * IXUuid.h - * Author: Benjamin Sergeant - * Copyright (c) 2017 Machine Zone. All rights reserved. - */ -#pragma once - -#include - -namespace ix -{ -/** - * Generate a random uuid - */ -std::string uuid4(); - -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocket.h b/ixwebsocket/include/IXWebSocket.h deleted file mode 100644 index 1e841f0..0000000 --- a/ixwebsocket/include/IXWebSocket.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * IXWebSocket.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. - * - * WebSocket RFC - * https://tools.ietf.org/html/rfc6455 - */ - -#pragma once - -#include "IXProgressCallback.h" -#include "IXSocketTLSOptions.h" -#include "IXWebSocketCloseConstants.h" -#include "IXWebSocketErrorInfo.h" -#include "IXWebSocketHttpHeaders.h" -#include "IXWebSocketMessage.h" -#include "IXWebSocketPerMessageDeflateOptions.h" -#include "IXWebSocketSendInfo.h" -#include "IXWebSocketTransport.h" -#include -#include -#include -#include -#include - -namespace ix -{ -// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants -enum class ReadyState { Connecting = 0, Open = 1, Closing = 2, Closed = 3 }; - -using OnMessageCallback = std::function; - -using OnTrafficTrackerCallback = - std::function; - -class WebSocket -{ -public: - WebSocket(); - ~WebSocket(); - - void setUrl(const std::string &url); - - // send extra headers in client handshake request - void setExtraHeaders(const WebSocketHttpHeaders &headers); - void setPerMessageDeflateOptions( - const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions); - void setTLSOptions(const SocketTLSOptions &socketTLSOptions); - void setPingInterval(int pingIntervalSecs); - void enablePong(); - void disablePong(); - void enablePerMessageDeflate(); - void disablePerMessageDeflate(); - void addSubProtocol(const std::string &subProtocol); - void setHandshakeTimeout(int handshakeTimeoutSecs); - - // Run asynchronously, by calling start and stop. - void start(); - - // stop is synchronous - void stop(uint16_t code = WebSocketCloseConstants::kNormalClosureCode, - const std::string &reason = - WebSocketCloseConstants::kNormalClosureMessage); - - // Run in blocking mode, by connecting first manually, and then calling run. - WebSocketInitResult connect(int timeoutSecs); - void run(); - - // send is in text mode by default - WebSocketSendInfo - send(const std::string &data, - bool binary = false, - const OnProgressCallback &onProgressCallback = nullptr); - WebSocketSendInfo - sendBinary(const std::string &text, - const OnProgressCallback &onProgressCallback = nullptr); - WebSocketSendInfo - sendText(const std::string &text, - const OnProgressCallback &onProgressCallback = nullptr); - WebSocketSendInfo ping(const std::string &text); - - void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode, - const std::string &reason = - WebSocketCloseConstants::kNormalClosureMessage); - - void setOnMessageCallback(const OnMessageCallback &callback); - bool isOnMessageCallbackRegistered() const; - 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 getPingInterval() const; - size_t bufferedAmount() const; - - void enableAutomaticReconnection(); - void disableAutomaticReconnection(); - bool isAutomaticReconnectionEnabled() const; - void setMaxWaitBetweenReconnectionRetries( - uint32_t maxWaitBetweenReconnectionRetries); - void setMinWaitBetweenReconnectionRetries( - uint32_t minWaitBetweenReconnectionRetries); - uint32_t getMaxWaitBetweenReconnectionRetries() const; - uint32_t getMinWaitBetweenReconnectionRetries() const; - const std::vector &getSubProtocols(); - -private: - WebSocketSendInfo sendMessage(const std::string &text, - SendMessageKind sendMessageKind, - const OnProgressCallback &callback = nullptr); - - bool isConnected() const; - bool isClosing() const; - void checkConnection(bool firstConnectionAttempt); - static void invokeTrafficTrackerCallback(size_t size, bool incoming); - - // Server - WebSocketInitResult connectToSocket(std::unique_ptr, - int timeoutSecs, - bool enablePerMessageDeflate); - - WebSocketTransport _ws; - - std::string _url; - WebSocketHttpHeaders _extraHeaders; - - WebSocketPerMessageDeflateOptions _perMessageDeflateOptions; - - SocketTLSOptions _socketTLSOptions; - - mutable std::mutex _configMutex; // protect all config variables access - - OnMessageCallback _onMessageCallback; - static OnTrafficTrackerCallback _onTrafficTrackerCallback; - - std::atomic _stop; - std::thread _thread; - std::mutex _writeMutex; - - // Automatic reconnection - std::atomic _automaticReconnection; - static const uint32_t kDefaultMaxWaitBetweenReconnectionRetries; - static const uint32_t kDefaultMinWaitBetweenReconnectionRetries; - uint32_t _maxWaitBetweenReconnectionRetries; - uint32_t _minWaitBetweenReconnectionRetries; - - // Make the sleeping in the automatic reconnection cancellable - std::mutex _sleepMutex; - std::condition_variable _sleepCondition; - - std::atomic _handshakeTimeoutSecs; - static const int kDefaultHandShakeTimeoutSecs; - - // enable or disable PONG frame response to received PING frame - bool _enablePong; - static const bool kDefaultEnablePong; - - // Optional ping and pong timeout - int _pingIntervalSecs; - int _pingTimeoutSecs; - static const int kDefaultPingIntervalSecs; - static const int kDefaultPingTimeoutSecs; - - // Subprotocols - std::vector _subProtocols; - - friend class WebSocketServer; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketCloseConstants.h b/ixwebsocket/include/IXWebSocketCloseConstants.h deleted file mode 100644 index d70dfd5..0000000 --- a/ixwebsocket/include/IXWebSocketCloseConstants.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * IXWebSocketCloseConstants.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include - -namespace ix -{ -struct WebSocketCloseConstants { - static const uint16_t kNormalClosureCode; - static const uint16_t kInternalErrorCode; - static const uint16_t kAbnormalCloseCode; - static const uint16_t kProtocolErrorCode; - static const uint16_t kNoStatusCodeErrorCode; - static const uint16_t kInvalidFramePayloadData; - - static const std::string kNormalClosureMessage; - 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; - static const std::string kProtocolErrorReservedBitUsed; - static const std::string kProtocolErrorPingPayloadOversized; - static const std::string kProtocolErrorCodeControlMessageFragmented; - static const std::string kProtocolErrorCodeDataOpcodeOutOfSequence; - static const std::string kProtocolErrorCodeContinuationOpCodeOutOfSequence; - static const std::string kInvalidFramePayloadDataMessage; - static const std::string kInvalidCloseCodeMessage; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketCloseInfo.h b/ixwebsocket/include/IXWebSocketCloseInfo.h deleted file mode 100644 index fd6abc8..0000000 --- a/ixwebsocket/include/IXWebSocketCloseInfo.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * IXWebSocketCloseInfo.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include - -namespace ix -{ -struct WebSocketCloseInfo { - uint16_t code; - std::string reason; - bool remote; - - WebSocketCloseInfo(uint16_t c = 0, - const std::string &r = std::string(), - bool rem = false) - : code(c), reason(r), remote(rem) - { - ; - } -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketErrorInfo.h b/ixwebsocket/include/IXWebSocketErrorInfo.h deleted file mode 100644 index 07cb8e6..0000000 --- a/ixwebsocket/include/IXWebSocketErrorInfo.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * IXWebSocketErrorInfo.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include -#include - -namespace ix -{ -struct WebSocketErrorInfo { - uint32_t retries = 0; - double wait_time = 0; - int http_status = 0; - std::string reason; - bool decompressionError = false; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketHandshake.h b/ixwebsocket/include/IXWebSocketHandshake.h deleted file mode 100644 index 0a86fb8..0000000 --- a/ixwebsocket/include/IXWebSocketHandshake.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * IXWebSocketHandshake.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXCancellationRequest.h" -#include "IXSocket.h" -#include "IXWebSocketHttpHeaders.h" -#include "IXWebSocketInitResult.h" -#include "IXWebSocketPerMessageDeflate.h" -#include "IXWebSocketPerMessageDeflateOptions.h" -#include -#include -#include -#include - -namespace ix -{ -class WebSocketHandshake -{ -public: - WebSocketHandshake( - std::atomic &requestInitCancellation, - std::unique_ptr &_socket, - WebSocketPerMessageDeflatePtr &perMessageDeflate, - WebSocketPerMessageDeflateOptions &perMessageDeflateOptions, - std::atomic &enablePerMessageDeflate); - - WebSocketInitResult - clientHandshake(const std::string &url, - const WebSocketHttpHeaders &extraHeaders, - const std::string &host, - const std::string &path, - int port, - int timeoutSecs); - - WebSocketInitResult serverHandshake(int timeoutSecs, - bool enablePerMessageDeflate); - -private: - std::string genRandomString(const int len); - - // Parse HTTP headers - WebSocketInitResult sendErrorResponse(int code, const std::string &reason); - - bool insensitiveStringCompare(const std::string &a, const std::string &b); - - std::atomic &_requestInitCancellation; - std::unique_ptr &_socket; - WebSocketPerMessageDeflatePtr &_perMessageDeflate; - WebSocketPerMessageDeflateOptions &_perMessageDeflateOptions; - std::atomic &_enablePerMessageDeflate; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketHandshakeKeyGen.h b/ixwebsocket/include/IXWebSocketHandshakeKeyGen.h deleted file mode 100644 index 285cbce..0000000 --- a/ixwebsocket/include/IXWebSocketHandshakeKeyGen.h +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2016 Alex Hultman and contributors - -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. - -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: - -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgement in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -#pragma once - -#include -#include -#include -#include - -class WebSocketHandshakeKeyGen -{ - template struct static_for { - void operator()(uint32_t *a, uint32_t *b) - { - static_for()(a, b); - T::template f(a, b); - } - }; - - template struct static_for<0, T> { - void operator()(uint32_t * /*a*/, uint32_t * /*hash*/) {} - }; - - template struct Sha1Loop { - static inline uint32_t rol(uint32_t value, size_t bits) - { - return (value << bits) | (value >> (32 - bits)); - } - static inline uint32_t blk(uint32_t b[16], size_t i) - { - return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ - b[i], - 1); - } - - template static inline void f(uint32_t *a, uint32_t *b) - { - switch (state) { - case 1: - a[i % 5] += - ((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ - a[(1 + i) % 5]) + - b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5); - a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); - break; - case 2: - b[i] = blk(b, i); - a[(1 + i) % 5] += - ((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ - a[(2 + i) % 5]) + - b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5); - a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30); - break; - case 3: - b[(i + 4) % 16] = blk(b, (i + 4) % 16); - a[i % 5] += - (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + - b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5); - a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); - break; - case 4: - b[(i + 8) % 16] = blk(b, (i + 8) % 16); - a[i % 5] += - (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) | - (a[(3 + i) % 5] & a[(2 + i) % 5])) + - b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5); - a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); - break; - case 5: - b[(i + 12) % 16] = blk(b, (i + 12) % 16); - a[i % 5] += - (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + - b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5); - a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30); - break; - case 6: - b[i] += a[4 - i]; - } - } - }; - - static inline void sha1(uint32_t hash[5], uint32_t b[16]) - { - uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]}; - static_for<16, Sha1Loop<1>>()(a, b); - static_for<4, Sha1Loop<2>>()(a, b); - static_for<20, Sha1Loop<3>>()(a, b); - static_for<20, Sha1Loop<4>>()(a, b); - static_for<20, Sha1Loop<5>>()(a, b); - static_for<5, Sha1Loop<6>>()(a, hash); - } - - static inline void base64(unsigned char *src, char *dst) - { - const char *b64 = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - for (int i = 0; i < 18; i += 3) { - *dst++ = b64[(src[i] >> 2) & 63]; - *dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)]; - *dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)]; - *dst++ = b64[src[i + 2] & 63]; - } - *dst++ = b64[(src[18] >> 2) & 63]; - *dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)]; - *dst++ = b64[((src[19] & 15) << 2)]; - *dst++ = '='; - } - -public: - static inline void generate(const std::string &inputStr, char output[28]) - { - char input[25] = {}; - strncpy(input, inputStr.c_str(), 25 - 1); - input[25 - 1] = '\0'; - - uint32_t b_output[5] = {0x67452301, - 0xefcdab89, - 0x98badcfe, - 0x10325476, - 0xc3d2e1f0}; - uint32_t b_input[16] = {0, - 0, - 0, - 0, - 0, - 0, - 0x32353845, - 0x41464135, - 0x2d453931, - 0x342d3437, - 0x44412d39, - 0x3543412d, - 0x43354142, - 0x30444338, - 0x35423131, - 0x80000000}; - - for (int i = 0; i < 6; i++) { - b_input[i] = (input[4 * i + 3] & 0xff) | - (input[4 * i + 2] & 0xff) << 8 | - (input[4 * i + 1] & 0xff) << 16 | - (input[4 * i + 0] & 0xff) << 24; - } - sha1(b_output, b_input); - uint32_t last_b[16] = - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480}; - sha1(b_output, last_b); - for (int i = 0; i < 5; i++) { - uint32_t tmp = b_output[i]; - char *bytes = (char *)&b_output[i]; - bytes[3] = tmp & 0xff; - bytes[2] = (tmp >> 8) & 0xff; - bytes[1] = (tmp >> 16) & 0xff; - bytes[0] = (tmp >> 24) & 0xff; - } - base64((unsigned char *)b_output, output); - } -}; diff --git a/ixwebsocket/include/IXWebSocketHttpHeaders.h b/ixwebsocket/include/IXWebSocketHttpHeaders.h deleted file mode 100644 index b93034f..0000000 --- a/ixwebsocket/include/IXWebSocketHttpHeaders.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * IXWebSocketHttpHeaders.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXCancellationRequest.h" -#include "IXStrCaseCompare.h" -#include -#include -#include - -namespace ix -{ -class Socket; - -using WebSocketHttpHeaders = - std::map; - -std::pair - parseHttpHeaders(std::unique_ptr &socket, - const CancellationRequest &isCancellationRequested); -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketInitResult.h b/ixwebsocket/include/IXWebSocketInitResult.h deleted file mode 100644 index 414ae0c..0000000 --- a/ixwebsocket/include/IXWebSocketInitResult.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * IXWebSocketInitResult.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXWebSocketHttpHeaders.h" - -namespace ix -{ -struct WebSocketInitResult { - bool success; - int http_status; - std::string errorStr; - WebSocketHttpHeaders headers; - std::string uri; - std::string protocol; - - WebSocketInitResult(bool s = false, - int status = 0, - const std::string &e = std::string(), - WebSocketHttpHeaders h = WebSocketHttpHeaders(), - const std::string &u = std::string()) - { - success = s; - http_status = status; - errorStr = e; - headers = h; - uri = u; - protocol = h["Sec-WebSocket-Protocol"]; - } -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketMessage.h b/ixwebsocket/include/IXWebSocketMessage.h deleted file mode 100644 index 006b7f4..0000000 --- a/ixwebsocket/include/IXWebSocketMessage.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * IXWebSocketMessage.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXWebSocketCloseInfo.h" -#include "IXWebSocketErrorInfo.h" -#include "IXWebSocketMessageType.h" -#include "IXWebSocketOpenInfo.h" -#include -#include - -namespace ix -{ -struct WebSocketMessage { - WebSocketMessageType type; - const std::string &str; - size_t wireSize; - WebSocketErrorInfo errorInfo; - WebSocketOpenInfo openInfo; - WebSocketCloseInfo closeInfo; - bool binary; - - WebSocketMessage(WebSocketMessageType t, - const std::string &s, - size_t w, - WebSocketErrorInfo e, - WebSocketOpenInfo o, - WebSocketCloseInfo c, - bool b = false) - : type(t), str(s), wireSize(w), errorInfo(e), openInfo(o), closeInfo(c), - binary(b) - { - ; - } - - /** - * @brief Deleted overload to prevent binding `str` to a temporary, which - * would cause undefined behavior since class members don't extend lifetime - * beyond the constructor call. - */ - WebSocketMessage(WebSocketMessageType t, - std::string &&s, - size_t w, - WebSocketErrorInfo e, - WebSocketOpenInfo o, - WebSocketCloseInfo c, - bool b = false) = delete; -}; - -using WebSocketMessagePtr = std::unique_ptr; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketMessageType.h b/ixwebsocket/include/IXWebSocketMessageType.h deleted file mode 100644 index 6073b9b..0000000 --- a/ixwebsocket/include/IXWebSocketMessageType.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * IXWebSocketMessageType.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -namespace ix -{ -enum class WebSocketMessageType { - Message = 0, - Open = 1, - Close = 2, - Error = 3, - Ping = 4, - Pong = 5, - Fragment = 6 -}; -} diff --git a/ixwebsocket/include/IXWebSocketOpenInfo.h b/ixwebsocket/include/IXWebSocketOpenInfo.h deleted file mode 100644 index 2221323..0000000 --- a/ixwebsocket/include/IXWebSocketOpenInfo.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * IXWebSocketOpenInfo.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXWebSocketHttpHeaders.h" -#include -#include - -namespace ix -{ -struct WebSocketOpenInfo { - std::string uri; - WebSocketHttpHeaders headers; - std::string protocol; - - WebSocketOpenInfo(const std::string &u = std::string(), - const WebSocketHttpHeaders &h = WebSocketHttpHeaders(), - const std::string &p = std::string()) - : uri(u), headers(h), protocol(p) - { - ; - } -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketPerMessageDeflate.h b/ixwebsocket/include/IXWebSocketPerMessageDeflate.h deleted file mode 100644 index 1b6aa21..0000000 --- a/ixwebsocket/include/IXWebSocketPerMessageDeflate.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2015, Peter Thorson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the WebSocket++ Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - * - * Adapted from websocketpp/extensions/permessage_deflate/enabled.hpp - * (same license as MZ: https://opensource.org/licenses/BSD-3-Clause) - */ - -#pragma once - -#include -#include - -namespace ix -{ -class WebSocketPerMessageDeflateOptions; -class WebSocketPerMessageDeflateCompressor; -class WebSocketPerMessageDeflateDecompressor; - -class WebSocketPerMessageDeflate -{ -public: - WebSocketPerMessageDeflate(); - ~WebSocketPerMessageDeflate(); - - bool - init(const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions); - bool compress(const std::string &in, std::string &out); - bool decompress(const std::string &in, std::string &out); - -private: - std::unique_ptr _compressor; - std::unique_ptr _decompressor; -}; - -using WebSocketPerMessageDeflatePtr = - std::unique_ptr; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketPerMessageDeflateCodec.h b/ixwebsocket/include/IXWebSocketPerMessageDeflateCodec.h deleted file mode 100644 index 0399817..0000000 --- a/ixwebsocket/include/IXWebSocketPerMessageDeflateCodec.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * IXWebSocketPerMessageDeflateCodec.h - * Author: Benjamin Sergeant - * Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#ifdef IXWEBSOCKET_USE_ZLIB -#include "zlib.h" -#endif -#include -#include -#include - -namespace ix -{ -class WebSocketPerMessageDeflateCompressor -{ -public: - WebSocketPerMessageDeflateCompressor(); - ~WebSocketPerMessageDeflateCompressor(); - - bool init(uint8_t deflateBits, bool clientNoContextTakeOver); - bool compress(const std::string &in, std::string &out); - bool compress(const std::string &in, std::vector &out); - bool compress(const std::vector &in, std::string &out); - bool compress(const std::vector &in, std::vector &out); - -private: - template bool compressData(const T &in, S &out); - template bool endsWithEmptyUnCompressedBlock(const T &value); - - int _flush; - std::array _compressBuffer; - -#ifdef IXWEBSOCKET_USE_ZLIB - z_stream _deflateState; -#endif -}; - -class WebSocketPerMessageDeflateDecompressor -{ -public: - WebSocketPerMessageDeflateDecompressor(); - ~WebSocketPerMessageDeflateDecompressor(); - - bool init(uint8_t inflateBits, bool clientNoContextTakeOver); - bool decompress(const std::string &in, std::string &out); - -private: - int _flush; - std::array _compressBuffer; - -#ifdef IXWEBSOCKET_USE_ZLIB - z_stream _inflateState; -#endif -}; - -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketPerMessageDeflateOptions.h b/ixwebsocket/include/IXWebSocketPerMessageDeflateOptions.h deleted file mode 100644 index 4938284..0000000 --- a/ixwebsocket/include/IXWebSocketPerMessageDeflateOptions.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * IXWebSocketPerMessageDeflateOptions.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include - -namespace ix -{ -class WebSocketPerMessageDeflateOptions -{ -public: - WebSocketPerMessageDeflateOptions( - bool enabled = false, - bool clientNoContextTakeover = false, - bool serverNoContextTakeover = false, - uint8_t clientMaxWindowBits = kDefaultClientMaxWindowBits, - uint8_t serverMaxWindowBits = kDefaultServerMaxWindowBits); - - WebSocketPerMessageDeflateOptions(std::string extension); - - std::string generateHeader(); - bool enabled() const; - bool getClientNoContextTakeover() const; - bool getServerNoContextTakeover() const; - uint8_t getServerMaxWindowBits() const; - uint8_t getClientMaxWindowBits() const; - - static bool startsWith(const std::string &str, const std::string &start); - static std::string removeSpaces(const std::string &str); - - static uint8_t const kDefaultClientMaxWindowBits; - static uint8_t const kDefaultServerMaxWindowBits; - -private: - bool _enabled; - bool _clientNoContextTakeover; - bool _serverNoContextTakeover; - uint8_t _clientMaxWindowBits; - uint8_t _serverMaxWindowBits; - - void sanitizeClientMaxWindowBits(); -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketProxyServer.h b/ixwebsocket/include/IXWebSocketProxyServer.h deleted file mode 100644 index b2f0da0..0000000 --- a/ixwebsocket/include/IXWebSocketProxyServer.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * IXWebSocketProxyServer.h - * Author: Benjamin Sergeant - * Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved. - */ -#pragma once - -#include "IXSocketTLSOptions.h" -#include -#include -#include -#include - -namespace ix -{ -using RemoteUrlsMapping = std::map; - -int websocket_proxy_server_main(int port, - const std::string &hostname, - const ix::SocketTLSOptions &tlsOptions, - const std::string &remoteUrl, - const RemoteUrlsMapping &remoteUrlsMapping, - bool verbose); -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketSendInfo.h b/ixwebsocket/include/IXWebSocketSendInfo.h deleted file mode 100644 index 78e1116..0000000 --- a/ixwebsocket/include/IXWebSocketSendInfo.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * IXWebSocketSendInfo.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -namespace ix -{ -struct WebSocketSendInfo { - bool success; - bool compressionError; - size_t payloadSize; - size_t wireSize; - - WebSocketSendInfo(bool s = false, - bool c = false, - size_t p = 0, - size_t w = 0) - : success(s), compressionError(c), payloadSize(p), wireSize(w) - { - ; - } -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketServer.h b/ixwebsocket/include/IXWebSocketServer.h deleted file mode 100644 index 63bb7f8..0000000 --- a/ixwebsocket/include/IXWebSocketServer.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * IXWebSocketServer.h - * Author: Benjamin Sergeant - * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#include "IXSocketServer.h" -#include "IXWebSocket.h" -#include -#include -#include -#include -#include -#include -#include -#include // pair - -namespace ix -{ -class WebSocketServer : public SocketServer -{ -public: - using OnConnectionCallback = - std::function, - std::shared_ptr)>; - - using OnClientMessageCallback = - std::function, - WebSocket &, - const WebSocketMessagePtr &)>; - - WebSocketServer( - int port = SocketServer::kDefaultPort, - const std::string &host = SocketServer::kDefaultHost, - int backlog = SocketServer::kDefaultTcpBacklog, - size_t maxConnections = SocketServer::kDefaultMaxConnections, - int handshakeTimeoutSecs = - WebSocketServer::kDefaultHandShakeTimeoutSecs, - int addressFamily = SocketServer::kDefaultAddressFamily); - virtual ~WebSocketServer(); - virtual void stop() final; - - void enablePong(); - void disablePong(); - void disablePerMessageDeflate(); - - void setOnConnectionCallback(const OnConnectionCallback &callback); - void setOnClientMessageCallback(const OnClientMessageCallback &callback); - - // Get all the connected clients - std::set> getClients(); - - void makeBroadcastServer(); - bool listenAndStart(); - - const static int kDefaultHandShakeTimeoutSecs; - - int getHandshakeTimeoutSecs(); - bool isPongEnabled(); - bool isPerMessageDeflateEnabled(); - -private: - // Member variables - int _handshakeTimeoutSecs; - bool _enablePong; - bool _enablePerMessageDeflate; - - OnConnectionCallback _onConnectionCallback; - OnClientMessageCallback _onClientMessageCallback; - - std::mutex _clientsMutex; - std::set> _clients; - - const static bool kDefaultEnablePong; - - // Methods - virtual void - handleConnection(std::unique_ptr socket, - std::shared_ptr connectionState); - virtual size_t getConnectedClientsCount() final; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketTransport.h b/ixwebsocket/include/IXWebSocketTransport.h deleted file mode 100644 index e58a66d..0000000 --- a/ixwebsocket/include/IXWebSocketTransport.h +++ /dev/null @@ -1,261 +0,0 @@ -/* - * IXWebSocketTransport.h - * Author: Benjamin Sergeant - * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -// -// Adapted from https://github.com/dhbaird/easywsclient -// - -#include "IXCancellationRequest.h" -#include "IXProgressCallback.h" -#include "IXSocketTLSOptions.h" -#include "IXWebSocketCloseConstants.h" -#include "IXWebSocketHandshake.h" -#include "IXWebSocketHttpHeaders.h" -#include "IXWebSocketPerMessageDeflate.h" -#include "IXWebSocketPerMessageDeflateOptions.h" -#include "IXWebSocketSendInfo.h" -#include -#include -#include -#include -#include -#include -#include - -namespace ix -{ -class Socket; - -enum class SendMessageKind { Text, Binary, Ping }; - -class WebSocketTransport -{ -public: - enum class ReadyState { CLOSING, CLOSED, CONNECTING, OPEN }; - - enum class MessageKind { MSG_TEXT, MSG_BINARY, PING, PONG, FRAGMENT }; - - enum class PollResult { Succeeded, AbnormalClose, CannotFlushSendBuffer }; - - using OnMessageCallback = - std::function; - using OnCloseCallback = - std::function; - - WebSocketTransport(); - ~WebSocketTransport(); - - void configure( - const WebSocketPerMessageDeflateOptions &perMessageDeflateOptions, - const SocketTLSOptions &socketTLSOptions, - bool enablePong, - int pingIntervalSecs); - - // Client - WebSocketInitResult connectToUrl(const std::string &url, - const WebSocketHttpHeaders &headers, - int timeoutSecs); - - // Server - WebSocketInitResult connectToSocket(std::unique_ptr socket, - int timeoutSecs, - bool enablePerMessageDeflate); - - PollResult poll(); - WebSocketSendInfo sendBinary(const std::string &message, - const OnProgressCallback &onProgressCallback); - WebSocketSendInfo sendText(const std::string &message, - const OnProgressCallback &onProgressCallback); - WebSocketSendInfo sendPing(const std::string &message); - - void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode, - const std::string &reason = - WebSocketCloseConstants::kNormalClosureMessage, - size_t closeWireSize = 0, - bool remote = false); - - void closeSocket(); - - ReadyState getReadyState() const; - void setReadyState(ReadyState readyState); - void setOnCloseCallback(const OnCloseCallback &onCloseCallback); - void dispatch(PollResult pollResult, - const OnMessageCallback &onMessageCallback); - size_t bufferedAmount() const; - - // internal - WebSocketSendInfo sendHeartBeat(); - -private: - std::string _url; - - struct wsheader_type { - unsigned header_size; - bool fin; - bool rsv1; - bool rsv2; - bool rsv3; - bool mask; - enum opcode_type { - CONTINUATION = 0x0, - TEXT_FRAME = 0x1, - BINARY_FRAME = 0x2, - CLOSE = 8, - PING = 9, - PONG = 0xa, - } opcode; - int N0; - uint64_t N; - uint8_t masking_key[4]; - }; - - // Tells whether we should mask the data we send. - // client should mask but server should not - std::atomic _useMask; - - // Tells whether we should flush the send buffer before - // saying that a send is complete. This is the mode for server code. - std::atomic _blockingSend; - - // Buffer for reading from our socket. That buffer is never resized. - std::vector _readbuf; - - // Contains all messages that were fetched in the last socket read. - // This could be a mix of control messages (Close, Ping, etc...) and - // data messages. That buffer is resized - std::vector _rxbuf; - - // Contains all messages that are waiting to be sent - std::vector _txbuf; - mutable std::mutex _txbufMutex; - - // Hold fragments for multi-fragments messages in a list. We support - // receiving very large messages (tested messages up to 700M) and we cannot - // put them in a single buffer that is resized, as this operation can be - // slow when a buffer has its size increased 2 fold, while appending to a - // list has a fixed cost. - std::list _chunks; - - // Record the message kind (will be TEXT or BINARY) for a fragmented - // message, present in the first chunk, since the final chunk will be a - // CONTINUATION opcode and doesn't tell the full message kind - MessageKind _fragmentedMessageKind; - - // Ditto for whether a message is compressed - bool _receivedMessageCompressed; - - // Fragments are 32K long - static constexpr size_t kChunkSize = 1 << 15; - - // Underlying TCP socket - std::unique_ptr _socket; - std::mutex _socketMutex; - - // Hold the state of the connection (OPEN, CLOSED, etc...) - std::atomic _readyState; - - OnCloseCallback _onCloseCallback; - std::string _closeReason; - mutable std::mutex _closeReasonMutex; - std::atomic _closeCode; - std::atomic _closeWireSize; - std::atomic _closeRemote; - - // Data used for Per Message Deflate compression (with zlib) - WebSocketPerMessageDeflatePtr _perMessageDeflate; - WebSocketPerMessageDeflateOptions _perMessageDeflateOptions; - std::atomic _enablePerMessageDeflate; - - std::string _decompressedMessage; - std::string _compressedMessage; - - // Used to control TLS connection behavior - SocketTLSOptions _socketTLSOptions; - - // Used to cancel dns lookup + socket connect + http upgrade - std::atomic _requestInitCancellation; - - mutable std::mutex _closingTimePointMutex; - std::chrono::time_point _closingTimePoint; - static const int kClosingMaximumWaitingDelayInMs; - - // enable auto response to ping - std::atomic _enablePong; - static const bool kDefaultEnablePong; - - // Optional ping and pong timeout - int _pingIntervalSecs; - std::atomic _pongReceived; - - static const int kDefaultPingIntervalSecs; - static const std::string kPingMessage; - std::atomic _pingCount; - - // We record when ping are being sent so that we can know when to send the - // next one - mutable std::mutex _lastSendPingTimePointMutex; - std::chrono::time_point _lastSendPingTimePoint; - - // If this function returns true, it is time to send a new ping - bool pingIntervalExceeded(); - void initTimePointsAfterConnect(); - - // after calling close(), if no CLOSE frame answer is received back from the - // remote, we should close the connexion - bool closingDelayExceeded(); - - void sendCloseFrame(uint16_t code, const std::string &reason); - - void closeSocketAndSwitchToClosedState(uint16_t code, - const std::string &reason, - size_t closeWireSize, - bool remote); - - bool wakeUpFromPoll(uint64_t wakeUpCode); - - bool flushSendBuffer(); - bool sendOnSocket(); - bool receiveFromSocket(); - - template - WebSocketSendInfo - sendData(wsheader_type::opcode_type type, - const T &message, - bool compress, - const OnProgressCallback &onProgressCallback = nullptr); - - template - bool sendFragment(wsheader_type::opcode_type type, - bool fin, - Iterator begin, - Iterator end, - bool compress); - - void emitMessage(MessageKind messageKind, - const std::string &message, - bool compressedMessage, - const OnMessageCallback &onMessageCallback); - - bool isSendBufferEmpty() const; - - template - void appendToSendBuffer(const std::vector &header, - Iterator begin, - Iterator end, - uint64_t message_size, - uint8_t masking_key[4]); - - unsigned getRandomUnsigned(); - void unmaskReceiveBuffer(const wsheader_type &ws); - - std::string getMergedChunks() const; - - void setCloseReason(const std::string &reason); - const std::string &getCloseReason() const; -}; -} // namespace ix diff --git a/ixwebsocket/include/IXWebSocketVersion.h b/ixwebsocket/include/IXWebSocketVersion.h deleted file mode 100644 index ccaec84..0000000 --- a/ixwebsocket/include/IXWebSocketVersion.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * IXWebSocketVersion.h - * Author: Benjamin Sergeant - * Copyright (c) 2019 Machine Zone, Inc. All rights reserved. - */ - -#pragma once - -#define IX_WEBSOCKET_VERSION "11.3.2" diff --git a/socket/include/socket.h b/socket/include/socket.h new file mode 100644 index 0000000..a920cbb --- /dev/null +++ b/socket/include/socket.h @@ -0,0 +1,36 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_SOCKET_H +#define GERMANAIRLINESVA_GACONNECTOR_SOCKET_H + +#define BUFSIZE 1024 +#define MD5LEN 16 + +#include + +#include "types.h" +#include "util.hpp" + +#include +#include +#include +#include +#include +#include +#include + +class Socket +{ + private: + char lastPath[513] = ""; + char lastHash[2 * MD5LEN + 1] = ""; + std::mutex wsLock; + std::string url; + std::function toLog; + + public: + explicit Socket(std::string url, + std::function toLog); + ~Socket(); + void sendData(data d); +}; + +#endif diff --git a/socket/include/types.h b/socket/include/types.h new file mode 100644 index 0000000..2a46798 --- /dev/null +++ b/socket/include/types.h @@ -0,0 +1,55 @@ +#ifndef GERMANAIRLINESVA_GACONNECTOR_SOCKET_TYPES_H +#define GERMANAIRLINESVA_GACONNECTOR_SOCKET_TYPES_H + +#include + +/* Structures and enums */ +typedef struct data { + std::int32_t pause = 0; + float pBrake = 0; + std::int32_t onGrnd = 0; + float totFuelKg = 0; + float truHdg = 0; + double alt = 0; + float gs = 0; + float ias = 0; + float vs = 0; + double lat = 0; + double lon = 0; + float ff[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + float maxSpd = 0; + char path[513] = ""; + float uptime = 0; + float magHeading = 0; + float payloadKg = 0; + float totalWeightKg = 0; +} data; + +typedef enum commands { + PROCESS, + SAVE, + LOAD, + TEXT, + TIME, + UNPAUSE, + PAUSE, + PORT, + END +} commands; + +typedef struct command_base { + commands type; +} command_base; + +#pragma pack(push) /* push current alignment to stack */ +#pragma pack(1) /* set alignment to 1 byte boundary */ + +typedef struct command_port { + double latitude; + double longitude; + float trueHeading; +} command_port; + +#pragma pack(pop) /* restore original alignment from stack */ + +#endif diff --git a/socket/socket.cpp b/socket/socket.cpp new file mode 100644 index 0000000..3b0d546 --- /dev/null +++ b/socket/socket.cpp @@ -0,0 +1,46 @@ +#include "include/socket.h" + +Socket::Socket(std::string url, std::function toLog) + : url(url), toLog(std::move(toLog)) +{ +#ifdef IBM + // WSA INIT + int a = 0; +#endif + // PLATFORM AGNOSTIC +} + +Socket::~Socket() +{ + // PLATFORM AGNOSTIC +#ifdef IBM + // WSA DEINIT + int a = 0; +#endif +} + +void Socket::sendData(data d) +{ + if (strcmp(d.path, lastPath) != 0) { + strcpy(lastPath, d.path); + if (util::generateMD5(d.path, lastHash, toLog)) { + strcpy(lastHash, "NOT SET"); + } + } + + nlohmann::json json = { + {"altitude", d.alt}, + {"vs", d.vs}, + {"ias", d.ias}, + {"magHdg", d.magHeading}, + {"truHdg", d.truHdg}, + {"totFuel", d.totFuelKg}, + {"fuelFlow", d.ff}, + {"hash", lastHash}, + }; + + { + std::lock_guard lock(wsLock); + // SEND + } +} diff --git a/websocket/include/types.h b/websocket/include/types.h deleted file mode 100644 index 8f707ed..0000000 --- a/websocket/include/types.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_TYPES_H -#define GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_TYPES_H - -#include - -/* Structures and enums */ -typedef struct data { - std::int32_t pause = 0; - float pBrake = 0; - std::int32_t onGrnd = 0; - float totFuelKg = 0; - float truHdg = 0; - double alt = 0; - float gs = 0; - float ias = 0; - float vs = 0; - double lat = 0; - double lon = 0; - float ff[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - float maxSpd = 0; - char path[513] = ""; - float uptime = 0; - float magHeading = 0; - float payloadKg = 0; - float totalWeightKg = 0; -} data; - -typedef enum commands { - PROCESS, - SAVE, - LOAD, - TEXT, - TIME, - UNPAUSE, - PAUSE, - PORT, - END -} commands; - -typedef struct command_base { - commands type; -} command_base; - -#pragma pack(push) /* push current alignment to stack */ -#pragma pack(1) /* set alignment to 1 byte boundary */ - -typedef struct command_port { - double latitude; - double longitude; - float trueHeading; -} command_port; - -#pragma pack(pop) /* restore original alignment from stack */ - -#endif diff --git a/websocket/include/websocket.h b/websocket/include/websocket.h deleted file mode 100644 index ed96f9a..0000000 --- a/websocket/include/websocket.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_H -#define GERMANAIRLINESVA_GACONNECTOR_WEBSOCKET_H - -#define BUFSIZE 1024 -#define MD5LEN 16 - -#include -#include - -#include - -#include "types.h" -#include "util.hpp" - -#include -#include -#include -#include -#include -#include - -class Websocket -{ -private: - char lastPath[513] = ""; - char lastHash[2 * MD5LEN + 1] = ""; - ix::WebSocket *webSocket = nullptr; - ix::WebSocketServer *server; - std::mutex wsLock; - std::function toLog; - -public: - explicit Websocket(std::function toLog); - ~Websocket(); - void onClientMessageCallback( - std::shared_ptr &connectionState, - ix::WebSocket &ws, - const ix::WebSocketMessagePtr &msg); - void sendData(data d); -}; - -#endif diff --git a/websocket/websocket.cpp b/websocket/websocket.cpp deleted file mode 100644 index 155ce61..0000000 --- a/websocket/websocket.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "include/websocket.h" - -Websocket::Websocket(std::function toLog) - : toLog(std::move(toLog)) -{ -#ifdef IBM - // Required on Windows - ix::initNetSystem(); -#endif - - server = new ix::WebSocketServer(8080, "0.0.0.0"); - - server->setOnClientMessageCallback( - [this](std::shared_ptr connectionState, - ix::WebSocket &ws, - const ix::WebSocketMessagePtr &msg) { - this->onClientMessageCallback(connectionState, ws, msg); - }); - - std::pair res = server->listen(); - if (!res.first) { - // Error handling - throw std::invalid_argument("Encountered: " + res.second + "\n"); - } - - // Run the server in the background. Server can be stopped by calling - server->start(); -} - -Websocket::~Websocket() -{ - server->stop(); - -#ifdef IBM - // Required on Windows - ix::uninitNetSystem(); -#endif -} - -void Websocket::onClientMessageCallback( - std::shared_ptr &connectionState, - ix::WebSocket &ws, - const ix::WebSocketMessagePtr &msg) -{ - if (msg->type == ix::WebSocketMessageType::Open) { - std::stringstream debug_msg; - - debug_msg << "New connection" << std::endl; - debug_msg << "Remote ip: " << connectionState->getRemoteIp() - << std::endl; - debug_msg << "id: " << connectionState->getId() << std::endl; - debug_msg << "Uri: " << msg->openInfo.uri << std::endl; - debug_msg << "Headers:" << std::endl; - for (const auto &it : msg->openInfo.headers) { - debug_msg << it.first << ": " << it.second << std::endl; - } - - toLog(debug_msg.str()); - - const std::lock_guard guard(wsLock); - webSocket = &ws; - } else if (msg->type == ix::WebSocketMessageType::Close) { - std::stringstream debug_msg; - - debug_msg << "Connection closed" << std::endl; - debug_msg << "id: " << connectionState->getId() << std::endl; - debug_msg << "Code: " << msg->closeInfo.code << std::endl; - debug_msg << "Reason: " << msg->closeInfo.reason << std::endl; - debug_msg << "Remote: " << msg->closeInfo.remote << std::endl; - - toLog(debug_msg.str()); - - const std::lock_guard guard(wsLock); - webSocket = nullptr; - } else if (msg->type == ix::WebSocketMessageType::Error) { - std::stringstream debug_msg; - - debug_msg << "Connection error" << std::endl; - debug_msg << "id: " << connectionState->getId() << std::endl; - debug_msg << "Decompression: " << msg->errorInfo.decompressionError - << std::endl; - debug_msg << "HTTP status: " << msg->errorInfo.http_status << std::endl; - debug_msg << "Reason: " << msg->errorInfo.reason << std::endl; - debug_msg << "Retries: " << msg->errorInfo.retries << std::endl; - debug_msg << "Wait time: " << msg->errorInfo.wait_time << std::endl; - - toLog(debug_msg.str()); - - const std::lock_guard guard(wsLock); - webSocket = nullptr; - } else if (msg->type == ix::WebSocketMessageType::Message) { - if (!msg->str.empty()) { - ws.send("Echo: " + msg->str, false); - } - } -} - -void Websocket::sendData(data d) -{ - if (strcmp(d.path, lastPath) != 0) { - strcpy(lastPath, d.path); - if (util::generateMD5(d.path, lastHash, toLog)) { - strcpy(lastHash, "NOT SET"); - } - } - - nlohmann::json json = { - {"altitude", d.alt}, - {"vs", d.vs}, - {"ias", d.ias}, - {"magHdg", d.magHeading}, - {"truHdg", d.truHdg}, - {"totFuel", d.totFuelKg}, - {"fuelFlow", d.ff}, - {"hash", lastHash}, - }; - - { - std::lock_guard lock(wsLock); - if (webSocket != nullptr) { - webSocket->send(json.dump(), false); - } - } -} diff --git a/xplugin/CMakeLists.txt b/xplugin/CMakeLists.txt index c085c82..217885f 100644 --- a/xplugin/CMakeLists.txt +++ b/xplugin/CMakeLists.txt @@ -1,23 +1,15 @@ -#For TLS -#target_compile_definitions(WebSocketServerXPlane PUBLIC -#IXWEBSOCKET_USE_TLS -#IXWEBSOCKET_USE_OPEN_SSL -#) -#Link to ssl(requires OpenSLL for macOS and Windows) - -file(GLOB websocket CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/websocket/*.cpp) +file(GLOB socket CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/socket/*.cpp) file(GLOB file CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/file/*.cpp) add_library(germanairlinesva_xplugin SHARED - ${websocket} + ${socket} ${file} makeRwysXP.cpp main.cpp ) target_include_directories(germanairlinesva_xplugin PRIVATE - ${CMAKE_SOURCE_DIR}/ixwebsocket/include - ${CMAKE_SOURCE_DIR}/websocket/include + ${CMAKE_SOURCE_DIR}/socket/include ${CMAKE_SOURCE_DIR}/XPSDK/CHeaders ${CMAKE_SOURCE_DIR}/nlohmann ${CMAKE_SOURCE_DIR}/file @@ -64,16 +56,16 @@ if(APPLE) APL ) target_compile_options(germanairlinesva_xplugin PRIVATE - #"SHELL:-arch i386" + "SHELL:-arch i386" "SHELL:-arch x86_64" ) target_link_options(germanairlinesva_xplugin PRIVATE - #"SHELL:-arch i386" + "SHELL:-arch i386" "SHELL:-arch x86_64" ) target_link_libraries(germanairlinesva_xplugin PRIVATE ${CMAKE_SOURCE_DIR}/XPSDK/Libraries/Mac/XPLM.framework/XPLM - ixwebsocket + "-framework Security" ) elseif(UNIX) message("Building for Linux ${BIT} into ${PROJECT_BINARY_DIR}/Plugin/${PLUGIN_NAME}/${BIT}") @@ -101,7 +93,6 @@ elseif(UNIX) target_link_libraries(germanairlinesva_xplugin PRIVATE crypto pthread - ixwebsocket ) elseif(WIN32) message("Building for Windows ${BIT} into ${PROJECT_BINARY_DIR}/Plugin/${PLUGIN_NAME}/${BIT}") @@ -123,13 +114,11 @@ elseif(WIN32) ) endif() target_link_options(germanairlinesva_xplugin PRIVATE - -static -static-libgcc -static-libstdc++ ) target_link_libraries(germanairlinesva_xplugin PRIVATE ws2_32.lib - ixwebsocket ) if(BIT STREQUAL "32") target_link_libraries(germanairlinesva_xplugin PRIVATE diff --git a/xplugin/include/main.h b/xplugin/include/main.h index 820e824..462fc62 100644 --- a/xplugin/include/main.h +++ b/xplugin/include/main.h @@ -5,7 +5,7 @@ #include "makeRwysXP.h" #include "recordingPath.hpp" #include "simulatorDatabase.hpp" -#include "websocket.h" +#include "socket.h" #include "XPLM/XPLMDataAccess.h" #include "XPLM/XPLMGraphics.h" diff --git a/xplugin/main.cpp b/xplugin/main.cpp index 92837c4..9fabc9a 100644 --- a/xplugin/main.cpp +++ b/xplugin/main.cpp @@ -3,8 +3,8 @@ std::mutex mutex; std::queue> &messageQueue() { - static std::queue> _messageQueue; - return _messageQueue; + static std::queue> _messageQueue; + return _messageQueue; } std::thread serverThread; std::thread recordingThread; @@ -12,7 +12,7 @@ std::atomic wantsExit; std::map configuration; -Websocket *connector; +Socket *connector; /* Datarefs */ XPLMDataRef pauseIndicator; @@ -51,124 +51,123 @@ Path p; PLUGIN_API int XPluginStart(char *outName, char *outSig, char *outDesc) { - XPLMEnableFeature("XPLM_USE_NATIVE_PATHS", 1); + XPLMEnableFeature("XPLM_USE_NATIVE_PATHS", 1); - wantsExit.store(false); + wantsExit.store(false); - /* First we must fill in the passed-in buffers to describe our - * plugin to the plugin-system. */ + /* First we must fill in the passed-in buffers to describe our + * plugin to the plugin-system. */ - strcpy(outName, "GAConnector"); - strcpy(outSig, "de.german-airlines.GAConnector"); - strcpy(outDesc, "GAConnector"); + strcpy(outName, "GAConnector"); + strcpy(outSig, "de.german-airlines.GAConnector"); + strcpy(outDesc, "GAConnector"); - /* Flight Loop */ - XPLMRegisterFlightLoopCallback(flightLoop, -1, nullptr); + /* Flight Loop */ + XPLMRegisterFlightLoopCallback(flightLoop, -1, nullptr); - /* Setup DataRefs */ - pauseIndicator = XPLMFindDataRef( - "sim/time/sim_speed"); // INT 0=paused, doubles as setting pause(1) - parkingBrake = - XPLMFindDataRef("sim/flightmodel/controls/parkbrake"); // FLOAT 1=max - acftOnGrnd = - XPLMFindDataRef("sim/flightmodel/failures/onground_any"); // INT 1=true - totalFuelKgs = - XPLMFindDataRef("sim/flightmodel/weight/m_fuel_total"); // FLOAT - trueHeading = XPLMFindDataRef( - "sim/flightmodel/position/true_psi"); // FLOAT degrees north - acftAlt = - XPLMFindDataRef("sim/flightmodel/position/elevation"); // DOUBLE meters - acftGS = XPLMFindDataRef( - "sim/flightmodel/position/groundspeed"); // FLOAT meters/second - acftIAS = XPLMFindDataRef( - "sim/flightmodel/position/indicated_airspeed"); // FLOAT kias - acftVS = XPLMFindDataRef( - "sim/flightmodel/position/vh_ind_fpm"); // FLOAT feet/minute - acftLatitude = - XPLMFindDataRef("sim/flightmodel/position/latitude"); // DOUBLE degrees - acftLongitude = - XPLMFindDataRef("sim/flightmodel/position/longitude"); // DOUBLE degrees - fuelFlow = XPLMFindDataRef( - "sim/flightmodel/engine/ENGN_FF_"); // FLOAT[8] kg/second - maxSpeed = XPLMFindDataRef("sim/aircraft/view/acf_Vne"); // FLOAT kias - uptime = - XPLMFindDataRef("sim/time/total_running_time_sec"); // FLOAT seconds - magHeading = XPLMFindDataRef( - "sim/flightmodel/position/mag_psi"); // FLOAT degrees north - timeZulu = XPLMFindDataRef( - "sim/time/zulu_time_sec"); // FLOAT seconds since midnight - payloadKgs = XPLMFindDataRef("sim/flightmodel/weight/m_fixed"); // FLOAT - totalWeightKgs = XPLMFindDataRef("sim/flightmodel/weight/m_total"); // FLOAT + /* Setup DataRefs */ + pauseIndicator = XPLMFindDataRef( + "sim/time/sim_speed"); // INT 0=paused, doubles as setting pause(1) + parkingBrake = + XPLMFindDataRef("sim/flightmodel/controls/parkbrake"); // FLOAT 1=max + acftOnGrnd = + XPLMFindDataRef("sim/flightmodel/failures/onground_any"); // INT 1=true + totalFuelKgs = + XPLMFindDataRef("sim/flightmodel/weight/m_fuel_total"); // FLOAT + trueHeading = XPLMFindDataRef( + "sim/flightmodel/position/true_psi"); // FLOAT degrees north + acftAlt = + XPLMFindDataRef("sim/flightmodel/position/elevation"); // DOUBLE meters + acftGS = XPLMFindDataRef( + "sim/flightmodel/position/groundspeed"); // FLOAT meters/second + acftIAS = XPLMFindDataRef( + "sim/flightmodel/position/indicated_airspeed"); // FLOAT kias + acftVS = XPLMFindDataRef( + "sim/flightmodel/position/vh_ind_fpm"); // FLOAT feet/minute + acftLatitude = + XPLMFindDataRef("sim/flightmodel/position/latitude"); // DOUBLE degrees + acftLongitude = + XPLMFindDataRef("sim/flightmodel/position/longitude"); // DOUBLE degrees + fuelFlow = + XPLMFindDataRef("sim/flightmodel/engine/ENGN_FF_"); // FLOAT[8] kg/second + maxSpeed = XPLMFindDataRef("sim/aircraft/view/acf_Vne"); // FLOAT kias + uptime = XPLMFindDataRef("sim/time/total_running_time_sec"); // FLOAT seconds + magHeading = XPLMFindDataRef( + "sim/flightmodel/position/mag_psi"); // FLOAT degrees north + timeZulu = + XPLMFindDataRef("sim/time/zulu_time_sec"); // FLOAT seconds since midnight + payloadKgs = XPLMFindDataRef("sim/flightmodel/weight/m_fixed"); // FLOAT + totalWeightKgs = XPLMFindDataRef("sim/flightmodel/weight/m_total"); // FLOAT - localX = XPLMFindDataRef("sim/flightmodel/position/local_x"); // DOUBLE - localY = XPLMFindDataRef("sim/flightmodel/position/local_y"); // DOUBLE - localZ = XPLMFindDataRef("sim/flightmodel/position/local_z"); // DOUBLE - elevation = XPLMFindDataRef("sim/flightmodel/position/elevation"); // DOUBLE - pitch = XPLMFindDataRef("sim/flightmodel/position/theta"); // FLOAT - roll = XPLMFindDataRef("sim/flightmodel/position/phi"); // FLOAT - quaternion = XPLMFindDataRef("sim/flightmodel/position/q"); // FLOAT[4] + localX = XPLMFindDataRef("sim/flightmodel/position/local_x"); // DOUBLE + localY = XPLMFindDataRef("sim/flightmodel/position/local_y"); // DOUBLE + localZ = XPLMFindDataRef("sim/flightmodel/position/local_z"); // DOUBLE + elevation = XPLMFindDataRef("sim/flightmodel/position/elevation"); // DOUBLE + pitch = XPLMFindDataRef("sim/flightmodel/position/theta"); // FLOAT + roll = XPLMFindDataRef("sim/flightmodel/position/phi"); // FLOAT + quaternion = XPLMFindDataRef("sim/flightmodel/position/q"); // FLOAT[4] - // Initialize connector - try { - connector = new Websocket(toLog); - } catch (const std::invalid_argument &e) { - toLog(e.what()); - return 0; + // Initialize connector + try { + connector = + new Socket("https://wsecho.hofmannnet.myhome-server.de/client", toLog); + } catch (const std::invalid_argument &e) { + toLog(e.what()); + return 0; + } + toLog("WebSocket started"); + + configuration = + config::readConfig("Resources/plugins/GAConnector/config.cfg"); + toLog("Config loaded"); + + char hash[2 * MD5LEN + 1] = ""; + if (util::generateMD5("Custom Scenery/scenery_packs.ini", hash, toLog) == 0) { + std::map, std::vector>> + airports; + + if (strcmp(configuration["scenery"].c_str(), hash) != 0) { + scan("Resources/default scenery/default apt dat/Earth nav " + "data/apt.dat", + "Custom Scenery/scenery_packs.ini", + "Resources/plugins/GAConnector/log.txt", + airports); + simulatorDatabase::toFile(airports, + "Resources/plugins/GAConnector/sim.bin"); + + configuration["scenery"] = hash; + config::writeConfig(configuration, + "Resources/plugins/GAConnector/config.cfg"); + toLog("Sim Database updated"); + } else { + airports = + simulatorDatabase::fromFile("Resources/plugins/GAConnector/sim.bin"); + toLog("Sim Database loaded"); } - toLog("WebSocket started"); + } - configuration = - config::readConfig("Resources/plugins/GAConnector/config.cfg"); - toLog("Config loaded"); + // Thread for sending data to web socket + serverThread = std::thread(&serverWorker); + recordingThread = std::thread(&recordingWorker); + toLog("Workers started"); - char hash[2 * MD5LEN + 1] = ""; - if (util::generateMD5("Custom Scenery/scenery_packs.ini", hash, toLog) == - 0) { - std::map, std::vector>> - airports; - - if (strcmp(configuration["scenery"].c_str(), hash) != 0) { - scan("Resources/default scenery/default apt dat/Earth nav " - "data/apt.dat", - "Custom Scenery/scenery_packs.ini", - "Resources/plugins/GAConnector/log.txt", - airports); - simulatorDatabase::toFile(airports, - "Resources/plugins/GAConnector/sim.bin"); - - configuration["scenery"] = hash; - config::writeConfig(configuration, - "Resources/plugins/GAConnector/config.cfg"); - toLog("Sim Database updated"); - } else { - airports = simulatorDatabase::fromFile( - "Resources/plugins/GAConnector/sim.bin"); - toLog("Sim Database loaded"); - } - } - - // Thread for sending data to web socket - serverThread = std::thread(&serverWorker); - recordingThread = std::thread(&recordingWorker); - toLog("Workers started"); - - return 1; + return 1; } PLUGIN_API void XPluginStop(void) { - /* Flight Loop */ - XPLMUnregisterFlightLoopCallback(flightLoop, nullptr); - /* End threads */ - wantsExit = true; - delete connector; - serverThread.join(); - recordingThread.join(); + /* Flight Loop */ + XPLMUnregisterFlightLoopCallback(flightLoop, nullptr); + /* End threads */ + wantsExit = true; + delete connector; + serverThread.join(); + recordingThread.join(); - std::ofstream out("Resources/plugins/GAConnector/flight.rec"); - out.write(reinterpret_cast(p.getBinaryData()), - (std::streamsize)p.getBinaryLength()); - out.close(); + std::ofstream out("Resources/plugins/GAConnector/flight.rec"); + out.write(reinterpret_cast(p.getBinaryData()), + (std::streamsize)p.getBinaryLength()); + out.close(); } PLUGIN_API void XPluginDisable(void) {} @@ -187,94 +186,93 @@ PLUGIN_API void #pragma clang diagnostic ignored "-Wunused-parameter" float flightLoop(float elapsedMe, float elapsedSim, int counter, void *refcon) { - const std::lock_guard lock(mutex); + const std::lock_guard lock(mutex); - memset(&toSend, 0, sizeof(data)); + memset(&toSend, 0, sizeof(data)); - toSend.pause = XPLMGetDatai(pauseIndicator); - toSend.pBrake = XPLMGetDataf(parkingBrake); - toSend.onGrnd = XPLMGetDatai(acftOnGrnd); - toSend.totFuelKg = XPLMGetDataf(totalFuelKgs); - toSend.truHdg = XPLMGetDataf(trueHeading); - toSend.alt = XPLMGetDatad(acftAlt); - toSend.gs = XPLMGetDataf(acftGS); - toSend.ias = XPLMGetDataf(acftIAS); - toSend.vs = XPLMGetDataf(acftVS); - toSend.lat = XPLMGetDatad(acftLatitude); - toSend.lon = XPLMGetDatad(acftLongitude); - XPLMGetDatavf(fuelFlow, toSend.ff, 0, 8); - toSend.maxSpd = XPLMGetDataf(maxSpeed); + toSend.pause = XPLMGetDatai(pauseIndicator); + toSend.pBrake = XPLMGetDataf(parkingBrake); + toSend.onGrnd = XPLMGetDatai(acftOnGrnd); + toSend.totFuelKg = XPLMGetDataf(totalFuelKgs); + toSend.truHdg = XPLMGetDataf(trueHeading); + toSend.alt = XPLMGetDatad(acftAlt); + toSend.gs = XPLMGetDataf(acftGS); + toSend.ias = XPLMGetDataf(acftIAS); + toSend.vs = XPLMGetDataf(acftVS); + toSend.lat = XPLMGetDatad(acftLatitude); + toSend.lon = XPLMGetDatad(acftLongitude); + XPLMGetDatavf(fuelFlow, toSend.ff, 0, 8); + toSend.maxSpd = XPLMGetDataf(maxSpeed); - char *name = (char *)calloc(256, sizeof(char)); - XPLMGetNthAircraftModel(0, name, toSend.path); - free(name); + char *name = (char *)calloc(256, sizeof(char)); + XPLMGetNthAircraftModel(0, name, toSend.path); + free(name); - toSend.uptime = XPLMGetDataf(uptime); - toSend.magHeading = XPLMGetDataf(magHeading); + toSend.uptime = XPLMGetDataf(uptime); + toSend.magHeading = XPLMGetDataf(magHeading); - toSend.payloadKg = XPLMGetDataf(payloadKgs); - toSend.totalWeightKg = XPLMGetDataf(totalWeightKgs); + toSend.payloadKg = XPLMGetDataf(payloadKgs); + toSend.totalWeightKg = XPLMGetDataf(totalWeightKgs); - if (!messageQueue().empty()) { - auto op = std::move(messageQueue().front()); - messageQueue().pop(); - op(); - } + if (!messageQueue().empty()) { + auto op = std::move(messageQueue().front()); + messageQueue().pop(); + op(); + } - return -1; + return -1; } #pragma clang diagnostic pop void serverWorker() { - util::setThreadName("GAServerWorker"); + util::setThreadName("GAServerWorker"); - while (!wantsExit) { - data copy; - { - const std::lock_guard lock(mutex); + while (!wantsExit) { + data copy; + { + const std::lock_guard lock(mutex); - memcpy(©, &toSend, sizeof(data)); - } - - connector->sendData(copy); - - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + memcpy(©, &toSend, sizeof(data)); } + + connector->sendData(copy); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } } void recordingWorker() { - util::setThreadName("GARecordingWorker"); + util::setThreadName("GARecordingWorker"); - PathSegment lastPath; + PathSegment lastPath; - while (!wantsExit) { - data copy; - { - const std::lock_guard lock(mutex); + while (!wantsExit) { + data copy; + { + const std::lock_guard lock(mutex); - memcpy(©, &toSend, sizeof(data)); - } - - PathSegment currentPath(static_cast(copy.alt), - static_cast(copy.gs), - copy.lat, - copy.lon); - - if (strcmp(copy.path, "") != 0 && copy.pause && - lastPath != currentPath) { - p.addSegment(currentPath); - lastPath = currentPath; - } - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + memcpy(©, &toSend, sizeof(data)); } + + PathSegment currentPath(static_cast(copy.alt), + static_cast(copy.gs), + copy.lat, + copy.lon); + + if (strcmp(copy.path, "") != 0 && copy.pause && lastPath != currentPath) { + p.addSegment(currentPath); + lastPath = currentPath; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } } void toLog(const std::string &message) { - std::stringstream msg; - msg << "German Airlines VA: " << message << std::endl; - XPLMDebugString(msg.str().c_str()); + std::stringstream msg; + msg << "German Airlines VA: " << message << std::endl; + XPLMDebugString(msg.str().c_str()); } diff --git a/xplugin/makeRwysXP.cpp b/xplugin/makeRwysXP.cpp index f87b23e..9b0c013 100644 --- a/xplugin/makeRwysXP.cpp +++ b/xplugin/makeRwysXP.cpp @@ -6,58 +6,58 @@ int scan(const char *defaultFile, std::map, std::vector>> &airports) { - std::ifstream base(defaultFile); - if (!base.good()) { - return 1; - } - std::ifstream custom(sceneryPack); - if (!custom.good()) { - base.close(); - return 2; - } - std::ofstream logfile(logFile, std::ios::out | std::ios::trunc); - if (!logfile.good()) { - base.close(); - custom.close(); - return 3; - } - - // Default - logfile << " " << defaultFile << std::endl; - makeAirport("DEFAULT", &base, &airports, &logfile); + std::ifstream base(defaultFile); + if (!base.good()) { + return 1; + } + std::ifstream custom(sceneryPack); + if (!custom.good()) { + base.close(); + return 2; + } + std::ofstream logfile(logFile, std::ios::out | std::ios::trunc); + if (!logfile.good()) { base.close(); - - std::string line; - size_t pos; - std::vector packs; - while (std::getline(custom, line)) { - if ((pos = line.find("SCENERY_PACK")) != std::string::npos) { - std::string path = - rtrim_copy(line.substr(pos + 13)) + "Earth nav data/apt.dat"; - packs.push_back(path); - } - } - std::reverse(packs.begin(), packs.end()); - - for (std::string const &path : packs) { - std::ifstream pack(path); - if (pack.good()) { - logfile << " " << path << std::endl; - makeAirport("CUSTOM", &pack, &airports, &logfile); - pack.close(); - } else { - pack.close(); - logfile << "" - << "Could not find " << path << std::endl; - } - } - - logfile << std::endl - << " Total airports: " << airports.size() << std::endl; - custom.close(); - logfile.close(); - return 0; + return 3; + } + + // Default + logfile << " " << defaultFile << std::endl; + makeAirport("DEFAULT", &base, &airports, &logfile); + base.close(); + + std::string line; + size_t pos; + std::vector packs; + while (std::getline(custom, line)) { + if ((pos = line.find("SCENERY_PACK")) != std::string::npos) { + std::string path = + rtrim_copy(line.substr(pos + 13)) + "Earth nav data/apt.dat"; + packs.push_back(path); + } + } + std::reverse(packs.begin(), packs.end()); + + for (std::string const &path : packs) { + std::ifstream pack(path); + if (pack.good()) { + logfile << " " << path << std::endl; + makeAirport("CUSTOM", &pack, &airports, &logfile); + pack.close(); + } else { + pack.close(); + logfile << "" + << "Could not find " << path << std::endl; + } + } + + logfile << std::endl + << " Total airports: " << airports.size() << std::endl; + + custom.close(); + logfile.close(); + return 0; } void makeAirport( @@ -67,114 +67,108 @@ void makeAirport( *airports, std::ofstream *logfile) { - std::string line; - std::string *currentIcao = nullptr; - std::vector tmpGates; - std::vector tmpRunways; + std::string line; + std::string *currentIcao = nullptr; + std::vector tmpGates; + std::vector tmpRunways; - int apCount = 0; - int validCount = 0; + int apCount = 0; + int validCount = 0; - while (std::getline(*infile, line)) { - std::vector fields = split(line, ' '); - fields = util::select_T(fields, [](const std::string &s) { - return s.length() > 0; - }); + while (std::getline(*infile, line)) { + std::vector fields = split(line, ' '); + fields = util::select_T(fields, [](const std::string &s) { + return s.length() > 0; + }); - if (fields.empty()) - continue; - if (fields[0] == "1") { - // Write to file if ICAO is valid, and we have gates and runways - if (currentIcao != nullptr && !tmpRunways.empty() && - !tmpGates.empty()) { - (*airports)[*currentIcao] = {tmpGates, tmpRunways}; - validCount += 1; - *logfile << "\t " << *currentIcao << " committed" - << std::endl; - } else if (currentIcao != nullptr) { - *logfile << "\t " << *currentIcao - << " had no gates or runways" << std::endl; - } - tmpGates = std::vector(); - tmpRunways = std::vector(); - currentIcao = new std::string(fields[4]); - apCount += 1; - *logfile << "\t<" << kind << "> " << line << std::endl; - } else if (currentIcao != nullptr && fields[0] == "15") { - makeGate15(&tmpGates, fields); - *logfile << "\t\t " << line << std::endl; - } else if (fields[0] == "16" || fields[0] == "17") { - // Write to file if ICAO is valid, and we have gates and runways - if (currentIcao != nullptr && !tmpRunways.empty() && - !tmpGates.empty()) { - (*airports)[*currentIcao] = {tmpGates, tmpRunways}; - validCount += 1; - *logfile << "\t " << *currentIcao << " committed" - << std::endl; - } else if (currentIcao != nullptr) { - *logfile << "\t " << *currentIcao - << " had no gates or runways" << std::endl; - } - tmpGates = std::vector(); - tmpRunways = std::vector(); - currentIcao = nullptr; - *logfile << "\t<" << kind << " SKIPPED> " << line << std::endl; - } else if (currentIcao != nullptr && fields[0] == "100") { - makeRunway(&tmpRunways, fields); - *logfile << "\t\t " << line << std::endl; - } else if (currentIcao != nullptr && fields[0] == "1300") { - makeGate1300(&tmpGates, fields); - *logfile << "\t\t " << line << std::endl; - } - } - - if (currentIcao != nullptr && !tmpRunways.empty() && !tmpGates.empty()) { + if (fields.empty()) + continue; + if (fields[0] == "1") { + // Write to file if ICAO is valid, and we have gates and runways + if (currentIcao != nullptr && !tmpRunways.empty() && !tmpGates.empty()) { (*airports)[*currentIcao] = {tmpGates, tmpRunways}; validCount += 1; *logfile << "\t " << *currentIcao << " committed" << std::endl; + } else if (currentIcao != nullptr) { + *logfile << "\t " << *currentIcao << " had no gates or runways" + << std::endl; + } + tmpGates = std::vector(); + tmpRunways = std::vector(); + currentIcao = new std::string(fields[4]); + apCount += 1; + *logfile << "\t<" << kind << "> " << line << std::endl; + } else if (currentIcao != nullptr && fields[0] == "15") { + makeGate15(&tmpGates, fields); + *logfile << "\t\t " << line << std::endl; + } else if (fields[0] == "16" || fields[0] == "17") { + // Write to file if ICAO is valid, and we have gates and runways + if (currentIcao != nullptr && !tmpRunways.empty() && !tmpGates.empty()) { + (*airports)[*currentIcao] = {tmpGates, tmpRunways}; + validCount += 1; + *logfile << "\t " << *currentIcao << " committed" << std::endl; + } else if (currentIcao != nullptr) { + *logfile << "\t " << *currentIcao << " had no gates or runways" + << std::endl; + } + tmpGates = std::vector(); + tmpRunways = std::vector(); + currentIcao = nullptr; + *logfile << "\t<" << kind << " SKIPPED> " << line << std::endl; + } else if (currentIcao != nullptr && fields[0] == "100") { + makeRunway(&tmpRunways, fields); + *logfile << "\t\t " << line << std::endl; + } else if (currentIcao != nullptr && fields[0] == "1300") { + makeGate1300(&tmpGates, fields); + *logfile << "\t\t " << line << std::endl; } - *logfile << " " << apCount << " airports found, of which " - << validCount << " are valid" << std::endl; + } + + if (currentIcao != nullptr && !tmpRunways.empty() && !tmpGates.empty()) { + (*airports)[*currentIcao] = {tmpGates, tmpRunways}; + validCount += 1; + *logfile << "\t " << *currentIcao << " committed" << std::endl; + } + *logfile << " " << apCount << " airports found, of which " + << validCount << " are valid" << std::endl; } void makeGate15(std::vector *gates, std::vector fields) { - std::string gateName; - for (size_t j = 4; j < fields.size() - 1; j++) { - gateName += fields[j] + " "; - } - gateName += fields.back(); - gateName = std::regex_replace(gateName, std::regex{","}, "0"); + std::string gateName; + for (size_t j = 4; j < fields.size() - 1; j++) { + gateName += fields[j] + " "; + } + gateName += fields.back(); + gateName = std::regex_replace(gateName, std::regex{","}, "0"); - gates->push_back( - Gate{gateName, std::stod(fields[1]), std::stod(fields[2])}); + gates->push_back(Gate{gateName, std::stod(fields[1]), std::stod(fields[2])}); } void makeRunway(std::vector *runways, std::vector fields) { - runways->push_back(Runway{fields[8], - std::stod(fields[9]), - std::stod(fields[10]), - std::stod(fields[18]), - std::stod(fields[19]), - std::stod(fields[1])}); - runways->push_back(Runway{fields[17], - std::stod(fields[18]), - std::stod(fields[19]), - std::stod(fields[9]), - std::stod(fields[10]), - std::stod(fields[1])}); + runways->push_back(Runway{fields[8], + std::stod(fields[9]), + std::stod(fields[10]), + std::stod(fields[18]), + std::stod(fields[19]), + std::stod(fields[1])}); + runways->push_back(Runway{fields[17], + std::stod(fields[18]), + std::stod(fields[19]), + std::stod(fields[9]), + std::stod(fields[10]), + std::stod(fields[1])}); } void makeGate1300(std::vector *gates, std::vector fields) { - std::string gateName; - for (size_t j = 6; j < fields.size() - 1; j++) { - gateName += fields[j] + " "; - } - gateName += fields.back(); - gateName = std::regex_replace(gateName, std::regex{","}, "0"); + std::string gateName; + for (size_t j = 6; j < fields.size() - 1; j++) { + gateName += fields[j] + " "; + } + gateName += fields.back(); + gateName = std::regex_replace(gateName, std::regex{","}, "0"); - gates->push_back( - Gate{gateName, std::stod(fields[1]), std::stod(fields[2])}); + gates->push_back(Gate{gateName, std::stod(fields[1]), std::stod(fields[2])}); } \ No newline at end of file diff --git a/xplugin/xPluginWin.cpp b/xplugin/xPluginWin.cpp index 3fdb60c..07fd884 100644 --- a/xplugin/xPluginWin.cpp +++ b/xplugin/xPluginWin.cpp @@ -5,13 +5,13 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { - switch (ul_reason_for_call) { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - default: - break; - } - return TRUE; + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + default: + break; + } + return TRUE; }