Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92159b0669 | |||
| 115e4d9265 | |||
| 07487e9e51 | |||
| 46a0607b55 | |||
| de8b282ab1 | |||
| 6027095c38 | |||
| 2cf03d2933 | |||
| 161941ac1b | |||
| 4445f1f12a | |||
| d6d3c0b4d9 | |||
| 94fbc1f558 | |||
| 2fc2170763 | |||
| 699e2a4784 | |||
| b050c23577 | |||
| a5e23f4b43 | |||
| 7d45ca71ba | |||
| 8b9f5d0406 | |||
| 2bda541bce | |||
| 25c4d8474d | |||
| 910afad3b5 | |||
| 2aa658c15e | |||
| 3559b124a7 | |||
| 2510f4968c | |||
| 752646c744 | |||
| b22a60c85e | |||
| 9dcca746ed | |||
| 0612afb37b | |||
| ff179ee3a4 | |||
| cd022c8063 | |||
| a536a9c918 | |||
| 3dce2ca488 | |||
| b695f4cb6c | |||
| 2914e1fa59 | |||
| 71656e4a66 | |||
| 5e80c41538 | |||
| c6ad412df9 | |||
| 0fe7335987 | |||
| 3b2f146679 | |||
| a820f7456a | |||
| fbee417594 | |||
| 7439747876 | |||
| 27edf28f42 | |||
| ca5aab1344 |
+5
-4
@@ -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
|
||||
|
||||
Generated
-8
@@ -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
@@ -1 +0,0 @@
|
||||
GermanAirlinesVA_GAConnector
|
||||
Generated
-2
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||
Generated
-5
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
Generated
-4
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
||||
Generated
-8
@@ -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
@@ -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>
|
||||
Vendored
+38
-5
@@ -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
|
||||
|
||||
Vendored
+17
@@ -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"
|
||||
}
|
||||
]
|
||||
Vendored
-34
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+26
-3
@@ -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"
|
||||
},
|
||||
}
|
||||
Vendored
-28
@@ -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
@@ -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
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,3 +9,6 @@
|
||||
|
||||
X-Plane Plugin for all supported platforms.
|
||||
Demo of WebSocket capabilities
|
||||
|
||||
Requirements:
|
||||
- openSSL 1.1.x
|
||||
@@ -0,0 +1,2 @@
|
||||
- Update OSXCross Docker image to SDK 11
|
||||
- Implement ARM64 arch for Plugin
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
Vendored
+21
@@ -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}"
|
||||
}
|
||||
}]
|
||||
}
|
||||
@@ -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"];
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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());
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user