43 Commits

Author SHA1 Message Date
Kilian 92159b0669 Implement FSUIPC FSData Classes 2022-09-23 22:16:01 +02:00
Kilian 115e4d9265 Migrate to radians instead of degrees to lessen conversion errors 2022-09-23 00:16:52 +02:00
Kilian 07487e9e51 Format 2022-09-11 23:26:28 +02:00
Kilian 46a0607b55 Fix Linux 2022-09-11 21:45:09 +02:00
Kilian de8b282ab1 Fix merge errors 2022-09-11 21:34:26 +02:00
Kilian 6027095c38 Merge branch 'develop' of https://git.hofmannnet.myhome-server.de/GermanAirlines/GermanAirlinesVA-GAConnector into develop 2022-09-11 21:20:13 +02:00
Kilian 2cf03d2933 Refactor SimDatabase 2022-09-11 21:14:46 +02:00
Kilian 161941ac1b REfactor SimDatabase PRODUCES CRASH ON EXIT 2022-09-11 04:31:01 +02:00
Kilian 4445f1f12a Refactor Config 2022-09-10 22:55:04 +02:00
Kilian d6d3c0b4d9 Recording now uses new write 2022-09-10 02:05:38 +02:00
Kilian 94fbc1f558 Runways and Gates now with new read/write 2022-09-10 01:07:00 +02:00
Kilian 2fc2170763 Future proofed file read/write 2022-09-09 23:28:25 +02:00
Kilian 699e2a4784 Consts and references 2022-09-09 20:10:38 +02:00
Kilian b050c23577 Initial Logbook read and write 2022-09-09 02:05:57 +02:00
Kilian a5e23f4b43 Fix linux 2022-09-08 19:10:55 +02:00
Kilian 7d45ca71ba Adjust for new Platform Requirements 2022-09-08 02:20:55 +02:00
Kilian 8b9f5d0406 Start on Logbook 2022-09-08 02:02:09 +02:00
Kilian 2bda541bce PHP Namespacing und Test 2022-09-07 20:32:14 +02:00
Kilian 25c4d8474d Refactor 2022-09-07 03:04:26 +02:00
Kilian 910afad3b5 Fix Recording file format 2022-09-07 02:49:42 +02:00
Kilian 2aa658c15e Refactor 2022-09-06 22:12:32 +02:00
Kilian 3559b124a7 Namespacing and Lib for files 2022-09-06 21:15:01 +02:00
Kilian 2510f4968c Version
Global AP
2022-09-06 16:41:34 +02:00
Kilian 752646c744 Fix linux 2022-09-06 01:16:36 +02:00
Kilian b22a60c85e Namespaces
Better File handling
2022-09-06 00:29:19 +02:00
Kilian 9dcca746ed Reimpl send prot 2022-09-05 17:52:52 +02:00
Kilian 0612afb37b adjust protocoll 2022-09-05 17:45:18 +02:00
Kilian ff179ee3a4 Specify 32 bit SSL 2022-09-05 16:19:59 +02:00
Kilian cd022c8063 System openSSL for linux 2022-09-05 16:04:25 +02:00
Kilian a536a9c918 Remove tlsOptions 2022-09-05 15:33:31 +02:00
Kilian 3dce2ca488 Mutate libs to use default system certs file 2022-09-05 14:59:08 +02:00
Kilian b695f4cb6c Allow non standard cert dir 2022-09-05 14:32:08 +02:00
Kilian 2914e1fa59 SSL Cert dir 2022-09-05 12:27:38 +02:00
Kilian 71656e4a66 Reimplementd IX with OpenSSL 2022-09-05 01:41:09 +02:00
Kilian 5e80c41538 Platform openSSL 2022-09-04 22:15:36 +02:00
Kilian c6ad412df9 Remove bug test 2022-09-04 18:56:48 +02:00
Kilian 0fe7335987 Style, Analyze 2022-09-04 03:06:09 +02:00
Kilian 3b2f146679 Move MakeRwys to own lib 2022-09-04 01:19:50 +02:00
Kilian a820f7456a Remove ixWebSocket 2022-09-04 00:50:49 +02:00
Kilian fbee417594 IX own module 2022-09-02 19:41:26 +02:00
Kilian 7439747876 merge branch 2022-09-02 17:42:48 +02:00
Kilian 27edf28f42 Merge branch 'develop' of https://git.hofmannnet.myhome-server.de/GermanAirlines/GermanAirlinesVA-GAConnector into develop 2022-09-02 17:40:27 +02:00
Kilian ca5aab1344 @rpath in plugin 2022-09-02 17:40:14 +02:00
383 changed files with 77623 additions and 10082 deletions
+5 -4
View File
@@ -18,18 +18,19 @@ BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CommentPragmas: "^ IWYU pragma:"
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerBinding: false
ExperimentalAutoDetectBinPacking: false
IndentAccessModifiers: true
IndentCaseLabels: true
IndentFunctionDeclarationAfterType: true
IndentWidth: 4
IndentWidth: 2
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
NamespaceIndentation: Inner
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
ObjCBlockIndentWidth: 4
@@ -50,5 +51,5 @@ SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
Standard: c++14
SortIncludes: true
TabWidth: 4
TabWidth: 2
UseTab: Never
-8
View File
@@ -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
Generated
-1
View File
@@ -1 +0,0 @@
GermanAirlinesVA_GAConnector
-2
View File
@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />
-5
View File
@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/GermanAirlinesVA-GAConnector.iml" filepath="$PROJECT_DIR$/.idea/GermanAirlinesVA-GAConnector.iml" />
</modules>
</component>
</project>
Generated
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+38 -5
View File
@@ -1,14 +1,47 @@
{
"configurations": [
{
"configurations": [{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/opt/llvm-mingw/bin/clang++",
"cStandard": "c17",
"defines": [
"LIN",
"XPLM200",
"XPLM210"
],
"compilerPath": "/opt/llvm-mingw/bin/clang",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "linux-clang-x64"
}, {
"name": "Windows",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"IBM",
"XPLM200",
"XPLM210"
],
"compilerPath": "/opt/llvm-mingw/bin/x86_64-w64-mingw32-clang",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "windows-clang-x64"
},
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"APL",
"XPLM200",
"XPLM210"
],
"compilerPath": "/opt/osxcross/target/bin/o64-clang",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "macos-clang-x64"
}
],
"version": 4
+17
View File
@@ -0,0 +1,17 @@
[{
"name": "Clang 13.0.0 Mac",
"toolchainFile": "${workspaceFolder}/toolchain-mac.cmake"
},
{
"name": "Clang 13.0.0 Linux",
"toolchainFile": "${workspaceFolder}/toolchain-lin.cmake"
},
{
"name": "Clang 13.0.0 Windows 32",
"toolchainFile": "${workspaceFolder}/toolchain-win-32.cmake"
},
{
"name": "Clang 13.0.0 Windows 64",
"toolchainFile": "${workspaceFolder}/toolchain-win-64.cmake"
}
]
-34
View File
@@ -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"
}
]
}
+26 -3
View File
@@ -1,6 +1,6 @@
{
"cmake.generator": "Unix Makefiles",
"files.associations": {
"thread": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
@@ -60,8 +60,31 @@
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"valarray": "cpp"
}
"valarray": "cpp",
"__threading_support": "cpp",
"__bit_reference": "cpp",
"__bits": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__errc": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__mutex_base": "cpp",
"__node_handle": "cpp",
"__nullptr": "cpp",
"__split_buffer": "cpp",
"__string": "cpp",
"__tree": "cpp",
"__tuple": "cpp",
"compare": "cpp",
"concepts": "cpp",
"ios": "cpp",
"locale": "cpp",
"queue": "cpp",
"stack": "cpp",
"variant": "cpp"
},
}
-28
View File
@@ -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"
}
+12 -1
View File
@@ -1,15 +1,26 @@
cmake_minimum_required(VERSION 3.8)
cmake_policy(SET CMP0068 NEW)
project(GermanAirlinesVA_GAConnector)
project(GermanAirlinesVA_GAConnector C CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
set(PLUGIN_NAME GAConnector)
option(DEBUG "Debug symbols" OFF)
add_subdirectory(
ixwebsocket
)
add_subdirectory(
file
)
if(NOT (WIN32 AND (BIT STREQUAL "32")))
add_subdirectory(
xplugin
)
endif()
Vendored
+8 -116
View File
@@ -17,7 +17,9 @@ pipeline {
}
steps {
sh 'bash ./build.sh win32'
sh 'analyze-build --verbose --cdb build/compile_commands.json --output build/Analysis/win32'
sh 'bash ./build.sh win64'
sh 'analyze-build --verbose --cdb build/compile_commands.json --output build/Analysis/win64'
}
}
stage('Build Linux Debug') {
@@ -35,8 +37,8 @@ pipeline {
DEBUG = 1
}
steps {
sh 'bash ./build.sh lin32'
sh 'bash ./build.sh lin64'
sh 'bash ./build.sh lin'
sh 'analyze-build --verbose --cdb build/compile_commands.json --output build/Analysis/lin'
}
}
stage('Build MacOSX Debug') {
@@ -55,6 +57,7 @@ pipeline {
}
steps {
sh 'bash ./build.sh mac'
sh 'analyze-build --verbose --cdb build/compile_commands.json --output build/Analysis/mac'
}
}
stage('Archive Debug') {
@@ -62,120 +65,9 @@ pipeline {
branch 'develop'
}
steps {
zip zipFile: 'Debug.zip', archive: true, dir: 'build/Plugin'
sh 'rm -rf build'
}
}
stage('Build Windows Stageing') {
when {
branch 'stage'
beforeAgent true
}
agent {
docker {
image 'llvm-mingw:latest'
reuseNode true
}
}
steps {
sh 'bash ./build.sh win32'
sh 'bash ./build.sh win64'
}
}
stage('Build Linux Stageing') {
when {
branch 'stage'
beforeAgent true
}
agent {
docker {
image 'llvm:latest'
reuseNode true
}
}
steps {
sh 'bash ./build.sh lin32'
sh 'bash ./build.sh lin64'
}
}
stage('Build MacOSX Stageing') {
when {
branch 'stage'
beforeAgent true
}
agent {
docker {
image 'osxcross:latest'
reuseNode true
}
}
steps {
sh 'bash ./build.sh mac'
}
}
stage('Archive Stageing') {
when {
branch 'stage'
}
steps {
zip zipFile: 'Stage.zip', archive: true, dir: 'build/Plugin'
sh 'rm -rf build'
}
}
stage('Build Windows Release') {
when {
branch 'master'
beforeAgent true
}
agent {
docker {
image 'llvm-mingw:latest'
reuseNode true
}
}
steps {
sh 'bash ./build.sh win32'
sh 'bash ./build.sh win64'
}
}
stage('Build Linux Release') {
when {
branch 'master'
beforeAgent true
}
agent {
docker {
image 'llvm:latest'
reuseNode true
}
}
steps {
sh 'bash ./build.sh lin32'
sh 'bash ./build.sh lin64'
}
}
stage('Build MacOSX Release') {
when {
branch 'master'
beforeAgent true
}
agent {
docker {
image 'osxcross:latest'
reuseNode true
}
}
steps {
sh 'bash ./build.sh mac'
}
}
stage('Archive Release') {
when {
branch 'master'
beforeAgent true
}
steps {
zip zipFile: 'Release.zip', archive: true, dir: 'build/Plugin'
zip zipFile: 'X-Plane Debug.zip', archive: true, dir: 'build/X-Plane'
zip zipFile: 'ESP Debug.zip', archive: true, dir: 'build/ESP'
zip zipFile: 'Analysis.zip', archive: true, dir: 'build/Analysis'
sh 'rm -rf build'
}
}
+3
View File
@@ -9,3 +9,6 @@
X-Plane Plugin for all supported platforms.
Demo of WebSocket capabilities
Requirements:
- openSSL 1.1.x
+2
View File
@@ -0,0 +1,2 @@
- Update OSXCross Docker image to SDK 11
- Implement ARM64 arch for Plugin
+14 -7
View File
@@ -2,25 +2,30 @@
mkdir -p build
cd build
mkdir -p Plugin/GAConnector/32
mkdir -p Plugin/GAConnector/64
mkdir -p X-Plane/GAConnector/64
mkdir -p X-Plane/GAConnector/recordings
mkdir -p ESP/GAConnector
mkdir -p ESP/GAConnector/recordings
rm -f CMakeCache.txt
case $1 in
"mac")
cmake -DDEBUG=$DEBUG -DCMAKE_TOOLCHAIN_FILE=../toolchain-mac.cmake ..
;;
"lin32")
cmake -DDEBUG=$DEBUG -DBIT=32 -DCMAKE_TOOLCHAIN_FILE=../toolchain-lin.cmake ..
;;
"lin64")
"lin")
cmake -DDEBUG=$DEBUG -DBIT=64 -DCMAKE_TOOLCHAIN_FILE=../toolchain-lin.cmake ..
;;
"win32")
cmake -DDEBUG=$DEBUG -DBIT=32 -DCMAKE_TOOLCHAIN_FILE=../toolchain-win-32.cmake ..
\cp -rf ../openSSL/win32/*.dll ESP/GAConnector/
\cp -rf /opt/llvm-mingw/i686-w64-mingw32/bin/libc++.dll ESP/GAConnector/
\cp -rf /opt/llvm-mingw/i686-w64-mingw32/bin/libunwind.dll ESP/GAConnector/
;;
"win64")
cmake -DDEBUG=$DEBUG -DBIT=64 -DCMAKE_TOOLCHAIN_FILE=../toolchain-win-64.cmake ..
\cp -rf ../openSSL/win64/*.dll X-Plane/GAConnector/64/
\cp -rf /opt/llvm-mingw/x86_64-w64-mingw32/bin/libc++.dll X-Plane/GAConnector/64
\cp -rf /opt/llvm-mingw/x86_64-w64-mingw32/bin/libunwind.dll X-Plane/GAConnector/64
;;
esac
@@ -33,5 +38,7 @@ fi
if [ "$1" = "mac" ] && [ "$DEBUG" = "1" ]
then
/opt/osxcross/target/bin/osxcross-llvm-dsymutil Plugin/GAConnector/mac.xpl
/opt/osxcross/target/bin/osxcross-llvm-dsymutil X-Plane/GAConnector/mac.xpl
fi
+84
View File
@@ -0,0 +1,84 @@
file(GLOB file CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/file/*.cpp)
add_library(file SHARED
${file}
)
target_include_directories(file PRIVATE
${CMAKE_SOURCE_DIR}/file/include
${CMAKE_SOURCE_DIR}/simdata/include
${CMAKE_SOURCE_DIR}/utilities/include
)
set_target_properties(file PROPERTIES
PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/file/include
)
target_compile_options(file PRIVATE
-Wall
-Wextra
-pedantic
)
if(DEBUG)
target_compile_options(file PRIVATE
-g
)
target_link_options(file PRIVATE
-g
)
else()
target_compile_options(file PRIVATE
-O2
)
endif()
if(APPLE)
message("Building file for MacOSX Universal into ${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}")
set_target_properties(file PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}"
BUILD_WITH_INSTALL_NAME_DIR TRUE
)
target_compile_options(file PRIVATE
"SHELL:-arch x86_64"
)
target_link_options(file PRIVATE
"SHELL:-arch x86_64"
)
target_link_libraries(file PRIVATE
"-framework Security"
)
elseif(UNIX)
message("Building file for Linux ${BIT} into ${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}")
set_target_properties(file PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}"
)
target_compile_options(file PRIVATE
-nodefaultlibs
)
elseif(WIN32)
if (BIT STREQUAL "32")
message("Building file for Windows ${BIT} into ${PROJECT_BINARY_DIR}/ESP/${PLUGIN_NAME}")
set_target_properties(file PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/ESP/${PLUGIN_NAME}"
)
else()
message("Building file for Windows ${BIT} into ${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}")
set_target_properties(file PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}"
)
endif()
if(DEBUG)
target_compile_options(file PRIVATE
-gcodeview
)
target_link_options(file PRIVATE
-Wl,-pdb=
)
endif()
endif()
+80
View File
@@ -0,0 +1,80 @@
VARIABLE STRING (>=3)
*********************
0 1 | 2 ... |
------+-------+
LEN |STRING |
UINT16|CHAR* |
LOGBOOK HEADER (28)
***********************************************************************************************************************************************
0 1 2 3 4 | 5 | 6 7 | 8 9 10 11 | 12 13 14 15 | 16 17 18 19 | 20 21 22 23 | 24 25 26 27 |
------------------+-------+-------------+---------------------+-------------------+-------------------+-------------------+-------------------+
IDENT |VERSION|TOTAL FLIGHTS|TOTAL TIME |TOTAL PTS |TOTAL FUEL |TOTAL DIST |RATE |
CHAR[5] |UINT8 |UINT16 |FLOAT32, Decimal, min|FLOAT32 |FLOAT32, kgs |FLOAT32, nm |FLOAT32, ft/min |
FLIGHT PREAMBLE (24)
**********************************************************************************************************************
0 1 2 3 4 5 6 7 8 9 | 10 11 12 13 | 14 15 16 17 | 18 19 20 21 22 23 |
--------------------------------------+----------------------------+-------------------+-----------------------------+
DATE |FLIGHT NUMBER |AIRCRAFT TYPE |AIRCRAFT REGISTRATION |
CHAR, DD.MM.YYYY |CHAR[4],[0-9][0-9A-z\0]{0,3}|CHAR[4], ICAO code |CHAR[6], D-[A-Z]{4} |
TIMES (24)
**************************************************************************************************************
0 1 2 3 4 | 5 6 7 8 9 | 10 11 12 13 14 | 15 16 17 18 19 | 20 21 22 23 |
------------------+-------------------+------------------------+------------------------+--------------------+
OFF BLOCK |TAKEOFF (OUT) |LANDING (ON) |ON BLOCK (IN) |TOTAL |
CHAR[5], HH:MM |CHAR[5], HH:MM |CHAR[5], HH:MM |CHAR[5], HH:MM |FLOAT32, Decimal min|
FUELS (16)
*********************************************************************
0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15 |
--------------+---------------+-----------------+-------------------+
TAXI OUT |IN FLIGHT |TAXI IN |TOTAL |
FLOAT32, kgs |FLOAT32, kgs |FLOAT32, kgs |FLOAT32, kgs |
DISTANCES (16)
*********************************************************************
0 1 2 3 | 4 5 6 7 | 8 9 10 11 | 12 13 14 15 |
--------------+---------------+-----------------+-------------------+
TAXI OUT |IN FLIGHT |TAXI IN |TOTAL |
FLOAT32, nm |FLOAT32, nm |FLOAT32, nm |FLOAT32, nm |
LANDING (9)
*******************************************
0 1 2 3 | 4 | 5 6 7 8 |
---------------+----------+---------------+
MAX RATE |TOUCHDOWNS|MAX G-FORCE |
FLOAT32, ft/min|CHAR |FLOAT32, g |
FLIGHT POSTAMBLE (5)
*********************
0 1 2 3 | 5 |
--------------+---------+
POINTS |FLAGS |
FLOAT32 |BITFIELD |
FLAGS BITFIELD
**********************************
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---+---+---+---+---+---+---+-----+
NIL|NIL|NIL|NIL|NIL|NIL|NIL|FILED|
LOGBOOK FILE:
LOGBOOK HEADER
[
FLIGHT PREAMBLE
VARIABLE STRING (DEPARTURE AIRPORT NAME AND CODE)
VARIABLE STRING (DEPARTURE GATE NAME)
VARIABLE STRING (DEPARTURE RUNWAY DESIGNATOR)
VARIABLE STRING (ARRIVAL AIRPORT NAME AND CODE)
VARIABLE STRING (ARRIVAL GATE NAME)
VARIABLE STRING (ARRIVAL RUNWAY DESIGNATOR)
TIMES
FUELS
DISTANCES
LANDING
VARIABLE STRING (RECORDING FILE NAME)
FLIGHT POSTAMBLE
VARIABLE STRING (FLIGHT RECORDING PATH RELATIVE TO LOGBOOK)
]0..n
+47
View File
@@ -0,0 +1,47 @@
#include "config/config.h"
namespace germanairlinesva
{
namespace file
{
namespace config
{
void Config::writeFile() const
{
std::ofstream out(XPLANE_PLUGIN_DIRECTORY CONFIG);
out << "scenery=" << this->scenery << "\n";
out << "user=" << this->user << "\n";
out << "token=" << this->token << "\n";
out.close();
}
Config::Config()
{
std::ifstream in(XPLANE_PLUGIN_DIRECTORY CONFIG);
std::string line;
while (std::getline(in, line)) {
std::vector<std::string> fields = util::split(line, '=');
if (fields.size() >= 2) {
util::trim(fields[0]);
util::trim(fields[1]);
if (fields[0] == "scenery") {
this->scenery = fields[1];
} else if (fields[0] == "user") {
this->user = fields[1];
} else if (fields[0] == "token") {
this->token = fields[1];
}
}
}
in.close();
}
void Config::updateScenery(std::string scenery)
{
this->scenery = scenery;
this->writeFile();
}
} // namespace config
} // namespace file
} // namespace germanairlinesva
-47
View File
@@ -1,47 +0,0 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_CONFIG_H
#define GERMANAIRLINESVA_GACONNECTOR_CONFIG_H
#include "stringExtensions.hpp"
#include <map>
#include <fstream>
#include <map>
#include <string>
#include <vector>
namespace config
{
static inline std::map<std::string, std::string>
readConfig(const std::string &file)
{
std::ifstream config(file);
std::map<std::string, std::string> settings;
std::string line;
while (std::getline(config, line)) {
std::vector<std::string> 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<std::string, std::string> &config,
const std::string &file)
{
std::ofstream cfg(file);
for (const std::pair<const std::string, std::string> &entry : config) {
cfg << entry.first << '=' << entry.second << '\n';
}
cfg.close();
}
} // namespace config
#endif
+42
View File
@@ -0,0 +1,42 @@
#include "simdata/gate.h"
namespace germanairlinesva
{
namespace file
{
namespace simdata
{
Gate::Gate(std::string designator,
double latitude,
double longitude,
std::uint8_t radius)
{
this->designator = designator;
this->center = {geodata::toRadians(latitude),
geodata::toRadians(longitude)};
this->radius = radius;
}
Gate::Gate(std::string designator,
struct geodata::point center,
std::uint8_t radius)
{
this->designator = designator;
this->center = center;
this->radius = radius;
}
void Gate::toFile(std::ofstream &out) const
{
writeString(out, this->designator);
write<decltype(this->center)>(out, this->center);
write<decltype(this->radius)>(out, this->radius);
}
bool Gate::contains(geodata::point coordinates) const
{
return geodata::distanceEarthP(this->center, coordinates);
}
} // namespace simdata
} // namespace file
} // namespace germanairlinesva
-56
View File
@@ -1,56 +0,0 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_GATE_H
#define GERMANAIRLINESVA_GACONNECTOR_GATE_H
#include <cstdint>
#include <cstring>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
/*
* Representation of X-Plane gate
* Heading in degrees (0...360)
*
* Length in bytes: 18 + length of designator
* Designator must be null terminated
*
* UINT8 | CHAR[] | DOUBLE | DOUBLE | UINT8
* ------+------------+--------+--------+------
* LEN | DESIGNATOR | LAT | LON | WIDTH
*/
class Gate
{
private:
std::string designator;
double latitude;
double longitude;
std::vector<std::uint8_t> file;
public:
Gate(const std::string &designator, double latitude, double longitude)
{
this->designator = designator;
this->latitude = latitude;
this->longitude = longitude;
file = std::vector<std::uint8_t>(18 + this->designator.length(), 0);
std::uint8_t *bufPtr = file.data();
memset(bufPtr,
static_cast<std::uint8_t>(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(); }
};
#endif
+18
View File
@@ -0,0 +1,18 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSGEODATA_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSGEODATA_H
#include "FsLatLonPoint.h"
#include "FsLatLonQuadrilateral.h"
#include "FsLatitude.h"
#include "FsLatitudeSpan.h"
#include "FsLongitude.h"
#include "FsLongitudeSpan.h"
#include "FsLatLonPointImpl.h"
#include "FsLatLonQuadrilateralImpl.h"
#include "FsLatitudeImpl.h"
#include "FsLatitudeSpanImpl.h"
#include "FsLongitudeImpl.h"
#include "FsLongitudeSpanImpl.h"
#endif
+57
View File
@@ -0,0 +1,57 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONPOINT_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONPOINT_H
#define _USE_MATH_DEFINES
#include <cmath>
#include <cstdint>
#include <string>
#include "FsLatitude.h"
#include "FsLatitudeSpan.h"
#include "FsLongitude.h"
#include "FsLongitudeSpan.h"
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
class FsLatLonPoint
{
private:
FsLatitude lat;
FsLongitude lon;
public:
inline FsLatLonPoint();
inline FsLatLonPoint(FsLatitude Latitude, FsLongitude Longitude);
inline const FsLongitude Longitude() const;
inline const FsLatitude Latitude() const;
inline double DistanceFromInFeet(const FsLatLonPoint &Point) const;
inline double
DistanceFromInNauticalMiles(const FsLatLonPoint &Point) const;
inline double DistanceFromInMetres(const FsLatLonPoint &Point) const;
inline double BearingTo(const FsLatLonPoint &Point) const;
inline double BearingFrom(const FsLatLonPoint &Point) const;
inline FsLatLonPoint OffsetByFeet(double Bearing,
double Distance) const;
inline FsLatLonPoint OffsetByMetres(double Bearing,
double Distance) const;
inline FsLatLonPoint OffsetByNauticalMiles(double Bearing,
double Distance) const;
inline const std::string to_string(bool HemisphereAsText,
char DetailLevel) const;
inline const std::string to_string() const;
};
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+145
View File
@@ -0,0 +1,145 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONPOINTIMPL_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONPOINTIMPL_H
#include "FsLatLonPoint.h"
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
inline FsLatLonPoint::FsLatLonPoint() = default;
inline FsLatLonPoint::FsLatLonPoint(FsLatitude Latitude,
FsLongitude Longitude)
{
this->lon = Longitude;
this->lat = Latitude;
}
inline const FsLongitude FsLatLonPoint::Longitude() const
{
return this->lon;
}
inline const FsLatitude FsLatLonPoint::Latitude() const
{
return this->lat;
}
inline double
FsLatLonPoint::DistanceFromInFeet(const FsLatLonPoint &Point) const
{
FsLongitudeSpan fsLongitudeSpan =
FsLongitudeSpan::BetweenTwoLongitudes(Point.Longitude(), this->lon);
double num = (fsLongitudeSpan.ToFeet(this->lat) +
fsLongitudeSpan.ToFeet(Point.lat)) /
2.0;
double feet = FsLatitudeSpan(Point.Latitude().DecimalDegrees() -
this->lat.DecimalDegrees())
.ToFeet();
return sqrt(num * num + feet * feet);
}
inline double FsLatLonPoint::DistanceFromInNauticalMiles(
const FsLatLonPoint &Point) const
{
return this->DistanceFromInFeet(Point) / 6076.1155;
}
inline double
FsLatLonPoint::DistanceFromInMetres(const FsLatLonPoint &Point) const
{
return this->DistanceFromInFeet(Point) / 3.2808;
}
inline double FsLatLonPoint::BearingTo(const FsLatLonPoint &Point) const
{
double num1 = 0.0;
double num2 = abs(FsLatitudeSpan(this->lat.DecimalDegrees() -
Point.Latitude().DecimalDegrees())
.ToFeet());
double num3 = abs((FsLongitudeSpan(Point.Longitude().DecimalDegrees() -
this->lon.DecimalDegrees())
.ToFeet(this->lat) +
FsLongitudeSpan(Point.Longitude().DecimalDegrees() -
this->lon.DecimalDegrees())
.ToFeet(Point.lat)) /
2.0);
if (num2 == 0.0)
num1 = this->lon.DecimalDegrees() > Point.Longitude().DecimalDegrees()
? 270.0
: 90.0;
else if (this->lat.DecimalDegrees() < Point.Latitude().DecimalDegrees() &&
this->lon.DecimalDegrees() < Point.Longitude().DecimalDegrees())
num1 = atan(num3 / num2) * 180.0 / M_PI;
else if (this->lat.DecimalDegrees() > Point.Latitude().DecimalDegrees() &&
this->lon.DecimalDegrees() < Point.Longitude().DecimalDegrees())
num1 = atan(num2 / num3) * 180.0 / M_PI + 90.0;
else if (this->lat.DecimalDegrees() > Point.Latitude().DecimalDegrees() &&
this->lon.DecimalDegrees() > Point.Longitude().DecimalDegrees())
num1 = atan(num3 / num2) * 180.0 / M_PI + 180.0;
else if (this->lat.DecimalDegrees() < Point.Latitude().DecimalDegrees() &&
this->lon.DecimalDegrees() > Point.Longitude().DecimalDegrees())
num1 = atan(num2 / num3) * 180.0 / M_PI + 270.0;
return num1;
}
inline double FsLatLonPoint::BearingFrom(const FsLatLonPoint &Point) const
{
double num = 180.0 + this->BearingTo(Point);
if (num >= 360.0)
num -= 360.0;
if (num < 0.0)
num += 360.0;
return num;
}
inline FsLatLonPoint FsLatLonPoint::OffsetByFeet(double Bearing,
double Distance) const
{
double Feet1 = sin(M_PI * Bearing / 180.0) * Distance;
double Feet2 = cos(M_PI * Bearing / 180.0) * Distance;
FsLatLonPoint fsLatLonPoint = FsLatLonPoint();
fsLatLonPoint.lat = this->lat.Add(FsLatitudeSpan::FromFeet(Feet2));
fsLatLonPoint.lon =
this->lon.Add(FsLongitudeSpan::FromFeet(Feet1, fsLatLonPoint.lat));
return fsLatLonPoint;
}
inline FsLatLonPoint FsLatLonPoint::OffsetByMetres(double Bearing,
double Distance) const
{
double Metres1 = sin(M_PI * Bearing / 180.0) * Distance;
double Metres2 = cos(M_PI * Bearing / 180.0) * Distance;
FsLatLonPoint fsLatLonPoint = FsLatLonPoint();
fsLatLonPoint.lat = this->lat.Add(FsLatitudeSpan::FromMetres(Metres2));
fsLatLonPoint.lon = this->lon.Add(
FsLongitudeSpan::FromMetres(Metres1, fsLatLonPoint.lat));
return fsLatLonPoint;
}
inline FsLatLonPoint
FsLatLonPoint::OffsetByNauticalMiles(double Bearing,
double Distance) const
{
double NauticalMiles1 = sin(M_PI * Bearing / 180.0) * Distance;
double NauticalMiles2 = cos(M_PI * Bearing / 180.0) * Distance;
FsLatLonPoint fsLatLonPoint = FsLatLonPoint();
fsLatLonPoint.lat =
this->lat.Add(FsLatitudeSpan::FromNauticalMiles(NauticalMiles2));
fsLatLonPoint.lon =
this->lon.Add(FsLongitudeSpan::FromNauticalMiles(NauticalMiles1,
fsLatLonPoint.lat));
return fsLatLonPoint;
}
inline const std::string FsLatLonPoint::to_string(bool HemisphereAsText,
char DetailLevel) const
{
return this->lat.to_string(HemisphereAsText, DetailLevel) + ", " +
this->lon.to_string(HemisphereAsText, DetailLevel);
}
inline const std::string FsLatLonPoint::to_string() const
{
return this->to_string(true, 'm');
}
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
@@ -0,0 +1,57 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONQUADRILATERAL_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONQUADRILATERAL_H
#define _USE_MATH_DEFINES
#include <cmath>
#include <string>
#include <vector>
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
class FsLatitude;
class FsLatitudeSpan;
class FsLongitude;
class FsLongitudeSpan;
class FsLatLonPoint;
class FsLatLonQuadrilateral
{
private:
FsLatLonPoint ne;
FsLatLonPoint se;
FsLatLonPoint sw;
FsLatLonPoint nw;
public:
inline FsLatLonQuadrilateral();
inline FsLatLonQuadrilateral(FsLatLonPoint P0,
FsLatLonPoint P1,
FsLatLonPoint P2,
FsLatLonPoint P3);
inline FsLatLonPoint NE() const;
inline FsLatLonPoint SE() const;
inline FsLatLonPoint SW() const;
inline FsLatLonPoint NW() const;
inline bool ContainsPoint(FsLatLonPoint point) const;
inline const std::string to_string(bool HemisphereAsText,
char DetailLevel) const;
inline const std::string to_string() const;
inline static FsLatLonQuadrilateral
ForRunway(FsLatLonPoint &ThresholdCentre,
double HeadingTrue,
double WidthInFeet,
double LengthInFeet);
};
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
@@ -0,0 +1,166 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONQUADRILATERALIMPL_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATLONQUADRILATERALIMPL_H
#include "FsLatLonQuadrilateral.h"
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
inline FsLatLonQuadrilateral::FsLatLonQuadrilateral() = default;
inline FsLatLonQuadrilateral::FsLatLonQuadrilateral(FsLatLonPoint P0,
FsLatLonPoint P1,
FsLatLonPoint P2,
FsLatLonPoint P3)
{
std::vector<FsLatLonPoint> fsLatLonPointList1;
std::vector<FsLatLonPoint> fsLatLonPointList2;
fsLatLonPointList1.push_back(P0);
fsLatLonPointList1.push_back(P1);
fsLatLonPointList1.push_back(P2);
fsLatLonPointList1.push_back(P3);
for (int index1 = 0; index1 < 3; ++index1) {
double num = 0.0;
int index2 = 0;
for (size_t index3 = 0; index3 < fsLatLonPointList1.size(); ++index3) {
if (fsLatLonPointList1[index3].Latitude().UDegrees() > num) {
num = fsLatLonPointList1[index3].Latitude().UDegrees();
index2 = index3;
}
}
fsLatLonPointList2.push_back(fsLatLonPointList1[index2]);
fsLatLonPointList1.erase(fsLatLonPointList1.begin() + index2);
}
fsLatLonPointList2.push_back(fsLatLonPointList1[0]);
if (fsLatLonPointList2[1].Longitude().UDegrees() >
fsLatLonPointList2[0].Longitude().UDegrees()) {
this->ne = fsLatLonPointList2[1];
this->nw = fsLatLonPointList2[0];
} else {
this->ne = fsLatLonPointList2[0];
this->nw = fsLatLonPointList2[1];
}
if (fsLatLonPointList2[3].Longitude().UDegrees() >
fsLatLonPointList2[2].Longitude().UDegrees()) {
this->se = fsLatLonPointList2[3];
this->sw = fsLatLonPointList2[2];
} else {
this->se = fsLatLonPointList2[2];
this->sw = fsLatLonPointList2[3];
}
}
inline FsLatLonPoint FsLatLonQuadrilateral::NE() const { return this->ne; }
inline FsLatLonPoint FsLatLonQuadrilateral::SE() const { return this->se; }
inline FsLatLonPoint FsLatLonQuadrilateral::SW() const { return this->sw; }
inline FsLatLonPoint FsLatLonQuadrilateral::NW() const { return this->nw; }
inline bool FsLatLonQuadrilateral::ContainsPoint(FsLatLonPoint point) const
{
bool flag = false;
double udegrees1 = this->nw.Latitude().UDegrees();
double udegrees2 = this->sw.Latitude().UDegrees();
double udegrees3 = this->se.Latitude().UDegrees();
double udegrees4 = this->ne.Latitude().UDegrees();
double udegrees5 = this->nw.Longitude().UDegrees();
double udegrees6 = this->sw.Longitude().UDegrees();
double udegrees7 = this->se.Longitude().UDegrees();
double udegrees8 = this->ne.Longitude().UDegrees();
double udegrees9 = point.Longitude().UDegrees();
double udegrees10 = point.Latitude().UDegrees();
if (udegrees9 > udegrees5 + (udegrees6 - udegrees5) /
(udegrees1 - udegrees2) *
(udegrees1 - udegrees10) &&
udegrees9 < udegrees8 + (udegrees7 - udegrees8) /
(udegrees4 - udegrees3) *
(udegrees4 - udegrees10) &&
udegrees10 > udegrees2 + (udegrees3 - udegrees2) /
(udegrees7 - udegrees6) *
(udegrees9 - udegrees6))
flag = udegrees10 < udegrees1 + (udegrees4 - udegrees1) /
(udegrees8 - udegrees5) *
(udegrees9 - udegrees5);
return flag;
}
inline const std::string
FsLatLonQuadrilateral::to_string(bool HemisphereAsText,
char DetailLevel) const
{
return this->ne.to_string(HemisphereAsText, DetailLevel) + ", " +
this->nw.to_string(HemisphereAsText, DetailLevel) + ", " +
this->sw.to_string(HemisphereAsText, DetailLevel) + ", " +
this->se.to_string(HemisphereAsText, DetailLevel);
}
inline const std::string FsLatLonQuadrilateral::to_string() const
{
return this->to_string(true, 'm');
}
inline FsLatLonQuadrilateral
FsLatLonQuadrilateral::ForRunway(FsLatLonPoint &ThresholdCentre,
double HeadingTrue,
double WidthInFeet,
double LengthInFeet)
{
double num = M_PI * HeadingTrue / 180.0;
double Feet = cos(num) * WidthInFeet / 2.0;
double decimalDegrees1 =
FsLatitudeSpan::FromFeet(sin(num) * WidthInFeet / 2.0)
.DecimalDegrees();
FsLatitude fsLatitude1 = FsLatitude(
ThresholdCentre.Latitude().DecimalDegrees() - decimalDegrees1);
double decimalDegrees2 =
FsLongitudeSpan::FromFeet(Feet, fsLatitude1).DecimalDegrees();
FsLongitude Longitude1 = FsLongitude(
ThresholdCentre.Longitude().DecimalDegrees() + decimalDegrees2);
FsLatLonPoint P1 = FsLatLonPoint(fsLatitude1, Longitude1);
fsLatitude1 = FsLatitude(ThresholdCentre.Latitude().DecimalDegrees() +
decimalDegrees1);
double decimalDegrees3 =
FsLongitudeSpan::FromFeet(Feet, fsLatitude1).DecimalDegrees();
Longitude1 = FsLongitude(ThresholdCentre.Longitude().DecimalDegrees() -
decimalDegrees3);
FsLatLonPoint P0 = FsLatLonPoint(fsLatitude1, Longitude1);
double decimalDegrees4 =
FsLatitudeSpan::FromFeet(cos(num) * LengthInFeet).DecimalDegrees();
FsLatitude fsLatitude2 = FsLatitude(
ThresholdCentre.Latitude().DecimalDegrees() + decimalDegrees4);
double decimalDegrees5 =
FsLongitudeSpan ::FromFeet(sin(num) * LengthInFeet, fsLatitude2)
.DecimalDegrees();
FsLongitude Longitude2 = FsLongitude(
ThresholdCentre.Longitude().DecimalDegrees() + decimalDegrees5);
FsLatLonPoint fsLatLonPoint = FsLatLonPoint(fsLatitude2, Longitude2);
fsLatitude1 = FsLatitude(fsLatLonPoint.Latitude().DecimalDegrees() -
decimalDegrees1);
double decimalDegrees6 =
FsLongitudeSpan::FromFeet(Feet, fsLatitude1).DecimalDegrees();
Longitude1 = FsLongitude(fsLatLonPoint.Longitude().DecimalDegrees() +
decimalDegrees6);
FsLatLonPoint P3 = FsLatLonPoint(fsLatitude1, Longitude1);
fsLatitude1 = FsLatitude(fsLatLonPoint.Latitude().DecimalDegrees() +
decimalDegrees1);
double decimalDegrees7 =
FsLongitudeSpan::FromFeet(Feet, fsLatitude1).DecimalDegrees();
Longitude1 = FsLongitude(fsLatLonPoint.Longitude().DecimalDegrees() -
decimalDegrees7);
FsLatLonPoint P2 = FsLatLonPoint(fsLatitude1, Longitude1);
return FsLatLonQuadrilateral(P0, P1, P2, P3);
}
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+60
View File
@@ -0,0 +1,60 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDE_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDE_H
#include <cmath>
#include <cstdint>
#include <sstream>
#include <string>
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
class FsLatitudeSpan;
class FsLatitude
{
private:
double pos;
public:
inline FsLatitude();
inline FsLatitude(double DecimalDegrees);
inline FsLatitude(std::int64_t FSUnits);
inline FsLatitude(std::int32_t FSUnits);
inline FsLatitude(std::int32_t Degrees, double DecimalMinutes);
inline FsLatitude(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds);
inline std::int64_t ToFSUnits8() const;
inline std::int32_t ToFSUnits4() const;
inline double DecimalDegrees() const;
inline double DecimalMinutes() const;
inline double DecimalSeconds() const;
inline std::int32_t Degree() const;
inline std::int32_t Minute() const;
inline std::int32_t Second() const;
inline double UDegrees() const;
inline const std::string to_string(bool HemisphereAsText,
char DetailLevel) const;
inline const std::string to_string() const;
inline FsLatitude Add(const FsLatitudeSpan &Distance) const;
inline FsLatitude Subtract(const FsLatitudeSpan &Distance) const;
inline FsLatitude AddDegrees(double Degrees) const;
inline FsLatitude AddMinutes(double Minutes) const;
inline FsLatitude AddSeconds(double Seconds) const;
};
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+129
View File
@@ -0,0 +1,129 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDEIMPL_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDEIMPL_H
#include "FsLatitude.h"
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
inline FsLatitude::FsLatitude() = default;
inline FsLatitude::FsLatitude(double DecimalDegrees)
{
this->pos = DecimalDegrees;
while (this->pos > 90.0 || this->pos < -90.0) {
if (this->pos > 90.0)
this->pos = 180.0 - this->pos;
if (this->pos < -90.0)
this->pos = -180.0 - this->pos;
}
}
inline FsLatitude::FsLatitude(std::int64_t FSUnits)
: FsLatitude(FSUnits * 90.0 / 4.2957189152768E+16)
{
}
inline FsLatitude::FsLatitude(std::int32_t FSUnits)
: FsLatitude(FSUnits * 90.0 / 10001750.0)
{
}
inline FsLatitude::FsLatitude(std::int32_t Degrees, double DecimalMinutes)
: FsLatitude(Degrees + DecimalMinutes / 60.0)
{
}
inline FsLatitude::FsLatitude(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds)
: FsLatitude(Degrees + Minutes / 60.0 + DecimalSeconds / 3600.0)
{
}
inline std::int64_t FsLatitude::ToFSUnits8() const
{
return this->pos * 4.2957189152768E+16 / 90.0;
}
inline std::int32_t FsLatitude::ToFSUnits4() const
{
return this->pos * 10001750.0 / 90.0;
}
inline double FsLatitude::DecimalDegrees() const { return this->pos; }
inline double FsLatitude::DecimalMinutes() const
{
return (this->pos - trunc(this->pos)) * 60.0;
}
inline double FsLatitude::DecimalSeconds() const
{
double decimalMinutes = this->DecimalMinutes();
return (decimalMinutes - trunc(decimalMinutes)) * 60.0;
}
inline std::int32_t FsLatitude::Degree() const { return trunc(this->pos); }
inline std::int32_t FsLatitude::Minute() const
{
return trunc(this->DecimalMinutes());
}
inline std::int32_t FsLatitude::Second() const
{
return trunc(this->DecimalSeconds());
}
inline double FsLatitude::UDegrees() const { return this->pos + 90.0; }
inline const std::string FsLatitude::to_string(bool HemisphereAsText,
char DetailLevel) const
{
std::ostringstream str;
if (!HemisphereAsText) {
str << (this->pos < 0.0 ? "-" : "");
} else {
str << (this->pos < 0.0 ? "S" : "N");
}
switch (DetailLevel) {
case 'm':
str << this->Degree() << "° " << this->DecimalMinutes() << "'";
break;
case 's':
str << this->Degree() << "° " << this->Minute() << "' "
<< this->DecimalSeconds() << "\"";
break;
default:
str << this->pos << "*";
break;
}
return str.str();
}
inline const std::string FsLatitude::to_string() const
{
return this->to_string(true, 'm');
}
inline FsLatitude FsLatitude::Add(const FsLatitudeSpan &Distance) const
{
return FsLatitude(this->pos + Distance.DecimalDegrees());
}
inline FsLatitude FsLatitude::Subtract(const FsLatitudeSpan &Distance) const
{
return FsLatitude(this->pos - Distance.DecimalDegrees());
}
inline FsLatitude FsLatitude::AddDegrees(double Degrees) const
{
return FsLatitude(this->pos + Degrees);
}
inline FsLatitude FsLatitude::AddMinutes(double Minutes) const
{
return FsLatitude(this->pos + Minutes / 60.0);
}
inline FsLatitude FsLatitude::AddSeconds(double Seconds) const
{
return FsLatitude(this->pos + Seconds / 3600.0);
}
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+58
View File
@@ -0,0 +1,58 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDESPAN_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDESPAN_H
#include <cmath>
#include <cstdint>
#include <sstream>
#include <string>
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
class FsLatitude;
class FsLatitudeSpan
{
private:
double span;
public:
inline FsLatitudeSpan();
inline FsLatitudeSpan(double DecimalDegrees);
inline FsLatitudeSpan(std::int32_t Degrees, double DecimalMinutes);
inline FsLatitudeSpan(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds);
inline static FsLatitudeSpan FromFeet(double Feet);
inline static FsLatitudeSpan FromNauticalMiles(double NauticalMiles);
inline static FsLatitudeSpan FromMetres(double Metres);
inline static FsLatitudeSpan
BetweenTwoLatitides(const FsLatitude &Lat1, const FsLatitude &Lat2);
inline double DecimalDegrees() const;
inline double DecimalMinutes() const;
inline double DecimalSeconds() const;
inline std::int32_t Degrees() const;
inline std::int32_t Minutes() const;
inline std::int32_t Seconds() const;
inline double TotalMinutes() const;
inline double TotalSeconds() const;
inline double ToFeet() const;
inline double ToNauticalMiles() const;
inline double ToMetres() const;
inline const std::string to_string(char DetailLevel) const;
inline const std::string to_string();
};
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+121
View File
@@ -0,0 +1,121 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDESPANIMPL_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLATITUDESPANIMPL_H
#include "FsLatitudeSpan.h"
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
inline FsLatitudeSpan::FsLatitudeSpan() = default;
inline FsLatitudeSpan::FsLatitudeSpan(double DecimalDegrees)
{
this->span = DecimalDegrees;
}
inline FsLatitudeSpan::FsLatitudeSpan(std::int32_t Degrees,
double DecimalMinutes)
: FsLatitudeSpan(Degrees + DecimalMinutes / 60.0)
{
}
inline FsLatitudeSpan::FsLatitudeSpan(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds)
: FsLatitudeSpan(Degrees + Minutes / 60.0 + DecimalSeconds / 3600.0)
{
}
inline FsLatitudeSpan FsLatitudeSpan::FromFeet(double Feet)
{
return FsLatitudeSpan(Feet / 364601.4567);
}
inline FsLatitudeSpan
FsLatitudeSpan::FromNauticalMiles(double NauticalMiles)
{
return FsLatitudeSpan::FromFeet(NauticalMiles * 6076.1155);
}
inline FsLatitudeSpan FsLatitudeSpan::FromMetres(double Metres)
{
return FsLatitudeSpan::FromFeet(Metres * 3.2808);
}
inline FsLatitudeSpan
FsLatitudeSpan::BetweenTwoLatitides(const FsLatitude &Lat1,
const FsLatitude &Lat2)
{
return FsLatitudeSpan(abs(Lat2.UDegrees() - Lat1.UDegrees()));
}
inline double FsLatitudeSpan::DecimalDegrees() const { return this->span; }
inline double FsLatitudeSpan::DecimalMinutes() const
{
return (this->span - trunc(this->span)) * 60.0;
}
inline double FsLatitudeSpan::DecimalSeconds() const
{
double decimalMinutes = this->DecimalMinutes();
return (decimalMinutes - trunc(decimalMinutes)) * 60.0;
}
inline std::int32_t FsLatitudeSpan::Degrees() const
{
return trunc(this->span);
}
inline std::int32_t FsLatitudeSpan::Minutes() const
{
return trunc(this->DecimalMinutes());
}
inline std::int32_t FsLatitudeSpan::Seconds() const
{
return trunc(this->DecimalSeconds());
}
inline double FsLatitudeSpan::TotalMinutes() const
{
return this->span * 60.0;
}
inline double FsLatitudeSpan::TotalSeconds() const
{
return this->span * 3600.0;
}
inline double FsLatitudeSpan::ToFeet() const
{
return 364601.4567 * this->span;
}
inline double FsLatitudeSpan::ToNauticalMiles() const
{
return this->ToFeet() / 6076.1155;
}
inline double FsLatitudeSpan::ToMetres() const
{
return this->ToFeet() / 3.2808;
}
inline const std::string FsLatitudeSpan::to_string(char DetailLevel) const
{
std::ostringstream str;
switch (DetailLevel) {
case 'm':
str << this->Degrees() << "* " << this->DecimalMinutes() << "'";
break;
case 's':
str << this->Degrees() << "* " << this->Minutes() << "' "
<< this->DecimalSeconds() << "\"";
break;
default:
str << this->span << "*";
break;
}
return str.str();
}
inline const std::string FsLatitudeSpan::to_string()
{
return this->to_string('m');
}
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+60
View File
@@ -0,0 +1,60 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDE_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDE_H
#include <cmath>
#include <cstdint>
#include <sstream>
#include <string>
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
class FsLongitudeSpan;
class FsLongitude
{
private:
double pos;
public:
inline FsLongitude();
inline FsLongitude(double DecimalDegrees);
inline FsLongitude(std::int64_t FSUnits);
inline FsLongitude(std::int32_t FSUnits);
inline FsLongitude(std::int32_t Degrees, double DecimalMinutes);
inline FsLongitude(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds);
inline long ToFSUnits8() const;
inline std::int32_t ToFSUnits4() const;
inline double DecimalDegrees() const;
inline double DecimalMinutes() const;
inline double DecimalSeconds() const;
inline std::int32_t Degree() const;
inline std::int32_t Minute() const;
inline std::int32_t Second() const;
inline double UDegrees() const;
inline const std::string to_string(bool HemisphereAsText,
char DetailLevel) const;
inline const std::string to_string() const;
inline FsLongitude Add(const FsLongitudeSpan &Distance) const;
inline FsLongitude Subtract(const FsLongitudeSpan &Distance) const;
inline FsLongitude AddDegrees(double Degrees) const;
inline FsLongitude AddMinutes(double Minutes) const;
inline FsLongitude AddSeconds(double Seconds) const;
};
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+133
View File
@@ -0,0 +1,133 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDEIMPL_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDEIMPL_H
#include "FsLongitude.h"
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
inline FsLongitude::FsLongitude() = default;
inline FsLongitude::FsLongitude(double DecimalDegrees)
{
this->pos = DecimalDegrees;
while (this->pos < -180.0 || this->pos > 180.0) {
if (this->pos > 180.0)
this->pos -= 360.0;
if (this->pos < -180.0)
this->pos += 360.0;
}
}
inline FsLongitude::FsLongitude(std::int64_t FSUnits)
: FsLongitude(FSUnits * 360.0 / 1.8446744073709552E+19)
{
}
inline FsLongitude::FsLongitude(std::int32_t FSUnits)
: FsLongitude(FSUnits * 360.0 / 4294967296.0)
{
}
inline FsLongitude::FsLongitude(std::int32_t Degrees, double DecimalMinutes)
: FsLongitude(Degrees + DecimalMinutes / 60.0)
{
}
inline FsLongitude::FsLongitude(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds)
: FsLongitude(Degrees + Minutes / 60.0 + DecimalSeconds / 3600.0)
{
}
inline long FsLongitude::ToFSUnits8() const
{
return this->pos * 1.8446744073709552E+19 / 360.0;
}
inline std::int32_t FsLongitude::ToFSUnits4() const
{
return this->pos * 4294967296.0 / 360.0;
}
inline double FsLongitude::DecimalDegrees() const { return this->pos; }
inline double FsLongitude::DecimalMinutes() const
{
return (this->pos - trunc(this->pos)) * 60.0;
}
inline double FsLongitude::DecimalSeconds() const
{
double decimalMinutes = this->DecimalMinutes();
return (decimalMinutes - trunc(decimalMinutes)) * 60.0;
}
inline std::int32_t FsLongitude::Degree() const
{
return trunc(this->DecimalDegrees());
}
inline std::int32_t FsLongitude::Minute() const
{
return trunc(this->DecimalMinutes());
}
inline std::int32_t FsLongitude::Second() const
{
return trunc(this->DecimalSeconds());
}
inline double FsLongitude::UDegrees() const { return this->pos + 180.0; }
inline const std::string FsLongitude::to_string(bool HemisphereAsText,
char DetailLevel) const
{
std::ostringstream str;
if (!HemisphereAsText) {
str << (this->pos < 0.0 ? "-" : "");
} else {
str << (this->pos < 0.0 ? "W" : "E");
}
switch (DetailLevel) {
case 'm':
str << this->Degree() << "° " << this->DecimalMinutes() << "'";
break;
case 's':
str << this->Degree() << "° " << this->Minute() << "' "
<< this->DecimalSeconds() << "\"";
break;
default:
str << this->pos << "*";
break;
}
return str.str();
}
inline const std::string FsLongitude::to_string() const
{
return this->to_string(true, 'm');
}
inline FsLongitude FsLongitude::Add(const FsLongitudeSpan &Distance) const
{
return FsLongitude(this->pos + Distance.DecimalDegrees());
}
inline FsLongitude
FsLongitude::Subtract(const FsLongitudeSpan &Distance) const
{
return FsLongitude(this->pos - Distance.DecimalDegrees());
}
inline FsLongitude FsLongitude::AddDegrees(double Degrees) const
{
return FsLongitude(this->pos + Degrees);
}
inline FsLongitude FsLongitude::AddMinutes(double Minutes) const
{
return FsLongitude(this->pos + Minutes / 60.0);
}
inline FsLongitude FsLongitude::AddSeconds(double Seconds) const
{
return FsLongitude(this->pos + Seconds / 3600.0);
}
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+65
View File
@@ -0,0 +1,65 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDESPAN_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDESPAN_H
#define _USE_MATH_DEFINES
#include <cmath>
#include <cstdint>
#include <sstream>
#include <string>
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
class FsLongitude;
class FsLatitude;
class FsLongitudeSpan
{
private:
double span;
public:
inline FsLongitudeSpan();
inline FsLongitudeSpan(double DecimalDegrees);
inline FsLongitudeSpan(std::int32_t Degrees, double DecimalMinutes);
inline FsLongitudeSpan(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds);
inline static FsLongitudeSpan FromFeet(double Feet,
const FsLatitude &AtLatitude);
inline static FsLongitudeSpan
FromNauticalMiles(double NauticalMiles,
const FsLatitude &AtLatitude);
inline static FsLongitudeSpan FromMetres(double Metres,
const FsLatitude &AtLatitude);
inline static FsLongitudeSpan
BetweenTwoLongitudes(const FsLongitude &Lon1,
const FsLongitude &Lon2);
inline double DecimalDegrees() const;
inline double DecimalMinutes() const;
inline double DecimalSeconds() const;
inline std::int32_t Degrees() const;
inline std::int32_t Minutes() const;
inline std::int32_t Seconds() const;
inline double TotalMinutes() const;
inline double TotalSeconds() const;
inline double ToFeet(const FsLatitude &AtLatitude) const;
inline double ToNauticalMiles(const FsLatitude &AtLatitude) const;
inline double ToMetres(const FsLatitude &AtLatitude) const;
inline const std::string to_string(char DetailLevel) const;
inline const std::string to_string();
};
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+133
View File
@@ -0,0 +1,133 @@
#ifndef GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDESPANIMPL_H
#define GERMANAIRLINESVA_FILE_FSUIPC_FSLONGITUDESPANIMPL_H
#include "FsLongitudeSpan.h"
namespace germanairlinesva
{
namespace file
{
namespace FSUIPC
{
inline FsLongitudeSpan::FsLongitudeSpan() = default;
inline FsLongitudeSpan::FsLongitudeSpan(double DecimalDegrees)
{
this->span = DecimalDegrees;
}
inline FsLongitudeSpan::FsLongitudeSpan(std::int32_t Degrees,
double DecimalMinutes)
: FsLongitudeSpan(Degrees + DecimalMinutes / 60.0)
{
}
inline FsLongitudeSpan::FsLongitudeSpan(std::int32_t Degrees,
std::int32_t Minutes,
double DecimalSeconds)
: FsLongitudeSpan(Degrees + Minutes / 60.0 + DecimalSeconds / 3600.0)
{
}
inline FsLongitudeSpan
FsLongitudeSpan::FromFeet(double Feet, const FsLatitude &AtLatitude)
{
double num =
cos(M_PI * AtLatitude.DecimalDegrees() / 180.0) * 131479672.3 / 360.0;
return FsLongitudeSpan(Feet / num);
}
inline FsLongitudeSpan
FsLongitudeSpan::FromNauticalMiles(double NauticalMiles,
const FsLatitude &AtLatitude)
{
return FsLongitudeSpan::FromFeet(NauticalMiles * 6076.1155, AtLatitude);
}
inline FsLongitudeSpan
FsLongitudeSpan::FromMetres(double Metres, const FsLatitude &AtLatitude)
{
return FsLongitudeSpan::FromFeet(Metres * 3.2808, AtLatitude);
}
inline FsLongitudeSpan
FsLongitudeSpan::BetweenTwoLongitudes(const FsLongitude &Lon1,
const FsLongitude &Lon2)
{
return fmod(Lon2.UDegrees() - Lon1.UDegrees(), 360.0) <
fmod(Lon1.UDegrees() - Lon2.UDegrees(), 360.0)
? FsLongitudeSpan(
fmod(Lon2.UDegrees() - Lon1.UDegrees(), 360.0))
: FsLongitudeSpan(
fmod(Lon1.UDegrees() - Lon2.UDegrees(), 360.0));
}
inline double FsLongitudeSpan::DecimalDegrees() const { return this->span; }
inline double FsLongitudeSpan::DecimalMinutes() const
{
return (this->span - trunc(this->span)) * 60.0;
}
inline double FsLongitudeSpan::DecimalSeconds() const
{
double decimalMinutes = this->DecimalMinutes();
return (decimalMinutes - trunc(decimalMinutes)) * 60.0;
}
inline std::int32_t FsLongitudeSpan::Degrees() const
{
return trunc(this->span);
}
inline std::int32_t FsLongitudeSpan::Minutes() const
{
return trunc(this->DecimalMinutes());
}
inline std::int32_t FsLongitudeSpan::Seconds() const
{
return trunc(this->DecimalSeconds());
}
inline double FsLongitudeSpan::TotalMinutes() const
{
return this->span * 60.0;
}
inline double FsLongitudeSpan::TotalSeconds() const
{
return this->span * 3600.0;
}
inline double FsLongitudeSpan::ToFeet(const FsLatitude &AtLatitude) const
{
return cos(M_PI * AtLatitude.DecimalDegrees() / 180.0) * 131479672.3 /
360.0 * this->span;
}
inline double
FsLongitudeSpan::ToNauticalMiles(const FsLatitude &AtLatitude) const
{
return this->ToFeet(AtLatitude) / 6076.1155;
}
inline double FsLongitudeSpan::ToMetres(const FsLatitude &AtLatitude) const
{
return this->ToFeet(AtLatitude) / 3.2808;
}
inline const std::string FsLongitudeSpan::to_string(char DetailLevel) const
{
std::ostringstream str;
switch (DetailLevel) {
case 'm':
str << this->Degrees() << "* " << this->DecimalMinutes() << "'";
break;
case 's':
str << this->Degrees() << "* " << this->Minutes() << "' "
<< this->DecimalSeconds() << "\"";
break;
default:
str << this->span << "*";
break;
}
return str.str();
}
inline const std::string FsLongitudeSpan::to_string()
{
return this->to_string('m');
}
} // namespace FSUIPC
} // namespace file
} // namespace germanairlinesva
#endif
+42
View File
@@ -0,0 +1,42 @@
#ifndef GERMANAIRLINESVA_FILE_CONFIG_H
#define GERMANAIRLINESVA_FILE_CONFIG_H
#include <map>
#include <fstream>
#include <map>
#include <string>
#include <vector>
#include "constants.h"
#include "util.hpp"
namespace germanairlinesva
{
namespace file
{
namespace config
{
class Config
{
private:
std::string user;
std::string scenery;
std::string token;
void writeFile() const;
public:
Config();
void updateScenery(std::string scenery);
inline const std::string getUser() const { return this->user; }
inline const std::string getScenery() const { return this->scenery; }
inline const std::string getToken() const { return this->token; }
};
} // namespace config
} // namespace file
} // namespace germanairlinesva
#endif
+57
View File
@@ -0,0 +1,57 @@
#ifndef GERMANAIRLINESVA_FILE_HELPERS_H
#define GERMANAIRLINESVA_FILE_HELPERS_H
#include <cstdint>
#include <fstream>
#include <string>
#include <vector>
namespace germanairlinesva
{
namespace file
{
static inline const std::string readString(std::ifstream &in,
std::uint8_t length)
{
std::vector<char> value(length + 1);
in.read(value.data(), length);
return std::string(std::move(value.data()));
}
static inline const std::string readString(std::ifstream &in)
{
std::uint8_t length;
in.read(reinterpret_cast<char *>(&length), 1);
std::vector<char> value(length + 1);
in.read(value.data(), length);
return std::string(std::move(value.data()));
}
template <typename T> static inline const T read(std::ifstream &in)
{
T value;
in.read(reinterpret_cast<char *>(&value), sizeof(value));
return value;
}
static inline void writeString(std::ofstream &out, std::string value)
{
std::uint8_t length = value.length();
out.write(reinterpret_cast<char *>(&length), sizeof(length));
out.write(value.data(), length);
}
static inline void
writeString(std::ofstream &out, std::string value, std::uint8_t length)
{
out.write(value.data(), length);
}
template <typename T> static inline void write(std::ofstream &out, T value)
{
out.write(reinterpret_cast<char *>(&value), sizeof(value));
}
} // namespace file
} // namespace germanairlinesva
#endif
+50
View File
@@ -0,0 +1,50 @@
#ifndef GERMANAIRLINESVA_FILE_LOGBOOK_H
#define GERMANAIRLINESVA_FILE_LOGBOOK_H
#include <cstdint>
#include <fstream>
#include <string>
#include <utility>
#include <vector>
#include "constants.h"
#include "helpers.hpp"
#include "logbookEntry.h"
#include "util.hpp"
namespace germanairlinesva
{
namespace file
{
namespace logbook
{
/*
* Logbook Header (6)
* CHAR[5] | UINT8
* --------+--------
* VGAL | VERSION
*
* Logbook Entries (n)
* LOGBOOKENTRY[]
*/
class Logbook
{
private:
std::vector<LogbookEntry> entries;
void fromFile();
void readVersion1(std::ifstream &in);
public:
Logbook();
template <class... Args> inline void addEntry(Args &&...args)
{
this->entries.emplace_back(std::forward<Args>(args)...);
}
void toFile() const;
};
} // namespace logbook
} // namespace file
} // namespace germanairlinesva
#endif
+158
View File
@@ -0,0 +1,158 @@
#ifndef GERMANAIRLINESVA_FILE_LOGBOOKENTRY_H
#define GERMANAIRLINESVA_FILE_LOGBOOKENTRY_H
#include <cstdint>
#include <fstream>
#include <string>
#include <vector>
#include "helpers.hpp"
namespace germanairlinesva
{
namespace file
{
namespace logbook
{
/*
* Preamble (24)
* CHAR[10] | CHAR[4] | CHAR[4] | CHAR[6]
* ---------+---------------+----------+-------------
* DATE | FLIGHT NUMBER | AIRCRAFT | REGISTRATION
* Departure Airport Name (2...256)
* UINT8 | CHAR[]
* -------+-------
* STRLEN | STRING
* Departure Gate Name (2...256)
* UINT8 | CHAR[]
* -------+-------
* STRLEN | STRING
* Departure Runway Name (2...256)
* UINT8 | CHAR[]
* -------+-------
* STRLEN | STRING
* Arrival Airport Name (2...256)
* UINT8 | CHAR[]
* -------+-------
* STRLEN | STRING
* Arrival Gate Name (2...256)
* UINT8 | CHAR[]
* -------+-------
* STRLEN | STRING
* Arrival Runway Name (2...256)
* UINT8 | CHAR[]
* -------+-------
* STRLEN | STRING
* Times (24)
* CHAR[5] | CHAR[5] | CHAR[5] | CHAR[4] | FLOAT
* ----------+---------------+--------------+---------------+------
* OFF BLOCK | TAKEOFF (OUT) | LANDING (ON) | ON BLOCK (IN) | TOTAL
* Fuels (16)
* FLOAT | FLOAT | FLOAT | FLOAT
* ---------+-----------+---------+------
* TAXI OUT | IN FLIGHT | TAXI IN | TOTAL
* Distances (16)
* FLOAT | FLOAT | FLOAT |FLOAT
* ---------+-----------+---------+-----
* TAXI OUT | IN FLIGHT | TAXI IN |TOTAL
* Landing (9)
* FLOAT | CHAR | FLOAT
* ---------+---------- +------------
* MAX RATE | TOUCHDOWNS | MAX G-FORCE
* Recording Filename (2...256)
* UINT8 | CHAR[]
* -------+-------
* STRLEN | STRING
* Postamble (5)
* FLOAT | BITFIELD
* -------+---------
* POINTS | FLAGS
* Flags Bitfield
* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
* ----+-----+-----+-----+-----+-----+-----+------
* NIL | NIL | NIL | NIL | NIL | NIL | NIL | FILED
*/
class LogbookEntry
{
private:
std::string date;
std::string flightNumber;
std::string aircraftType;
std::string aircraftRegistration;
std::string departureAirport;
std::string departureGate;
std::string departureRunway;
std::string arrivalAirport;
std::string arrivalGate;
std::string arrivalRunway;
std::string offBlockTime;
std::string outTime;
std::string onTime;
std::string onBlockTime;
float totalFlightTime;
float taxiOutFuel;
float inFlightFuel;
float taxiInFuel;
float totalFuel;
float taxiOutDistance;
float inFlightDistance;
float taxiInDistance;
float totalDistance;
float maxLandingRate;
std::uint8_t touchdowns;
float maxLandingGees;
std::string recordingFilename;
float points;
std::uint8_t flags;
public:
LogbookEntry(std::string date,
std::string flightNumber,
std::string aircraftType,
std::string aircraftRegistration,
std::string departureAirport,
std::string departureGate,
std::string departureRunway,
std::string arrivalAirport,
std::string arrivalGate,
std::string arrivalRunway,
std::string offBlockTime,
std::string outTime,
std::string onTime,
std::string onBlockTime,
float totalFlightTime,
float taxiOutFuel,
float inFlightFuel,
float taxiInFuel,
float totalFuel,
float taxiOutDistance,
float inFlightDistance,
float taxiInDistance,
float totalDistance,
float maxLandingRate,
std::uint8_t touchdowns,
float maxLandingGees,
std::string recordingFilename,
float points,
std::uint8_t flags);
void toFile(std::ofstream &out) const;
};
} // namespace logbook
} // namespace file
} // namespace germanairlinesva
#endif
+45
View File
@@ -0,0 +1,45 @@
#ifndef GERMANAIRLINESVA_FILE_RECORDING_H
#define GERMANAIRLINESVA_FILE_RECORDING_H
#include <cstdint>
#include <fstream>
#include <vector>
#include "constants.h"
#include "recordingEntry.h"
namespace germanairlinesva
{
namespace file
{
namespace recording
{
/*
* Path Recording (6 + n * 24)
* HEADER | SEGMENTS
* Header (6)
* CHAR[5] | UINT8
* --------+--------
* VGAR | VERSION
* Path Segments (n)
* PATHSEGMENT[]
*/
class Recording
{
private:
std::vector<RecordingEntry> entries;
public:
template <class... Args> inline void addEntry(Args &&...args)
{
this->entries.emplace_back(std::forward<Args>(args)...);
}
void toFile(std::string fileName) const;
};
} // namespace recording
} // namespace file
} // namespace germanairlinesva
#endif
+60
View File
@@ -0,0 +1,60 @@
#ifndef GERMANAIRLINESVA_FILE_RECORDINGENTRY_H
#define GERMANAIRLINESVA_FILE_RECORDINGENTRY_H
#include <cstdint>
#include <fstream>
#include <vector>
#include "geodata.hpp"
#include "helpers.hpp"
namespace germanairlinesva
{
namespace file
{
namespace recording
{
/*
* Path Segment (24)
* UINT32 | UINT16 | UINT16 | POINT
* -------+----------+-------------+------------
* TIME | ALTITUDE | GROUNDSPEED | COORDINATES
*/
class RecordingEntry
{
private:
std::uint32_t time;
std::uint16_t altitude = 0;
std::uint16_t groundSpeed = 0;
struct geodata::point coordinates {
NAN, NAN
};
public:
RecordingEntry() = default;
RecordingEntry(std::uint32_t time,
std::uint16_t altitude,
std::uint16_t groundSpeed,
struct geodata::point coordinates);
void toFile(std::ofstream &out) const;
friend inline bool operator==(const RecordingEntry &lhs,
const RecordingEntry &rhs)
{
return lhs.altitude == rhs.altitude &&
lhs.groundSpeed == rhs.groundSpeed &&
lhs.coordinates.latitude == rhs.coordinates.latitude &&
lhs.coordinates.longitude == rhs.coordinates.longitude;
}
friend inline bool operator!=(const RecordingEntry &lhs,
const RecordingEntry &rhs)
{
return !(lhs == rhs);
}
};
} // namespace recording
} // namespace file
} // namespace germanairlinesva
#endif
+72
View File
@@ -0,0 +1,72 @@
#ifndef GERMANAIRLINESVA_FILE_GATE_H
#define GERMANAIRLINESVA_FILE_GATE_H
#include <cstdint>
#include <fstream>
#include <iostream>
#include <sstream>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include "geodata.hpp"
#include "helpers.hpp"
namespace germanairlinesva
{
namespace file
{
namespace simdata
{
/*
* Representation of gate
* Center in lat/lon radians
* Radius in metres
*
* UINT8 | CHAR[] | POINT | UINT8
* -------+------------+--------+-------
* STRLEN | DESIGNATOR | CENTER | RADIUS
*/
class Gate
{
private:
std::string designator;
struct geodata::point center;
std::uint8_t radius;
public:
// From X-Plane or MakeRwys
Gate(std::string designator,
double latitude,
double longitude,
std::uint8_t radius);
// From Database
Gate(std::string designator,
struct geodata::point center,
std::uint8_t radius);
void toFile(std::ofstream &out) const;
bool contains(geodata::point coordinates) const;
inline const std::string to_string() const
{
std::ostringstream str;
str << "Gate " << this->designator << " at "
<< geodata::toDegrees(this->center.latitude) << "N "
<< geodata::toDegrees(this->center.longitude) << "E, Radius "
<< (int)this->radius;
return str.str();
}
friend inline std::ostream &operator<<(std::ostream &os,
const Gate &gate)
{
return os << gate.to_string();
}
};
} // namespace simdata
} // namespace file
} // namespace germanairlinesva
#endif
+125
View File
@@ -0,0 +1,125 @@
#ifndef GERMANAIRLINESVA_FILE_SIMDATA_RUNWAY_H
#define GERMANAIRLINESVA_FILE_SIMDATA_RUNWAY_H
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <utility>
#include "FSUIPC/FsGeoData.h"
#include "geodata.hpp"
#include "helpers.hpp"
#include "util.hpp"
namespace germanairlinesva
{
namespace file
{
namespace simdata
{
/*
* Representation of one runway with supplementary information
* Heading in degrees true
* Threshold center in lat/lon degrees
* Width and length in feet
*
* UINT8 | CHAR[] | POINT | UINT8 | UINT16 | DOUBLE
* -------+------------+--------+-------+--------+-------
* STRLEN | DESIGNATOR | CENTER | WIDTH | LENGTH | TRUHDG
*/
class Runway
{
private:
std::string designator;
struct geodata::point center;
std::uint8_t width;
std::uint16_t length;
double trueHeading;
// QUAD TEST
FSUIPC::FsLatLonQuadrilateral quad;
public:
// From X-Plane or MakeRwys
inline Runway(std::string designator,
double latitudeStart,
double longitudeStart,
double latitudeEnd,
double longitudeEnd,
double width)
{
this->designator = designator;
this->width = width * 33.280839895;
this->length = geodata::distanceEarthD(latitudeStart,
longitudeStart,
latitudeEnd,
longitudeEnd) *
3.280839895;
this->trueHeading = geodata::bearingDD(latitudeStart,
longitudeStart,
latitudeEnd,
longitudeEnd);
this->center = {latitudeStart, longitudeStart};
FSUIPC::FsLatLonPoint threshold(latitudeStart, longitudeStart);
quad = FSUIPC::FsLatLonQuadrilateral::ForRunway(threshold,
this->trueHeading,
this->width,
this->length);
}
// From database
inline Runway(std::string designator,
struct geodata::point center,
std::uint8_t width,
std::uint16_t length,
double trueHeading)
{
this->designator = designator;
this->center = center;
this->width = width;
this->length = length;
this->trueHeading = trueHeading;
FSUIPC::FsLatLonPoint threshold(center.latitude, center.longitude);
quad = FSUIPC::FsLatLonQuadrilateral::ForRunway(threshold,
trueHeading,
width,
length);
}
inline void toFile(std::ofstream &out) const
{
writeString(out, this->designator);
write<decltype(this->center)>(out, this->center);
write<decltype(this->width)>(out, this->width);
write<decltype(this->length)>(out, this->length);
write<decltype(this->trueHeading)>(out, this->trueHeading);
}
inline const std::string to_string() const
{
std::ostringstream str;
str << "Runway " << this->designator << " with threshold center "
<< this->center.latitude << "N " << this->center.longitude
<< "E, Width " << (int)this->width << "ft, Length "
<< this->length << "ft, True Heading " << this->trueHeading
<< "°, Quad " << quad.to_string(true, ' ');
return str.str();
}
friend inline std::ostream &operator<<(std::ostream &os,
const Runway &runway)
{
return os << runway.to_string();
}
};
} // namespace simdata
} // namespace file
} // namespace germanairlinesva
#endif
+76
View File
@@ -0,0 +1,76 @@
#ifndef GERMANAIRLINESVA_FILE_SIMDATABASE_H
#define GERMANAIRLINESVA_FILE_SIMDATABASE_H
#include <cstdint>
#include <cstring>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "config/config.h"
#include "constants.h"
#include "simdata/gate.h"
#include "simdata/runway.h"
#include "simdata/simdataXP.h"
/*
* Header
*
* CHAR[5] | UINT8
* --------+--------
* VGAS | VERSION
*/
/*
* Airport
*
* UINT8 | CHAR[] | UINT16 | GATE[] | UINT8 | RUNWAY[]
* --------+--------+----------+--------+---------+---------
* STRLEN | ICAO | NUMGATES | GATES | NUMRWYS | RUNWAYS
*/
namespace germanairlinesva
{
namespace file
{
namespace simdata
{
class SimDatabase
{
private:
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
airports;
void fromFile();
void readVersion1(std::ifstream &in);
public:
SimDatabase(int xPlaneVersion,
const char *hash,
std::unique_ptr<config::Config> &configuration,
std::function<void(const std::string)> toLog);
void toFile() const;
inline std::size_t getCount() const { return this->airports.size(); }
inline const std::pair<const std::vector<Gate>,
const std::vector<Runway>>
operator[](std::string key)
{
auto it = this->airports.find(key);
if (it != this->airports.end()) {
return this->airports[key];
} else {
return std::pair<std::vector<Gate>, std::vector<Runway>>();
}
}
};
} // namespace simdata
} // namespace file
} // namespace germanairlinesva
#endif
+42
View File
@@ -0,0 +1,42 @@
#ifndef GERMANAIRLINESVA_FILE_SIMDATAXP_H
#define GERMANAIRLINESVA_FILE_SIMDATAXP_H
#include <fstream>
#include <map>
#include <string>
#include "simdata/gate.h"
#include "simdata/runway.h"
#include "simdata/simDatabase.h"
#include "util.hpp"
namespace germanairlinesva
{
namespace file
{
namespace simdata
{
int scan(
const std::string defaultFile,
const std::string sceneryPack,
const std::string logFile,
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
&airports);
void makeAirport(
const std::string &kind,
std::ifstream &infile,
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
&airports,
std::ofstream &logfile);
void makeGate15(std::vector<Gate> &gates,
const std::vector<std::string> &fields);
void makeRunway(std::vector<Runway> &runways,
const std::vector<std::string> &fields);
void makeGate1300(std::vector<Gate> &gates,
const std::vector<std::string> &fields);
} // namespace simdata
} // namespace file
} // namespace germanairlinesva
#endif
+109
View File
@@ -0,0 +1,109 @@
#include "logbook/logbook.h"
namespace germanairlinesva
{
namespace file
{
namespace logbook
{
Logbook::Logbook()
{
if (util::fileExists(XPLANE_PLUGIN_DIRECTORY LOGBOOK)) {
this->fromFile();
}
}
void Logbook::fromFile()
{
std::ifstream in(XPLANE_PLUGIN_DIRECTORY LOGBOOK, std::ifstream::binary);
// File Header
std::string ident = readString(in, 5);
if (ident.compare("VGAL") != 0) {
throw std::invalid_argument("Wrong file");
}
std::uint8_t version = read<std::uint8_t>(in);
if (version == 1) {
while (in.peek() != EOF) {
this->readVersion1(in);
}
}
}
void Logbook::readVersion1(std::ifstream &in)
{
std::string date = readString(in, 10);
std::string flightNumber = readString(in, 4);
std::string aircraftType = readString(in, 4);
std::string aircraftRegistration = readString(in, 6);
std::string departureAirport = readString(in);
std::string departureGate = readString(in);
std::string departureRunway = readString(in);
std::string arrivalAirport = readString(in);
std::string arrivalGate = readString(in);
std::string arrivalRunway = readString(in);
std::string offBlockTime = readString(in, 5);
std::string outTime = readString(in, 5);
std::string inTime = readString(in, 5);
std::string onBlockTime = readString(in, 5);
float totalFlightTime = read<float>(in);
float taxiOutFuel = read<float>(in);
float inFlightFuel = read<float>(in);
float taxiInFuel = read<float>(in);
float totalFuel = read<float>(in);
float taxiOutDistance = read<float>(in);
float inFlightDistance = read<float>(in);
float taxiInDistance = read<float>(in);
float totalDistance = read<float>(in);
float maxLandingRate = read<float>(in);
std::uint8_t touchdowns = read<std::uint8_t>(in);
float maxLandingGees = read<float>(in);
std::string recordingFilename = readString(in);
float points = read<float>(in);
std::uint8_t flags = read<std::uint8_t>(in);
this->addEntry(date,
flightNumber,
aircraftType,
aircraftRegistration,
departureAirport,
departureGate,
departureRunway,
arrivalAirport,
arrivalGate,
arrivalRunway,
offBlockTime,
outTime,
inTime,
onBlockTime,
totalFlightTime,
taxiOutFuel,
inFlightFuel,
taxiInFuel,
totalFuel,
taxiOutDistance,
inFlightDistance,
taxiInDistance,
totalDistance,
maxLandingRate,
touchdowns,
maxLandingGees,
recordingFilename,
points,
flags);
}
void Logbook::toFile() const
{
std::ofstream out(XPLANE_PLUGIN_DIRECTORY LOGBOOK, std::fstream::binary);
char header[] = {'V', 'G', 'A', 'L', '\0', 1};
out.write(header, 6);
for (const LogbookEntry &entry : this->entries) {
entry.toFile(out);
}
out.close();
}
} // namespace logbook
} // namespace file
} // namespace germanairlinesva
+104
View File
@@ -0,0 +1,104 @@
#include "logbook/logbookEntry.h"
namespace germanairlinesva
{
namespace file
{
namespace logbook
{
LogbookEntry::LogbookEntry(std::string date,
std::string flightNumber,
std::string aircraftType,
std::string aircraftRegistration,
std::string departureAirport,
std::string departureGate,
std::string departureRunway,
std::string arrivalAirport,
std::string arrivalGate,
std::string arrivalRunway,
std::string offBlockTime,
std::string outTime,
std::string onTime,
std::string onBlockTime,
float totalFlightTime,
float taxiOutFuel,
float inFlightFuel,
float taxiInFuel,
float totalFuel,
float taxiOutDistance,
float inFlightDistance,
float taxiInDistance,
float totalDistance,
float maxLandingRate,
std::uint8_t touchdowns,
float maxLandingGees,
std::string recordingFilename,
float points,
std::uint8_t flags)
{
this->date = date;
this->flightNumber = flightNumber;
this->aircraftType = aircraftType;
this->aircraftRegistration = aircraftRegistration;
this->departureAirport = departureAirport;
this->departureGate = departureGate;
this->departureRunway = departureRunway;
this->arrivalAirport = arrivalAirport;
this->arrivalGate = arrivalGate;
this->arrivalRunway = arrivalRunway;
this->offBlockTime = offBlockTime;
this->outTime = outTime;
this->onTime = onTime;
this->onBlockTime = onBlockTime;
this->totalFlightTime = totalFlightTime;
this->taxiOutFuel = taxiOutFuel;
this->inFlightFuel = inFlightFuel;
this->taxiInFuel = taxiInFuel;
this->totalFuel = totalFuel;
this->taxiOutDistance = taxiOutDistance;
this->inFlightDistance = inFlightDistance;
this->taxiInDistance = taxiInDistance;
this->totalDistance = totalDistance;
this->maxLandingRate = maxLandingRate;
this->touchdowns = touchdowns;
this->maxLandingGees = maxLandingGees;
this->recordingFilename = recordingFilename;
this->points = points;
this->flags = flags;
}
void LogbookEntry::toFile(std::ofstream &out) const
{
writeString(out, this->date, 10);
writeString(out, this->flightNumber, 4);
writeString(out, this->aircraftType, 4);
writeString(out, this->aircraftRegistration, 6);
writeString(out, this->departureAirport);
writeString(out, this->departureGate);
writeString(out, this->departureRunway);
writeString(out, this->arrivalAirport);
writeString(out, this->arrivalGate);
writeString(out, this->arrivalRunway);
writeString(out, this->offBlockTime, 5);
writeString(out, this->outTime, 5);
writeString(out, this->onTime, 5);
writeString(out, this->onBlockTime, 5);
write<decltype(this->totalFlightTime)>(out, this->totalFlightTime);
write<decltype(this->taxiOutFuel)>(out, this->taxiOutFuel);
write<decltype(this->inFlightFuel)>(out, this->inFlightFuel);
write<decltype(this->taxiInFuel)>(out, this->taxiInFuel);
write<decltype(this->totalFuel)>(out, this->totalFuel);
write<decltype(this->taxiOutDistance)>(out, this->taxiOutDistance);
write<decltype(this->inFlightDistance)>(out, this->inFlightDistance);
write<decltype(this->taxiInDistance)>(out, this->taxiInDistance);
write<decltype(this->totalDistance)>(out, this->totalDistance);
write<decltype(this->maxLandingRate)>(out, this->maxLandingRate);
write<decltype(this->touchdowns)>(out, this->touchdowns);
write<decltype(this->maxLandingGees)>(out, this->maxLandingGees);
writeString(out, this->recordingFilename);
write<decltype(this->points)>(out, this->points);
write<decltype(this->flags)>(out, this->flags);
}
} // namespace logbook
} // namespace file
} // namespace germanairlinesva
-57
View File
@@ -1,57 +0,0 @@
#include <cstdint>
#include <cstring>
#include <vector>
/*
* Length in bytes: 20
*
* UINT16 | UINT16 | DOUBLE | DOUBLE |
* ---------+-------------+----------+-----------+
* ALTITUDE | GROUNDSPEED | LATITUDE | LONGITUDE |
*/
class PathSegment
{
private:
std::uint16_t altitude = 0;
std::uint16_t groundSpeed = 0;
double latitude = 0;
double longitude = 0;
std::vector<std::uint8_t> 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;
file = std::vector<std::uint8_t>(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(); }
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);
}
};
+21
View File
@@ -0,0 +1,21 @@
{
// 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": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 0,
"runtimeArgs": [
"-dxdebug.start_with_request=yes"
],
"env": {
"XDEBUG_MODE": "debug,develop",
"XDEBUG_CONFIG": "client_port=${port}"
}
}]
}
+136
View File
@@ -0,0 +1,136 @@
<?php
namespace germanairlinesva_recording;
class Recording
{
private const ident = "VGAR";
private const header_unpack = "Z5ident/Cversion";
private const segment_unpack = "Vtime/Saltitude/Sgroundspeed/elatitude/elongitude";
private string $file_name;
public function __construct(string $file)
{
$this->file_name = $file;
}
/**
* Generates a geoJSON representation
*
* @return geoJSON representation of path
*/
public function geoJSON()
{
$geoJSON = [
"type" => "FeatureCollection",
"features" => [
0 => [
"type" => "Feature",
"properties" => [
"stroke" => "#ff0000",
"stroke-width" => 2,
"stroke-opacity" => 1
],
"geometry" => [
"type" => "LineString",
"coordinates" => []
]
]
]
];
$segments = $this->read();
$geoJSON["features"][0]["geometry"]["coordinates"] = array_map(function ($segment) {
return array($segment["longitude"], $segment["latitude"]);
}, $segments);
return json_encode($geoJSON);
}
/**
* Reads recording
*
* Array of objects with
* @return time Segment time (number)
* @return altitude Altitude
* @return groundspeed Ground speed
* @return latitude Latitude
* @return longitude Longitude
*/
public function read()
{
$file = fopen($this->file_name, "rb");
flock($file, LOCK_SH);
$segments = [];
$version = $this->read_header($file);
if ($version == 1) {
while (($segment = $this->read_segment_1($file)) != false) {
array_push($segments, $segment);
}
}
flock($file, LOCK_UN);
fclose($file);
return $segments;
}
/**
* Reads one segment entry
*
* @param resource $file File handle
*
* If not EOF
* @return time Segment time (number)
* @return altitude Altitude
* @return groundspeed Ground speed
* @return latitude Latitude
* @return longitude Longitude
* Else
* @return false
*
* @throws InvalidArgumentException If file is not a resource
*/
private function read_segment_1($file)
{
if (false === is_resource($file)) {
throw new \InvalidArgumentException(sprintf('Argument must be a valid resource type. %s given.', gettype($file)));
}
$data = fread($file, 24);
if ($data) {
return unpack(Recording::segment_unpack, $data);
}
return false;
}
/**
* Reads the file header
*
* @param resource file File handle
*
* @return version File version
*
* @throws InvalidArgumentException If file is not a resource
* @throws UnexpectedValueException If ident mismatches
*/
private function read_header($file)
{
if (false === is_resource($file)) {
throw new \InvalidArgumentException(sprintf('Argument must be a valid resource type. %s given.', gettype($file)));
}
$header = unpack(Recording::header_unpack, fread($file, 6));
if ($header["ident"] !== Recording::ident) {
throw
new \UnexpectedValueException(sprintf("Ident mismatch. Got %s, expected %s", $header["ident"], Recording::ident));
}
return $header["version"];
}
}
+6
View File
@@ -0,0 +1,6 @@
<?php
require "Recording.php";
$r = new germanairlinesva_recording\Recording("/mnt/f/X-Plane 11/Resources/plugins/GAConnector/recordings/flight.rec");
print_r($r->geoJSON());
+22
View File
@@ -0,0 +1,22 @@
#include "recording/recording.h"
namespace germanairlinesva
{
namespace file
{
namespace recording
{
void Recording::toFile(std::string fileName) const
{
std::ofstream out(XPLANE_PLUGIN_DIRECTORY RECORDING_DIRECTORY + fileName,
std::fstream::binary);
char header[] = {'V', 'G', 'A', 'R', '\0', 1};
out.write(header, 6);
for (const RecordingEntry &entry : this->entries) {
entry.toFile(out);
}
out.close();
}
} // namespace recording
} // namespace file
} // namespace germanairlinesva
+30
View File
@@ -0,0 +1,30 @@
#include "recording/recordingEntry.h"
namespace germanairlinesva
{
namespace file
{
namespace recording
{
RecordingEntry::RecordingEntry(std::uint32_t time,
std::uint16_t altitude,
std::uint16_t groundSpeed,
struct geodata::point coordinates)
{
this->time = time;
this->altitude = altitude;
this->groundSpeed = groundSpeed;
this->coordinates = coordinates;
};
void RecordingEntry::toFile(std::ofstream &out) const
{
write<decltype(this->time)>(out, this->time);
write<decltype(this->altitude)>(out, this->altitude);
write<decltype(this->groundSpeed)>(out, this->groundSpeed);
write<decltype(this->coordinates)>(out, this->coordinates);
}
} // namespace recording
} // namespace file
} // namespace germanairlinesva
-24
View File
@@ -1,24 +0,0 @@
#include <cstdint>
#include <cstring>
#include <vector>
#include "pathSegment.hpp"
class Path
{
private:
std::uint64_t count = 0;
std::vector<std::uint8_t> 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++;
}
std::uint8_t *getBinaryData() { return file.data(); }
std::size_t getBinaryLength() { return file.size(); }
};
-114
View File
@@ -1,114 +0,0 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_RUNWAY_H
#define GERMANAIRLINESVA_GACONNECTOR_RUNWAY_H
#include <iomanip>
#include <regex>
#include <sstream>
#include <string>
#include <utility>
#include "util.hpp"
/*
* Representation of one X-Plane runway with supplementary information
* Heading in degrees (0...360)
* Width and length in feet
*
* Length in bytes: 23 + length of designator
* Designator must be null terminated
*
* UINT8 | CHAR[] | DOUBLE | DOUBLE | UINT8 | UINT16 | UINT16
* ------+------------+--------+--------+-------+--------+-------
* LEN | DESIGNATOR | LAT | LON | WIDTH | LENGTH | TRUHDG
*/
class Runway
{
private:
std::string designator;
double latitudeStart;
double longitudeStart;
std::uint8_t width;
std::uint16_t length;
std::uint16_t trueHeading;
std::vector<std::uint8_t> 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));
file = std::vector<std::uint8_t>(23 + this->designator.length(), 0);
std::uint8_t *bufPtr = file.data();
memset(bufPtr,
static_cast<std::uint8_t>(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;
file = std::vector<std::uint8_t>(23 + this->designator.length(), 0);
std::uint8_t *bufPtr = file.data();
memset(bufPtr,
static_cast<std::uint8_t>(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(); }
};
#endif
+121
View File
@@ -0,0 +1,121 @@
#include "simdata/simDatabase.h"
namespace germanairlinesva
{
namespace file
{
namespace simdata
{
SimDatabase::SimDatabase(int xPlaneVersion,
const char *hash,
std::unique_ptr<config::Config> &configuration,
std::function<void(const std::string)> toLog)
{
if (strcmp(configuration->getScenery().c_str(), hash) != 0 ||
!util::fileExists(XPLANE_PLUGIN_DIRECTORY SIMDATABASE)) {
scan(xPlaneVersion < 12000 ? XPLANE11_BASE_SCENERY
: XPLANE12_BASE_SCENERY,
XPLANE_CUSTOM_SCENERY,
XPLANE_PLUGIN_DIRECTORY "log.txt",
airports);
configuration->updateScenery(hash);
this->toFile();
toLog("Sim Database updated");
} else {
this->fromFile();
toLog("Sim Database loaded");
}
}
void SimDatabase::fromFile()
{
std::ifstream in(XPLANE_PLUGIN_DIRECTORY SIMDATABASE,
std::ifstream::binary);
// File Header
std::string ident = readString(in, 5);
if (ident.compare("VGAS") != 0) {
throw std::invalid_argument("Wrong file");
}
std::uint8_t version = read<std::uint8_t>(in);
if (version == 1) {
this->readVersion1(in);
}
}
void SimDatabase::readVersion1(std::ifstream &in)
{
std::uint16_t numAirports = read<std::uint16_t>(in);
for (int i = 0; i < numAirports; i++) {
// ICAO
std::string icao = readString(in);
// Gates
std::uint16_t numGates = read<std::uint16_t>(in);
for (int j = 0; j < numGates; j++) {
std::string designator = readString(in);
struct geodata::point center = read<struct geodata::point>(in);
std::uint8_t radius = read<std::uint8_t>(in);
this->airports[icao].first.emplace_back(designator, center, radius);
}
// Runways
std::uint8_t numRunways = read<std::uint8_t>(in);
for (int j = 0; j < numRunways; j++) {
std::string designator = readString(in);
// Center
struct geodata::point center = read<struct geodata::point>(in);
std::uint8_t width = read<std::uint8_t>(in);
std::uint16_t length = read<std::uint16_t>(in);
double trueHeading = read<double>(in);
this->airports[icao].second.emplace_back(designator,
center,
width,
length,
trueHeading);
}
}
in.close();
}
void SimDatabase::toFile() const
{
std::ofstream out(XPLANE_PLUGIN_DIRECTORY SIMDATABASE,
std::fstream::binary);
// File Header, Last member is version
std::uint8_t header[] = {'V', 'G', 'A', 'S', '\0', 1};
out.write(reinterpret_cast<const char *>(header), 6);
// Num Airports
write<std::uint16_t>(out, airports.size());
// Airport
for (const std::pair<const std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>>
&airport : airports) {
std::string icao = airport.first;
const std::vector<Gate> gates = airport.second.first;
const std::vector<Runway> runways = airport.second.second;
// ICAO
writeString(out, icao);
// Gates
write<std::uint16_t>(out, gates.size());
for (const Gate &gate : gates) {
gate.toFile(out);
}
// Runways
write<std::uint8_t>(out, runways.size());
for (const Runway &runway : runways) {
runway.toFile(out);
}
}
out.close();
}
} // namespace simdata
} // namespace file
} // namespace germanairlinesva
+196
View File
@@ -0,0 +1,196 @@
#include "simdata/simdataXP.h"
namespace germanairlinesva
{
namespace file
{
namespace simdata
{
int scan(
const std::string defaultFile,
const std::string sceneryPack,
const std::string logFile,
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
&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::fstream::trunc);
if (!logfile.good()) {
base.close();
custom.close();
return 3;
}
// Default
logfile << "<FILE> " << defaultFile << std::endl;
makeAirport("DEFAULT", base, airports, logfile);
base.close();
std::string line;
size_t pos;
std::vector<std::string> packs;
while (std::getline(custom, line)) {
if ((pos = line.find("SCENERY_PACK")) != std::string::npos) {
std::string path = util::rtrim_copy(line.substr(pos + 13)) +
"Earth nav data/apt.dat";
packs.emplace_back(path);
}
}
std::reverse(packs.begin(), packs.end());
for (std::string const &path : packs) {
std::ifstream pack(path);
if (pack.good()) {
logfile << "<FILE> " << path << std::endl;
makeAirport("CUSTOM", pack, airports, logfile);
pack.close();
} else {
pack.close();
logfile << "<STATUS>"
<< "Could not find " << path << std::endl;
}
}
logfile << std::endl
<< "<STATUS> Total airports: " << airports.size() << std::endl;
custom.close();
logfile.close();
return 0;
}
void makeAirport(
const std::string &kind,
std::ifstream &infile,
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
&airports,
std::ofstream &logfile)
{
std::string line;
std::string *currentIcao = nullptr;
std::vector<Gate> tmpGates;
std::vector<Runway> tmpRunways;
int apCount = 0;
int validCount = 0;
while (std::getline(infile, line)) {
std::vector<std::string> fields = util::split(line, ' ');
fields = util::select_T<std::string>(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<STATUS> " << *currentIcao << " committed"
<< std::endl;
} else if (currentIcao != nullptr) {
logfile << "\t<STATUS> " << *currentIcao
<< " had no gates or runways" << std::endl;
}
tmpGates = std::vector<Gate>();
tmpRunways = std::vector<Runway>();
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<GATE OLD> " << 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<STATUS> " << *currentIcao << " committed"
<< std::endl;
} else if (currentIcao != nullptr) {
logfile << "\t<STATUS> " << *currentIcao
<< " had no gates or runways" << std::endl;
}
tmpGates = std::vector<Gate>();
tmpRunways = std::vector<Runway>();
currentIcao = nullptr;
logfile << "\t<" << kind << " SKIPPED> " << line << std::endl;
} else if (currentIcao != nullptr && fields[0] == "100") {
makeRunway(tmpRunways, fields);
logfile << "\t\t<RUNWAY> " << line << std::endl;
} else if (currentIcao != nullptr && fields[0] == "1300") {
makeGate1300(tmpGates, fields);
logfile << "\t\t<GATE> " << line << std::endl;
}
}
if (currentIcao != nullptr && !tmpRunways.empty() && !tmpGates.empty()) {
airports[*currentIcao] = {tmpGates, tmpRunways};
validCount += 1;
logfile << "\t<STATUS> " << *currentIcao << " committed" << std::endl;
}
logfile << "<STATUS> " << apCount << " airports found, of which "
<< validCount << " are valid" << std::endl;
}
void makeGate15(std::vector<Gate> &gates,
const std::vector<std::string> &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");
gates.emplace_back(gateName,
std::stod(fields[1]),
std::stod(fields[2]),
40);
}
void makeRunway(std::vector<Runway> &runways,
const std::vector<std::string> &fields)
{
runways.emplace_back(fields[8],
std::stod(fields[9]),
std::stod(fields[10]),
std::stod(fields[18]),
std::stod(fields[19]),
std::stod(fields[1]));
runways.emplace_back(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<Gate> &gates,
const std::vector<std::string> &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");
gates.emplace_back(gateName,
std::stod(fields[1]),
std::stod(fields[2]),
40);
}
} // namespace simdata
} // namespace file
} // namespace germanairlinesva
-191
View File
@@ -1,191 +0,0 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_SIMULATORDATABASE_H
#define GERMANAIRLINESVA_GACONNECTOR_SIMULATORDATABASE_H
#include "gate.hpp"
#include "runway.hpp"
#include "stringExtensions.hpp"
#define CURRENT_VERSION 1
#include <cstdint>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>
/*
* Header
*
* CHAR[5] | UINT8
* --------+--------
* VGAS | VERSION
*/
/*
* Airport
*
* UINT8 | CHAR[] | UINT16 | GATE[] | UINT8 | RUNWAY[]
* --------+--------+----------+--------+---------+---------
* STRLEN | ICAO | NUMGATES | GATES | NUMRWYS | RUNWAYS
*/
namespace simulatorDatabase
{
static inline void toFile(
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
&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<const char *>(header), 6);
// Num Airports
std::uint16_t numAirports = airports.size();
out.write(reinterpret_cast<const char *>(&numAirports),
sizeof(numAirports));
// Airport
for (const std::pair<const std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>>
&airport : airports) {
std::string icao = airport.first;
std::vector<Gate> gates = airport.second.first;
std::vector<Runway> runways = airport.second.second;
// ICAO
std::uint8_t icaoLength = icao.length();
out.write(reinterpret_cast<const char *>(&icaoLength),
sizeof(icaoLength));
out.write(icao.c_str(), icaoLength);
out.write(reinterpret_cast<const char *>(&null), sizeof(null));
// Gates
std::uint16_t numGates = gates.size();
out.write(reinterpret_cast<const char *>(&numGates),
sizeof(numGates));
for (Gate &gate : gates) {
out.write(reinterpret_cast<const char *>(gate.getBinaryData()),
(std::streamsize)gate.getBinaryLength());
}
// Runways
std::uint8_t numRunways = runways.size();
out.write(reinterpret_cast<const char *>(&numRunways),
sizeof(numRunways));
for (Runway &runway : runways) {
out.write(
reinterpret_cast<const char *>(runway.getBinaryData()),
(std::streamsize)runway.getBinaryLength());
}
}
out.close();
}
static inline std::map<std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>>
readVersion1(std::ifstream &in)
{
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
airports;
std::uint16_t numAirports;
in.read(reinterpret_cast<char *>(&numAirports), sizeof(numAirports));
for (int i = 0; i < numAirports; i++) {
// ICAO
std::uint8_t icaoLength;
in.read(reinterpret_cast<char *>(&icaoLength), sizeof(icaoLength));
char *icao =
static_cast<char *>(calloc(icaoLength + 1, sizeof(char)));
in.read(icao, icaoLength + 1);
// Gates
std::uint16_t numGates;
in.read(reinterpret_cast<char *>(&numGates), sizeof(numGates));
for (int j = 0; j < numGates; j++) {
// ICAO
std::uint8_t designatorLength;
in.read(reinterpret_cast<char *>(&designatorLength),
sizeof(designatorLength));
char *designator = static_cast<char *>(
calloc(designatorLength + 1, sizeof(char)));
in.read(designator, designatorLength + 1);
// Latitude
double latitude;
in.read(reinterpret_cast<char *>(&latitude), sizeof(latitude));
// Latitude
double longitude;
in.read(reinterpret_cast<char *>(&longitude),
sizeof(longitude));
airports[icao].first.emplace_back(designator,
latitude,
longitude);
}
// Runways
std::uint8_t numRunways;
in.read(reinterpret_cast<char *>(&numRunways), sizeof(numRunways));
for (int j = 0; j < numRunways; j++) {
// ICAO
std::uint8_t designatorLength;
in.read(reinterpret_cast<char *>(&designatorLength),
sizeof(designatorLength));
char *designator = static_cast<char *>(
calloc(designatorLength + 1, sizeof(char)));
in.read(designator, designatorLength + 1);
// Latitude
double latitude;
in.read(reinterpret_cast<char *>(&latitude), sizeof(latitude));
// Latitude
double longitude;
in.read(reinterpret_cast<char *>(&longitude),
sizeof(longitude));
// Width
std::uint8_t width;
in.read(reinterpret_cast<char *>(&width), sizeof(width));
// Length
std::uint16_t length;
in.read(reinterpret_cast<char *>(&length), sizeof(length));
// True Heading
std::uint16_t trueHeading;
in.read(reinterpret_cast<char *>(&trueHeading),
sizeof(trueHeading));
airports[icao].second.emplace_back(designator,
latitude,
longitude,
width,
length,
trueHeading);
}
}
in.close();
return airports;
}
static inline std::map<std::string,
std::pair<std::vector<Gate>, std::vector<Runway>>>
fromFile(const std::string &file)
{
std::map<std::string, std::pair<std::vector<Gate>, std::vector<Runway>>>
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<char *>(&version), 1);
if (version == 1) {
return readVersion1(in);
}
return airports;
}
} // namespace simulatorDatabase
#endif
-53
View File
@@ -1,53 +0,0 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_STRINGEXTENSIONS_H
#define GERMANAIRLINESVA_GACONNECTOR_STRINGEXTENSIONS_H
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
// 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);
}));
}
// 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());
}
static inline std::string rtrim_copy(std::string s)
{
rtrim(s);
return s;
}
// trim from both ends (in place)
static inline void trim(std::string &s)
{
ltrim(s);
rtrim(s);
}
static inline std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> result;
std::stringstream ss(s);
std::string item;
while (getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
#endif
-304
View File
@@ -1,304 +0,0 @@
#ifndef GERMANAIRLINESVA_GACONNECTOR_UTIL_H
#define GERMANAIRLINESVA_GACONNECTOR_UTIL_H
#ifdef IBM
#define WIN32_LEAN_AND_MEAN
#endif
#define BUFSIZE 1024
#define MD5LEN 16
#define EARTH_M 6371000
#ifdef IBM
// clang-format off
#include <wtypes.h>
#include <wincrypt.h>
// clang-format on
#endif
#ifdef APL
#include <CommonCrypto/CommonDigest.h>
#include <sys/mman.h>
#endif
#ifdef LIN
#include <openssl/md5.h>
#include <sys/mman.h>
#endif
#if defined APL || defined LIN
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#include <cmath>
#include <functional>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace util
{
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_radians(double value) { return value * M_PI / 180; }
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));
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));
}
template <typename T>
static inline std::vector<T>
select_T(const std::vector<T> &inVec,
std::function<bool(const T &)> predicate)
{
std::vector<T> 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;
}
#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);
}
}
}
#ifdef IBM
static inline int
generateMD5(const char *filepath,
char *lastHash,
const std::function<void(const std::string)> 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);
// 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;
}
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;
}
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());
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);
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<void(const std::string)> &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_size = get_size_by_fd(file_descript);
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);
munmap(file_buffer, file_size);
close(file_descript);
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<void(const std::string)> &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_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);
MD5((unsigned char *)file_buffer, file_size, result);
munmap(file_buffer, file_size);
close(file_descriptor);
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)
{
#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());
#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());
#endif
}
#pragma clang diagnostic pop
} // namespace util
#endif
+1 -1
View File
@@ -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:**/ixwebsocket/**:ixwebsocket/**:**/ixwebsocket:**/nlohmann/**:nlohmann/**:**/nlohmann:**/XPSDK/**:XPSDK/**:**/XPSDK:**/build*/**:build*/**:**/build*:**/openSSL/**:openSSL/**:**/openSSL'
clang-format -verbose -i **/*.cpp
clang-format -verbose -i **/*.h
+126
View File
@@ -0,0 +1,126 @@
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
)
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}/X-Plane/${PLUGIN_NAME}")
set_target_properties(ixwebsocket PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}
BUILD_WITH_INSTALL_NAME_DIR TRUE
)
target_compile_definitions(ixwebsocket PRIVATE
IXWEBSOCKET_USE_TLS
IXWEBSOCKET_USE_SECURE_TRANSPORT
)
target_compile_options(ixwebsocket PRIVATE
"SHELL:-arch x86_64"
)
target_link_options(ixwebsocket PRIVATE
"SHELL:-arch x86_64"
)
target_link_libraries(ixwebsocket PRIVATE
"-framework Foundation"
"-framework Security"
)
elseif(UNIX)
message("Building ixwebsocket for Linux ${BIT} into ${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}")
set_target_properties(ixwebsocket PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}
INSTALL_RPATH "\$ORIGIN"
)
target_compile_definitions(ixwebsocket PRIVATE
IXWEBSOCKET_USE_TLS
IXWEBSOCKET_USE_OPEN_SSL
)
target_compile_options(ixwebsocket PRIVATE
-nodefaultlibs
)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
message(STATUS "System OpenSSL: " ${OPENSSL_VERSION})
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(ixwebsocket PRIVATE
${OPENSSL_INCLUDE_DIR}
)
target_link_libraries(ixwebsocket PRIVATE
${OPENSSL_LIBRARIES}
)
elseif(WIN32)
if (BIT STREQUAL "32")
message("Building ixwebsocket for Windows ${BIT} into ${PROJECT_BINARY_DIR}/ESP/${PLUGIN_NAME}")
set_target_properties(ixwebsocket PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/ESP/${PLUGIN_NAME}"
)
else()
message("Building fileixwebsocket for Windows ${BIT} into ${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}")
set_target_properties(ixwebsocket PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/X-Plane/${PLUGIN_NAME}/${BIT}"
)
endif()
target_compile_definitions(ixwebsocket PRIVATE
IXWEBSOCKET_USE_TLS
IXWEBSOCKET_USE_OPEN_SSL
)
if(DEBUG)
target_compile_options(ixwebsocket PRIVATE
-gcodeview
)
target_link_options(ixwebsocket PRIVATE
-Wl,-pdb=
)
endif()
target_include_directories(ixwebsocket PRIVATE
${CMAKE_SOURCE_DIR}/openSSL/win${BIT}/include
)
target_link_libraries(ixwebsocket PRIVATE
wsock32
ws2_32
shlwapi
crypt32
${CMAKE_SOURCE_DIR}/openSSL/win${BIT}/libcrypto-1_1.dll
${CMAKE_SOURCE_DIR}/openSSL/win${BIT}/libssl-1_1.dll
)
endif()
target_link_libraries(ixwebsocket PRIVATE
pthread
)
add_library(ixwebsocket::ixwebsocket ALIAS ixwebsocket)
+15 -10
View File
@@ -10,14 +10,16 @@
namespace ix
{
Bench::Bench(const std::string &description) : _description(description)
Bench::Bench(const std::string& description)
: _description(description)
{
reset();
}
Bench::~Bench()
{
if (!_reported) {
if (!_reported)
{
report();
}
}
@@ -31,12 +33,10 @@ void Bench::reset()
void Bench::report()
{
auto now = std::chrono::high_resolution_clock::now();
auto microseconds =
std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
_duration = microseconds.count();
std::cerr << _description << " completed in " << _duration << " us"
<< std::endl;
std::cerr << _description << " completed in " << _duration << " us" << std::endl;
setReported();
}
@@ -44,13 +44,18 @@ void Bench::report()
void Bench::record()
{
auto now = std::chrono::high_resolution_clock::now();
auto microseconds =
std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - _start);
_duration = microseconds.count();
}
void Bench::setReported() { _reported = true; }
void Bench::setReported()
{
_reported = true;
}
uint64_t Bench::getDuration() const { return _duration; }
uint64_t Bench::getDuration() const
{
return _duration;
}
} // namespace ix
+4 -8
View File
@@ -12,23 +12,19 @@
namespace ix
{
CancellationRequest makeCancellationRequestWithTimeout(
int secs,
std::atomic<bool> &requestInitCancellation)
int secs, std::atomic<bool>& requestInitCancellation)
{
assert(secs > 0);
auto start = std::chrono::system_clock::now();
auto timeout = std::chrono::seconds(secs);
auto isCancellationRequested =
[&requestInitCancellation, start, timeout]() -> bool {
auto isCancellationRequested = [&requestInitCancellation, start, timeout]() -> bool {
// Was an explicit cancellation requested ?
if (requestInitCancellation)
return true;
if (requestInitCancellation) return true;
auto now = std::chrono::system_clock::now();
if ((now - start) > timeout)
return true;
if ((now - start) > timeout) return true;
// No cancellation request
return false;
+28 -9
View File
@@ -10,37 +10,56 @@ namespace ix
{
std::atomic<uint64_t> ConnectionState::_globalId(0);
ConnectionState::ConnectionState() : _terminated(false) { computeId(); }
ConnectionState::ConnectionState()
: _terminated(false)
{
computeId();
}
void ConnectionState::computeId() { _id = std::to_string(_globalId++); }
void ConnectionState::computeId()
{
_id = std::to_string(_globalId++);
}
const std::string &ConnectionState::getId() const { return _id; }
const std::string& ConnectionState::getId() const
{
return _id;
}
std::shared_ptr<ConnectionState> ConnectionState::createConnectionState()
{
return std::make_shared<ConnectionState>();
}
void ConnectionState::setOnSetTerminatedCallback(
const OnSetTerminatedCallback &callback)
void ConnectionState::setOnSetTerminatedCallback(const OnSetTerminatedCallback& callback)
{
_onSetTerminatedCallback = callback;
}
bool ConnectionState::isTerminated() const { return _terminated; }
bool ConnectionState::isTerminated() const
{
return _terminated;
}
void ConnectionState::setTerminated()
{
_terminated = true;
if (_onSetTerminatedCallback) {
if (_onSetTerminatedCallback)
{
_onSetTerminatedCallback();
}
}
const std::string &ConnectionState::getRemoteIp() { return _remoteIp; }
const std::string& ConnectionState::getRemoteIp()
{
return _remoteIp;
}
int ConnectionState::getRemotePort() { return _remotePort; }
int ConnectionState::getRemotePort()
{
return _remotePort;
}
void ConnectionState::setRemoteIp(const std::string& remoteIp)
{
+36 -31
View File
@@ -5,10 +5,10 @@
*/
//
// 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.
// 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
@@ -35,7 +35,11 @@ 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)
: _hostname(hostname)
, _port(port)
, _wait(wait)
, _res(nullptr)
, _done(false)
{
;
}
@@ -53,17 +57,16 @@ struct addrinfo *DNSLookup::getAddrInfo(const std::string &hostname,
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) {
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,
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
const CancellationRequest& isCancellationRequested,
bool cancellable)
{
@@ -71,17 +74,19 @@ struct addrinfo *
: resolveUnCancellable(errMsg, isCancellationRequested);
}
void DNSLookup::release(struct addrinfo *addr) { freeaddrinfo(addr); }
void DNSLookup::release(struct addrinfo* addr)
{
freeaddrinfo(addr);
}
struct addrinfo* DNSLookup::resolveUnCancellable(
std::string &errMsg,
const CancellationRequest &isCancellationRequested)
std::string& errMsg, const CancellationRequest& isCancellationRequested)
{
errMsg = "no error";
// Maybe a cancellation request got in before the background thread
// terminated ?
if (isCancellationRequested()) {
// Maybe a cancellation request got in before the background thread terminated ?
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
}
@@ -90,14 +95,14 @@ struct addrinfo *DNSLookup::resolveUnCancellable(
}
struct addrinfo* DNSLookup::resolveCancellable(
std::string &errMsg,
const CancellationRequest &isCancellationRequested)
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) {
if (_done)
{
return nullptr; // programming error, create a second DNSLookup instance
// if you need a second lookup.
}
@@ -115,28 +120,27 @@ struct addrinfo *DNSLookup::resolveCancellable(
// 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<std::thread>(&DNSLookup::run,
this,
self,
hostname,
port);
auto t = std::make_shared<std::thread>(&DNSLookup::run, this, self, hostname, port);
t->detach();
while (!_done) {
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()) {
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
}
}
// Maybe a cancellation request got in before the bg terminated ?
if (isCancellationRequested()) {
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
}
@@ -149,13 +153,14 @@ void DNSLookup::run(std::weak_ptr<DNSLookup> 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.
// 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()) {
if (auto lock = self.lock())
{
// Copy result into the member variables
setRes(res);
setErrMsg(errMsg);
+19 -5
View File
@@ -10,18 +10,32 @@
namespace ix
{
uint32_t
calculateRetryWaitMilliseconds(uint32_t retryCount,
uint32_t calculateRetryWaitMilliseconds(uint32_t retryCount,
uint32_t maxWaitBetweenReconnectionRetries,
uint32_t minWaitBetweenReconnectionRetries)
{
uint32_t waitTime = (retryCount < 26) ? (std::pow(2, retryCount) * 100) : 0;
// It's easy with a power function to go beyond 2^32, and then
// have unexpected results, so prepare for that
const uint32_t maxRetryCountWithoutOverflow = 26;
if (waitTime < minWaitBetweenReconnectionRetries) {
uint32_t waitTime = 0;
if (retryCount < maxRetryCountWithoutOverflow)
{
waitTime = std::pow(2, retryCount) * 100;
}
if (waitTime < minWaitBetweenReconnectionRetries)
{
waitTime = minWaitBetweenReconnectionRetries;
}
if (waitTime > maxWaitBetweenReconnectionRetries || waitTime == 0) {
if (waitTime > maxWaitBetweenReconnectionRetries)
{
waitTime = maxWaitBetweenReconnectionRetries;
}
if (retryCount >= maxRetryCountWithoutOverflow)
{
waitTime = maxWaitBetweenReconnectionRetries;
}
+12 -10
View File
@@ -32,16 +32,14 @@ int getAnyFreePortRandom()
int getAnyFreePort()
{
socket_t sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
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) {
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*) &enable, sizeof(enable)) < 0)
{
return getAnyFreePortRandom();
}
@@ -51,14 +49,16 @@ int getAnyFreePort()
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) {
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) {
if (getsockname(sockfd, (struct sockaddr*) &sa, &len) < 0)
{
Socket::closeSocket(sockfd);
return getAnyFreePortRandom();
}
@@ -71,7 +71,8 @@ int getAnyFreePort()
int getFreePort()
{
while (true) {
while (true)
{
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
int port = getAnyFreePortRandom();
@@ -85,7 +86,8 @@ int getFreePort()
// 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) {
if (port > 1024)
{
return port;
}
}
+12 -78
View File
@@ -14,56 +14,12 @@
#include <zlib.h>
#endif
#ifdef IXWEBSOCKET_USE_DEFLATE
#include <libdeflate.h>
#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<char *>(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));
@@ -87,13 +43,15 @@ std::string gzipCompress(const std::string &str)
std::string outstring;
// retrieve the compressed bytes blockwise
do {
do
{
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out) {
if (outstring.size() < zs.total_out)
{
// append the block to the output string
outstring.append(outbuffer, zs.total_out - outstring.size());
}
@@ -102,15 +60,14 @@ std::string gzipCompress(const std::string &str)
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);
return ((uint32_t) p[0] << 0) | ((uint32_t) p[1] << 8) | ((uint32_t) p[2] << 16) |
((uint32_t) p[3] << 24);
}
#endif
@@ -118,31 +75,6 @@ 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<const uint8_t *>(&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));
@@ -153,7 +85,8 @@ bool gzipDecompress(const std::string &in, std::string &out)
inflateState.avail_in = 0;
inflateState.next_in = Z_NULL;
if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK) {
if (inflateInit2(&inflateState, 16 + MAX_WBITS) != Z_OK)
{
return false;
}
@@ -163,13 +96,15 @@ bool gzipDecompress(const std::string &in, std::string &out)
const int kBufferSize = 1 << 14;
std::array<unsigned char, kBufferSize> compressBuffer;
do {
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) {
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
inflateEnd(&inflateState);
return false;
}
@@ -180,7 +115,6 @@ bool gzipDecompress(const std::string &in, std::string &out)
inflateEnd(&inflateState);
return true;
#endif // IXWEBSOCKET_USE_DEFLATE
#endif // IXWEBSOCKET_USE_ZLIB
}
} // namespace ix
+62 -57
View File
@@ -17,8 +17,10 @@ namespace ix
std::string Http::trim(const std::string& str)
{
std::string out;
for (auto c : str) {
if (c != ' ' && c != '\n' && c != '\r') {
for (auto c : str)
{
if (c != ' ' && c != '\n' && c != '\r')
{
out += c;
}
}
@@ -34,17 +36,20 @@ std::pair<std::string, int> Http::parseStatusLine(const std::string &line)
std::vector<std::string> tokens;
// Split by ' '
while (std::getline(tokenStream, token, ' ')) {
while (std::getline(tokenStream, token, ' '))
{
tokens.push_back(token);
}
std::string httpVersion;
if (tokens.size() >= 1) {
if (tokens.size() >= 1)
{
httpVersion = trim(tokens[0]);
}
int statusCode = -1;
if (tokens.size() >= 2) {
if (tokens.size() >= 2)
{
std::stringstream ss;
ss << trim(tokens[1]);
ss >> statusCode;
@@ -53,8 +58,8 @@ std::pair<std::string, int> Http::parseStatusLine(const std::string &line)
return std::make_pair(httpVersion, statusCode);
}
std::tuple<std::string, std::string, std::string>
Http::parseRequestLine(const std::string &line)
std::tuple<std::string, std::string, std::string> Http::parseRequestLine(
const std::string& line)
{
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
std::string token;
@@ -62,48 +67,50 @@ std::tuple<std::string, std::string, std::string>
std::vector<std::string> tokens;
// Split by ' '
while (std::getline(tokenStream, token, ' ')) {
while (std::getline(tokenStream, token, ' '))
{
tokens.push_back(token);
}
std::string method;
if (tokens.size() >= 1) {
if (tokens.size() >= 1)
{
method = trim(tokens[0]);
}
std::string requestUri;
if (tokens.size() >= 2) {
if (tokens.size() >= 2)
{
requestUri = trim(tokens[1]);
}
std::string httpVersion;
if (tokens.size() >= 3) {
if (tokens.size() >= 3)
{
httpVersion = trim(tokens[2]);
}
return std::make_tuple(method, requestUri, httpVersion);
}
std::tuple<bool, std::string, HttpRequestPtr>
Http::parseRequest(std::unique_ptr<Socket> &socket, int timeoutSecs)
std::tuple<bool, std::string, HttpRequestPtr> Http::parseRequest(
std::unique_ptr<Socket>& socket, int timeoutSecs)
{
HttpRequestPtr httpRequest;
std::atomic<bool> requestInitCancellation(false);
auto isCancellationRequested =
makeCancellationRequestWithTimeout(timeoutSecs,
requestInitCancellation);
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);
if (!lineValid)
{
return std::make_tuple(false, "Error reading HTTP request line", httpRequest);
}
// Parse request line (GET /foo HTTP/1.1\r\n)
@@ -117,66 +124,62 @@ std::tuple<bool, std::string, HttpRequestPtr>
auto headersValid = result.first;
auto headers = result.second;
if (!headersValid) {
return std::make_tuple(false,
"Error parsing HTTP headers",
httpRequest);
if (!headersValid)
{
return std::make_tuple(false, "Error parsing HTTP headers", httpRequest);
}
std::string body;
if (headers.find("Content-Length") != headers.end()) {
if (headers.find("Content-Length") != headers.end())
{
int contentLength = 0;
try {
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) {
catch (const std::exception&)
{
return std::make_tuple(
false,
"Error: 'Content-Length' should be a positive integer",
httpRequest);
false, "Error parsing HTTP Header 'Content-Length'", httpRequest);
}
auto res =
socket->readBytes(contentLength, nullptr, isCancellationRequested);
if (!res.first) {
return std::make_tuple(false,
std::string("Error reading request: ") +
res.second,
httpRequest);
if (contentLength < 0)
{
return std::make_tuple(
false, "Error: 'Content-Length' should be a positive integer", httpRequest);
}
auto res = socket->readBytes(contentLength, nullptr, 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") {
if (headers["Content-Encoding"] == "gzip")
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string decompressedPayload;
if (!gzipDecompress(body, decompressedPayload)) {
if (!gzipDecompress(body, decompressedPayload))
{
return std::make_tuple(
false,
std::string("Error during gzip decompression of the body"),
httpRequest);
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");
std::string errorMsg("ixwebsocket was not compiled with gzip support on");
return std::make_tuple(false, errorMsg, httpRequest);
#endif
}
httpRequest =
std::make_shared<HttpRequest>(uri, method, httpVersion, body, headers);
httpRequest = std::make_shared<HttpRequest>(uri, method, httpVersion, body, headers);
return std::make_tuple(true, "", httpRequest);
}
bool Http::sendResponse(HttpResponsePtr response,
std::unique_ptr<Socket> &socket)
bool Http::sendResponse(HttpResponsePtr response, std::unique_ptr<Socket>& socket)
{
// Write the response to the socket
std::stringstream ss;
@@ -186,23 +189,25 @@ bool Http::sendResponse(HttpResponsePtr response,
ss << response->description;
ss << "\r\n";
if (!socket->writeBytes(ss.str(), nullptr)) {
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) {
for (auto&& it : response->headers)
{
ss << it.first << ": " << it.second << "\r\n";
}
ss << "\r\n";
if (!socket->writeBytes(ss.str(), nullptr)) {
if (!socket->writeBytes(ss.str(), nullptr))
{
return false;
}
return response->body.empty() ? true
: socket->writeBytes(response->body, nullptr);
return response->body.empty() ? true : socket->writeBytes(response->body, nullptr);
}
} // namespace ix
+171 -123
View File
@@ -29,18 +29,18 @@ const std::string HttpClient::kPut = "PUT";
const std::string HttpClient::kPatch = "PATCH";
HttpClient::HttpClient(bool async)
: _async(async), _stop(false), _forceBody(false)
: _async(async)
, _stop(false)
, _forceBody(false)
{
if (!_async)
return;
if (!_async) return;
_thread = std::thread(&HttpClient::run, this);
}
HttpClient::~HttpClient()
{
if (!_thread.joinable())
return;
if (!_thread.joinable()) return;
_stop = true;
_condition.notify_one();
@@ -52,10 +52,12 @@ void HttpClient::setTLSOptions(const SocketTLSOptions &tlsOptions)
_tlsOptions = tlsOptions;
}
void HttpClient::setForceBody(bool value) { _forceBody = value; }
void HttpClient::setForceBody(bool value)
{
_forceBody = value;
}
HttpRequestArgsPtr HttpClient::createRequest(const std::string &url,
const std::string &verb)
HttpRequestArgsPtr HttpClient::createRequest(const std::string& url, const std::string& verb)
{
auto request = std::make_shared<HttpRequestArgs>();
request->url = url;
@@ -68,8 +70,7 @@ bool HttpClient::performRequest(HttpRequestArgsPtr args,
{
assert(_async && "HttpClient needs its async parameter set to true "
"in order to call performRequest");
if (!_async)
return false;
if (!_async) return false;
// Enqueue the task
{
@@ -88,19 +89,20 @@ bool HttpClient::performRequest(HttpRequestArgsPtr args,
void HttpClient::run()
{
while (true) {
while (true)
{
HttpRequestArgsPtr args;
OnResponseCallback onResponseCallback;
{
std::unique_lock<std::mutex> lock(_queueMutex);
while (!_stop && _queue.empty()) {
while (!_stop && _queue.empty())
{
_condition.wait(lock);
}
if (_stop)
return;
if (_stop) return;
auto p = _queue.front();
_queue.pop();
@@ -109,15 +111,12 @@ void HttpClient::run()
onResponseCallback = p.second;
}
if (_stop)
return;
if (_stop) return;
HttpResponsePtr response =
request(args->url, args->verb, args->body, args);
HttpResponsePtr response = request(args->url, args->verb, args->body, args);
onResponseCallback(response);
if (_stop)
return;
if (_stop) return;
}
}
@@ -141,7 +140,8 @@ HttpResponsePtr HttpClient::request(const std::string &url,
std::string protocol, host, path, query;
int port;
if (!UrlParser::parse(url, protocol, host, path, query, port)) {
if (!UrlParser::parse(url, protocol, host, path, query, port))
{
std::stringstream ss;
ss << "Cannot parse url: " << url;
return std::make_shared<HttpResponse>(code,
@@ -158,7 +158,8 @@ HttpResponsePtr HttpClient::request(const std::string &url,
std::string errorMsg;
_socket = createSocket(tls, -1, errorMsg, _tlsOptions);
if (!_socket) {
if (!_socket)
{
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::CannotCreateSocket,
@@ -175,32 +176,38 @@ HttpResponsePtr HttpClient::request(const std::string &url,
ss << "Host: " << host << "\r\n";
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compress) {
if (args->compress && !args->onChunkCallback)
{
ss << "Accept-Encoding: gzip"
<< "\r\n";
}
#endif
// Append extra headers
for (auto &&it : args->extraHeaders) {
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()) {
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()) {
if (args->extraHeaders.find("User-Agent") == args->extraHeaders.end())
{
ss << "User-Agent: " << userAgent() << "\r\n";
}
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody) {
if (verb == kPost || verb == kPut || verb == kPatch || _forceBody)
{
// Set request compression header
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compressRequest) {
if (args->compressRequest)
{
ss << "Content-Encoding: gzip"
<< "\r\n";
}
@@ -209,19 +216,24 @@ HttpResponsePtr HttpClient::request(const std::string &url,
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()) {
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";
}
else
{
ss << "Content-Type: multipart/form-data; boundary=" << args->multipartBoundary
<< "\r\n";
}
}
ss << "\r\n";
ss << body;
} else {
}
else
{
ss << "\r\n";
}
@@ -229,17 +241,21 @@ HttpResponsePtr HttpClient::request(const std::string &url,
std::string errMsg;
// Make a cancellation object dealing with connection timeout
auto isCancellationRequested =
makeCancellationRequestWithTimeout(args->connectTimeout, _stop);
auto cancelled = makeCancellationRequestWithTimeout(args->connectTimeout, args->cancel);
bool success =
_socket->connect(host, port, errMsg, isCancellationRequested);
if (!success) {
auto isCancellationRequested = [&]() {
return cancelled() || _stop;
};
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
if (!success)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::CannotConnect;
std::stringstream ss;
ss << "Cannot connect to url: " << url << " / error : " << errMsg;
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::CannotConnect,
errorCode,
headers,
payload,
ss.str(),
@@ -248,10 +264,10 @@ HttpResponsePtr HttpClient::request(const std::string &url,
}
// Make a new cancellation object dealing with transfer timeout
isCancellationRequested =
makeCancellationRequestWithTimeout(args->transferTimeout, _stop);
cancelled = makeCancellationRequestWithTimeout(args->transferTimeout, args->cancel);
if (args->verbose) {
if (args->verbose)
{
std::stringstream ss;
ss << "Sending " << verb << " request "
<< "to " << host << ":" << port << std::endl
@@ -263,11 +279,13 @@ HttpResponsePtr HttpClient::request(const std::string &url,
log(ss.str(), args);
}
if (!_socket->writeBytes(req, isCancellationRequested)) {
if (!_socket->writeBytes(req, isCancellationRequested))
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::SendError;
std::string errorMsg("Cannot send request");
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::SendError,
errorCode,
headers,
payload,
errorMsg,
@@ -281,12 +299,13 @@ HttpResponsePtr HttpClient::request(const std::string &url,
auto lineValid = lineResult.first;
auto line = lineResult.second;
if (!lineValid) {
if (!lineValid)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::CannotReadStatusLine;
std::string errorMsg("Cannot retrieve status line");
return std::make_shared<HttpResponse>(
code,
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::CannotReadStatusLine,
errorCode,
headers,
payload,
errorMsg,
@@ -294,13 +313,15 @@ HttpResponsePtr HttpClient::request(const std::string &url,
downloadSize);
}
if (args->verbose) {
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) {
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<HttpResponse>(code,
description,
@@ -316,11 +337,13 @@ HttpResponsePtr HttpClient::request(const std::string &url,
auto headersValid = result.first;
headers = result.second;
if (!headersValid) {
if (!headersValid)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::HeaderParsingError;
std::string errorMsg("Cannot parse http headers");
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::HeaderParsingError,
errorCode,
headers,
payload,
errorMsg,
@@ -329,11 +352,12 @@ HttpResponsePtr HttpClient::request(const std::string &url,
}
// Redirect ?
if ((code >= 301 && code <= 308) && args->followRedirects) {
if (headers.find("Location") == headers.end()) {
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<HttpResponse>(
code,
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::MissingLocation,
headers,
@@ -343,11 +367,11 @@ HttpResponsePtr HttpClient::request(const std::string &url,
downloadSize);
}
if (redirects >= args->maxRedirects) {
if (redirects >= args->maxRedirects)
{
std::stringstream ss;
ss << "Too many redirects: " << redirects;
return std::make_shared<HttpResponse>(
code,
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::TooManyRedirects,
headers,
@@ -362,7 +386,8 @@ HttpResponsePtr HttpClient::request(const std::string &url,
return request(location, verb, body, args, redirects + 1);
}
if (verb == "HEAD") {
if (verb == "HEAD")
{
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Ok,
@@ -374,42 +399,53 @@ HttpResponsePtr HttpClient::request(const std::string &url,
}
// Parse response:
if (headers.find("Content-Length") != headers.end()) {
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,
args->onChunkCallback,
isCancellationRequested);
if (!chunkResult.first) {
if (!chunkResult.first)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
errorMsg = "Cannot read chunk";
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::ChunkReadError,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
if (!args->onChunkCallback)
{
payload.reserve(contentLength);
payload += chunkResult.second;
} else if (headers.find("Transfer-Encoding") != headers.end() &&
headers["Transfer-Encoding"] == "chunked") {
}
}
else if (headers.find("Transfer-Encoding") != headers.end() &&
headers["Transfer-Encoding"] == "chunked")
{
std::stringstream ss;
while (true) {
while (true)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
lineResult = _socket->readLine(isCancellationRequested);
line = lineResult.second;
if (!lineResult.first) {
return std::make_shared<HttpResponse>(
code,
if (!lineResult.first)
{
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::ChunkReadError,
errorCode,
headers,
payload,
errorMsg,
@@ -422,40 +458,47 @@ HttpResponsePtr HttpClient::request(const std::string &url,
ss << std::hex << line;
ss >> chunkSize;
if (args->verbose) {
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,
args->onChunkCallback,
isCancellationRequested);
if (!chunkResult.first) {
if (!chunkResult.first)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
errorMsg = "Cannot read chunk";
return std::make_shared<HttpResponse>(
code,
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::ChunkReadError,
errorCode,
headers,
payload,
errorMsg,
uploadSize,
downloadSize);
}
if (!args->onChunkCallback)
{
payload.reserve(payload.size() + (size_t) chunkSize);
payload += chunkResult.second;
}
// Read the line that terminates the chunk (\r\n)
lineResult = _socket->readLine(isCancellationRequested);
if (!lineResult.first) {
return std::make_shared<HttpResponse>(
code,
if (!lineResult.first)
{
auto errorCode = args->cancel ? HttpErrorCode::Cancelled : HttpErrorCode::ChunkReadError;
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::ChunkReadError,
errorCode,
headers,
payload,
errorMsg,
@@ -463,12 +506,15 @@ HttpResponsePtr HttpClient::request(const std::string &url,
downloadSize);
}
if (chunkSize == 0)
break;
if (chunkSize == 0) break;
}
} else if (code == 204) {
}
else if (code == 204)
{
; // 204 is NoContent response code
} else {
}
else
{
std::string errorMsg("Cannot read http body");
return std::make_shared<HttpResponse>(code,
description,
@@ -483,10 +529,12 @@ HttpResponsePtr HttpClient::request(const std::string &url,
downloadSize = payload.size();
// If the content was compressed with gzip, decode it
if (headers["Content-Encoding"] == "gzip") {
if (headers["Content-Encoding"] == "gzip")
{
#ifdef IXWEBSOCKET_USE_ZLIB
std::string decompressedPayload;
if (!gzipDecompress(payload, decompressedPayload)) {
if (!gzipDecompress(payload, decompressedPayload))
{
std::string errorMsg("Error decompressing payload");
return std::make_shared<HttpResponse>(code,
description,
@@ -499,8 +547,7 @@ HttpResponsePtr HttpClient::request(const std::string &url,
}
payload = decompressedPayload;
#else
std::string errorMsg(
"ixwebsocket was not compiled with gzip support on");
std::string errorMsg("ixwebsocket was not compiled with gzip support on");
return std::make_shared<HttpResponse>(code,
description,
HttpErrorCode::Gzip,
@@ -527,20 +574,17 @@ 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)
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)
HttpResponsePtr HttpClient::Delete(const std::string& url, HttpRequestArgsPtr args)
{
return request(url, kDelete, std::string(), args);
}
HttpResponsePtr
HttpClient::request(const std::string &url,
HttpResponsePtr HttpClient::request(const std::string& url,
const std::string& verb,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
@@ -548,18 +592,21 @@ HttpResponsePtr
{
std::string body;
if (httpFormDataParameters.empty()) {
if (httpFormDataParameters.empty())
{
body = serializeHttpParameters(httpParameters);
} else {
}
else
{
std::string multipartBoundary = generateMultipartBoundary();
args->multipartBoundary = multipartBoundary;
body = serializeHttpFormDataParameters(multipartBoundary,
httpFormDataParameters,
httpParameters);
body = serializeHttpFormDataParameters(
multipartBoundary, httpFormDataParameters, httpParameters);
}
#ifdef IXWEBSOCKET_USE_ZLIB
if (args->compressRequest) {
if (args->compressRequest)
{
body = gzipCompress(body);
}
#endif
@@ -567,8 +614,7 @@ HttpResponsePtr
return request(url, verb, body, args);
}
HttpResponsePtr
HttpClient::post(const std::string &url,
HttpResponsePtr HttpClient::post(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
@@ -583,8 +629,7 @@ HttpResponsePtr HttpClient::post(const std::string &url,
return request(url, kPost, body, args);
}
HttpResponsePtr
HttpClient::put(const std::string &url,
HttpResponsePtr HttpClient::put(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
@@ -599,8 +644,7 @@ HttpResponsePtr HttpClient::put(const std::string &url,
return request(url, kPut, body, args);
}
HttpResponsePtr
HttpClient::patch(const std::string &url,
HttpResponsePtr HttpClient::patch(const std::string& url,
const HttpParameters& httpParameters,
const HttpFormDataParameters& httpFormDataParameters,
HttpRequestArgsPtr args)
@@ -621,12 +665,13 @@ std::string HttpClient::urlEncode(const std::string &value)
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n;
++i) {
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 == '~') {
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
{
escaped << c;
continue;
}
@@ -640,17 +685,18 @@ std::string HttpClient::urlEncode(const std::string &value)
return escaped.str();
}
std::string
HttpClient::serializeHttpParameters(const HttpParameters &httpParameters)
std::string HttpClient::serializeHttpParameters(const HttpParameters& httpParameters)
{
std::stringstream ss;
size_t count = httpParameters.size();
size_t i = 0;
for (auto &&it : httpParameters) {
for (auto&& it : httpParameters)
{
ss << urlEncode(it.first) << "=" << urlEncode(it.second);
if (i++ < (count - 1)) {
if (i++ < (count - 1))
{
ss << "&";
}
}
@@ -676,7 +722,8 @@ std::string HttpClient::serializeHttpFormDataParameters(
//
std::stringstream ss;
for (auto &&it : httpFormDataParameters) {
for (auto&& it : httpFormDataParameters)
{
ss << "--" << multipartBoundary << "\r\n"
<< "Content-Disposition:"
<< " form-data; name=\"" << it.first << "\";"
@@ -688,7 +735,8 @@ std::string HttpClient::serializeHttpFormDataParameters(
<< it.second << "\r\n";
}
for (auto &&it : httpParameters) {
for (auto&& it : httpParameters)
{
ss << "--" << multipartBoundary << "\r\n"
<< "Content-Disposition:"
<< " form-data; name=\"" << it.first << "\";"
@@ -704,15 +752,15 @@ std::string HttpClient::serializeHttpFormDataParameters(
void HttpClient::log(const std::string& msg, HttpRequestArgsPtr args)
{
if (args->logger) {
if (args->logger)
{
args->logger(msg);
}
}
std::string HttpClient::generateMultipartBoundary()
{
std::string str(
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
std::string str("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
static std::random_device rd;
static std::mt19937 generator(rd());
+58 -62
View File
@@ -22,8 +22,7 @@ std::pair<bool, std::vector<uint8_t>> load(const std::string &path)
std::vector<uint8_t> memblock;
std::ifstream file(path);
if (!file.is_open())
return std::make_pair(false, memblock);
if (!file.is_open()) return std::make_pair(false, memblock);
file.seekg(0, file.end);
std::streamoff size = file.tellg();
@@ -53,13 +52,17 @@ HttpServer::HttpServer(int port,
size_t maxConnections,
int addressFamily,
int timeoutSecs)
: SocketServer(port, host, backlog, maxConnections, addressFamily),
_connectedClientsCount(0), _timeoutSecs(timeoutSecs)
: SocketServer(port, host, backlog, maxConnections, addressFamily)
, _connectedClientsCount(0)
, _timeoutSecs(timeoutSecs)
{
setDefaultConnectionCallback();
}
HttpServer::~HttpServer() { stop(); }
HttpServer::~HttpServer()
{
stop();
}
void HttpServer::stop()
{
@@ -75,8 +78,7 @@ void HttpServer::setOnConnectionCallback(const OnConnectionCallback &callback)
_onConnectionCallback = callback;
}
void HttpServer::handleConnection(
std::unique_ptr<Socket> socket,
void HttpServer::handleConnection(std::unique_ptr<Socket> socket,
std::shared_ptr<ConnectionState> connectionState)
{
_connectedClientsCount++;
@@ -84,10 +86,11 @@ void HttpServer::handleConnection(
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)) {
if (std::get<0>(ret))
{
auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
if (!Http::sendResponse(response, socket))
{
logError("Cannot send response");
}
}
@@ -96,16 +99,19 @@ void HttpServer::handleConnection(
_connectedClientsCount--;
}
size_t HttpServer::getConnectedClientsCount() { return _connectedClientsCount; }
size_t HttpServer::getConnectedClientsCount()
{
return _connectedClientsCount;
}
void HttpServer::setDefaultConnectionCallback()
{
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState)
-> HttpResponsePtr {
std::shared_ptr<ConnectionState> connectionState) -> HttpResponsePtr {
std::string uri(request->uri);
if (uri.empty() || uri == "/") {
if (uri.empty() || uri == "/")
{
uri = "/index.html";
}
@@ -115,20 +121,18 @@ void HttpServer::setDefaultConnectionCallback()
std::string path("." + uri);
auto res = readAsString(path);
bool found = res.first;
if (!found) {
return std::make_shared<HttpResponse>(404,
"Not Found",
HttpErrorCode::Ok,
WebSocketHttpHeaders(),
std::string());
if (!found)
{
return std::make_shared<HttpResponse>(
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) {
if (acceptEncoding == "*" || acceptEncoding.find("gzip") != std::string::npos)
{
content = gzipCompress(content);
headers["Content-Encoding"] = "gzip";
}
@@ -136,25 +140,22 @@ void HttpServer::setDefaultConnectionCallback()
// Log request
std::stringstream ss;
ss << connectionState->getRemoteIp() << ":"
<< connectionState->getRemotePort() << " " << request->method
<< " " << request->headers["User-Agent"] << " " << request->uri
<< " " << content.size();
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) {
for (auto&& it : request->headers)
{
headers[it.first] = it.second;
}
return std::make_shared<HttpResponse>(200,
"OK",
HttpErrorCode::Ok,
headers,
content);
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, content);
});
}
@@ -164,34 +165,29 @@ 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> connectionState)
-> HttpResponsePtr {
[this,
redirectUrl](HttpRequestPtr request,
std::shared_ptr<ConnectionState> 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;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
logInfo(ss.str());
if (request->method == "POST") {
return std::make_shared<HttpResponse>(200,
"OK",
HttpErrorCode::Ok,
headers,
std::string());
if (request->method == "POST")
{
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, std::string());
}
headers["Location"] = redirectUrl;
return std::make_shared<HttpResponse>(301,
"OK",
HttpErrorCode::Ok,
headers,
std::string());
return std::make_shared<HttpResponse>(
301, "OK", HttpErrorCode::Ok, headers, std::string());
});
}
@@ -202,20 +198,20 @@ void HttpServer::makeDebugServer()
{
setOnConnectionCallback(
[this](HttpRequestPtr request,
std::shared_ptr<ConnectionState> connectionState)
-> HttpResponsePtr {
std::shared_ptr<ConnectionState> 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;
ss << connectionState->getRemoteIp() << ":" << connectionState->getRemotePort()
<< " " << request->method << " " << request->headers["User-Agent"] << " "
<< request->uri;
logInfo(ss.str());
logInfo("== Headers == ");
for (auto &&it : request->headers) {
for (auto&& it : request->headers)
{
std::ostringstream oss;
oss << it.first << ": " << it.second;
logInfo(oss.str());
@@ -226,14 +222,14 @@ void HttpServer::makeDebugServer()
logInfo(request->body);
logInfo("");
return std::make_shared<HttpResponse>(200,
"OK",
HttpErrorCode::Ok,
headers,
std::string("OK"));
return std::make_shared<HttpResponse>(
200, "OK", HttpErrorCode::Ok, headers, std::string("OK"));
});
}
int HttpServer::getTimeoutSecs() { return _timeoutSecs; }
int HttpServer::getTimeoutSecs()
{
return _timeoutSecs;
}
} // namespace ix
+236 -71
View File
@@ -7,6 +7,15 @@
#include "IXNetSystem.h"
#include <cstdint>
#include <cstdio>
#ifdef _WIN32
#ifndef EAFNOSUPPORT
#define EAFNOSUPPORT 102
#endif
#ifndef ENOSPC
#define ENOSPC 28
#endif
#include <vector>
#endif
namespace ix
{
@@ -37,6 +46,51 @@ bool uninitNetSystem()
#endif
}
#ifdef _WIN32
struct WSAEvent
{
public:
WSAEvent(struct pollfd* fd)
: _fd(fd)
{
_event = WSACreateEvent();
}
WSAEvent(WSAEvent&& source) noexcept
{
_event = source._event;
source._event = WSA_INVALID_EVENT; // invalidate the event in the source
_fd = source._fd;
}
~WSAEvent()
{
if (_event != WSA_INVALID_EVENT)
{
// We must deselect the networkevents from the socket event. Otherwise the
// socket will report states that aren't there.
if (_fd != nullptr && _fd->fd != -1)
WSAEventSelect(_fd->fd, _event, 0);
WSACloseEvent(_event);
}
}
operator HANDLE()
{
return _event;
}
operator struct pollfd*()
{
return _fd;
}
private:
HANDLE _event;
struct pollfd* _fd;
};
#endif
//
// That function could 'return WSAPoll(pfd, nfds, timeout);'
// but WSAPoll is said to have weird behaviors on the internet
@@ -44,28 +98,142 @@ bool uninitNetSystem()
//
// So we make it a select wrapper
//
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
// UPDATE: WSAPoll was fixed in Windows 10 Version 2004
//
// The optional "event" is set to nullptr if it wasn't signaled.
int poll(struct pollfd* fds, nfds_t nfds, int timeout, void** event)
{
#ifdef _WIN32
if (event && *event)
{
HANDLE interruptEvent = reinterpret_cast<HANDLE>(*event);
*event = nullptr; // the event wasn't signaled yet
if (nfds < 0 || nfds >= MAXIMUM_WAIT_OBJECTS - 1)
{
WSASetLastError(WSAEINVAL);
return SOCKET_ERROR;
}
std::vector<WSAEvent> socketEvents;
std::vector<HANDLE> handles;
// put the interrupt event as first element, making it highest priority
handles.push_back(interruptEvent);
// create the WSAEvents for the sockets
for (nfds_t i = 0; i < nfds; ++i)
{
struct pollfd* fd = &fds[i];
fd->revents = 0;
if (fd->fd >= 0)
{
// create WSAEvent and add it to the vectors
socketEvents.push_back(std::move(WSAEvent(fd)));
HANDLE handle = socketEvents.back();
if (handle == WSA_INVALID_EVENT)
{
WSASetLastError(WSAENOBUFS);
return SOCKET_ERROR;
}
handles.push_back(handle);
// mapping
long networkEvents = 0;
if (fd->events & (POLLIN )) networkEvents |= FD_READ | FD_ACCEPT;
if (fd->events & (POLLOUT /*| POLLWRNORM | POLLWRBAND*/)) networkEvents |= FD_WRITE | FD_CONNECT;
//if (fd->events & (POLLPRI | POLLRDBAND )) networkEvents |= FD_OOB;
if (WSAEventSelect(fd->fd, handle, networkEvents) != 0)
{
fd->revents = POLLNVAL;
socketEvents.pop_back();
handles.pop_back();
}
}
}
DWORD n = WSAWaitForMultipleEvents(handles.size(), handles.data(), FALSE, timeout != -1 ? static_cast<DWORD>(timeout) : WSA_INFINITE, FALSE);
if (n == WSA_WAIT_FAILED) return SOCKET_ERROR;
if (n == WSA_WAIT_TIMEOUT) return 0;
if (n == WSA_WAIT_EVENT_0)
{
// the interrupt event was signaled
*event = reinterpret_cast<void*>(interruptEvent);
return 1;
}
int handleIndex = n - WSA_WAIT_EVENT_0;
int socketIndex = handleIndex - 1;
WSANETWORKEVENTS netEvents;
int count = 0;
// WSAWaitForMultipleEvents returns the index of the first signaled event. And to emulate WSAPoll()
// all the signaled events must be processed.
while (socketIndex < socketEvents.size())
{
struct pollfd* fd = socketEvents[socketIndex];
memset(&netEvents, 0, sizeof(netEvents));
if (WSAEnumNetworkEvents(fd->fd, socketEvents[socketIndex], &netEvents) != 0)
{
fd->revents = POLLERR;
}
else if (netEvents.lNetworkEvents != 0)
{
// mapping
if (netEvents.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_OOB)) fd->revents |= POLLIN;
if (netEvents.lNetworkEvents & (FD_WRITE | FD_CONNECT )) fd->revents |= POLLOUT;
for (int i = 0; i < FD_MAX_EVENTS; ++i)
{
if (netEvents.iErrorCode[i] != 0)
{
fd->revents |= POLLERR;
break;
}
}
if (fd->revents != 0)
{
// only signaled sockets count
count++;
}
}
socketIndex++;
}
return count;
}
else
{
if (event && *event) *event = nullptr;
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) {
for (nfds_t i = 0; i < nfds; ++i)
{
struct pollfd* fd = &fds[i];
if (fd->fd > maxfd) {
if (fd->fd > maxfd)
{
maxfd = fd->fd;
}
if ((fd->events & POLLIN)) {
if ((fd->events & POLLIN))
{
FD_SET(fd->fd, &readfds);
}
if ((fd->events & POLLOUT)) {
if ((fd->events & POLLOUT))
{
FD_SET(fd->fd, &writefds);
}
if ((fd->events & POLLERR)) {
if ((fd->events & POLLERR))
{
FD_SET(fd->fd, &errorfds);
}
}
@@ -74,33 +242,36 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
int ret = select(maxfd + 1,
&readfds,
&writefds,
&errorfds,
timeout != -1 ? &tv : NULL);
int ret = select(maxfd + 1, &readfds, &writefds, &errorfds, timeout != -1 ? &tv : NULL);
if (ret < 0) {
if (ret < 0)
{
return ret;
}
for (nfds_t i = 0; i < nfds; ++i) {
for (nfds_t i = 0; i < nfds; ++i)
{
struct pollfd* fd = &fds[i];
fd->revents = 0;
if (FD_ISSET(fd->fd, &readfds)) {
if (FD_ISSET(fd->fd, &readfds))
{
fd->revents |= POLLIN;
}
if (FD_ISSET(fd->fd, &writefds)) {
if (FD_ISSET(fd->fd, &writefds))
{
fd->revents |= POLLOUT;
}
if (FD_ISSET(fd->fd, &errorfds)) {
if (FD_ISSET(fd->fd, &errorfds))
{
fd->revents |= POLLERR;
}
}
return ret;
}
#else
if (event && *event) *event = nullptr;
//
// It was reported that on Android poll can fail and return -1 with
// errno == EINTR, which should be a temp error and should typically
@@ -111,7 +282,8 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
// The style from libuv is as such.
//
int ret = -1;
do {
do
{
ret = ::poll(fds, nfds, timeout);
} while (ret == -1 && errno == EINTR);
@@ -120,8 +292,7 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
}
//
// mingw does not have inet_ntop, which were taken as is from the musl C
// library.
// 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)
{
@@ -130,10 +301,10 @@ const char *inet_ntop(int af, const void *a0, char *s, socklen_t l)
int i, j, max, best;
char buf[100];
switch (af) {
switch (af)
{
case AF_INET:
if (snprintf(s, l, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]) < l)
return s;
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))
@@ -163,25 +334,24 @@ const char *inet_ntop(int af, const void *a0, char *s, socklen_t l)
a[14],
a[15]);
/* Replace longest /(^0|:)[:0]{2,}/ with "::" */
for (i = best = 0, max = 2; buf[i]; i++) {
if (i && buf[i] != ':')
continue;
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 (j > max) best = i, max = j;
}
if (max > 3) {
if (max > 3)
{
buf[best] = buf[best + 1] = ':';
memmove(buf + best + 2, buf + best + max, i - best - max + 1);
}
if (strlen(buf) < l) {
if (strlen(buf) < l)
{
strcpy(s, buf);
return s;
}
break;
default:
errno = EAFNOSUPPORT;
return 0;
default: errno = EAFNOSUPPORT; return 0;
}
errno = ENOSPC;
return 0;
@@ -193,18 +363,15 @@ const char *inet_ntop(int af, const void *a0, char *s, socklen_t l)
#if defined(_WIN32) && defined(__GNUC__)
static int hexval(unsigned c)
{
if (c - '0' < 10)
return c - '0';
if (c - '0' < 10) return c - '0';
c |= 32;
if (c - 'a' < 6)
return c - 'a' + 10;
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.
// 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)
{
@@ -213,67 +380,65 @@ int inet_pton(int af, const char *s, void *a0)
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++) {
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;
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;
if (s[j] == 0 && i == 3) return 1;
if (s[j] != '.') return 0;
s += j + 1;
}
return 0;
} else if (af != AF_INET6) {
}
else if (af != AF_INET6)
{
errno = EAFNOSUPPORT;
return -1;
}
if (*s == ':' && *++s != ':')
return 0;
if (*s == ':' && *++s != ':') return 0;
for (i = 0;; i++) {
if (s[0] == ':' && brk < 0) {
for (i = 0;; i++)
{
if (s[0] == ':' && brk < 0)
{
brk = i;
ip[i & 7] = 0;
if (!*++s)
break;
if (i == 7)
return 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;
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;
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) {
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++) {
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;
if (need_v4 && inet_pton(AF_INET, (const char*) s, a - 4) <= 0) return 0;
return 1;
#else
return ::inet_pton(af, s, a0);
@@ -285,8 +450,8 @@ unsigned short network_to_host_short(unsigned short value)
{
#if defined(_WIN32)
unsigned char* value_p = reinterpret_cast<unsigned char*>(&value);
unsigned short result = (static_cast<unsigned short>(value_p[0]) << 8) |
static_cast<unsigned short>(value_p[1]);
unsigned short result = (static_cast<unsigned short>(value_p[0]) << 8)
| static_cast<unsigned short>(value_p[1]);
return result;
#else // defined(_WIN32)
return ntohs(value);
+33 -7
View File
@@ -11,17 +11,43 @@ namespace ix
const uint64_t SelectInterrupt::kSendRequest = 1;
const uint64_t SelectInterrupt::kCloseRequest = 2;
SelectInterrupt::SelectInterrupt() { ; }
SelectInterrupt::SelectInterrupt()
{
;
}
SelectInterrupt::~SelectInterrupt() { ; }
SelectInterrupt::~SelectInterrupt()
{
;
}
bool SelectInterrupt::init(std::string & /*errorMsg*/) { return true; }
bool SelectInterrupt::init(std::string& /*errorMsg*/)
{
return true;
}
bool SelectInterrupt::notify(uint64_t /*value*/) { return true; }
bool SelectInterrupt::notify(uint64_t /*value*/)
{
return true;
}
uint64_t SelectInterrupt::read() { return 0; }
uint64_t SelectInterrupt::read()
{
return 0;
}
bool SelectInterrupt::clear() { return true; }
bool SelectInterrupt::clear()
{
return true;
}
int SelectInterrupt::getFd() const { return -1; }
int SelectInterrupt::getFd() const
{
return -1;
}
void* SelectInterrupt::getEvent() const
{
return nullptr;
}
} // namespace ix
+85
View File
@@ -0,0 +1,85 @@
/*
* IXSelectInterruptEvent.cpp
*/
//
// On Windows we use a Windows Event to wake up ix::poll() (WSAWaitForMultipleEvents).
// And on any other platform that doesn't support pipe file descriptors we
// emulate the interrupt event by using a short timeout with ix::poll() and
// read from the SelectInterrupt. (see Socket::poll() "Emulation mode")
//
#include <algorithm>
#include "IXSelectInterruptEvent.h"
namespace ix
{
SelectInterruptEvent::SelectInterruptEvent()
{
#ifdef _WIN32
_event = CreateEvent(NULL, TRUE, FALSE, NULL);
#endif
}
SelectInterruptEvent::~SelectInterruptEvent()
{
#ifdef _WIN32
CloseHandle(_event);
#endif
}
bool SelectInterruptEvent::init(std::string& /*errorMsg*/)
{
return true;
}
bool SelectInterruptEvent::notify(uint64_t value)
{
std::lock_guard<std::mutex> lock(_valuesMutex);
// WebSocket implementation detail: We only need one of the values in the queue
if (std::find(_values.begin(), _values.end(), value) == _values.end())
_values.push_back(value);
#ifdef _WIN32
SetEvent(_event); // wake up
#endif
return true;
}
uint64_t SelectInterruptEvent::read()
{
std::lock_guard<std::mutex> lock(_valuesMutex);
if (_values.size() > 0)
{
uint64_t value = _values.front();
_values.pop_front();
#ifdef _WIN32
// signal the event if there is still data in the queue
if (_values.size() == 0)
ResetEvent(_event);
#endif
return value;
}
return 0;
}
bool SelectInterruptEvent::clear()
{
std::lock_guard<std::mutex> lock(_valuesMutex);
_values.clear();
#ifdef _WIN32
ResetEvent(_event);
#endif
return true;
}
void* SelectInterruptEvent::getEvent() const
{
#ifdef _WIN32
return reinterpret_cast<void*>(_event);
#else
return nullptr;
#endif
}
} // namespace ix
+6 -6
View File
@@ -7,20 +7,20 @@
#include "IXSelectInterruptFactory.h"
#include "IXUniquePtr.h"
#if defined(__linux__) || defined(__APPLE__)
#include "IXSelectInterruptPipe.h"
#if _WIN32
#include "IXSelectInterruptEvent.h"
#else
#include "IXSelectInterrupt.h"
#include "IXSelectInterruptPipe.h"
#endif
namespace ix
{
SelectInterruptPtr createSelectInterrupt()
{
#if defined(__linux__) || defined(__APPLE__)
return ix::make_unique<SelectInterruptPipe>();
#ifdef _WIN32
return ix::make_unique<SelectInterruptEvent>();
#else
return ix::make_unique<SelectInterrupt>();
return ix::make_unique<SelectInterruptPipe>();
#endif
}
} // namespace ix
+23 -18
View File
@@ -48,7 +48,8 @@ bool SelectInterruptPipe::init(std::string &errorMsg)
assert(_fildes[kPipeReadIndex] == -1);
assert(_fildes[kPipeWriteIndex] == -1);
if (pipe(_fildes) < 0) {
if (pipe(_fildes) < 0)
{
std::stringstream ss;
ss << "SelectInterruptPipe::init() failed in pipe() call"
<< " : " << strerror(errno);
@@ -56,10 +57,10 @@ bool SelectInterruptPipe::init(std::string &errorMsg)
return false;
}
if (fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK) == -1) {
if (fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK) == -1)
{
std::stringstream ss;
ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) "
"call"
ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
<< " : " << strerror(errno);
errorMsg = ss.str();
@@ -68,10 +69,10 @@ bool SelectInterruptPipe::init(std::string &errorMsg)
return false;
}
if (fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK) == -1) {
if (fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK) == -1)
{
std::stringstream ss;
ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) "
"call"
ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
<< " : " << strerror(errno);
errorMsg = ss.str();
@@ -81,10 +82,10 @@ bool SelectInterruptPipe::init(std::string &errorMsg)
}
#ifdef F_SETNOSIGPIPE
if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1) {
if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
{
std::stringstream ss;
ss << "SelectInterruptPipe::init() failed in fcntl(.... "
"F_SETNOSIGPIPE) call"
ss << "SelectInterruptPipe::init() failed in fcntl(.... F_SETNOSIGPIPE) call"
<< " : " << strerror(errno);
errorMsg = ss.str();
@@ -93,10 +94,10 @@ bool SelectInterruptPipe::init(std::string &errorMsg)
return false;
}
if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1) {
if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
{
std::stringstream ss;
ss << "SelectInterruptPipe::init() failed in fcntl(..., "
"F_SETNOSIGPIPE) call"
ss << "SelectInterruptPipe::init() failed in fcntl(..., F_SETNOSIGPIPE) call"
<< " : " << strerror(errno);
errorMsg = ss.str();
@@ -114,11 +115,11 @@ bool SelectInterruptPipe::notify(uint64_t value)
std::lock_guard<std::mutex> lock(_fildesMutex);
int fd = _fildes[kPipeWriteIndex];
if (fd == -1)
return false;
if (fd == -1) return false;
ssize_t ret = -1;
do {
do
{
ret = ::write(fd, &value, sizeof(value));
} while (ret == -1 && errno == EINTR);
@@ -136,14 +137,18 @@ uint64_t SelectInterruptPipe::read()
uint64_t value = 0;
ssize_t ret = -1;
do {
do
{
ret = ::read(fd, &value, sizeof(value));
} while (ret == -1 && errno == EINTR);
return value;
}
bool SelectInterruptPipe::clear() { return true; }
bool SelectInterruptPipe::clear()
{
return true;
}
int SelectInterruptPipe::getFd() const
{
+9 -7
View File
@@ -26,7 +26,8 @@ namespace ix
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO {
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).
@@ -43,12 +44,13 @@ void SetThreadName(DWORD dwThreadID, const char *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) {
__try
{
RaiseException(
MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
#endif
}
+158 -78
View File
@@ -30,27 +30,33 @@ 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(int fd)
: _sockfd(fd)
, _selectInterrupt(createSelectInterrupt())
{
;
}
Socket::~Socket() { close(); }
Socket::~Socket()
{
close();
}
PollResultType Socket::poll(bool readyToRead,
int timeoutMs,
int sockfd,
const SelectInterruptPtr& selectInterrupt)
{
PollResultType pollResult = PollResultType::ReadyForRead;
//
// 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.
// ::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.
// https://github.com/mpv-player/mpv/pull/5203/files for such a select wrapper.
//
nfds_t nfds = 1;
struct pollfd fds[2];
@@ -59,46 +65,70 @@ PollResultType Socket::poll(bool readyToRead,
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
// 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) {
void* interruptEvent = nullptr;
if (selectInterrupt)
{
interruptFd = selectInterrupt->getFd();
interruptEvent = selectInterrupt->getEvent();
if (interruptFd != -1) {
if (interruptFd != -1)
{
nfds = 2;
fds[1].fd = interruptFd;
fds[1].events = POLLIN;
}
else if (interruptEvent == nullptr)
{
// Emulation mode: SelectInterrupt neither supports file descriptors nor events
// Check the selectInterrupt for requests before doing the poll().
if (readSelectInterruptRequest(selectInterrupt, &pollResult))
{
return pollResult;
}
}
}
int ret = ix::poll(fds, nfds, timeoutMs);
void* event = interruptEvent; // ix::poll will set event to nullptr if it wasn't signaled
int ret = ix::poll(fds, nfds, timeoutMs, &event);
PollResultType pollResult = PollResultType::ReadyForRead;
if (ret < 0) {
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) {
else if (ret == 0)
{
pollResult = PollResultType::Timeout;
if (selectInterrupt && interruptFd == -1 && interruptEvent == nullptr)
{
// Emulation mode: SelectInterrupt neither supports fd nor events
// Check the selectInterrupt for requests
readSelectInterruptRequest(selectInterrupt, &pollResult);
}
}
else if ((interruptFd != -1 && fds[1].revents & POLLIN) || (interruptEvent != nullptr && event != nullptr))
{
// The InterruptEvent was signaled
readSelectInterruptRequest(selectInterrupt, &pollResult);
}
else if (sockfd != -1 && readyToRead && fds[0].revents & POLLIN)
{
pollResult = PollResultType::ReadyForRead;
} else if (sockfd != -1 && !readyToRead && fds[0].revents & POLLOUT) {
}
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) {
// On connect error, in async mode, windows will write to the exceptions fds
if (fds[0].revents & POLLERR)
{
pollResult = PollResultType::Error;
}
#else
@@ -107,8 +137,8 @@ PollResultType Socket::poll(bool readyToRead,
// 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) {
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
@@ -116,18 +146,39 @@ PollResultType Socket::poll(bool readyToRead,
errno = optval;
}
#endif
} else if (sockfd != -1 &&
(fds[0].revents & POLLERR || fds[0].revents & POLLHUP ||
fds[0].revents & POLLNVAL)) {
}
else if (sockfd != -1 && (fds[0].revents & POLLERR || fds[0].revents & POLLHUP ||
fds[0].revents & POLLNVAL))
{
pollResult = PollResultType::Error;
}
return pollResult;
}
bool Socket::readSelectInterruptRequest(const SelectInterruptPtr& selectInterrupt,
PollResultType* pollResult)
{
uint64_t value = selectInterrupt->read();
if (value == SelectInterrupt::kSendRequest)
{
*pollResult = PollResultType::SendRequest;
return true;
}
else if (value == SelectInterrupt::kCloseRequest)
{
*pollResult = PollResultType::CloseRequest;
return true;
}
return false;
}
PollResultType Socket::isReadyToRead(int timeoutMs)
{
if (_sockfd == -1) {
if (_sockfd == -1)
{
return PollResultType::Error;
}
@@ -137,7 +188,8 @@ PollResultType Socket::isReadyToRead(int timeoutMs)
PollResultType Socket::isReadyToWrite(int timeoutMs)
{
if (_sockfd == -1) {
if (_sockfd == -1)
{
return PollResultType::Error;
}
@@ -151,9 +203,15 @@ bool Socket::wakeUpFromPoll(uint64_t wakeUpCode)
return _selectInterrupt->notify(wakeUpCode);
}
bool Socket::isWakeUpFromPollSupported()
{
return _selectInterrupt->getFd() != -1 || _selectInterrupt->getEvent() != nullptr;
}
bool Socket::accept(std::string& errMsg)
{
if (_sockfd == -1) {
if (_sockfd == -1)
{
errMsg = "Socket is uninitialized";
return false;
}
@@ -167,11 +225,9 @@ bool Socket::connect(const std::string &host,
{
std::lock_guard<std::mutex> lock(_socketMutex);
if (!_selectInterrupt->clear())
return false;
if (!_selectInterrupt->clear()) return false;
_sockfd =
SocketConnect::connect(host, port, errMsg, isCancellationRequested);
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
return _sockfd != -1;
}
@@ -179,8 +235,7 @@ void Socket::close()
{
std::lock_guard<std::mutex> lock(_socketMutex);
if (_sockfd == -1)
return;
if (_sockfd == -1) return;
closeSocket(_sockfd);
_sockfd = -1;
@@ -228,7 +283,8 @@ bool Socket::isWaitNeeded()
{
int err = getErrno();
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS) {
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS)
{
return true;
}
@@ -255,72 +311,82 @@ bool Socket::writeBytes(const std::string &str,
int offset = 0;
int len = (int) str.size();
while (true) {
if (isCancellationRequested && isCancellationRequested())
return false;
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) {
if (ret > 0)
{
if (ret == len)
{
return true;
} else {
}
else
{
offset += ret;
len -= ret;
continue;
}
}
// There is possibly something to be writen, try again
else if (ret < 0 && Socket::isWaitNeeded()) {
else if (ret < 0 && Socket::isWaitNeeded())
{
continue;
}
// There was an error during the write, abort
else {
else
{
return false;
}
}
}
bool Socket::readByte(void *buffer,
const CancellationRequest &isCancellationRequested)
bool Socket::readByte(void* buffer, const CancellationRequest& isCancellationRequested)
{
while (true) {
if (isCancellationRequested && isCancellationRequested())
return false;
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) {
if (ret == 1)
{
return true;
}
// There is possibly something to be read, try again
else if (ret < 0 && Socket::isWaitNeeded()) {
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) {
if (isReadyToRead(1) == PollResultType::Error)
{
return false;
}
}
// There was an error during the read, abort
else {
else
{
return false;
}
}
}
std::pair<bool, std::string>
Socket::readLine(const CancellationRequest &isCancellationRequested)
std::pair<bool, std::string> 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)) {
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);
}
@@ -331,38 +397,52 @@ std::pair<bool, std::string>
return std::make_pair(true, line);
}
std::pair<bool, std::string>
Socket::readBytes(size_t length,
std::pair<bool, std::string> Socket::readBytes(
size_t length,
const OnProgressCallback& onProgressCallback,
const OnChunkCallback& onChunkCallback,
const CancellationRequest& isCancellationRequested)
{
std::array<uint8_t, 1 << 14> readBuffer;
std::vector<uint8_t> output;
while (output.size() != length) {
if (isCancellationRequested && isCancellationRequested()) {
size_t bytesRead = 0;
while (bytesRead != 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());
size_t size = std::min(readBuffer.size(), length - bytesRead);
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()) {
if (ret > 0)
{
if (onChunkCallback)
{
std::string chunk(readBuffer.begin(), readBuffer.begin() + ret);
onChunkCallback(chunk);
}
else
{
output.insert(output.end(), readBuffer.begin(), readBuffer.begin() + ret);
}
bytesRead += 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);
if (onProgressCallback) onProgressCallback((int) bytesRead, (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) {
if (isReadyToRead(1) == PollResultType::Error)
{
const std::string errorMsg("Poll Error");
return std::make_pair(false, errorMsg);
}
+93 -87
View File
@@ -29,31 +29,33 @@
namespace ix
{
SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd), _sslContext(nullptr), _tlsOptions(tlsOptions)
: Socket(fd)
, _sslContext(nullptr)
, _tlsOptions(tlsOptions)
{
;
}
SocketAppleSSL::~SocketAppleSSL() { SocketAppleSSL::close(); }
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) {
CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
if (error)
{
CFStringRef message = CFErrorCopyDescription(error);
if (message) {
if (message)
{
char localBuffer[128];
Boolean success;
success = CFStringGetCString(message,
localBuffer,
128,
kCFStringEncodingUTF8);
if (success) {
success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8);
if (success)
{
errMsg = localBuffer;
}
CFRelease(message);
@@ -64,13 +66,10 @@ std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status)
return errMsg;
}
OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection,
void *data,
size_t *len)
OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection, void* data, size_t* len)
{
int fd = (int) (long) connection;
if (fd < 0)
return errSSLInternal;
if (fd < 0) return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
@@ -79,33 +78,36 @@ OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection,
ssize_t status = read(fd, data, requested_sz);
if (status > 0) {
if (status > 0)
{
*len = (size_t) status;
if (requested_sz > *len) {
if (requested_sz > *len)
{
return errSSLWouldBlock;
} else {
}
else
{
return noErr;
}
} else if (status == 0) {
}
else if (status == 0)
{
*len = 0;
return errSSLClosedGraceful;
} else {
}
else
{
*len = 0;
switch (errno) {
case ENOENT:
return errSSLClosedGraceful;
switch (errno)
{
case ENOENT: return errSSLClosedGraceful;
case EAGAIN:
return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN
// on osx
case EINPROGRESS:
return errSSLWouldBlock;
case EAGAIN: return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN on osx
case EINPROGRESS: return errSSLWouldBlock;
case ECONNRESET:
return errSSLClosedAbort;
case ECONNRESET: return errSSLClosedAbort;
default:
return errSecIO;
default: return errSecIO;
}
}
}
@@ -115,8 +117,7 @@ OSStatus SocketAppleSSL::writeToSocket(SSLConnectionRef connection,
size_t* len)
{
int fd = (int) (long) connection;
if (fd < 0)
return errSSLInternal;
if (fd < 0) return errSSLInternal;
assert(data != nullptr);
assert(len != nullptr);
@@ -124,33 +125,36 @@ OSStatus SocketAppleSSL::writeToSocket(SSLConnectionRef connection,
size_t to_write_sz = *len;
ssize_t status = write(fd, data, to_write_sz);
if (status > 0) {
if (status > 0)
{
*len = (size_t) status;
if (to_write_sz > *len) {
if (to_write_sz > *len)
{
return errSSLWouldBlock;
} else {
}
else
{
return noErr;
}
} else if (status == 0) {
}
else if (status == 0)
{
*len = 0;
return errSSLClosedGraceful;
} else {
}
else
{
*len = 0;
switch (errno) {
case ENOENT:
return errSSLClosedGraceful;
switch (errno)
{
case ENOENT: return errSSLClosedGraceful;
case EAGAIN:
return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN
// on osx
case EINPROGRESS:
return errSSLWouldBlock;
case EAGAIN: return errSSLWouldBlock; // EWOULDBLOCK is a define for EAGAIN on osx
case EINPROGRESS: return errSSLWouldBlock;
case ECONNRESET:
return errSSLClosedAbort;
case ECONNRESET: return errSSLClosedAbort;
default:
return errSecIO;
default: return errSecIO;
}
}
}
@@ -162,17 +166,18 @@ bool SocketAppleSSL::accept(std::string &errMsg)
return false;
}
OSStatus SocketAppleSSL::tlsHandShake(
std::string &errMsg,
OSStatus SocketAppleSSL::tlsHandShake(std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
OSStatus status;
do {
do
{
status = SSLHandshake(_sslContext);
// Interrupt the handshake
if (isCancellationRequested()) {
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
return errSSLInternal;
}
@@ -191,40 +196,38 @@ bool SocketAppleSSL::connect(const std::string &host,
{
std::lock_guard<std::mutex> lock(_mutex);
_sockfd =
SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1)
return false;
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1) return false;
_sslContext = SSLCreateContext(kCFAllocatorDefault,
kSSLClientSide,
kSSLStreamType);
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
SSLSetIOFuncs(_sslContext,
SocketAppleSSL::readFromSocket,
SocketAppleSSL::writeToSocket);
SSLSetIOFuncs(
_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
if (_tlsOptions.isPeerVerifyDisabled()) {
if (_tlsOptions.isPeerVerifyDisabled())
{
Boolean option(1);
SSLSetSessionOption(_sslContext,
kSSLSessionOptionBreakOnServerAuth,
option);
SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option);
status = tlsHandShake(errMsg, isCancellationRequested);
if (status == errSSLServerAuthCompleted) {
if (status == errSSLServerAuthCompleted)
{
// proceed with the handshake
status = tlsHandShake(errMsg, isCancellationRequested);
}
} else {
}
else
{
status = tlsHandShake(errMsg, isCancellationRequested);
}
}
if (status != noErr) {
if (status != noErr)
{
errMsg = getSSLErrorDescription(status);
close();
return false;
@@ -237,8 +240,7 @@ void SocketAppleSSL::close()
{
std::lock_guard<std::mutex> lock(_mutex);
if (_sslContext == nullptr)
return;
if (_sslContext == nullptr) return;
SSLClose(_sslContext);
CFRelease(_sslContext);
@@ -250,23 +252,25 @@ void SocketAppleSSL::close()
ssize_t SocketAppleSSL::send(char* buf, size_t nbyte)
{
OSStatus status = errSSLWouldBlock;
while (status == errSSLWouldBlock) {
while (status == errSSLWouldBlock)
{
size_t processed = 0;
std::lock_guard<std::mutex> lock(_mutex);
status = SSLWrite(_sslContext, buf, nbyte, &processed);
if (processed > 0)
return (ssize_t)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) {
status == errSSLClosedAbort)
{
errno = ECONNRESET;
return -1;
}
if (status == errSSLWouldBlock) {
if (status == errSSLWouldBlock)
{
errno = EWOULDBLOCK;
return -1;
}
@@ -278,23 +282,25 @@ ssize_t SocketAppleSSL::send(char *buf, size_t nbyte)
ssize_t SocketAppleSSL::recv(void* buf, size_t nbyte)
{
OSStatus status = errSSLWouldBlock;
while (status == errSSLWouldBlock) {
while (status == errSSLWouldBlock)
{
size_t processed = 0;
std::lock_guard<std::mutex> lock(_mutex);
status = SSLRead(_sslContext, buf, nbyte, &processed);
if (processed > 0)
return (ssize_t)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) {
status == errSSLClosedAbort)
{
errno = ECONNRESET;
return -1;
}
if (status == errSSLWouldBlock) {
if (status == errSSLWouldBlock)
{
errno = EWOULDBLOCK;
return -1;
}
+34 -32
View File
@@ -20,26 +20,25 @@
#include <linux/in.h>
#include <linux/tcp.h>
#endif
#include "IXSelectInterruptFactory.h"
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
// 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,
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) {
socket_t fd = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
if (fd < 0)
{
errMsg = "Cannot create a socket";
return -1;
}
@@ -50,15 +49,16 @@ int SocketConnect::connectToAddress(
int res = ::connect(fd, address->ai_addr, address->ai_addrlen);
if (res == -1 && !Socket::isWaitNeeded()) {
if (res == -1 && !Socket::isWaitNeeded())
{
errMsg = strerror(Socket::getErrno());
Socket::closeSocket(fd);
return -1;
}
for (;;) {
if (isCancellationRequested &&
isCancellationRequested()) // Must handle timeout as well
for (;;)
{
if (isCancellationRequested && isCancellationRequested()) // Must handle timeout as well
{
Socket::closeSocket(fd);
errMsg = "Cancelled";
@@ -67,30 +67,30 @@ int SocketConnect::connectToAddress(
int timeoutMs = 10;
bool readyToRead = false;
auto selectInterrupt = ix::make_unique<SelectInterrupt>();
PollResultType pollResult =
Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
SelectInterruptPtr selectInterrupt = ix::createSelectInterrupt();
PollResultType pollResult = Socket::poll(readyToRead, timeoutMs, fd, selectInterrupt);
if (pollResult == PollResultType::Timeout) {
if (pollResult == PollResultType::Timeout)
{
continue;
} else if (pollResult == PollResultType::Error) {
}
else if (pollResult == PollResultType::Error)
{
Socket::closeSocket(fd);
errMsg =
std::string("Connect error: ") + strerror(Socket::getErrno());
errMsg = std::string("Connect error: ") + strerror(Socket::getErrno());
return -1;
} else if (pollResult == PollResultType::ReadyForWrite) {
}
else if (pollResult == PollResultType::ReadyForWrite)
{
return fd;
} else {
}
else
{
Socket::closeSocket(fd);
errMsg =
std::string("Connect error: ") + strerror(Socket::getErrno());
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,
@@ -103,7 +103,8 @@ int SocketConnect::connect(const std::string &hostname,
//
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
if (res == nullptr) {
if (res == nullptr)
{
return -1;
}
@@ -111,12 +112,14 @@ int SocketConnect::connect(const std::string &hostname,
// iterate through the records to find a working peer
struct addrinfo* address;
for (address = res; address != nullptr; address = address->ai_next) {
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) {
if (sockfd != -1)
{
break;
}
}
@@ -140,8 +143,7 @@ void SocketConnect::configure(int sockfd)
fcntl(sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking
#endif
// 3. (apple) prevent SIGPIPE from being emitted when the remote end
// disconnect
// 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));
+7 -3
View File
@@ -34,9 +34,12 @@ std::unique_ptr<Socket> createSocket(bool tls,
errorMsg.clear();
std::unique_ptr<Socket> socket;
if (!tls) {
if (!tls)
{
socket = ix::make_unique<Socket>(fd);
} else {
}
else
{
#ifdef IXWEBSOCKET_USE_TLS
#if defined(IXWEBSOCKET_USE_MBED_TLS)
socket = ix::make_unique<SocketMbedTLS>(tlsOptions, fd);
@@ -51,7 +54,8 @@ std::unique_ptr<Socket> createSocket(bool tls,
#endif
}
if (!socket->init(errorMsg)) {
if (!socket->init(errorMsg))
{
socket.reset();
}
+95 -84
View File
@@ -24,12 +24,16 @@
namespace ix
{
SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd), _tlsOptions(tlsOptions)
: Socket(fd)
, _tlsOptions(tlsOptions)
{
initMBedTLS();
}
SocketMbedTLS::~SocketMbedTLS() { SocketMbedTLS::close(); }
SocketMbedTLS::~SocketMbedTLS()
{
SocketMbedTLS::close();
}
void SocketMbedTLS::initMBedTLS()
{
@@ -49,10 +53,10 @@ 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");
HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root");
if (!systemStore) {
if (!systemStore)
{
errorMsg = "CertOpenStore failed with ";
errorMsg += std::to_string(GetLastError());
return false;
@@ -61,14 +65,15 @@ bool SocketMbedTLS::loadSystemCertificates(std::string &errorMsg)
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,
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) {
if (ret == 0)
{
++certificateCount;
}
}
@@ -77,7 +82,8 @@ bool SocketMbedTLS::loadSystemCertificates(std::string &errorMsg)
CertFreeCertificateContext(certificateIterator);
CertCloseStore(systemStore, 0);
if (certificateCount == 0) {
if (certificateCount == 0)
{
errorMsg = "No certificates found";
return false;
}
@@ -91,9 +97,7 @@ bool SocketMbedTLS::loadSystemCertificates(std::string &errorMsg)
#endif
}
bool SocketMbedTLS::init(const std::string &host,
bool isClient,
std::string &errMsg)
bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg)
{
initMBedTLS();
std::lock_guard<std::mutex> lock(_mutex);
@@ -104,73 +108,79 @@ bool SocketMbedTLS::init(const std::string &host,
mbedtls_entropy_func,
&_entropy,
(const unsigned char*) pers,
strlen(pers)) != 0) {
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,
(isClient) ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
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) {
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)
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)
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) {
if (mbedtls_ssl_conf_own_cert(&_conf, &_cert, &_pkey) < 0)
{
errMsg = "Problem configuring cert '" + _tlsOptions.certFile + "'";
return false;
}
}
if (_tlsOptions.isPeerVerifyDisabled()) {
if (_tlsOptions.isPeerVerifyDisabled())
{
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_NONE);
} else {
}
else
{
// FIXME: should we call mbedtls_ssl_conf_verify ?
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
if (_tlsOptions.isUsingSystemDefaults()) {
if (!loadSystemCertificates(errMsg)) {
if (_tlsOptions.isUsingSystemDefaults())
{
if (!loadSystemCertificates(errMsg))
{
return false;
}
} else {
if (_tlsOptions.isUsingInMemoryCAs()) {
}
else
{
if (_tlsOptions.isUsingInMemoryCAs())
{
const char* buffer = _tlsOptions.caFile.c_str();
size_t bufferSize = _tlsOptions.caFile.size() +
1; // Needs to include null terminating
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) {
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) {
}
else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0)
{
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
return false;
}
@@ -179,12 +189,14 @@ bool SocketMbedTLS::init(const std::string &host,
mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
}
if (mbedtls_ssl_setup(&_ssl, &_conf) != 0) {
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) {
if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
{
errMsg = "SNI setup failed";
return false;
}
@@ -196,39 +208,35 @@ bool SocketMbedTLS::accept(std::string &errMsg)
{
bool isClient = false;
bool initialized = init(std::string(), isClient, errMsg);
if (!initialized) {
if (!initialized)
{
close();
return false;
}
mbedtls_ssl_set_bio(&_ssl,
&_sockfd,
mbedtls_net_send,
mbedtls_net_recv,
NULL);
mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
int res;
do {
do
{
std::lock_guard<std::mutex> lock(_mutex);
res = mbedtls_ssl_handshake(&_ssl);
} while (res == MBEDTLS_ERR_SSL_WANT_READ ||
res == MBEDTLS_ERR_SSL_WANT_WRITE);
} while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
if (res != 0) {
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) {
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);
mbedtls_x509_crt_verify_info(verifyBuf, sizeof(verifyBuf), " ! ", flags);
errMsg += " : ";
errMsg += verifyBuf;
}
@@ -247,41 +255,38 @@ bool SocketMbedTLS::connect(const std::string &host,
{
{
std::lock_guard<std::mutex> lock(_mutex);
_sockfd =
SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1)
return false;
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1) return false;
}
bool isClient = true;
bool initialized = init(host, isClient, errMsg);
if (!initialized) {
if (!initialized)
{
close();
return false;
}
mbedtls_ssl_set_bio(&_ssl,
&_sockfd,
mbedtls_net_send,
mbedtls_net_recv,
NULL);
mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
int res;
do {
do
{
{
std::lock_guard<std::mutex> lock(_mutex);
res = mbedtls_ssl_handshake(&_ssl);
}
if (isCancellationRequested()) {
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
close();
return false;
}
} while (res == MBEDTLS_ERR_SSL_WANT_READ ||
res == MBEDTLS_ERR_SSL_WANT_WRITE);
} while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
if (res != 0) {
if (res != 0)
{
char buf[256];
mbedtls_strerror(res, buf, sizeof(buf));
@@ -315,30 +320,36 @@ ssize_t SocketMbedTLS::send(char *buf, size_t nbyte)
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
if (res > 0) {
if (res > 0)
{
return res;
} else if (res == MBEDTLS_ERR_SSL_WANT_READ ||
res == MBEDTLS_ERR_SSL_WANT_WRITE) {
}
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
} else {
}
else
{
return -1;
}
}
ssize_t SocketMbedTLS::recv(void* buf, size_t nbyte)
{
while (true) {
while (true)
{
std::lock_guard<std::mutex> lock(_mutex);
ssize_t res = mbedtls_ssl_read(&_ssl, (unsigned char*) buf, (int) nbyte);
if (res > 0) {
if (res > 0)
{
return res;
}
if (res == MBEDTLS_ERR_SSL_WANT_READ ||
res == MBEDTLS_ERR_SSL_WANT_WRITE) {
if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
{
errno = EWOULDBLOCK;
}
return -1;
+295 -207
View File
@@ -15,7 +15,7 @@
#include <errno.h>
#include <vector>
#ifdef _WIN32
#include <Shlwapi.h>
#include <shlwapi.h>
#else
#include <fnmatch.h>
#endif
@@ -36,10 +36,10 @@ 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");
HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root");
if (!systemStore) {
if (!systemStore)
{
errorMsg = "CertOpenStore failed with ";
errorMsg += std::to_string(GetLastError());
return false;
@@ -49,15 +49,16 @@ bool loadWindowsSystemCertificates(SSL_CTX *ssl, std::string &errorMsg)
X509_STORE* opensslStore = SSL_CTX_get_cert_store(ssl);
int certificateCount = 0;
while (certificateIterator =
CertEnumCertificatesInStore(systemStore, certificateIterator)) {
X509 *x509 = d2i_X509(
NULL,
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) {
if (x509)
{
if (X509_STORE_add_cert(opensslStore, x509) == 1)
{
++certificateCount;
}
@@ -68,7 +69,8 @@ bool loadWindowsSystemCertificates(SSL_CTX *ssl, std::string &errorMsg)
CertFreeCertificateContext(certificateIterator);
CertCloseStore(systemStore, 0);
if (certificateCount == 0) {
if (certificateCount == 0)
{
errorMsg = "No certificates found";
return false;
}
@@ -81,12 +83,9 @@ bool loadWindowsSystemCertificates(SSL_CTX *ssl, std::string &errorMsg)
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-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";
@@ -96,25 +95,31 @@ std::once_flag SocketOpenSSL::_openSSLInitFlag;
std::vector<std::unique_ptr<std::mutex>> openSSLMutexes;
SocketOpenSSL::SocketOpenSSL(const SocketTLSOptions& tlsOptions, int fd)
: Socket(fd), _ssl_connection(nullptr), _ssl_context(nullptr),
_tlsOptions(tlsOptions)
: Socket(fd)
, _ssl_connection(nullptr)
, _ssl_context(nullptr)
, _tlsOptions(tlsOptions)
{
std::call_once(_openSSLInitFlag, &SocketOpenSSL::openSSLInitialize, this);
}
SocketOpenSSL::~SocketOpenSSL() { SocketOpenSSL::close(); }
SocketOpenSSL::~SocketOpenSSL()
{
SocketOpenSSL::close();
}
void SocketOpenSSL::openSSLInitialize()
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr))
return;
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, nullptr)) return;
#else
(void) OPENSSL_config(nullptr);
if (CRYPTO_get_locking_callback() == nullptr) {
if (CRYPTO_get_locking_callback() == nullptr)
{
openSSLMutexes.clear();
for (int i = 0; i < CRYPTO_num_locks(); ++i) {
for (int i = 0; i < CRYPTO_num_locks(); ++i)
{
openSSLMutexes.push_back(ix::make_unique<std::mutex>());
}
CRYPTO_set_locking_callback(SocketOpenSSL::openSSLLockingCallback);
@@ -132,9 +137,12 @@ void SocketOpenSSL::openSSLLockingCallback(int mode,
const char* /*file*/,
int /*line*/)
{
if (mode & CRYPTO_LOCK) {
if (mode & CRYPTO_LOCK)
{
openSSLMutexes[type]->lock();
} else {
}
else
{
openSSLMutexes[type]->unlock();
}
}
@@ -145,31 +153,49 @@ std::string SocketOpenSSL::getSSLError(int ret)
int err = SSL_get_error(_ssl_connection, ret);
if (err == SSL_ERROR_WANT_CONNECT || err == SSL_ERROR_WANT_ACCEPT) {
if (err == SSL_ERROR_WANT_CONNECT || err == SSL_ERROR_WANT_ACCEPT)
{
return "OpenSSL failed - connection failure";
} else if (err == SSL_ERROR_WANT_X509_LOOKUP) {
}
else if (err == SSL_ERROR_WANT_X509_LOOKUP)
{
return "OpenSSL failed - x509 error";
} else if (err == SSL_ERROR_SYSCALL) {
}
else if (err == SSL_ERROR_SYSCALL)
{
e = ERR_get_error();
if (e > 0) {
if (e > 0)
{
std::string errMsg("OpenSSL failed - ");
errMsg += ERR_error_string(e, nullptr);
return errMsg;
} else if (e == 0 && ret == 0) {
}
else if (e == 0 && ret == 0)
{
return "OpenSSL failed - received early EOF";
} else {
}
else
{
return "OpenSSL failed - underlying BIO reported an I/O error";
}
} else if (err == SSL_ERROR_SSL) {
}
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) {
}
else if (err == SSL_ERROR_NONE)
{
return "OpenSSL failed - err none";
} else if (err == SSL_ERROR_ZERO_RETURN) {
}
else if (err == SSL_ERROR_ZERO_RETURN)
{
return "OpenSSL failed - err zero return";
} else {
}
else
{
return "OpenSSL failed - unknown error";
}
}
@@ -177,24 +203,24 @@ std::string SocketOpenSSL::getSSLError(int ret)
SSL_CTX* SocketOpenSSL::openSSLCreateContext(std::string& errMsg)
{
const SSL_METHOD* method = SSLv23_client_method();
if (method == nullptr) {
if (method == nullptr)
{
errMsg = "SSLv23_client_method failure";
return nullptr;
}
_ssl_method = method;
SSL_CTX* ctx = SSL_CTX_new(_ssl_method);
if (ctx) {
if (ctx)
{
SSL_CTX_set_mode(ctx,
SSL_MODE_ENABLE_PARTIAL_WRITE |
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
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;
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
// (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);
@@ -206,28 +232,26 @@ 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;
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<int>(roots.length()));
if (buffer == nullptr)
return false;
BIO* buffer = BIO_new_mem_buf((void*) roots.c_str(), static_cast<int>(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) {
while (true)
{
// Read the next root in the buffer
X509 *root =
PEM_read_bio_X509_AUX(buffer, nullptr, nullptr, (void *)"");
if (root == nullptr) {
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;
@@ -235,12 +259,14 @@ bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots)
// 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.
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) {
ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE)
{
// Failed. Clean up and bail.
success = false;
X509_free(root);
@@ -257,8 +283,7 @@ bool SocketOpenSSL::openSSLAddCARootsFromString(const std::string roots)
BIO_free(buffer);
// Make sure we loaded at least one certificate.
if (number_of_roots == 0)
success = false;
if (number_of_roots == 0) success = false;
return success;
}
@@ -280,7 +305,8 @@ bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl,
std::string& errMsg)
{
X509* server_cert = SSL_get_peer_certificate(ssl);
if (server_cert == nullptr) {
if (server_cert == nullptr)
{
errMsg = "OpenSSL failed - peer didn't present a X509 certificate.";
return false;
}
@@ -288,16 +314,19 @@ bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl,
#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++) {
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) {
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)) {
if ((size_t) ASN1_STRING_length(sk_name->d.dNSName) == strlen(name) &&
checkHost(hostname, name))
{
hostname_verifies_ok = true;
break;
}
@@ -306,31 +335,32 @@ bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl,
}
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
if (!hostname_verifies_ok) {
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_get_subject_name((X509*) server_cert), NID_commonName, -1);
if (cn_pos >= 0)
{
X509_NAME_ENTRY* cn_entry =
X509_NAME_get_entry(X509_get_subject_name((X509 *)server_cert),
cn_pos);
X509_NAME_get_entry(X509_get_subject_name((X509*) server_cert), cn_pos);
if (cn_entry) {
if (cn_entry != nullptr)
{
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)) {
checkHost(hostname, cn))
{
hostname_verifies_ok = true;
}
}
}
}
if (!hostname_verifies_ok) {
errMsg =
"OpenSSL failed - certificate was issued for a different domain.";
if (!hostname_verifies_ok)
{
errMsg = "OpenSSL failed - certificate was issued for a different domain.";
return false;
}
#endif
@@ -339,37 +369,44 @@ bool SocketOpenSSL::openSSLCheckServerCert(SSL *ssl,
return true;
}
bool SocketOpenSSL::openSSLClientHandshake(
const std::string &host,
bool SocketOpenSSL::openSSLClientHandshake(const std::string& host,
std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
while (true) {
if (_ssl_connection == nullptr || _ssl_context == nullptr) {
while (true)
{
if (_ssl_connection == nullptr || _ssl_context == nullptr)
{
return false;
}
if (isCancellationRequested()) {
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
return false;
}
ERR_clear_error();
int connect_result = SSL_connect(_ssl_connection);
if (connect_result == 1) {
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) {
if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
rc = true;
} else {
}
else
{
errMsg = getSSLError(connect_result);
rc = false;
}
if (!rc) {
if (!rc)
{
return false;
}
}
@@ -377,27 +414,34 @@ bool SocketOpenSSL::openSSLClientHandshake(
bool SocketOpenSSL::openSSLServerHandshake(std::string& errMsg)
{
while (true) {
if (_ssl_connection == nullptr || _ssl_context == nullptr) {
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) {
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) {
if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
rc = true;
} else {
}
else
{
errMsg = getSSLError(accept_result);
rc = false;
}
if (!rc) {
if (!rc)
{
return false;
}
}
@@ -406,56 +450,66 @@ bool SocketOpenSSL::openSSLServerHandshake(std::string &errMsg)
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) {
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) {
}
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 = "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)) {
}
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 = "OpenSSL failed - cert/key mismatch(\"" + _tlsOptions.certFile + ", " +
_tlsOptions.keyFile + "\")";
errMsg += ERR_error_string(sslErr, nullptr);
}
}
ERR_clear_error();
if (!_tlsOptions.isPeerVerifyDisabled()) {
if (_tlsOptions.isUsingSystemDefaults()) {
if (!_tlsOptions.isPeerVerifyDisabled())
{
if (_tlsOptions.isUsingSystemDefaults())
{
#ifdef _WIN32
if (!loadWindowsSystemCertificates(_ssl_context, errMsg)) {
if (!loadWindowsSystemCertificates(_ssl_context, errMsg))
{
return false;
}
#else
if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) {
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 = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
#endif
} else {
if (_tlsOptions.isUsingInMemoryCAs()) {
}
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) {
}
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(\"" +
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
_tlsOptions.caFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
@@ -463,29 +517,32 @@ bool SocketOpenSSL::handleTLSOptions(std::string &errMsg)
}
}
SSL_CTX_set_verify(
_ssl_context,
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 {
}
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) {
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 = "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) {
}
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 = "OpenSSL failed - SSL_CTX_set_cipher_list(\"" + _tlsOptions.ciphers +
"\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
return false;
}
@@ -499,53 +556,58 @@ bool SocketOpenSSL::accept(std::string &errMsg)
{
std::lock_guard<std::mutex> lock(_mutex);
if (!_openSSLInitializationSuccessful) {
if (!_openSSLInitializationSuccessful)
{
errMsg = "OPENSSL_init_ssl failure";
return false;
}
if (_sockfd == -1) {
if (_sockfd == -1)
{
return false;
}
{
const SSL_METHOD* method = SSLv23_server_method();
if (method == nullptr) {
if (method == nullptr)
{
errMsg = "SSLv23_server_method failure";
_ssl_context = nullptr;
} else {
}
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);
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);
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
}
}
}
if (_ssl_context == nullptr) {
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) {
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(\"" +
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) {
}
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: ";
@@ -555,35 +617,44 @@ bool SocketOpenSSL::accept(std::string &errMsg)
ERR_clear_error();
if (!_tlsOptions.isPeerVerifyDisabled()) {
if (_tlsOptions.isUsingSystemDefaults()) {
if (SSL_CTX_set_default_verify_paths(_ssl_context) == 0) {
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 = "OpenSSL failed - SSL_CTX_default_verify_paths loading failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
} else {
if (_tlsOptions.isUsingInMemoryCAs()) {
}
else
{
if (_tlsOptions.isUsingInMemoryCAs())
{
// Load from memory
openSSLAddCARootsFromString(_tlsOptions.caFile);
} else {
}
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) {
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 {
}
else
{
SSL_CTX_set_client_CA_list(_ssl_context, rootCAs);
if (SSL_CTX_load_verify_locations(_ssl_context,
root_ca_file,
nullptr) != 1) {
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(\"" +
errMsg = "OpenSSL failed - SSL_CTX_load_verify_locations(\"" +
_tlsOptions.caFile + "\") failed: ";
errMsg += ERR_error_string(sslErr, nullptr);
}
@@ -591,26 +662,29 @@ bool SocketOpenSSL::accept(std::string &errMsg)
}
}
SSL_CTX_set_verify(_ssl_context,
SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
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 {
}
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) {
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) {
}
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) {
if (_ssl_connection == nullptr)
{
errMsg = "OpenSSL failed to connect";
SSL_CTX_free(_ssl_context);
_ssl_context = nullptr;
@@ -624,7 +698,8 @@ bool SocketOpenSSL::accept(std::string &errMsg)
handshakeSuccessful = openSSLServerHandshake(errMsg);
}
if (!handshakeSuccessful) {
if (!handshakeSuccessful)
{
close();
return false;
}
@@ -641,27 +716,29 @@ bool SocketOpenSSL::connect(const std::string &host,
{
std::lock_guard<std::mutex> lock(_mutex);
if (!_openSSLInitializationSuccessful) {
if (!_openSSLInitializationSuccessful)
{
errMsg = "OPENSSL_init_ssl failure";
return false;
}
_sockfd =
SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1)
return false;
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
if (_sockfd == -1) return false;
_ssl_context = openSSLCreateContext(errMsg);
if (_ssl_context == nullptr) {
if (_ssl_context == nullptr)
{
return false;
}
if (!handleTLSOptions(errMsg)) {
if (!handleTLSOptions(errMsg))
{
return false;
}
_ssl_connection = SSL_new(_ssl_context);
if (_ssl_connection == nullptr) {
if (_ssl_connection == nullptr)
{
errMsg = "OpenSSL failed to connect";
SSL_CTX_free(_ssl_context);
_ssl_context = nullptr;
@@ -674,17 +751,17 @@ bool SocketOpenSSL::connect(const std::string &host,
#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
// (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);
X509_VERIFY_PARAM_set1_host(param, host.c_str(), host.size());
#endif
handshakeSuccessful =
openSSLClientHandshake(host, errMsg, isCancellationRequested);
handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested);
}
if (!handshakeSuccessful) {
if (!handshakeSuccessful)
{
close();
return false;
}
@@ -696,11 +773,13 @@ void SocketOpenSSL::close()
{
std::lock_guard<std::mutex> lock(_mutex);
if (_ssl_connection != nullptr) {
if (_ssl_connection != nullptr)
{
SSL_free(_ssl_connection);
_ssl_connection = nullptr;
}
if (_ssl_context != nullptr) {
if (_ssl_context != nullptr)
{
SSL_CTX_free(_ssl_context);
_ssl_context = nullptr;
}
@@ -712,7 +791,8 @@ ssize_t SocketOpenSSL::send(char *buf, size_t nbyte)
{
std::lock_guard<std::mutex> lock(_mutex);
if (_ssl_connection == nullptr || _ssl_context == nullptr) {
if (_ssl_connection == nullptr || _ssl_context == nullptr)
{
return 0;
}
@@ -720,36 +800,44 @@ ssize_t SocketOpenSSL::send(char *buf, size_t nbyte)
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) {
if (reason == SSL_ERROR_NONE)
{
return write_result;
} else if (reason == SSL_ERROR_WANT_READ ||
reason == SSL_ERROR_WANT_WRITE) {
}
else if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
errno = EWOULDBLOCK;
return -1;
} else {
}
else
{
return -1;
}
}
ssize_t SocketOpenSSL::recv(void* buf, size_t nbyte)
{
while (true) {
while (true)
{
std::lock_guard<std::mutex> lock(_mutex);
if (_ssl_connection == nullptr || _ssl_context == nullptr) {
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) {
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) {
if (reason == SSL_ERROR_WANT_READ || reason == SSL_ERROR_WANT_WRITE)
{
errno = EWOULDBLOCK;
}
return -1;
+141 -113
View File
@@ -26,20 +26,25 @@ 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(
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(); }
SocketServer::~SocketServer()
{
stop();
}
void SocketServer::logError(const std::string& str)
{
@@ -56,7 +61,8 @@ void SocketServer::logInfo(const std::string &str)
std::pair<bool, std::string> SocketServer::listen()
{
std::string acceptSelectInterruptInitErrorMsg;
if (!_acceptSelectInterrupt->init(acceptSelectInterruptInitErrorMsg)) {
if (!_acceptSelectInterrupt->init(acceptSelectInterruptInitErrorMsg))
{
std::stringstream ss;
ss << "SocketServer::listen() error in SelectInterrupt::init: "
<< acceptSelectInterruptInitErrorMsg;
@@ -64,46 +70,42 @@ std::pair<bool, std::string> SocketServer::listen()
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 "
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) {
if ((_serverFd = socket(_addressFamily, SOCK_STREAM, 0)) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error creating socket): "
<< strerror(Socket::getErrno());
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) {
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());
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
}
if (_addressFamily == AF_INET) {
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) {
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 << " : "
@@ -114,7 +116,8 @@ std::pair<bool, std::string> SocketServer::listen()
}
// Bind the socket to the server address.
if (bind(_serverFd, (struct sockaddr *)&server, sizeof(server)) < 0) {
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling bind "
<< "at address " << _host << ":" << _port << " : "
@@ -123,14 +126,15 @@ std::pair<bool, std::string> SocketServer::listen()
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
}
} else // AF_INET6
}
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) {
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 << " : "
@@ -141,7 +145,8 @@ std::pair<bool, std::string> SocketServer::listen()
}
// Bind the socket to the server address.
if (bind(_serverFd, (struct sockaddr *)&server, sizeof(server)) < 0) {
if (bind(_serverFd, (struct sockaddr*) &server, sizeof(server)) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling bind "
<< "at address " << _host << ":" << _port << " : "
@@ -155,11 +160,11 @@ std::pair<bool, std::string> SocketServer::listen()
//
// Listen for connections. Specify the tcp backlog.
//
if (::listen(_serverFd, _backlog) < 0) {
if (::listen(_serverFd, _backlog) < 0)
{
std::stringstream ss;
ss << "SocketServer::listen() error calling listen "
<< "at address " << _host << ":" << _port << " : "
<< strerror(Socket::getErrno());
<< "at address " << _host << ":" << _port << " : " << strerror(Socket::getErrno());
Socket::closeSocket(_serverFd);
return std::make_pair(false, ss.str());
@@ -172,11 +177,13 @@ void SocketServer::start()
{
_stop = false;
if (!_thread.joinable()) {
if (!_thread.joinable())
{
_thread = std::thread(&SocketServer::run, this);
}
if (!_gcThread.joinable()) {
if (!_gcThread.joinable())
{
_gcThread = std::thread(&SocketServer::runGC, this);
}
}
@@ -187,15 +194,20 @@ void SocketServer::wait()
_conditionVariable.wait(lock);
}
void SocketServer::stopAcceptingConnections() { _stop = true; }
void SocketServer::stopAcceptingConnections()
{
_stop = true;
}
void SocketServer::stop()
{
// Stop accepting connections, and close the 'accept' thread
if (_thread.joinable()) {
if (_thread.joinable())
{
_stop = true;
// Wake up select
if (!_acceptSelectInterrupt->notify(SelectInterrupt::kCloseRequest)) {
if (!_acceptSelectInterrupt->notify(SelectInterrupt::kCloseRequest))
{
logError("SocketServer::stop: Cannot wake up from select");
}
@@ -204,7 +216,8 @@ void SocketServer::stop()
}
// Join all threads and make sure that all connections are terminated
if (_gcThread.joinable()) {
if (_gcThread.joinable())
{
_stopGc = true;
_conditionVariableGC.notify_one();
_gcThread.join();
@@ -234,61 +247,57 @@ void SocketServer::closeTerminatedThreads()
auto it = _connectionsThreads.begin();
auto itEnd = _connectionsThreads.end();
while (it != itEnd) {
while (it != itEnd)
{
auto& connectionState = it->first;
auto& thread = it->second;
if (!connectionState->isTerminated()) {
if (!connectionState->isTerminated())
{
++it;
continue;
}
if (thread.joinable())
thread.join();
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
// Set the socket to non blocking mode, so that accept calls are not blocking
SocketConnect::configure(_serverFd);
setThreadName("SocketServer::accept");
// Use a cryptic name to stay within the 16 bytes limit thread name limitation
// $ echo Srv:gc:64000 | wc -c
// 13
setThreadName("Srv:ac:" + std::to_string(_port));
for (;;) {
if (_stop)
return;
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
// 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);
PollResultType pollResult =
Socket::poll(readyToRead, timeoutMs, _serverFd, _acceptSelectInterrupt);
if (pollResult == PollResultType::Error) {
if (pollResult == PollResultType::Error)
{
std::stringstream ss;
ss << "SocketServer::run() error in select: " <<
#ifndef _WIN32
strerror(Socket::getErrno());
#else
Socket::getErrno();
#endif
ss << "SocketServer::run() error in select: " << strerror(Socket::getErrno());
logError(ss.str());
continue;
}
if (pollResult != PollResultType::ReadyForRead) {
if (pollResult != PollResultType::ReadyForRead)
{
continue;
}
@@ -299,24 +308,24 @@ void SocketServer::run()
socklen_t addressLen = sizeof(client);
memset(&client, 0, sizeof(client));
if ((clientFd =
accept(_serverFd, (struct sockaddr *)&client, &addressLen)) <
0) {
if (!Socket::isWaitNeeded()) {
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);
ss << "SocketServer::run() error accepting connection: " << err << ", "
<< strerror(err);
logError(ss.str());
}
continue;
}
if (getConnectedClientsCount() >= _maxConnections) {
if (getConnectedClientsCount() >= _maxConnections)
{
std::stringstream ss;
ss << "SocketServer::run() reached max connections = "
<< _maxConnections << ". "
ss << "SocketServer::run() reached max connections = " << _maxConnections << ". "
<< "Not accepting connection";
logError(ss.str());
@@ -329,16 +338,15 @@ void SocketServer::run()
std::string remoteIp;
int remotePort;
if (_addressFamily == AF_INET) {
if (_addressFamily == AF_INET)
{
char remoteIp4[INET_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET,
&client.sin_addr,
remoteIp4,
INET_ADDRSTRLEN) == nullptr) {
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);
ss << "SocketServer::run() error calling inet_ntop (ipv4): " << err << ", "
<< strerror(err);
logError(ss.str());
Socket::closeSocket(clientFd);
@@ -348,17 +356,17 @@ void SocketServer::run()
remotePort = ix::network_to_host_short(client.sin_port);
remoteIp = remoteIp4;
} else // AF_INET6
}
else // AF_INET6
{
char remoteIp6[INET6_ADDRSTRLEN];
if (ix::inet_ntop(AF_INET6,
&client.sin_addr,
remoteIp6,
INET6_ADDRSTRLEN) == nullptr) {
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);
ss << "SocketServer::run() error calling inet_ntop (ipv6): " << err << ", "
<< strerror(err);
logError(ss.str());
Socket::closeSocket(clientFd);
@@ -371,23 +379,23 @@ void SocketServer::run()
}
std::shared_ptr<ConnectionState> connectionState;
if (_connectionStateFactory) {
if (_connectionStateFactory)
{
connectionState = _connectionStateFactory();
}
connectionState->setOnSetTerminatedCallback(
[this] { onSetTerminatedCallback(); });
connectionState->setOnSetTerminatedCallback([this] { onSetTerminatedCallback(); });
connectionState->setRemoteIp(remoteIp);
connectionState->setRemotePort(remotePort);
if (_stop)
return;
if (_stop) return;
// create socket
std::string errorMsg;
bool tls = _socketTLSOptions.tls;
auto socket = createSocket(tls, clientFd, errorMsg, _socketTLSOptions);
if (socket == nullptr) {
if (socket == nullptr)
{
logError("SocketServer::run() cannot create socket: " + errorMsg);
Socket::closeSocket(clientFd);
continue;
@@ -396,7 +404,8 @@ void SocketServer::run()
// Set the socket to non blocking mode + other tweaks
SocketConnect::configure(clientFd);
if (!socket->accept(errorMsg)) {
if (!socket->accept(errorMsg))
{
logError("SocketServer::run() tls accept failed: " + errorMsg);
Socket::closeSocket(clientFd);
continue;
@@ -404,12 +413,10 @@ void SocketServer::run()
// Launch the handleConnection work asynchronously in its own thread.
std::lock_guard<std::mutex> lock(_connectionsThreadsMutex);
_connectionsThreads.push_back(
std::make_pair(connectionState,
std::thread(&SocketServer::handleConnection,
this,
std::move(socket),
connectionState)));
_connectionsThreads.push_back(std::make_pair(
connectionState,
std::thread(
&SocketServer::handleConnection, this, std::move(socket), connectionState)));
}
}
@@ -421,22 +428,28 @@ size_t SocketServer::getConnectionsThreadsCount()
void SocketServer::runGC()
{
setThreadName("SocketServer::GC");
// Use a cryptic name to stay within the 16 bytes limit thread name limitation
// $ echo Srv:gc:64000 | wc -c
// 13
setThreadName("Srv:gc:" + std::to_string(_port));
for (;;) {
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) {
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) {
if (!_stopGc)
{
std::unique_lock<std::mutex> lock(_conditionVariableMutexGC);
_conditionVariableGC.wait(lock);
}
@@ -455,13 +468,28 @@ void SocketServer::onSetTerminatedCallback()
_conditionVariableGC.notify_one();
}
int SocketServer::getPort() { return _port; }
int SocketServer::getPort()
{
return _port;
}
std::string SocketServer::getHost() { return _host; }
std::string SocketServer::getHost()
{
return _host;
}
int SocketServer::getBacklog() { return _backlog; }
int SocketServer::getBacklog()
{
return _backlog;
}
std::size_t SocketServer::getMaxConnections() { return _maxConnections; }
std::size_t SocketServer::getMaxConnections()
{
return _maxConnections;
}
int SocketServer::getAddressFamily() { return _addressFamily; }
int SocketServer::getAddressFamily()
{
return _addressFamily;
}
} // namespace ix
+16 -9
View File
@@ -19,24 +19,28 @@ const char *kTLSInMemoryMarker = "-----BEGIN CERTIFICATE-----";
bool SocketTLSOptions::isValid() const
{
if (!_validated) {
if (!certFile.empty() && !std::ifstream(certFile)) {
if (!_validated)
{
if (!certFile.empty() && !std::ifstream(certFile))
{
_errMsg = "certFile not found: " + certFile;
return false;
}
if (!keyFile.empty() && !std::ifstream(keyFile)) {
if (!keyFile.empty() && !std::ifstream(keyFile))
{
_errMsg = "keyFile not found: " + keyFile;
return false;
}
if (!caFile.empty() && caFile != kTLSCAFileDisableVerify &&
caFile != kTLSCAFileUseSystemDefaults && !std::ifstream(caFile)) {
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";
if (certFile.empty() != keyFile.empty())
{
_errMsg = "certFile and keyFile must be both present, or both absent";
return false;
}
@@ -70,7 +74,10 @@ bool SocketTLSOptions::isUsingDefaultCiphers() const
return ciphers.empty() || ciphers == kTLSCiphersUseDefault;
}
const std::string &SocketTLSOptions::getErrorMsg() const { return _errMsg; }
const std::string& SocketTLSOptions::getErrorMsg() const
{
return _errMsg;
}
std::string SocketTLSOptions::getDescription() const
{
@@ -80,7 +87,7 @@ std::string SocketTLSOptions::getDescription() const
ss << " keyFile = " << keyFile << std::endl;
ss << " caFile = " << caFile << std::endl;
ss << " ciphers = " << ciphers << std::endl;
ss << " ciphers = " << ciphers << std::endl;
ss << " tls = " << tls << std::endl;
return ss.str();
}
} // namespace ix
+2 -4
View File
@@ -11,8 +11,7 @@
namespace ix
{
bool CaseInsensitiveLess::NocaseCompare::operator()(
const unsigned char &c1,
bool CaseInsensitiveLess::NocaseCompare::operator()(const unsigned char& c1,
const unsigned char& c2) const
{
#if defined(_WIN32) && !defined(__GNUC__)
@@ -31,8 +30,7 @@ bool CaseInsensitiveLess::cmp(const std::string &s1, const std::string &s2)
NocaseCompare()); // comparison
}
bool CaseInsensitiveLess::operator()(const std::string &s1,
const std::string &s2) const
bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const
{
return CaseInsensitiveLess::cmp(s1, s2);
}
+20 -19
View File
@@ -12,14 +12,20 @@
namespace ix
{
UdpSocket::UdpSocket(int fd) : _sockfd(fd) { ; }
UdpSocket::UdpSocket(int fd)
: _sockfd(fd)
{
;
}
UdpSocket::~UdpSocket() { close(); }
UdpSocket::~UdpSocket()
{
close();
}
void UdpSocket::close()
{
if (_sockfd == -1)
return;
if (_sockfd == -1) return;
closeSocket(_sockfd);
_sockfd = -1;
@@ -42,7 +48,8 @@ bool UdpSocket::isWaitNeeded()
{
int err = getErrno();
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS) {
if (err == EWOULDBLOCK || err == EAGAIN || err == EINPROGRESS)
{
return true;
}
@@ -61,7 +68,8 @@ void UdpSocket::closeSocket(int fd)
bool UdpSocket::init(const std::string& host, int port, std::string& errMsg)
{
_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_sockfd < 0) {
if (_sockfd < 0)
{
errMsg = "Could not create socket";
return false;
}
@@ -84,7 +92,8 @@ bool UdpSocket::init(const std::string &host, int port, std::string &errMsg)
hints.ai_socktype = SOCK_DGRAM;
int ret = getaddrinfo(host.c_str(), nullptr, &hints, &result);
if (ret != 0) {
if (ret != 0)
{
errMsg = strerror(UdpSocket::getErrno());
freeaddrinfo(result);
close();
@@ -100,12 +109,8 @@ bool UdpSocket::init(const std::string &host, int port, std::string &errMsg)
ssize_t UdpSocket::sendto(const std::string& buffer)
{
return (ssize_t)::sendto(_sockfd,
buffer.data(),
buffer.size(),
0,
(struct sockaddr *)&_server,
sizeof(_server));
return (ssize_t)::sendto(
_sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr*) &_server, sizeof(_server));
}
ssize_t UdpSocket::recvfrom(char* buffer, size_t length)
@@ -115,11 +120,7 @@ ssize_t UdpSocket::recvfrom(char *buffer, size_t length)
#else
socklen_t addressLen = (socklen_t) sizeof(_server);
#endif
return (ssize_t)::recvfrom(_sockfd,
buffer,
length,
0,
(struct sockaddr *)&_server,
&addressLen);
return (ssize_t)::recvfrom(
_sockfd, buffer, length, 0, (struct sockaddr*) &_server, &addressLen);
}
} // namespace ix
+91 -59
View File
@@ -13,8 +13,8 @@
* 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 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,
@@ -37,7 +37,8 @@
namespace
{
enum LUrlParserError {
enum LUrlParserError
{
LUrlParserError_Ok = 0,
LUrlParserError_Uninitialized = 1,
LUrlParserError_NoUrlCharacter = 2,
@@ -61,27 +62,36 @@ public:
std::string m_UserName;
std::string m_Password;
clParseURL() : m_ErrorCode(LUrlParserError_Uninitialized) {}
clParseURL()
: m_ErrorCode(LUrlParserError_Uninitialized)
{
}
/// return 'true' if the parsing was successful
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
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)
/// 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) {}
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;
for (auto c : SchemeName)
{
if (!isalpha(c) && c != '+' && c != '-' && c != '.') return false;
}
return true;
@@ -89,17 +99,20 @@ static bool IsSchemeValid(const std::string &SchemeName)
bool clParseURL::GetPort(int* OutPort) const
{
if (!IsValid()) {
if (!IsValid())
{
return false;
}
int Port = atoi(m_Port.c_str());
if (Port <= 0 || Port > 65535) {
if (Port <= 0 || Port > 65535)
{
return false;
}
if (OutPort) {
if (OutPort)
{
*OutPort = Port;
}
@@ -116,31 +129,30 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
/*
* <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+
* For resiliency, programs interpreting URLs should treat upper case
*letters as equivalent to lower case in scheme names
* 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) {
if (!LocalString)
{
return clParseURL(LUrlParserError_NoUrlCharacter);
}
// save the scheme name
Result.m_Scheme =
std::string(CurrentString, LocalString - CurrentString);
Result.m_Scheme = std::string(CurrentString, LocalString - CurrentString);
if (!IsSchemeValid(Result.m_Scheme)) {
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);
std::transform(
Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower);
// skip ':'
CurrentString = LocalString + 1;
@@ -152,22 +164,24 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
*/
// skip "//"
if (*CurrentString++ != '/')
return clParseURL(LUrlParserError_NoDoubleSlash);
if (*CurrentString++ != '/')
return clParseURL(LUrlParserError_NoDoubleSlash);
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 == '@') {
while (*LocalString)
{
if (*LocalString == '@')
{
// user name and password are specified
bHasUserName = true;
break;
} else if (*LocalString == '/') {
}
else if (*LocalString == '/')
{
// end of <host>:<port> specification
bHasUserName = false;
break;
@@ -179,18 +193,19 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
// user name and password
LocalString = CurrentString;
if (bHasUserName) {
if (bHasUserName)
{
// read user name
while (*LocalString && *LocalString != ':' && *LocalString != '@')
LocalString++;
Result.m_UserName =
std::string(CurrentString, LocalString - CurrentString);
Result.m_UserName = std::string(CurrentString, LocalString - CurrentString);
// proceed with the current pointer
CurrentString = LocalString;
if (*CurrentString == ':') {
if (*CurrentString == ':')
{
// skip ':'
CurrentString++;
@@ -200,14 +215,14 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
while (*LocalString && *LocalString != '@')
LocalString++;
Result.m_Password =
std::string(CurrentString, LocalString - CurrentString);
Result.m_Password = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// skip '@'
if (*CurrentString != '@') {
if (*CurrentString != '@')
{
return clParseURL(LUrlParserError_NoAtSign);
}
@@ -219,13 +234,16 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
// go ahead, read the host name
LocalString = CurrentString;
while (*LocalString) {
if (bHasBracket && *LocalString == ']') {
while (*LocalString)
{
if (bHasBracket && *LocalString == ']')
{
// end of IPv6 address
LocalString++;
break;
} else if (!bHasBracket &&
(*LocalString == ':' || *LocalString == '/')) {
}
else if (!bHasBracket && (*LocalString == ':' || *LocalString == '/'))
{
// port number is specified
break;
}
@@ -238,7 +256,8 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
CurrentString = LocalString;
// is port number specified?
if (*CurrentString == ':') {
if (*CurrentString == ':')
{
CurrentString++;
// read port number
@@ -253,14 +272,16 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
}
// end of string
if (!*CurrentString) {
if (!*CurrentString)
{
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}
// skip '/'
if (*CurrentString != '/') {
if (*CurrentString != '/')
{
return clParseURL(LUrlParserError_NoSlash);
}
@@ -277,7 +298,8 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
CurrentString = LocalString;
// check for query
if (*CurrentString == '?') {
if (*CurrentString == '?')
{
// skip '?'
CurrentString++;
@@ -287,14 +309,14 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
while (*LocalString && *LocalString != '#')
LocalString++;
Result.m_Query =
std::string(CurrentString, LocalString - CurrentString);
Result.m_Query = std::string(CurrentString, LocalString - CurrentString);
CurrentString = LocalString;
}
// check for fragment
if (*CurrentString == '#') {
if (*CurrentString == '#')
{
// skip '#'
CurrentString++;
@@ -304,8 +326,7 @@ clParseURL clParseURL::ParseURL(const std::string &URL)
while (*LocalString)
LocalString++;
Result.m_Fragment =
std::string(CurrentString, LocalString - CurrentString);
Result.m_Fragment = std::string(CurrentString, LocalString - CurrentString);
}
Result.m_ErrorCode = LUrlParserError_Ok;
@@ -325,7 +346,8 @@ bool UrlParser::parse(const std::string &url,
{
clParseURL res = clParseURL::ParseURL(url);
if (!res.IsValid()) {
if (!res.IsValid())
{
return false;
}
@@ -334,25 +356,35 @@ bool UrlParser::parse(const std::string &url,
path = res.m_Path;
query = res.m_Query;
if (!res.GetPort(&port)) {
if (protocol == "ws" || protocol == "http") {
if (!res.GetPort(&port))
{
if (protocol == "ws" || protocol == "http")
{
port = 80;
} else if (protocol == "wss" || protocol == "https") {
}
else if (protocol == "wss" || protocol == "https")
{
port = 443;
} else {
}
else
{
// Invalid protocol. Should be caught by regex check
// but this missing branch trigger cpplint linter.
return false;
}
}
if (path.empty()) {
if (path.empty())
{
path = "/";
} else if (path[0] != '/') {
}
else if (path[0] != '/')
{
path = '/' + path;
}
if (!query.empty()) {
if (!query.empty())
{
path += "?";
path += query;
}
+2 -5
View File
@@ -20,12 +20,9 @@
#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)
#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
#define PLATFORM_NAME "linux" // Debian, Ubuntu, Gentoo, Fedora, openSUSE, RedHat, Centos and other
#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__)
#include <sys/param.h>
#if defined(BSD)
+153 -127
View File
@@ -27,27 +27,23 @@ 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::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)
: _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<WebSocketMessage>(
WebSocketMessageType::Close,
_ws.setOnCloseCallback(
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote) {
_onMessageCallback(
ix::make_unique<WebSocketMessage>(WebSocketMessageType::Close,
emptyMsg,
wireSize,
WebSocketErrorInfo(),
@@ -98,8 +94,7 @@ void WebSocket::setTLSOptions(const SocketTLSOptions &socketTLSOptions)
_socketTLSOptions = socketTLSOptions;
}
const WebSocketPerMessageDeflateOptions
WebSocket::getPerMessageDeflateOptions() const
const WebSocketPerMessageDeflateOptions WebSocket::getPerMessageDeflateOptions() const
{
std::lock_guard<std::mutex> lock(_configMutex);
return _perMessageDeflateOptions;
@@ -143,15 +138,13 @@ void WebSocket::disablePerMessageDeflate()
_perMessageDeflateOptions = perMessageDeflateOptions;
}
void WebSocket::setMaxWaitBetweenReconnectionRetries(
uint32_t maxWaitBetweenReconnectionRetries)
void WebSocket::setMaxWaitBetweenReconnectionRetries(uint32_t maxWaitBetweenReconnectionRetries)
{
std::lock_guard<std::mutex> lock(_configMutex);
_maxWaitBetweenReconnectionRetries = maxWaitBetweenReconnectionRetries;
}
void WebSocket::setMinWaitBetweenReconnectionRetries(
uint32_t minWaitBetweenReconnectionRetries)
void WebSocket::setMinWaitBetweenReconnectionRetries(uint32_t minWaitBetweenReconnectionRetries)
{
std::lock_guard<std::mutex> lock(_configMutex);
_minWaitBetweenReconnectionRetries = minWaitBetweenReconnectionRetries;
@@ -171,8 +164,7 @@ uint32_t WebSocket::getMinWaitBetweenReconnectionRetries() const
void WebSocket::start()
{
if (_thread.joinable())
return; // we've already been started
if (_thread.joinable()) return; // we've already been started
_thread = std::thread(&WebSocket::run, this);
}
@@ -181,7 +173,8 @@ void WebSocket::stop(uint16_t code, const std::string &reason)
{
close(code, reason);
if (_thread.joinable()) {
if (_thread.joinable())
{
// wait until working thread will exit
// it will exit after close operation is finished
_stop = true;
@@ -195,16 +188,15 @@ WebSocketInitResult WebSocket::connect(int timeoutSecs)
{
{
std::lock_guard<std::mutex> lock(_configMutex);
_ws.configure(_perMessageDeflateOptions,
_socketTLSOptions,
_enablePong,
_pingIntervalSecs);
_ws.configure(
_perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs);
}
WebSocketHttpHeaders headers(_extraHeaders);
std::string subProtocolsHeader;
auto subProtocols = getSubProtocols();
if (!subProtocols.empty()) {
if (!subProtocols.empty())
{
//
// Sub Protocol strings are comma separated.
// Python code to do that is:
@@ -212,8 +204,10 @@ WebSocketInitResult WebSocket::connect(int timeoutSecs)
// 'json,msgpack'
//
int i = 0;
for (auto subProtocol : subProtocols) {
if (i++ != 0) {
for (auto subProtocol : subProtocols)
{
if (i++ != 0)
{
subProtocolsHeader += ",";
}
subProtocolsHeader += subProtocol;
@@ -222,7 +216,8 @@ WebSocketInitResult WebSocket::connect(int timeoutSecs)
}
WebSocketInitResult status = _ws.connectToUrl(_url, headers, timeoutSecs);
if (!status.success) {
if (!status.success)
{
return status;
}
@@ -234,7 +229,8 @@ WebSocketInitResult WebSocket::connect(int timeoutSecs)
WebSocketOpenInfo(status.uri, status.headers, status.protocol),
WebSocketCloseInfo()));
if (_pingIntervalSecs > 0) {
if (_pingIntervalSecs > 0)
{
// Send a heart beat right away
_ws.sendHeartBeat();
}
@@ -248,28 +244,27 @@ WebSocketInitResult WebSocket::connectToSocket(std::unique_ptr<Socket> socket,
{
{
std::lock_guard<std::mutex> lock(_configMutex);
_ws.configure(_perMessageDeflateOptions,
_socketTLSOptions,
_enablePong,
_pingIntervalSecs);
_ws.configure(
_perMessageDeflateOptions, _socketTLSOptions, _enablePong, _pingIntervalSecs);
}
WebSocketInitResult status = _ws.connectToSocket(std::move(socket),
timeoutSecs,
enablePerMessageDeflate);
if (!status.success) {
WebSocketInitResult status =
_ws.connectToSocket(std::move(socket), timeoutSecs, enablePerMessageDeflate);
if (!status.success)
{
return status;
}
_onMessageCallback(ix::make_unique<WebSocketMessage>(
WebSocketMessageType::Open,
_onMessageCallback(
ix::make_unique<WebSocketMessage>(WebSocketMessageType::Open,
emptyMsg,
0,
WebSocketErrorInfo(),
WebSocketOpenInfo(status.uri, status.headers),
WebSocketCloseInfo()));
if (_pingIntervalSecs > 0) {
if (_pingIntervalSecs > 0)
{
// Send a heart beat right away
_ws.sendHeartBeat();
}
@@ -300,12 +295,15 @@ void WebSocket::checkConnection(bool firstConnectionAttempt)
millis duration(0);
// Try to connect perpertually
while (true) {
if (isConnected() || isClosing() || _stop) {
while (true)
{
if (isConnected() || isClosing() || _stop)
{
break;
}
if (!firstConnectionAttempt && !_automaticReconnection) {
if (!firstConnectionAttempt && !_automaticReconnection)
{
// Do not attempt to reconnect
break;
}
@@ -313,24 +311,28 @@ void WebSocket::checkConnection(bool firstConnectionAttempt)
firstConnectionAttempt = false;
// Only sleep if we are retrying
if (duration.count() > 0) {
if (duration.count() > 0)
{
std::unique_lock<std::mutex> lock(_sleepMutex);
_sleepCondition.wait_for(lock, duration);
}
if (_stop) {
if (_stop)
{
break;
}
// Try to connect synchronously
ix::WebSocketInitResult status = connect(_handshakeTimeoutSecs);
if (!status.success) {
if (!status.success)
{
WebSocketErrorInfo connectErr;
if (_automaticReconnection) {
duration = millis(calculateRetryWaitMilliseconds(
retries++,
if (_automaticReconnection)
{
duration =
millis(calculateRetryWaitMilliseconds(retries++,
_maxWaitBetweenReconnectionRetries,
_minWaitBetweenReconnectionRetries));
@@ -341,8 +343,7 @@ void WebSocket::checkConnection(bool firstConnectionAttempt)
connectErr.reason = status.errorStr;
connectErr.http_status = status.http_status;
_onMessageCallback(
ix::make_unique<WebSocketMessage>(WebSocketMessageType::Error,
_onMessageCallback(ix::make_unique<WebSocketMessage>(WebSocketMessageType::Error,
emptyMsg,
0,
connectErr,
@@ -358,20 +359,21 @@ void WebSocket::run()
bool firstConnectionAttempt = true;
while (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) {
if (getReadyState() == ReadyState::Closed)
{
break;
}
// We can avoid to poll if we want to stop and are not closing
if (_stop && !isClosing())
break;
if (_stop && !isClosing()) break;
// 2. Poll to see if there's any new data available
WebSocketTransport::PollResult pollResult = _ws.poll();
@@ -383,35 +385,41 @@ void WebSocket::run()
size_t wireSize,
bool decompressionError,
WebSocketTransport::MessageKind messageKind) {
WebSocketMessageType webSocketMessageType{
WebSocketMessageType::Error};
switch (messageKind) {
WebSocketMessageType webSocketMessageType{WebSocketMessageType::Error};
switch (messageKind)
{
case WebSocketTransport::MessageKind::MSG_TEXT:
case WebSocketTransport::MessageKind::MSG_BINARY: {
case WebSocketTransport::MessageKind::MSG_BINARY:
{
webSocketMessageType = WebSocketMessageType::Message;
} break;
}
break;
case WebSocketTransport::MessageKind::PING: {
case WebSocketTransport::MessageKind::PING:
{
webSocketMessageType = WebSocketMessageType::Ping;
} break;
}
break;
case WebSocketTransport::MessageKind::PONG: {
case WebSocketTransport::MessageKind::PONG:
{
webSocketMessageType = WebSocketMessageType::Pong;
} break;
}
break;
case WebSocketTransport::MessageKind::FRAGMENT: {
case WebSocketTransport::MessageKind::FRAGMENT:
{
webSocketMessageType = WebSocketMessageType::Fragment;
} break;
}
break;
}
WebSocketErrorInfo webSocketErrorInfo;
webSocketErrorInfo.decompressionError = decompressionError;
bool binary =
messageKind == WebSocketTransport::MessageKind::MSG_BINARY;
bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY;
_onMessageCallback(
ix::make_unique<WebSocketMessage>(webSocketMessageType,
_onMessageCallback(ix::make_unique<WebSocketMessage>(webSocketMessageType,
msg,
wireSize,
webSocketErrorInfo,
@@ -434,8 +442,7 @@ bool WebSocket::isOnMessageCallbackRegistered() const
return _onMessageCallback != nullptr;
}
void WebSocket::setTrafficTrackerCallback(
const OnTrafficTrackerCallback &callback)
void WebSocket::setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback)
{
_onTrafficTrackerCallback = callback;
}
@@ -447,7 +454,8 @@ void WebSocket::resetTrafficTrackerCallback()
void WebSocket::invokeTrafficTrackerCallback(size_t size, bool incoming)
{
if (_onTrafficTrackerCallback) {
if (_onTrafficTrackerCallback)
{
_onTrafficTrackerCallback(size, incoming);
}
}
@@ -456,22 +464,38 @@ WebSocketSendInfo WebSocket::send(const std::string &data,
bool binary,
const OnProgressCallback& onProgressCallback)
{
return (binary) ? sendBinary(data, onProgressCallback)
: sendText(data, onProgressCallback);
return (binary) ? sendBinary(data, onProgressCallback) : sendText(data, onProgressCallback);
}
WebSocketSendInfo
WebSocket::sendBinary(const std::string &text,
WebSocketSendInfo WebSocket::sendBinary(const std::string& data,
const OnProgressCallback& onProgressCallback)
{
return sendMessage(text, SendMessageKind::Binary, onProgressCallback);
return sendMessage(data, SendMessageKind::Binary, onProgressCallback);
}
WebSocketSendInfo
WebSocket::sendText(const std::string &text,
WebSocketSendInfo WebSocket::sendBinary(const IXWebSocketSendData& data,
const OnProgressCallback& onProgressCallback)
{
if (!validateUtf8(text)) {
return sendMessage(data, SendMessageKind::Binary, onProgressCallback);
}
WebSocketSendInfo WebSocket::sendUtf8Text(const std::string& text,
const OnProgressCallback& onProgressCallback)
{
return sendMessage(text, SendMessageKind::Text, onProgressCallback);
}
WebSocketSendInfo WebSocket::sendUtf8Text(const IXWebSocketSendData& text,
const OnProgressCallback& onProgressCallback)
{
return sendMessage(text, SendMessageKind::Text, onProgressCallback);
}
WebSocketSendInfo WebSocket::sendText(const std::string& text,
const OnProgressCallback& onProgressCallback)
{
if (!validateUtf8(text))
{
close(WebSocketCloseConstants::kInvalidFramePayloadData,
WebSocketCloseConstants::kInvalidFramePayloadDataMessage);
return false;
@@ -483,19 +507,16 @@ WebSocketSendInfo WebSocket::ping(const std::string &text)
{
// Standard limit ping message size
constexpr size_t pingMaxPayloadSize = 125;
if (text.size() > pingMaxPayloadSize)
return WebSocketSendInfo(false);
if (text.size() > pingMaxPayloadSize) return WebSocketSendInfo(false);
return sendMessage(text, SendMessageKind::Ping);
}
WebSocketSendInfo
WebSocket::sendMessage(const std::string &text,
WebSocketSendInfo WebSocket::sendMessage(const IXWebSocketSendData& message,
SendMessageKind sendMessageKind,
const OnProgressCallback& onProgressCallback)
{
if (!isConnected())
return WebSocketSendInfo(false);
if (!isConnected()) return WebSocketSendInfo(false);
//
// It is OK to read and write on the same socket in 2 different threads.
@@ -509,18 +530,25 @@ WebSocketSendInfo
std::lock_guard<std::mutex> lock(_writeMutex);
WebSocketSendInfo webSocketSendInfo;
switch (sendMessageKind) {
case SendMessageKind::Text: {
webSocketSendInfo = _ws.sendText(text, onProgressCallback);
} break;
switch (sendMessageKind)
{
case SendMessageKind::Text:
{
webSocketSendInfo = _ws.sendText(message, onProgressCallback);
}
break;
case SendMessageKind::Binary: {
webSocketSendInfo = _ws.sendBinary(text, onProgressCallback);
} break;
case SendMessageKind::Binary:
{
webSocketSendInfo = _ws.sendBinary(message, onProgressCallback);
}
break;
case SendMessageKind::Ping: {
webSocketSendInfo = _ws.sendPing(text);
} break;
case SendMessageKind::Ping:
{
webSocketSendInfo = _ws.sendPing(message);
}
break;
}
WebSocket::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
@@ -530,37 +558,32 @@ 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;
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";
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::enableAutomaticReconnection()
{
_automaticReconnection = true;
}
void WebSocket::disableAutomaticReconnection()
{
@@ -572,7 +595,10 @@ bool WebSocket::isAutomaticReconnectionEnabled() const
return _automaticReconnection;
}
size_t WebSocket::bufferedAmount() const { return _ws.bufferedAmount(); }
size_t WebSocket::bufferedAmount() const
{
return _ws.bufferedAmount();
}
void WebSocket::addSubProtocol(const std::string& subProtocol)
{
+10 -20
View File
@@ -15,32 +15,22 @@ 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::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::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(
const std::string WebSocketCloseConstants::kProtocolErrorCodeControlMessageFragmented(
"Control message fragmented");
const std::string
WebSocketCloseConstants::kProtocolErrorCodeDataOpcodeOutOfSequence(
const std::string WebSocketCloseConstants::kProtocolErrorCodeDataOpcodeOutOfSequence(
"Fragmentation: data message out of sequence");
const std::string
WebSocketCloseConstants::kProtocolErrorCodeContinuationOpCodeOutOfSequence(
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");
const std::string WebSocketCloseConstants::kInvalidCloseCodeMessage("Invalid close code");
} // namespace ix
+82 -84
View File
@@ -6,6 +6,7 @@
#include "IXWebSocketHandshake.h"
#include "IXBase64.h"
#include "IXHttp.h"
#include "IXSocketConnect.h"
#include "IXStrCaseCompare.h"
@@ -17,7 +18,6 @@
#include <random>
#include <sstream>
namespace ix
{
WebSocketHandshake::WebSocketHandshake(
@@ -26,15 +26,15 @@ WebSocketHandshake::WebSocketHandshake(
WebSocketPerMessageDeflatePtr& perMessageDeflate,
WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
std::atomic<bool>& enablePerMessageDeflate)
: _requestInitCancellation(requestInitCancellation), _socket(socket),
_perMessageDeflate(perMessageDeflate),
_perMessageDeflateOptions(perMessageDeflateOptions),
_enablePerMessageDeflate(enablePerMessageDeflate)
: _requestInitCancellation(requestInitCancellation)
, _socket(socket)
, _perMessageDeflate(perMessageDeflate)
, _perMessageDeflateOptions(perMessageDeflateOptions)
, _enablePerMessageDeflate(enablePerMessageDeflate)
{
}
bool WebSocketHandshake::insensitiveStringCompare(const std::string &a,
const std::string &b)
bool WebSocketHandshake::insensitiveStringCompare(const std::string& a, const std::string& b)
{
return CaseInsensitiveLess::cmp(a, b) == 0;
}
@@ -52,7 +52,8 @@ std::string WebSocketHandshake::genRandomString(const int len)
std::string s;
s.resize(len);
for (int i = 0; i < len; ++i) {
for (int i = 0; i < len; ++i)
{
int x = dist(e1);
s[i] = alphanum[x];
}
@@ -60,8 +61,7 @@ std::string WebSocketHandshake::genRandomString(const int len)
return s;
}
WebSocketInitResult
WebSocketHandshake::sendErrorResponse(int code, const std::string &reason)
WebSocketInitResult WebSocketHandshake::sendErrorResponse(int code, const std::string& reason)
{
std::stringstream ss;
ss << "HTTP/1.1 ";
@@ -76,10 +76,9 @@ WebSocketInitResult
auto isCancellationRequested =
makeCancellationRequestWithTimeout(1, requestInitCancellation);
if (!_socket->writeBytes(ss.str(), isCancellationRequested)) {
return WebSocketInitResult(false,
500,
"Timed out while sending error response");
if (!_socket->writeBytes(ss.str(), isCancellationRequested))
{
return WebSocketInitResult(false, 500, "Timed out while sending error response");
}
return WebSocketInitResult(false, code, reason);
@@ -96,29 +95,21 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
_requestInitCancellation = false;
auto isCancellationRequested =
makeCancellationRequestWithTimeout(timeoutSecs,
_requestInitCancellation);
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
std::string errMsg;
bool success =
_socket->connect(host, port, errMsg, isCancellationRequested);
if (!success) {
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
if (!success)
{
std::stringstream ss;
ss << "Unable to connect to " << host << " on port " << port
<< ", error: " << errMsg;
ss << "Unable to connect to " << host << " on port " << port << ", error: " << errMsg;
return WebSocketInitResult(false, 0, ss.str());
}
// Generate a random 16 bytes string and base64 encode it.
//
// 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 += "==";
// See https://stackoverflow.com/questions/18265128/what-is-sec-websocket-key-for
std::string secWebSocketKey = macaron::Base64::Encode(genRandomString(16));
std::stringstream ss;
ss << "GET " << path << " HTTP/1.1\r\n";
@@ -129,25 +120,27 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
ss << "Sec-WebSocket-Key: " << secWebSocketKey << "\r\n";
// User-Agent can be customized by users
if (extraHeaders.find("User-Agent") == extraHeaders.end()) {
if (extraHeaders.find("User-Agent") == extraHeaders.end())
{
ss << "User-Agent: " << userAgent() << "\r\n";
}
for (auto &it : extraHeaders) {
for (auto& it : extraHeaders)
{
ss << it.first << ": " << it.second << "\r\n";
}
if (_enablePerMessageDeflate) {
if (_enablePerMessageDeflate)
{
ss << _perMessageDeflateOptions.generateHeader();
}
ss << "\r\n";
if (!_socket->writeBytes(ss.str(), isCancellationRequested)) {
if (!_socket->writeBytes(ss.str(), isCancellationRequested))
{
return WebSocketInitResult(
false,
0,
std::string("Failed sending GET request to ") + url);
false, 0, std::string("Failed sending GET request to ") + url);
}
// Read HTTP status line
@@ -155,11 +148,10 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
auto lineValid = lineResult.first;
auto line = lineResult.second;
if (!lineValid) {
if (!lineValid)
{
return WebSocketInitResult(
false,
0,
std::string("Failed reading HTTP status line from ") + url);
false, 0, std::string("Failed reading HTTP status line from ") + url);
}
// Validate status
@@ -168,7 +160,8 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
int status = statusLine.second;
// HTTP/1.0 is too old.
if (httpVersion != "HTTP/1.1") {
if (httpVersion != "HTTP/1.1")
{
std::stringstream ss;
ss << "Expecting HTTP/1.1, got " << httpVersion << ". "
<< "Rejecting connection to " << url << ", status: " << status
@@ -180,13 +173,15 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
auto headersValid = result.first;
auto headers = result.second;
if (!headersValid) {
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) {
if (status != 101)
{
std::stringstream ss;
ss << "Expecting status 101 (Switching Protocol), got " << status
<< " status connecting to " << url << ", HTTP Status line: " << line;
@@ -195,7 +190,8 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
}
// Check the presence of the connection field
if (headers.find("connection") == headers.end()) {
if (headers.find("connection") == headers.end())
{
std::string errorMsg("Missing connection value");
return WebSocketInitResult(false, status, errorMsg);
}
@@ -204,10 +200,10 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
// 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
// See https://github.com/apache/thrift/commit/7c4bdf9914fcba6c89e0f69ae48b9675578f084a
//
if (!insensitiveStringCompare(headers["connection"], "Upgrade")) {
if (!insensitiveStringCompare(headers["connection"], "Upgrade"))
{
std::stringstream ss;
ss << "Invalid connection value: " << headers["connection"];
return WebSocketInitResult(false, status, ss.str());
@@ -215,49 +211,49 @@ WebSocketInitResult WebSocketHandshake::clientHandshake(
char output[29] = {};
WebSocketHandshakeKeyGen::generate(secWebSocketKey, output);
if (std::string(output) != headers["sec-websocket-accept"]) {
if (std::string(output) != headers["sec-websocket-accept"])
{
std::string errorMsg("Invalid Sec-WebSocket-Accept value");
return WebSocketInitResult(false, status, errorMsg);
}
if (_enablePerMessageDeflate) {
if (_enablePerMessageDeflate)
{
// Parse the server response. Does it support deflate ?
std::string header = headers["sec-websocket-extensions"];
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
header);
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header);
// If the server does not support that extension, disable it.
if (!webSocketPerMessageDeflateOptions.enabled()) {
if (!webSocketPerMessageDeflateOptions.enabled())
{
_enablePerMessageDeflate = false;
}
// Otherwise try to initialize the deflate engine (zlib)
else if (!_perMessageDeflate->init(webSocketPerMessageDeflateOptions)) {
else if (!_perMessageDeflate->init(webSocketPerMessageDeflateOptions))
{
return WebSocketInitResult(
false,
0,
"Failed to initialize per message deflate engine");
false, 0, "Failed to initialize per message deflate engine");
}
}
return WebSocketInitResult(true, status, "", headers, path);
}
WebSocketInitResult
WebSocketHandshake::serverHandshake(int timeoutSecs,
WebSocketInitResult WebSocketHandshake::serverHandshake(int timeoutSecs,
bool enablePerMessageDeflate)
{
_requestInitCancellation = false;
auto isCancellationRequested =
makeCancellationRequestWithTimeout(timeoutSecs,
_requestInitCancellation);
makeCancellationRequestWithTimeout(timeoutSecs, _requestInitCancellation);
// Read first line
auto lineResult = _socket->readLine(isCancellationRequested);
auto lineValid = lineResult.first;
auto line = lineResult.second;
if (!lineValid) {
if (!lineValid)
{
return sendErrorResponse(400, "Error reading HTTP request line");
}
@@ -267,16 +263,15 @@ WebSocketInitResult
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 (method != "GET")
{
return sendErrorResponse(400, "Invalid HTTP method, need GET, got " + method);
}
if (httpVersion != "HTTP/1.1") {
if (httpVersion != "HTTP/1.1")
{
return sendErrorResponse(400,
"Invalid HTTP version, need HTTP/1.1, got: " +
httpVersion);
"Invalid HTTP version, need HTTP/1.1, got: " + httpVersion);
}
// Retrieve and validate HTTP headers
@@ -284,15 +279,18 @@ WebSocketInitResult
auto headersValid = result.first;
auto headers = result.second;
if (!headersValid) {
if (!headersValid)
{
return sendErrorResponse(400, "Error parsing HTTP headers");
}
if (headers.find("sec-websocket-key") == headers.end()) {
if (headers.find("sec-websocket-key") == headers.end())
{
return sendErrorResponse(400, "Missing Sec-WebSocket-Key value");
}
if (headers.find("upgrade") == headers.end()) {
if (headers.find("upgrade") == headers.end())
{
return sendErrorResponse(400, "Missing Upgrade header");
}
@@ -305,7 +303,8 @@ WebSocketInitResult
headers["upgrade"]);
}
if (headers.find("sec-websocket-version") == headers.end()) {
if (headers.find("sec-websocket-version") == headers.end())
{
return sendErrorResponse(400, "Missing Sec-WebSocket-Version value");
}
@@ -315,7 +314,8 @@ WebSocketInitResult
int version;
ss >> version;
if (version != 13) {
if (version != 13)
{
return sendErrorResponse(400,
"Invalid Sec-WebSocket-Version, "
"need 13, got " +
@@ -338,26 +338,24 @@ WebSocketInitResult
WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(header);
// If the client has requested that extension,
if (webSocketPerMessageDeflateOptions.enabled() &&
enablePerMessageDeflate) {
if (webSocketPerMessageDeflateOptions.enabled() && enablePerMessageDeflate)
{
_enablePerMessageDeflate = true;
if (!_perMessageDeflate->init(webSocketPerMessageDeflateOptions)) {
if (!_perMessageDeflate->init(webSocketPerMessageDeflateOptions))
{
return WebSocketInitResult(
false,
0,
"Failed to initialize per message deflate engine");
false, 0, "Failed to initialize per message deflate engine");
}
ss << webSocketPerMessageDeflateOptions.generateHeader();
}
ss << "\r\n";
if (!_socket->writeBytes(ss.str(), isCancellationRequested)) {
if (!_socket->writeBytes(ss.str(), isCancellationRequested))
{
return WebSocketInitResult(
false,
0,
std::string("Failed sending response to remote end"));
false, 0, std::string("Failed sending response to remote end"));
}
return WebSocketInitResult(true, 200, "", headers, uri);
+26 -23
View File
@@ -12,55 +12,58 @@
namespace ix
{
std::pair<bool, WebSocketHttpHeaders>
parseHttpHeaders(std::unique_ptr<Socket> &socket,
const CancellationRequest &isCancellationRequested)
std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(
std::unique_ptr<Socket>& socket, const CancellationRequest& isCancellationRequested)
{
WebSocketHttpHeaders headers;
char line[1024];
int i;
while (true) {
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)) {
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) {
if (line[i] == ':' && colon == 0)
{
colon = i;
}
}
if (line[0] == '\r' && line[1] == '\n') {
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) {
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
// 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] == ' ') {
while (lineStr[start] == ' ')
{
start++;
}
std::string name(lineStr.substr(0, colon));
std::string value(
lineStr.substr(start, lineStr.size() - start - 2));
std::string value(lineStr.substr(start, lineStr.size() - start - 2));
headers[name] = value;
}
+19 -16
View File
@@ -32,14 +32,12 @@
* (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.
* - 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 ?
* 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
@@ -57,19 +55,21 @@
namespace ix
{
WebSocketPerMessageDeflate::WebSocketPerMessageDeflate()
: _compressor(ix::make_unique<WebSocketPerMessageDeflateCompressor>()),
_decompressor(ix::make_unique<WebSocketPerMessageDeflateDecompressor>())
: _compressor(ix::make_unique<WebSocketPerMessageDeflateCompressor>())
, _decompressor(ix::make_unique<WebSocketPerMessageDeflateDecompressor>())
{
;
}
WebSocketPerMessageDeflate::~WebSocketPerMessageDeflate() { ; }
WebSocketPerMessageDeflate::~WebSocketPerMessageDeflate()
{
;
}
bool WebSocketPerMessageDeflate::init(
const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions)
{
bool clientNoContextTakeover =
perMessageDeflateOptions.getClientNoContextTakeover();
bool clientNoContextTakeover = perMessageDeflateOptions.getClientNoContextTakeover();
uint8_t deflateBits = perMessageDeflateOptions.getClientMaxWindowBits();
uint8_t inflateBits = perMessageDeflateOptions.getServerMaxWindowBits();
@@ -78,14 +78,17 @@ bool WebSocketPerMessageDeflate::init(
_decompressor->init(inflateBits, clientNoContextTakeover);
}
bool WebSocketPerMessageDeflate::compress(const std::string &in,
std::string &out)
bool WebSocketPerMessageDeflate::compress(const IXWebSocketSendData& in, std::string& out)
{
return _compressor->compress(in, out);
}
bool WebSocketPerMessageDeflate::decompress(const std::string &in,
std::string &out)
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);
}
@@ -52,8 +52,7 @@ bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
4, // memory level 1-9
Z_DEFAULT_STRATEGY);
if (ret != Z_OK)
return false;
if (ret != Z_OK) return false;
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
@@ -64,11 +63,9 @@ bool WebSocketPerMessageDeflateCompressor::init(uint8_t deflateBits,
}
template<typename T>
bool WebSocketPerMessageDeflateCompressor::endsWithEmptyUnCompressedBlock(
const T &value)
bool WebSocketPerMessageDeflateCompressor::endsWithEmptyUnCompressedBlock(const T& value)
{
if (kEmptyUncompressedBlock.size() > value.size())
return false;
if (kEmptyUncompressedBlock.size() > value.size()) return false;
auto N = value.size();
return value[N - 1] == kEmptyUncompressedBlock[3] &&
value[N - 2] == kEmptyUncompressedBlock[2] &&
@@ -76,7 +73,12 @@ bool WebSocketPerMessageDeflateCompressor::endsWithEmptyUnCompressedBlock(
value[N - 4] == kEmptyUncompressedBlock[0];
}
bool WebSocketPerMessageDeflateCompressor::compress(const std::string &in,
bool WebSocketPerMessageDeflateCompressor::compress(const std::string& in, std::string& out)
{
return compressData(in, out);
}
bool WebSocketPerMessageDeflateCompressor::compress(const IXWebSocketSendData& in,
std::string& out)
{
return compressData(in, out);
@@ -88,15 +90,13 @@ bool WebSocketPerMessageDeflateCompressor::compress(const std::string &in,
return compressData(in, out);
}
bool WebSocketPerMessageDeflateCompressor::compress(
const std::vector<uint8_t> &in,
bool WebSocketPerMessageDeflateCompressor::compress(const std::vector<uint8_t>& in,
std::string& out)
{
return compressData(in, out);
}
bool WebSocketPerMessageDeflateCompressor::compress(
const std::vector<uint8_t> &in,
bool WebSocketPerMessageDeflateCompressor::compress(const std::vector<uint8_t>& in,
std::vector<uint8_t>& out)
{
return compressData(in, out);
@@ -128,7 +128,8 @@ bool WebSocketPerMessageDeflateCompressor::compressData(const T &in, S &out)
// Clear output
out.clear();
if (in.empty()) {
if (in.empty())
{
// See issue #167
// The normal buffer size should be 6 but
// we remove the 4 octets from the tail (#4)
@@ -142,7 +143,8 @@ bool WebSocketPerMessageDeflateCompressor::compressData(const T &in, S &out)
_deflateState.avail_in = (uInt) in.size();
_deflateState.next_in = (Bytef*) in.data();
do {
do
{
// Output to local buffer
_deflateState.avail_out = (uInt) _compressBuffer.size();
_deflateState.next_out = &_compressBuffer.front();
@@ -151,12 +153,11 @@ bool WebSocketPerMessageDeflateCompressor::compressData(const T &in, S &out)
output = _compressBuffer.size() - _deflateState.avail_out;
out.insert(out.end(),
_compressBuffer.begin(),
_compressBuffer.begin() + output);
out.insert(out.end(), _compressBuffer.begin(), _compressBuffer.begin() + output);
} while (_deflateState.avail_out == 0);
if (endsWithEmptyUnCompressedBlock(out)) {
if (endsWithEmptyUnCompressedBlock(out))
{
out.resize(out.size() - 4);
}
@@ -182,8 +183,7 @@ WebSocketPerMessageDeflateDecompressor::WebSocketPerMessageDeflateDecompressor()
#endif
}
WebSocketPerMessageDeflateDecompressor::
~WebSocketPerMessageDeflateDecompressor()
WebSocketPerMessageDeflateDecompressor::~WebSocketPerMessageDeflateDecompressor()
{
#ifdef IXWEBSOCKET_USE_ZLIB
inflateEnd(&_inflateState);
@@ -196,8 +196,7 @@ bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
#ifdef IXWEBSOCKET_USE_ZLIB
int ret = inflateInit2(&_inflateState, -1 * inflateBits);
if (ret != Z_OK)
return false;
if (ret != Z_OK) return false;
_flush = (clientNoContextTakeOver) ? Z_FULL_FLUSH : Z_SYNC_FLUSH;
@@ -207,8 +206,7 @@ bool WebSocketPerMessageDeflateDecompressor::init(uint8_t inflateBits,
#endif
}
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string &in,
std::string &out)
bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string& in, std::string& out)
{
#ifdef IXWEBSOCKET_USE_ZLIB
//
@@ -225,19 +223,20 @@ bool WebSocketPerMessageDeflateDecompressor::decompress(const std::string &in,
inFixed += kEmptyUncompressedBlock;
_inflateState.avail_in = (uInt) inFixed.size();
_inflateState.next_in =
(unsigned char *)(const_cast<char *>(inFixed.data()));
_inflateState.next_in = (unsigned char*) (const_cast<char*>(inFixed.data()));
// Clear output
out.clear();
do {
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) {
if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
{
return false; // zlib error
}
@@ -13,13 +13,11 @@
namespace ix
{
/// Default values as defined in the RFC
const uint8_t WebSocketPerMessageDeflateOptions::kDefaultServerMaxWindowBits =
15;
const uint8_t WebSocketPerMessageDeflateOptions::kDefaultServerMaxWindowBits = 15;
static const uint8_t minServerMaxWindowBits = 8;
static const uint8_t maxServerMaxWindowBits = 15;
const uint8_t WebSocketPerMessageDeflateOptions::kDefaultClientMaxWindowBits =
15;
const uint8_t WebSocketPerMessageDeflateOptions::kDefaultClientMaxWindowBits = 15;
static const uint8_t minClientMaxWindowBits = 8;
static const uint8_t maxClientMaxWindowBits = 15;
@@ -53,8 +51,7 @@ WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(
// Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover;
// server_no_context_takeover
//
WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(
std::string extension)
WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(std::string extension)
{
extension = removeSpaces(extension);
@@ -69,43 +66,41 @@ WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(
std::string token;
std::stringstream tokenStream(extension);
while (std::getline(tokenStream, token, ';')) {
if (token == "permessage-deflate") {
while (std::getline(tokenStream, token, ';'))
{
if (token == "permessage-deflate")
{
_enabled = true;
}
if (token == "server_no_context_takeover") {
if (token == "server_no_context_takeover")
{
_serverNoContextTakeover = true;
}
if (token == "client_no_context_takeover") {
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);
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));
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);
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));
std::min(maxClientMaxWindowBits, std::max(x, minClientMaxWindowBits));
sanitizeClientMaxWindowBits();
}
@@ -115,9 +110,10 @@ WebSocketPerMessageDeflateOptions::WebSocketPerMessageDeflateOptions(
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) {
// 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;
}
}
@@ -128,13 +124,11 @@ std::string WebSocketPerMessageDeflateOptions::generateHeader()
std::stringstream ss;
ss << "Sec-WebSocket-Extensions: permessage-deflate";
if (_clientNoContextTakeover)
ss << "; client_no_context_takeover";
if (_serverNoContextTakeover)
ss << "; server_no_context_takeover";
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 << "; server_max_window_bits=" << static_cast<int>(_serverMaxWindowBits);
ss << "; client_max_window_bits=" << static_cast<int>(_clientMaxWindowBits);
ss << "\r\n";
@@ -179,13 +173,11 @@ bool WebSocketPerMessageDeflateOptions::startsWith(const std::string &str,
return str.compare(0, start.length(), start) == 0;
}
std::string
WebSocketPerMessageDeflateOptions::removeSpaces(const std::string &str)
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.erase(
std::remove_if(out.begin(), out.end(), [](unsigned char x) { return std::isspace(x); }),
out.end());
return out;
+45 -25
View File
@@ -14,13 +14,25 @@ namespace ix
class ProxyConnectionState : public ix::ConnectionState
{
public:
ProxyConnectionState() : _connected(false) {}
ProxyConnectionState()
: _connected(false)
{
}
ix::WebSocket &webSocket() { return _serverWebSocket; }
ix::WebSocket& webSocket()
{
return _serverWebSocket;
}
bool isConnected() { return _connected; }
bool isConnected()
{
return _connected;
}
void setConnected() { _connected = true; }
void setConnected()
{
_connected = true;
}
private:
ix::WebSocket _serverWebSocket;
@@ -43,21 +55,23 @@ int websocket_proxy_server_main(int port,
server.setConnectionStateFactory(factory);
server.setOnConnectionCallback(
[remoteUrl,
remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket,
[remoteUrl, remoteUrlsMapping](std::weak_ptr<ix::WebSocket> webSocket,
std::shared_ptr<ConnectionState> connectionState) {
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(
connectionState);
auto state = std::dynamic_pointer_cast<ProxyConnectionState>(connectionState);
auto remoteIp = connectionState->getRemoteIp();
// Server connection
state->webSocket().setOnMessageCallback(
[webSocket, state, remoteIp](const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Close) {
if (msg->type == ix::WebSocketMessageType::Close)
{
state->setTerminated();
} else if (msg->type == ix::WebSocketMessageType::Message) {
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
auto ws = webSocket.lock();
if (ws) {
if (ws)
{
ws->send(msg->str, msg->binary);
}
}
@@ -65,17 +79,20 @@ int websocket_proxy_server_main(int port,
// Client connection
auto ws = webSocket.lock();
if (ws) {
if (ws)
{
ws->setOnMessageCallback([state, remoteUrl, remoteUrlsMapping](
const WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Open) {
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()) {
if (it != remoteUrlsMapping.end())
{
url = it->second;
}
@@ -87,17 +104,19 @@ int websocket_proxy_server_main(int port,
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));
// 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) {
}
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);
}
});
@@ -105,7 +124,8 @@ int websocket_proxy_server_main(int port,
});
auto res = server.listen();
if (!res.first) {
if (!res.first)
{
return 1;
}

Some files were not shown because too many files have changed in this diff Show More