mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Add HTTPS Lua module.
It can be loaded via https = require("https").
Basic API is responsecode, body = https.request(url).
Slightly more advanced API is responsecode, body, headers = https.request(url, {method="post" or "get", data=str, headers={}}).
This commit is contained in:
@@ -1489,6 +1489,73 @@ set(LOVE_SRC_3P_LUA53
|
||||
add_library(love_3p_lua53 ${LOVE_SRC_3P_LUA53})
|
||||
target_link_libraries(love_3p_lua53 ${LOVE_LUA_LIBRARY})
|
||||
|
||||
#
|
||||
# Lua HTTPS
|
||||
#
|
||||
|
||||
set(LOVE_SRC_3P_LUAHTTPS_ANDROID
|
||||
src/libraries/luahttps/src/android/AndroidClient.cpp
|
||||
src/libraries/luahttps/src/android/AndroidClient.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_3P_LUAHTTPS_APPLE
|
||||
src/libraries/luahttps/src/apple/NSURLClient.mm
|
||||
src/libraries/luahttps/src/apple/NSURLClient.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_3P_LUAHTTPS_COMMON
|
||||
src/libraries/luahttps/src/common/config.h
|
||||
src/libraries/luahttps/src/common/Connection.h
|
||||
src/libraries/luahttps/src/common/ConnectionClient.h
|
||||
src/libraries/luahttps/src/common/HTTPRequest.cpp
|
||||
src/libraries/luahttps/src/common/HTTPRequest.h
|
||||
src/libraries/luahttps/src/common/HTTPS.cpp
|
||||
src/libraries/luahttps/src/common/HTTPS.h
|
||||
src/libraries/luahttps/src/common/HTTPSClient.cpp
|
||||
src/libraries/luahttps/src/common/HTTPSClient.h
|
||||
src/libraries/luahttps/src/common/PlaintextConnection.cpp
|
||||
src/libraries/luahttps/src/common/PlaintextConnection.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_3P_LUAHTTPS_GENERIC
|
||||
src/libraries/luahttps/src/generic/CurlClient.cpp
|
||||
src/libraries/luahttps/src/generic/CurlClient.h
|
||||
src/libraries/luahttps/src/generic/OpenSSLConnection.cpp
|
||||
src/libraries/luahttps/src/generic/OpenSSLConnection.h
|
||||
)
|
||||
|
||||
set(LOVE_SRC_3P_LUAHTTPS_LUA
|
||||
src/libraries/luahttps/src/lua/main.cpp
|
||||
)
|
||||
|
||||
set(LOVE_SRC_3P_LUAHTTPS_WINDOWS
|
||||
src/libraries/luahttps/src/windows/SChannelConnection.cpp
|
||||
src/libraries/luahttps/src/windows/SChannelConnection.h
|
||||
)
|
||||
|
||||
# These are platform-dependent but have ifdef guards to make sure they only
|
||||
# compile on supported platforms.
|
||||
set(LOVE_SRC_3P_LUAHTTPS
|
||||
${LOVE_SRC_3P_LUAHTTPS_ANDROID}
|
||||
${LOVE_SRC_3P_LUAHTTPS_APPLE}
|
||||
${LOVE_SRC_3P_LUAHTTPS_COMMON}
|
||||
${LOVE_SRC_3P_LUAHTTPS_GENERIC}
|
||||
${LOVE_SRC_3P_LUAHTTPS_LUA}
|
||||
${LOVE_SRC_3P_LUAHTTPS_WINDOWS}
|
||||
)
|
||||
|
||||
set(LOVE_LINK_L3P_LUAHTTPS)
|
||||
if(MSVC)
|
||||
set(LOVE_LINK_L3P_LUAHTTPS
|
||||
${LOVE_LINK_L3P_LUASOCKET_LIBLUASOCKET}
|
||||
ws2_32
|
||||
secur32
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(love_3p_luahttps $${LOVE_SRC_3P_LUAHTTPS})
|
||||
target_link_libraries(love_3p_luahttps ${LOVE_LUA_LIBRARY} ${LOVE_LINK_L3P_LUAHTTPS})
|
||||
|
||||
#
|
||||
# lz4
|
||||
#
|
||||
@@ -1636,6 +1703,7 @@ set(LOVE_3P
|
||||
love_3p_lodepng
|
||||
love_3p_luasocket
|
||||
love_3p_lua53
|
||||
love_3p_luahttps
|
||||
love_3p_lz4
|
||||
love_3p_noise1234
|
||||
love_3p_physfs
|
||||
|
||||
@@ -829,9 +829,9 @@
|
||||
FA76344A1E28722A0066EF9E /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7634481E28722A0066EF9E /* StreamBuffer.cpp */; };
|
||||
FA76344B1E28722A0066EF9E /* StreamBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7634481E28722A0066EF9E /* StreamBuffer.cpp */; };
|
||||
FA76344C1E28722A0066EF9E /* StreamBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7634491E28722A0066EF9E /* StreamBuffer.h */; };
|
||||
FA7E9207277E120900C24CB2 /* theora.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA7E9206277E120900C24CB2 /* theora.xcframework */; };
|
||||
FA84DE612778D7F3002674C6 /* SpirvIntrinsics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA84DE602778D7F3002674C6 /* SpirvIntrinsics.cpp */; };
|
||||
FA84DE622778D7F3002674C6 /* SpirvIntrinsics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA84DE602778D7F3002674C6 /* SpirvIntrinsics.cpp */; };
|
||||
FA7E9207277E120900C24CB2 /* theora.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA7E9206277E120900C24CB2 /* theora.xcframework */; };
|
||||
FA84DE76277CB3D5002674C6 /* SDL2.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA84DE75277CB3D4002674C6 /* SDL2.xcframework */; };
|
||||
FA84DE7A277D4C88002674C6 /* modplug.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA84DE79277D4C88002674C6 /* modplug.xcframework */; };
|
||||
FA84DE7C277E045E002674C6 /* ogg.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA84DE7B277E045E002674C6 /* ogg.xcframework */; };
|
||||
@@ -844,6 +844,26 @@
|
||||
FA91DA8D1F377C3900C80E33 /* deprecation.h in Headers */ = {isa = PBXBuildFile; fileRef = FA91DA8A1F377C3900C80E33 /* deprecation.h */; };
|
||||
FA93C4531F315B960087CCD4 /* FormatHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA93C4501F315B960087CCD4 /* FormatHandler.h */; };
|
||||
FA93C4541F315B960087CCD4 /* FormatHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA93C4511F315B960087CCD4 /* FormatHandler.cpp */; };
|
||||
FA94727827A6EE1B00817677 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725227A6EE1B00817677 /* main.cpp */; };
|
||||
FA94727927A6EE1B00817677 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725227A6EE1B00817677 /* main.cpp */; };
|
||||
FA94727A27A6EE1B00817677 /* Connection.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94725427A6EE1B00817677 /* Connection.h */; };
|
||||
FA94727B27A6EE1B00817677 /* HTTPSClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725527A6EE1B00817677 /* HTTPSClient.cpp */; };
|
||||
FA94727C27A6EE1B00817677 /* HTTPSClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725527A6EE1B00817677 /* HTTPSClient.cpp */; };
|
||||
FA94727D27A6EE1B00817677 /* HTTPSClient.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94725627A6EE1B00817677 /* HTTPSClient.h */; };
|
||||
FA94727E27A6EE1B00817677 /* PlaintextConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94725727A6EE1B00817677 /* PlaintextConnection.h */; };
|
||||
FA94727F27A6EE1B00817677 /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94725827A6EE1B00817677 /* config.h */; };
|
||||
FA94728127A6EE1B00817677 /* HTTPS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725A27A6EE1B00817677 /* HTTPS.cpp */; };
|
||||
FA94728227A6EE1B00817677 /* HTTPS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725A27A6EE1B00817677 /* HTTPS.cpp */; };
|
||||
FA94728327A6EE1B00817677 /* HTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94725B27A6EE1B00817677 /* HTTPRequest.h */; };
|
||||
FA94728427A6EE1B00817677 /* ConnectionClient.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94725C27A6EE1B00817677 /* ConnectionClient.h */; };
|
||||
FA94728527A6EE1B00817677 /* PlaintextConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725D27A6EE1B00817677 /* PlaintextConnection.cpp */; };
|
||||
FA94728627A6EE1B00817677 /* PlaintextConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725D27A6EE1B00817677 /* PlaintextConnection.cpp */; };
|
||||
FA94728727A6EE1B00817677 /* HTTPS.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94725E27A6EE1B00817677 /* HTTPS.h */; };
|
||||
FA94728827A6EE1B00817677 /* HTTPRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725F27A6EE1B00817677 /* HTTPRequest.cpp */; };
|
||||
FA94728927A6EE1B00817677 /* HTTPRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA94725F27A6EE1B00817677 /* HTTPRequest.cpp */; };
|
||||
FA94729B27A6F9AD00817677 /* NSURLClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA94729927A6F9AC00817677 /* NSURLClient.mm */; };
|
||||
FA94729C27A6F9AD00817677 /* NSURLClient.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA94729927A6F9AC00817677 /* NSURLClient.mm */; };
|
||||
FA94729D27A6F9AD00817677 /* NSURLClient.h in Headers */ = {isa = PBXBuildFile; fileRef = FA94729A27A6F9AC00817677 /* NSURLClient.h */; };
|
||||
FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0716E1578300074F42 /* SDL2.framework */; };
|
||||
FA9D53AC1F5307E900125C6B /* Deprecations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9D53AA1F5307E900125C6B /* Deprecations.cpp */; };
|
||||
FA9D53AD1F5307E900125C6B /* Deprecations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9D53AA1F5307E900125C6B /* Deprecations.cpp */; };
|
||||
@@ -1892,11 +1912,11 @@
|
||||
FA7634481E28722A0066EF9E /* StreamBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StreamBuffer.cpp; sourceTree = "<group>"; };
|
||||
FA7634491E28722A0066EF9E /* StreamBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamBuffer.h; sourceTree = "<group>"; };
|
||||
FA7DA04C1C16874A0056B200 /* wrap_Math.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = wrap_Math.lua; sourceTree = "<group>"; };
|
||||
FA7E9206277E120900C24CB2 /* theora.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = theora.xcframework; path = ios/libraries/theora.xcframework; sourceTree = "<group>"; };
|
||||
FA84DE5D2778D7DB002674C6 /* SpirvIntrinsics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpirvIntrinsics.h; sourceTree = "<group>"; };
|
||||
FA84DE5E2778D7DC002674C6 /* glslang_c_interface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = glslang_c_interface.h; sourceTree = "<group>"; };
|
||||
FA84DE5F2778D7DC002674C6 /* glslang_c_shader_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = glslang_c_shader_types.h; sourceTree = "<group>"; };
|
||||
FA84DE602778D7F3002674C6 /* SpirvIntrinsics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpirvIntrinsics.cpp; sourceTree = "<group>"; };
|
||||
FA7E9206277E120900C24CB2 /* theora.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = theora.xcframework; path = ios/libraries/theora.xcframework; sourceTree = "<group>"; };
|
||||
FA84DE75277CB3D4002674C6 /* SDL2.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = SDL2.xcframework; path = ios/libraries/SDL2.xcframework; sourceTree = "<group>"; };
|
||||
FA84DE79277D4C88002674C6 /* modplug.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = modplug.xcframework; path = ios/libraries/modplug.xcframework; sourceTree = "<group>"; };
|
||||
FA84DE7B277E045E002674C6 /* ogg.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ogg.xcframework; path = ios/libraries/ogg.xcframework; sourceTree = "<group>"; };
|
||||
@@ -1907,6 +1927,20 @@
|
||||
FA91DA8A1F377C3900C80E33 /* deprecation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = deprecation.h; sourceTree = "<group>"; };
|
||||
FA93C4501F315B960087CCD4 /* FormatHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormatHandler.h; sourceTree = "<group>"; };
|
||||
FA93C4511F315B960087CCD4 /* FormatHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FormatHandler.cpp; sourceTree = "<group>"; };
|
||||
FA94725227A6EE1B00817677 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
|
||||
FA94725427A6EE1B00817677 /* Connection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Connection.h; sourceTree = "<group>"; };
|
||||
FA94725527A6EE1B00817677 /* HTTPSClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPSClient.cpp; sourceTree = "<group>"; };
|
||||
FA94725627A6EE1B00817677 /* HTTPSClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPSClient.h; sourceTree = "<group>"; };
|
||||
FA94725727A6EE1B00817677 /* PlaintextConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaintextConnection.h; sourceTree = "<group>"; };
|
||||
FA94725827A6EE1B00817677 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
|
||||
FA94725A27A6EE1B00817677 /* HTTPS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPS.cpp; sourceTree = "<group>"; };
|
||||
FA94725B27A6EE1B00817677 /* HTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPRequest.h; sourceTree = "<group>"; };
|
||||
FA94725C27A6EE1B00817677 /* ConnectionClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConnectionClient.h; sourceTree = "<group>"; };
|
||||
FA94725D27A6EE1B00817677 /* PlaintextConnection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlaintextConnection.cpp; sourceTree = "<group>"; };
|
||||
FA94725E27A6EE1B00817677 /* HTTPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPS.h; sourceTree = "<group>"; };
|
||||
FA94725F27A6EE1B00817677 /* HTTPRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPRequest.cpp; sourceTree = "<group>"; };
|
||||
FA94729927A6F9AC00817677 /* NSURLClient.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NSURLClient.mm; sourceTree = "<group>"; };
|
||||
FA94729A27A6F9AC00817677 /* NSURLClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSURLClient.h; sourceTree = "<group>"; };
|
||||
FA9B4A0716E1578300074F42 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = macosx/Frameworks/SDL2.framework; sourceTree = "<group>"; };
|
||||
FA9D53AA1F5307E900125C6B /* Deprecations.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Deprecations.cpp; sourceTree = "<group>"; };
|
||||
FA9D53AB1F5307E900125C6B /* Deprecations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Deprecations.h; sourceTree = "<group>"; };
|
||||
@@ -2368,6 +2402,7 @@
|
||||
FA0B79D31A958EA3000E1D17 /* glad */,
|
||||
FAF13FBF1E20934C00F898D2 /* glslang */,
|
||||
FA0B79D81A958EA3000E1D17 /* lodepng */,
|
||||
FA94724527A6EE1B00817677 /* luahttps */,
|
||||
FAAA3FD21F64B3AD00F89E99 /* lua53 */,
|
||||
FA0B79DB1A958EA3000E1D17 /* luasocket */,
|
||||
FAB17BE31ABFAA9000F9BA27 /* lz4 */,
|
||||
@@ -3469,6 +3504,59 @@
|
||||
name = ios;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA94724527A6EE1B00817677 /* luahttps */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FA94724D27A6EE1B00817677 /* src */,
|
||||
);
|
||||
path = luahttps;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA94724D27A6EE1B00817677 /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FA94729827A6F9AC00817677 /* apple */,
|
||||
FA94725327A6EE1B00817677 /* common */,
|
||||
FA94725127A6EE1B00817677 /* lua */,
|
||||
);
|
||||
path = src;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA94725127A6EE1B00817677 /* lua */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FA94725227A6EE1B00817677 /* main.cpp */,
|
||||
);
|
||||
path = lua;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA94725327A6EE1B00817677 /* common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FA94725827A6EE1B00817677 /* config.h */,
|
||||
FA94725427A6EE1B00817677 /* Connection.h */,
|
||||
FA94725C27A6EE1B00817677 /* ConnectionClient.h */,
|
||||
FA94725F27A6EE1B00817677 /* HTTPRequest.cpp */,
|
||||
FA94725B27A6EE1B00817677 /* HTTPRequest.h */,
|
||||
FA94725A27A6EE1B00817677 /* HTTPS.cpp */,
|
||||
FA94725E27A6EE1B00817677 /* HTTPS.h */,
|
||||
FA94725527A6EE1B00817677 /* HTTPSClient.cpp */,
|
||||
FA94725627A6EE1B00817677 /* HTTPSClient.h */,
|
||||
FA94725D27A6EE1B00817677 /* PlaintextConnection.cpp */,
|
||||
FA94725727A6EE1B00817677 /* PlaintextConnection.h */,
|
||||
);
|
||||
path = common;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA94729827A6F9AC00817677 /* apple */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FA94729A27A6F9AC00817677 /* NSURLClient.h */,
|
||||
FA94729927A6F9AC00817677 /* NSURLClient.mm */,
|
||||
);
|
||||
path = apple;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FAAA3FD21F64B3AD00F89E99 /* lua53 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -3861,7 +3949,6 @@
|
||||
FAF6C9C623C2DE2900D7B5BC /* InReadableOrder.cpp */,
|
||||
FAF6C9D523C2DE2900D7B5BC /* Logger.cpp */,
|
||||
FAF6C9D723C2DE2900D7B5BC /* Logger.h */,
|
||||
FAEC22942534EE6700EBD925 /* NonSemanticDebugPrintf.h */,
|
||||
FAF6C9C923C2DE2900D7B5BC /* spirv.hpp */,
|
||||
FAF6C9CA23C2DE2900D7B5BC /* SpvBuilder.cpp */,
|
||||
FAF6C9C223C2DE2900D7B5BC /* SpvBuilder.h */,
|
||||
@@ -3907,6 +3994,7 @@
|
||||
217DFBEA1D9F6D490055D849 /* io.h in Headers */,
|
||||
FA0B79221A958E3B000E1D17 /* delay.h in Headers */,
|
||||
FA0B79481A958E3B000E1D17 /* Vector.h in Headers */,
|
||||
FA94728427A6EE1B00817677 /* ConnectionClient.h in Headers */,
|
||||
FA0B7CCF1A95902C000E1D17 /* Audio.h in Headers */,
|
||||
FA0B7EC01A95902C000E1D17 /* Thread.h in Headers */,
|
||||
FABDA9E12552448300B5C523 /* b2_wheel_joint.h in Headers */,
|
||||
@@ -3971,6 +4059,7 @@
|
||||
FA0B7AC31A958EA3000E1D17 /* list.h in Headers */,
|
||||
FA0B7B2D1A958EA3000E1D17 /* core.h in Headers */,
|
||||
FA1E88841DF363DB00E808AA /* Filter.h in Headers */,
|
||||
FA94727D27A6EE1B00817677 /* HTTPSClient.h in Headers */,
|
||||
217DFC061D9F6D490055D849 /* tp.lua.h in Headers */,
|
||||
FABDA9E02552448300B5C523 /* b2_polygon_shape.h in Headers */,
|
||||
FAB17BF71ABFC4B100F9BA27 /* lz4hc.h in Headers */,
|
||||
@@ -4103,7 +4192,9 @@
|
||||
217DFBFD1D9F6D490055D849 /* smtp.lua.h in Headers */,
|
||||
FABDA9772552448200B5C523 /* b2_shape.h in Headers */,
|
||||
FA27B39F1B498151008A9DCE /* Video.h in Headers */,
|
||||
FA94727A27A6EE1B00817677 /* Connection.h in Headers */,
|
||||
FA18CF3223DCF67900263725 /* spirv_cfg.hpp in Headers */,
|
||||
FA94727F27A6EE1B00817677 /* config.h in Headers */,
|
||||
217DFC041D9F6D490055D849 /* timeout.h in Headers */,
|
||||
FA0B7E7A1A95902C000E1D17 /* wrap_WheelJoint.h in Headers */,
|
||||
FA0B7DD81A95902C000E1D17 /* MathModule.h in Headers */,
|
||||
@@ -4165,6 +4256,7 @@
|
||||
FA0B7EAB1A95902C000E1D17 /* wrap_Sound.h in Headers */,
|
||||
FA0B7B2C1A958EA3000E1D17 /* checked.h in Headers */,
|
||||
FABDA9A62552448300B5C523 /* b2_polygon_contact.h in Headers */,
|
||||
FA94728727A6EE1B00817677 /* HTTPS.h in Headers */,
|
||||
FA0B7D2A1A95902C000E1D17 /* wrap_GlyphData.h in Headers */,
|
||||
FACA02F11F5E396B0084B28F /* DataModule.h in Headers */,
|
||||
FABDA9C82552448300B5C523 /* b2_body.h in Headers */,
|
||||
@@ -4176,6 +4268,7 @@
|
||||
FAF1407F1E20934C00F898D2 /* localintermediate.h in Headers */,
|
||||
FAF1406B1E20934C00F898D2 /* glslang_tab.cpp.h in Headers */,
|
||||
217DFBF71D9F6D490055D849 /* options.h in Headers */,
|
||||
FA94728327A6EE1B00817677 /* HTTPRequest.h in Headers */,
|
||||
FAF6C9ED23C2DE2900D7B5BC /* spvIR.h in Headers */,
|
||||
FA0B7E3B1A95902C000E1D17 /* World.h in Headers */,
|
||||
FA0B7DC31A95902C000E1D17 /* wrap_Joystick.h in Headers */,
|
||||
@@ -4186,6 +4279,7 @@
|
||||
FA0B7E651A95902C000E1D17 /* wrap_PolygonShape.h in Headers */,
|
||||
FA18CEE323DBC6E000263725 /* Metal.h in Headers */,
|
||||
FABDA9952552448300B5C523 /* b2_edge_polygon_contact.h in Headers */,
|
||||
FA94729D27A6F9AD00817677 /* NSURLClient.h in Headers */,
|
||||
FA0B7AC91A958EA3000E1D17 /* win32.h in Headers */,
|
||||
FABDA9B92552448300B5C523 /* b2_common.h in Headers */,
|
||||
FA0B7DFC1A95902C000E1D17 /* Body.h in Headers */,
|
||||
@@ -4315,6 +4409,7 @@
|
||||
FA0B7E411A95902C000E1D17 /* wrap_ChainShape.h in Headers */,
|
||||
FA18CF1A23DCF67900263725 /* barrier.hpp in Headers */,
|
||||
FA0B7E171A95902C000E1D17 /* Joint.h in Headers */,
|
||||
FA94727E27A6EE1B00817677 /* PlaintextConnection.h in Headers */,
|
||||
FA0B793F1A958E3B000E1D17 /* types.h in Headers */,
|
||||
FABDA9BD2552448300B5C523 /* b2_stack_allocator.h in Headers */,
|
||||
FABDA9CF2552448300B5C523 /* b2_mouse_joint.h in Headers */,
|
||||
@@ -4513,6 +4608,7 @@
|
||||
FA18CF2323DCF67900263725 /* spirv_cfg.cpp in Sources */,
|
||||
FAE64A962071365100BC7981 /* physfs_platform_windows.c in Sources */,
|
||||
FA4B66CA1ABBCF1900558F15 /* Timer.cpp in Sources */,
|
||||
FA94727927A6EE1B00817677 /* main.cpp in Sources */,
|
||||
FA0B7E921A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */,
|
||||
FA0B7E521A95902C000E1D17 /* wrap_FrictionJoint.cpp in Sources */,
|
||||
FADF54351E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp in Sources */,
|
||||
@@ -4584,6 +4680,7 @@
|
||||
FA0B7E011A95902C000E1D17 /* CircleShape.cpp in Sources */,
|
||||
FAE64A8C2071363100BC7981 /* physfs_byteorder.c in Sources */,
|
||||
FA0B7E461A95902C000E1D17 /* wrap_Contact.cpp in Sources */,
|
||||
FA94727C27A6EE1B00817677 /* HTTPSClient.cpp in Sources */,
|
||||
FA0B7D161A95902C000E1D17 /* Font.cpp in Sources */,
|
||||
FA0B7EB61A95902C000E1D17 /* wrap_System.cpp in Sources */,
|
||||
FA0B7DAC1A95902C000E1D17 /* STBHandler.cpp in Sources */,
|
||||
@@ -4642,6 +4739,7 @@
|
||||
FA0B7DA01A95902C000E1D17 /* KTXHandler.cpp in Sources */,
|
||||
FA0B7CEC1A95902C000E1D17 /* Event.cpp in Sources */,
|
||||
FA27B3AB1B498151008A9DCE /* VideoStream.cpp in Sources */,
|
||||
FA94728227A6EE1B00817677 /* HTTPS.cpp in Sources */,
|
||||
FA0B7E581A95902C000E1D17 /* wrap_Joint.cpp in Sources */,
|
||||
FAECA1B51F31648A0095D008 /* FormatHandler.cpp in Sources */,
|
||||
FA4F2C0A1DE936E600CA37D7 /* mime.c in Sources */,
|
||||
@@ -4650,6 +4748,8 @@
|
||||
FA4F2C101DE936FE00CA37D7 /* udp.c in Sources */,
|
||||
FAF6C9E023C2DE2900D7B5BC /* SpvTools.cpp in Sources */,
|
||||
FAE64A8F2071364200BC7981 /* physfs_platform_unix.c in Sources */,
|
||||
FA94729C27A6F9AD00817677 /* NSURLClient.mm in Sources */,
|
||||
FA94728927A6EE1B00817677 /* HTTPRequest.cpp in Sources */,
|
||||
FA18CEC623D3AE6800263725 /* wrap_Buffer.cpp in Sources */,
|
||||
FA0B7E2B1A95902C000E1D17 /* RevoluteJoint.cpp in Sources */,
|
||||
FA0B7B291A958EA3000E1D17 /* simplexnoise1234.cpp in Sources */,
|
||||
@@ -4715,6 +4815,7 @@
|
||||
FA29C0061E12355B00268CD8 /* StreamBuffer.cpp in Sources */,
|
||||
FABDA9B62552448300B5C523 /* b2_fixture.cpp in Sources */,
|
||||
FAE64A882071363100BC7981 /* physfs_archiver_unpacked.c in Sources */,
|
||||
FA94728627A6EE1B00817677 /* PlaintextConnection.cpp in Sources */,
|
||||
FA18CEDF23DBC6E000263725 /* Shader.mm in Sources */,
|
||||
FA0B7CF51A95902C000E1D17 /* File.cpp in Sources */,
|
||||
FA0B7E341A95902C000E1D17 /* WeldJoint.cpp in Sources */,
|
||||
@@ -4927,6 +5028,7 @@
|
||||
FAF1406E1E20934C00F898D2 /* Initialize.cpp in Sources */,
|
||||
FAF6C9DF23C2DE2900D7B5BC /* SpvTools.cpp in Sources */,
|
||||
FA0B7EB81A95902C000E1D17 /* Channel.cpp in Sources */,
|
||||
FA94727827A6EE1B00817677 /* main.cpp in Sources */,
|
||||
217DFC091D9F6D490055D849 /* unix.c in Sources */,
|
||||
FACA02EE1F5E396B0084B28F /* Compressor.cpp in Sources */,
|
||||
FAF140801E20934C00F898D2 /* parseConst.cpp in Sources */,
|
||||
@@ -4998,6 +5100,7 @@
|
||||
FA0B7D821A95902C000E1D17 /* CompressedImageData.cpp in Sources */,
|
||||
FA18CF2823DCF67900263725 /* spirv_msl.cpp in Sources */,
|
||||
FAF1409D1E20934C00F898D2 /* reflection.cpp in Sources */,
|
||||
FA94727B27A6EE1B00817677 /* HTTPSClient.cpp in Sources */,
|
||||
FAAA3FDB1F64B3AD00F89E99 /* lutf8lib.c in Sources */,
|
||||
FA18CEDE23DBC6E000263725 /* Shader.mm in Sources */,
|
||||
FAF1408A1E20934C00F898D2 /* Pp.cpp in Sources */,
|
||||
@@ -5056,6 +5159,7 @@
|
||||
FA41A3C81C0A1F950084430C /* ASTCHandler.cpp in Sources */,
|
||||
FABDA9DA2552448300B5C523 /* b2_math.cpp in Sources */,
|
||||
FA0B7E781A95902C000E1D17 /* wrap_WheelJoint.cpp in Sources */,
|
||||
FA94728127A6EE1B00817677 /* HTTPS.cpp in Sources */,
|
||||
FA0B7DDC1A95902C000E1D17 /* wrap_BezierCurve.cpp in Sources */,
|
||||
FA0B7D061A95902C000E1D17 /* wrap_File.cpp in Sources */,
|
||||
FAC7CD871FE35E95006A60C7 /* physfs_archiver_vdf.c in Sources */,
|
||||
@@ -5064,6 +5168,8 @@
|
||||
FADF54201E3DA52C00012CC0 /* wrap_ParticleSystem.cpp in Sources */,
|
||||
FA0B7D9F1A95902C000E1D17 /* KTXHandler.cpp in Sources */,
|
||||
FA1E88831DF363DB00E808AA /* Filter.cpp in Sources */,
|
||||
FA94729B27A6F9AD00817677 /* NSURLClient.mm in Sources */,
|
||||
FA94728827A6EE1B00817677 /* HTTPRequest.cpp in Sources */,
|
||||
FA0B7CEB1A95902C000E1D17 /* Event.cpp in Sources */,
|
||||
FA1557C31CE90BD200AFF582 /* EXRHandler.cpp in Sources */,
|
||||
FA27B3AA1B498151008A9DCE /* VideoStream.cpp in Sources */,
|
||||
@@ -5129,6 +5235,7 @@
|
||||
FA18CF3F23DCF67900263725 /* spirv_parser.cpp in Sources */,
|
||||
217DFBF21D9F6D490055D849 /* mime.c in Sources */,
|
||||
217DFBDF1D9F6D490055D849 /* except.c in Sources */,
|
||||
FA94728527A6EE1B00817677 /* PlaintextConnection.cpp in Sources */,
|
||||
FA28EBD51E352DB5003446F4 /* FenceSync.cpp in Sources */,
|
||||
FA0B7E1B1A95902C000E1D17 /* MouseJoint.cpp in Sources */,
|
||||
FA0B7CF41A95902C000E1D17 /* File.cpp in Sources */,
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
# define LOVE_ENABLE_ENET
|
||||
# define LOVE_ENABLE_LUASOCKET
|
||||
# define LOVE_ENABLE_LUA53
|
||||
# define LOVE_ENABLE_LUAHTTPS
|
||||
#endif
|
||||
|
||||
// Check we have a sane configuration
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := https
|
||||
LOCAL_MODULE_FILENAME := https
|
||||
|
||||
LOCAL_CFLAGS := -DNOMINMAX
|
||||
LOCAL_CPPFLAGS := -std=c++11
|
||||
|
||||
LOCAL_ARM_NEON := true
|
||||
|
||||
LOCAL_C_INCLUDES := \
|
||||
${LOCAL_PATH}/src \
|
||||
${LOCAL_PATH}/src/android
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
src/lua/main.cpp \
|
||||
src/common/HTTPS.cpp \
|
||||
src/common/HTTPRequest.cpp \
|
||||
src/common/HTTPSClient.cpp \
|
||||
src/common/PlaintextConnection.cpp \
|
||||
src/android/AndroidClient.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := liblove
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -0,0 +1 @@
|
||||
src/android/java
|
||||
@@ -0,0 +1,17 @@
|
||||
Copyright (c) 2019-2022 LOVE Development Team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,212 @@
|
||||
#include "AndroidClient.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_ANDROID
|
||||
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
static std::string replace(const std::string &str, const std::string &from, const std::string &to)
|
||||
{
|
||||
std::stringstream ss;
|
||||
size_t oldpos = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
size_t pos = str.find(from, oldpos);
|
||||
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
ss << str.substr(oldpos);
|
||||
break;
|
||||
}
|
||||
|
||||
ss << str.substr(oldpos, pos - oldpos) << to;
|
||||
oldpos = pos + from.length();
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static jstring newStringUTF(JNIEnv *env, const std::string &str)
|
||||
{
|
||||
// We want std::string that contains null byte, hence length of 1.
|
||||
static std::string null("", 1);
|
||||
|
||||
std::string newStr = replace(str, null, "\xC0\x80");
|
||||
jstring jstr = env->NewStringUTF(newStr.c_str());
|
||||
return jstr;
|
||||
}
|
||||
|
||||
static std::string getStringUTF(JNIEnv *env, jstring str)
|
||||
{
|
||||
// We want std::string that contains null byte, hence length of 1.
|
||||
static std::string null("", 1);
|
||||
|
||||
const char *c = env->GetStringUTFChars(str, nullptr);
|
||||
std::string result = replace(c, "\xC0\x80", null);
|
||||
|
||||
env->ReleaseStringUTFChars(str, c);
|
||||
return result;
|
||||
}
|
||||
|
||||
AndroidClient::AndroidClient()
|
||||
: HTTPSClient()
|
||||
, SDL_AndroidGetJNIEnv(nullptr)
|
||||
{
|
||||
// Look for SDL_AndroidGetJNIEnv
|
||||
SDL_AndroidGetJNIEnv = (decltype(SDL_AndroidGetJNIEnv)) dlsym(RTLD_DEFAULT, "SDL_AndroidGetJNIEnv");
|
||||
// Look for SDL_AndroidGetActivity
|
||||
SDL_AndroidGetActivity = (decltype(SDL_AndroidGetActivity)) dlsym(RTLD_DEFAULT, "SDL_AndroidGetActivity");
|
||||
}
|
||||
|
||||
bool AndroidClient::valid() const
|
||||
{
|
||||
if (SDL_AndroidGetJNIEnv && SDL_AndroidGetActivity)
|
||||
{
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
|
||||
if (env)
|
||||
{
|
||||
jclass httpsClass = getHTTPSClass();
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
return false;
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(httpsClass);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HTTPSClient::Reply AndroidClient::request(const HTTPSClient::Request &req)
|
||||
{
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
jclass httpsClass = getHTTPSClass();
|
||||
|
||||
if (httpsClass == nullptr)
|
||||
{
|
||||
env->ExceptionClear();
|
||||
throw std::runtime_error("Could not find class 'org.love2d.luahttps.LuaHTTPS'");
|
||||
}
|
||||
|
||||
jmethodID constructor = env->GetMethodID(httpsClass, "<init>", "()V");
|
||||
jmethodID setURL = env->GetMethodID(httpsClass, "setUrl", "(Ljava/lang/String;)V");
|
||||
jmethodID request = env->GetMethodID(httpsClass, "request", "()Z");
|
||||
jmethodID getInterleavedHeaders = env->GetMethodID(httpsClass, "getInterleavedHeaders", "()[Ljava/lang/String;");
|
||||
jmethodID getResponse = env->GetMethodID(httpsClass, "getResponse", "()[B");
|
||||
jmethodID getResponseCode = env->GetMethodID(httpsClass, "getResponseCode", "()I");
|
||||
|
||||
jobject httpsObject = env->NewObject(httpsClass, constructor);
|
||||
|
||||
// Set URL
|
||||
jstring url = env->NewStringUTF(req.url.c_str());
|
||||
env->CallVoidMethod(httpsObject, setURL, url);
|
||||
env->DeleteLocalRef(url);
|
||||
|
||||
// Set post data
|
||||
if (req.method == Request::POST)
|
||||
{
|
||||
jmethodID setPostData = env->GetMethodID(httpsClass, "setPostData", "([B)V");
|
||||
jbyteArray byteArray = env->NewByteArray((jsize) req.postdata.length());
|
||||
jbyte *byteArrayData = env->GetByteArrayElements(byteArray, nullptr);
|
||||
|
||||
memcpy(byteArrayData, req.postdata.data(), req.postdata.length());
|
||||
env->ReleaseByteArrayElements(byteArray, byteArrayData, 0);
|
||||
|
||||
env->CallVoidMethod(httpsObject, setPostData, byteArray);
|
||||
env->DeleteLocalRef(byteArray);
|
||||
}
|
||||
|
||||
// Set headers
|
||||
if (!req.headers.empty())
|
||||
{
|
||||
jmethodID addHeader = env->GetMethodID(httpsClass, "addHeader", "(Ljava/lang/String;Ljava/lang/String;)V");
|
||||
|
||||
for (auto &header : req.headers)
|
||||
{
|
||||
jstring headerKey = newStringUTF(env, header.first);
|
||||
jstring headerValue = newStringUTF(env, header.second);
|
||||
|
||||
env->CallVoidMethod(httpsObject, addHeader, headerKey, headerValue);
|
||||
env->DeleteLocalRef(headerKey);
|
||||
env->DeleteLocalRef(headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Do request
|
||||
HTTPSClient::Reply response;
|
||||
jboolean status = env->CallBooleanMethod(httpsObject, request);
|
||||
|
||||
// Get response
|
||||
response.responseCode = env->CallIntMethod(httpsObject, getResponseCode);
|
||||
|
||||
if (status)
|
||||
{
|
||||
// Get headers
|
||||
jobjectArray interleavedHeaders = (jobjectArray) env->CallObjectMethod(httpsObject, getInterleavedHeaders);
|
||||
int len = env->GetArrayLength(interleavedHeaders);
|
||||
|
||||
for (int i = 0; i < len; i += 2)
|
||||
{
|
||||
jstring key = (jstring) env->GetObjectArrayElement(interleavedHeaders, i);
|
||||
jstring value = (jstring) env->GetObjectArrayElement(interleavedHeaders, i + 1);
|
||||
|
||||
response.headers[getStringUTF(env, key)] = getStringUTF(env, value);
|
||||
|
||||
env->DeleteLocalRef(key);
|
||||
env->DeleteLocalRef(value);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(interleavedHeaders);
|
||||
|
||||
// Get response data
|
||||
jbyteArray responseData = (jbyteArray) env->CallObjectMethod(httpsObject, getResponse);
|
||||
|
||||
if (responseData)
|
||||
{
|
||||
int len = env->GetArrayLength(responseData);
|
||||
jbyte *responseByte = env->GetByteArrayElements(responseData, nullptr);
|
||||
|
||||
response.body = std::string((char *) responseByte, len);
|
||||
|
||||
env->DeleteLocalRef(responseData);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
jclass AndroidClient::getHTTPSClass() const
|
||||
{
|
||||
JNIEnv *env = SDL_AndroidGetJNIEnv();
|
||||
|
||||
jclass classLoaderClass = env->FindClass("java/lang/ClassLoader");
|
||||
jmethodID loadClass = env->GetMethodID(classLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
|
||||
jobject activity = SDL_AndroidGetActivity();
|
||||
|
||||
if (activity == nullptr)
|
||||
return nullptr;
|
||||
|
||||
jclass gameActivity = env->GetObjectClass(activity);
|
||||
jmethodID getLoader = env->GetMethodID(gameActivity, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
jobject classLoader = env->CallObjectMethod(activity, getLoader);
|
||||
|
||||
jstring httpsClassName = env->NewStringUTF("org.love2d.luahttps.LuaHTTPS");
|
||||
jclass httpsClass = (jclass) env->CallObjectMethod(classLoader, loadClass, httpsClassName);
|
||||
|
||||
env->DeleteLocalRef(gameActivity);
|
||||
env->DeleteLocalRef(httpsClassName);
|
||||
env->DeleteLocalRef(activity);
|
||||
env->DeleteLocalRef(classLoaderClass);
|
||||
|
||||
return httpsClass;
|
||||
}
|
||||
|
||||
#endif // HTTPS_BACKEND_ANDROID
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/config.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_ANDROID
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "../common/HTTPSClient.h"
|
||||
|
||||
class AndroidClient: public HTTPSClient
|
||||
{
|
||||
public:
|
||||
AndroidClient();
|
||||
|
||||
bool valid() const override;
|
||||
HTTPSClient::Reply request(const HTTPSClient::Request &req) override;
|
||||
|
||||
private:
|
||||
JNIEnv *(*SDL_AndroidGetJNIEnv)();
|
||||
jobject (*SDL_AndroidGetActivity)();
|
||||
|
||||
jclass getHTTPSClass() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
package org.love2d.luahttps;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Keep
|
||||
class LuaHTTPS {
|
||||
static private String TAG = "LuaHTTPS";
|
||||
|
||||
private String urlString;
|
||||
private byte[] postData;
|
||||
private byte[] response;
|
||||
private int responseCode;
|
||||
private HashMap<String, String> headers;
|
||||
|
||||
public LuaHTTPS() {
|
||||
headers = new HashMap<String, String>();
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
urlString = null;
|
||||
postData = null;
|
||||
response = null;
|
||||
responseCode = 0;
|
||||
headers.clear();
|
||||
}
|
||||
|
||||
@Keep
|
||||
public void setUrl(String url) {
|
||||
urlString = url;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public void setPostData(byte[] postData) {
|
||||
this.postData = postData;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public void addHeader(String key, String value) {
|
||||
headers.put(key, value);
|
||||
}
|
||||
|
||||
@Keep
|
||||
public String[] getInterleavedHeaders() {
|
||||
ArrayList<String> resultInterleaved = new ArrayList<String>();
|
||||
|
||||
for (Map.Entry<String, String> header: headers.entrySet()) {
|
||||
String key = header.getKey();
|
||||
String value = header.getValue();
|
||||
|
||||
if (key != null && value != null) {
|
||||
resultInterleaved.add(key);
|
||||
resultInterleaved.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
String[] result = new String[resultInterleaved.size()];
|
||||
resultInterleaved.toArray(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public int getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public byte[] getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public boolean request() {
|
||||
if (urlString == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
|
||||
if (!url.getProtocol().equals("http") && !url.getProtocol().equals("https")) {
|
||||
return false;
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
Log.e(TAG, "Error", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
HttpURLConnection connection;
|
||||
try {
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set header
|
||||
for (Map.Entry<String, String> headerData: headers.entrySet()) {
|
||||
connection.setRequestProperty(headerData.getKey(), headerData.getValue());
|
||||
}
|
||||
|
||||
// Set post data
|
||||
if (postData != null) {
|
||||
connection.setDoOutput(true);
|
||||
connection.setChunkedStreamingMode(0);
|
||||
|
||||
try {
|
||||
OutputStream out = connection.getOutputStream();
|
||||
out.write(postData);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error", e);
|
||||
connection.disconnect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Request
|
||||
try {
|
||||
InputStream in;
|
||||
|
||||
// Set response code
|
||||
responseCode = connection.getResponseCode();
|
||||
if (responseCode >= 400) {
|
||||
in = connection.getErrorStream();
|
||||
} else {
|
||||
in = connection.getInputStream();
|
||||
}
|
||||
|
||||
// Read response
|
||||
int readed;
|
||||
byte[] temp = new byte[4096];
|
||||
ByteArrayOutputStream response = new ByteArrayOutputStream();
|
||||
|
||||
while ((readed = in.read(temp)) != -1) {
|
||||
response.write(temp, 0, readed);
|
||||
}
|
||||
|
||||
this.response = response.toByteArray();
|
||||
response.close();
|
||||
|
||||
// Read headers
|
||||
headers.clear();
|
||||
for (Map.Entry<String, List<String>> header: connection.getHeaderFields().entrySet()) {
|
||||
headers.put(header.getKey(), TextUtils.join(", ", header.getValue()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error", e);
|
||||
connection.disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
connection.disconnect();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/config.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_NSURL
|
||||
|
||||
#include "../common/HTTPSClient.h"
|
||||
|
||||
class NSURLClient : public HTTPSClient
|
||||
{
|
||||
public:
|
||||
virtual bool valid() const override;
|
||||
virtual HTTPSClient::Reply request(const HTTPSClient::Request &req) override;
|
||||
};
|
||||
|
||||
#endif // HTTPS_BACKEND_NSURL
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "NSURLClient.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_NSURL
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error "ARC is off"
|
||||
#endif
|
||||
|
||||
bool NSURLClient::valid() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string toCppString(NSData *data)
|
||||
{
|
||||
return std::string((const char*) data.bytes, (size_t) data.length);
|
||||
}
|
||||
|
||||
static std::string toCppString(NSString *str)
|
||||
{
|
||||
return std::string([str UTF8String]);
|
||||
}
|
||||
|
||||
HTTPSClient::Reply NSURLClient::request(const HTTPSClient::Request &req)
|
||||
{ @autoreleasepool {
|
||||
NSURL *url = [NSURL URLWithString:@(req.url.c_str())];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
|
||||
NSData *bodydata = nil;
|
||||
switch(req.method)
|
||||
{
|
||||
case Request::GET:
|
||||
[request setHTTPMethod:@"GET"];
|
||||
break;
|
||||
case Request::POST:
|
||||
bodydata = [NSData dataWithBytesNoCopy:(void*) req.postdata.data() length:req.postdata.size() freeWhenDone:NO];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
[request setHTTPBody:bodydata];
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto &header : req.headers)
|
||||
[request setValue:@(header.second.c_str()) forHTTPHeaderField:@(header.first.c_str())];
|
||||
|
||||
__block NSHTTPURLResponse *response = nil;
|
||||
__block NSError *error = nil;
|
||||
__block NSData *body = nil;
|
||||
|
||||
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
|
||||
|
||||
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request
|
||||
completionHandler:^(NSData *data, NSURLResponse *resp, NSError *err) {
|
||||
body = data;
|
||||
response = (NSHTTPURLResponse *)resp;
|
||||
error = err;
|
||||
dispatch_semaphore_signal(sem);
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
|
||||
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
|
||||
|
||||
HTTPSClient::Reply reply;
|
||||
reply.responseCode = 400;
|
||||
|
||||
if (body)
|
||||
{
|
||||
reply.body = toCppString(body);
|
||||
}
|
||||
|
||||
if (response)
|
||||
{
|
||||
reply.responseCode = [response statusCode];
|
||||
|
||||
NSDictionary *headers = [response allHeaderFields];
|
||||
for (NSString *key in headers)
|
||||
{
|
||||
NSString *value = headers[key];
|
||||
reply.headers[toCppString(key)] = toCppString(value);
|
||||
}
|
||||
}
|
||||
|
||||
return reply;
|
||||
}}
|
||||
|
||||
#endif // HTTPS_BACKEND_NSURL
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
virtual bool connect(const std::string &hostname, uint16_t port) = 0;
|
||||
virtual size_t read(char *buffer, size_t size) = 0;
|
||||
virtual size_t write(const char *buffer, size_t size) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual ~Connection() {};
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "HTTPSClient.h"
|
||||
#include "HTTPRequest.h"
|
||||
#include "Connection.h"
|
||||
|
||||
template<typename Connection>
|
||||
class ConnectionClient : public HTTPSClient
|
||||
{
|
||||
public:
|
||||
virtual bool valid() const override;
|
||||
virtual HTTPSClient::Reply request(const HTTPSClient::Request &req) override;
|
||||
|
||||
private:
|
||||
static Connection *factory();
|
||||
};
|
||||
|
||||
template<typename Connection>
|
||||
bool ConnectionClient<Connection>::valid() const
|
||||
{
|
||||
return Connection::valid();
|
||||
}
|
||||
|
||||
template<typename Connection>
|
||||
Connection *ConnectionClient<Connection>::factory()
|
||||
{
|
||||
return new Connection();
|
||||
}
|
||||
|
||||
template<typename Connection>
|
||||
HTTPSClient::Reply ConnectionClient<Connection>::request(const HTTPSClient::Request &req)
|
||||
{
|
||||
HTTPRequest request(factory);
|
||||
return request.request(req);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "HTTPRequest.h"
|
||||
#include "PlaintextConnection.h"
|
||||
|
||||
HTTPRequest::HTTPRequest(ConnectionFactory factory)
|
||||
: factory(factory)
|
||||
{
|
||||
}
|
||||
|
||||
HTTPSClient::Reply HTTPRequest::request(const HTTPSClient::Request &req)
|
||||
{
|
||||
HTTPSClient::Reply reply;
|
||||
reply.responseCode = 0;
|
||||
|
||||
auto info = parseUrl(req.url);
|
||||
if (!info.valid)
|
||||
return reply;
|
||||
|
||||
std::unique_ptr<Connection> conn;
|
||||
if (info.schema == "http")
|
||||
conn.reset(new PlaintextConnection());
|
||||
else if (info.schema == "https")
|
||||
conn.reset(factory());
|
||||
else
|
||||
throw std::runtime_error("Unknown url schema");
|
||||
|
||||
if (!conn->connect(info.hostname, info.port))
|
||||
return reply;
|
||||
|
||||
// Build the request
|
||||
{
|
||||
std::stringstream request;
|
||||
request << (req.method == HTTPSClient::Request::GET ? "GET " : "POST ") << info.query << " HTTP/1.1\r\n";
|
||||
|
||||
for (auto &header : req.headers)
|
||||
request << header.first << ": " << header.second << "\r\n";
|
||||
|
||||
request << "Connection: Close\r\n";
|
||||
|
||||
request << "Host: " << info.hostname << "\r\n";
|
||||
|
||||
if (req.method == HTTPSClient::Request::POST && req.headers.count("Content-Type") == 0)
|
||||
request << "Content-Type: application/x-www-form-urlencoded\r\n";
|
||||
|
||||
if (req.method == HTTPSClient::Request::POST)
|
||||
request << "Content-Length: " << req.postdata.size() << "\r\n";
|
||||
|
||||
request << "\r\n";
|
||||
|
||||
if (req.method == HTTPSClient::Request::POST)
|
||||
request << req.postdata;
|
||||
|
||||
// Send it
|
||||
std::string requestData = request.str();
|
||||
conn->write(requestData.c_str(), requestData.size());
|
||||
}
|
||||
|
||||
// Now receive the reply
|
||||
std::stringstream response;
|
||||
{
|
||||
char buffer[8192];
|
||||
|
||||
while (true)
|
||||
{
|
||||
size_t read = conn->read(buffer, sizeof(buffer));
|
||||
response.write(buffer, read);
|
||||
if (read == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
conn->close();
|
||||
}
|
||||
|
||||
reply.responseCode = 500;
|
||||
// And parse it
|
||||
{
|
||||
std::string protocol;
|
||||
response >> protocol;
|
||||
if (protocol != "HTTP/1.1")
|
||||
return reply;
|
||||
|
||||
response >> reply.responseCode;
|
||||
response.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
|
||||
for (std::string line; getline(response, line, '\n') && line != "\r"; )
|
||||
{
|
||||
auto sep = line.find(':');
|
||||
reply.headers[line.substr(0, sep)] = line.substr(sep+1, line.size()-sep-1);
|
||||
}
|
||||
|
||||
auto begin = std::istreambuf_iterator<char>(response);
|
||||
auto end = std::istreambuf_iterator<char>();
|
||||
reply.body = std::string(begin, end);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
HTTPRequest::DissectedURL HTTPRequest::parseUrl(const std::string &url)
|
||||
{
|
||||
DissectedURL dis;
|
||||
dis.valid = false;
|
||||
|
||||
// Schema
|
||||
auto schemaStart = 0;
|
||||
auto schemaEnd = url.find("://");
|
||||
dis.schema = url.substr(schemaStart, schemaEnd-schemaStart);
|
||||
|
||||
// Auth+Hostname+Port
|
||||
auto connStart = schemaEnd+3;
|
||||
auto connEnd = url.find('/', connStart);
|
||||
if (connEnd == std::string::npos)
|
||||
connEnd = url.size();
|
||||
|
||||
// TODO: Auth
|
||||
if (url.find("@", connStart, connEnd-connStart) != std::string::npos)
|
||||
return dis;
|
||||
|
||||
// Port
|
||||
auto portStart = url.find(':', connStart);
|
||||
auto portEnd = connEnd;
|
||||
if (portStart == std::string::npos || portStart > portEnd)
|
||||
{
|
||||
dis.port = dis.schema == "http" ? 80 : 443;
|
||||
portStart = portEnd;
|
||||
}
|
||||
else
|
||||
dis.port = std::stoi(url.substr(portStart+1, portEnd-portStart-1));
|
||||
|
||||
// Hostname
|
||||
auto hostnameStart = connStart;
|
||||
auto hostnameEnd = portStart;
|
||||
dis.hostname = url.substr(hostnameStart, hostnameEnd-hostnameStart);
|
||||
|
||||
// And the query
|
||||
dis.query = url.substr(connEnd);
|
||||
if (dis.query.size() == 0)
|
||||
dis.query = "/";
|
||||
|
||||
dis.valid = true;
|
||||
|
||||
return dis;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "HTTPSClient.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class HTTPRequest
|
||||
{
|
||||
public:
|
||||
typedef std::function<Connection *()> ConnectionFactory;
|
||||
HTTPRequest(ConnectionFactory factory);
|
||||
|
||||
HTTPSClient::Reply request(const HTTPSClient::Request &req);
|
||||
|
||||
private:
|
||||
ConnectionFactory factory;
|
||||
|
||||
struct DissectedURL
|
||||
{
|
||||
bool valid;
|
||||
std::string schema;
|
||||
std::string hostname;
|
||||
uint16_t port;
|
||||
std::string query;
|
||||
// TODO: Auth?
|
||||
};
|
||||
|
||||
DissectedURL parseUrl(const std::string &url);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "HTTPS.h"
|
||||
#include "config.h"
|
||||
#include "ConnectionClient.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef HTTPS_BACKEND_CURL
|
||||
# include "../generic/CurlClient.h"
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_OPENSSL
|
||||
# include "../generic/OpenSSLConnection.h"
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_SCHANNEL
|
||||
# include "../windows/SChannelConnection.h"
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_NSURL
|
||||
# include "../apple/NSURLClient.h"
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_ANDROID
|
||||
# include "../android/AndroidClient.h"
|
||||
#endif
|
||||
|
||||
#ifdef HTTPS_BACKEND_CURL
|
||||
static CurlClient curlclient;
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_OPENSSL
|
||||
static ConnectionClient<OpenSSLConnection> opensslclient;
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_SCHANNEL
|
||||
static ConnectionClient<SChannelConnection> schannelclient;
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_NSURL
|
||||
static NSURLClient nsurlclient;
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_ANDROID
|
||||
static AndroidClient androidclient;
|
||||
#endif
|
||||
|
||||
static HTTPSClient *clients[] = {
|
||||
#ifdef HTTPS_BACKEND_CURL
|
||||
&curlclient,
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_OPENSSL
|
||||
&opensslclient,
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_SCHANNEL
|
||||
&schannelclient,
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_NSURL
|
||||
&nsurlclient,
|
||||
#endif
|
||||
#ifdef HTTPS_BACKEND_ANDROID
|
||||
&androidclient,
|
||||
#endif
|
||||
nullptr,
|
||||
};
|
||||
|
||||
HTTPSClient::Reply request(const HTTPSClient::Request &req)
|
||||
{
|
||||
for (size_t i = 0; clients[i]; ++i)
|
||||
{
|
||||
HTTPSClient &client = *clients[i];
|
||||
|
||||
if (client.valid())
|
||||
return client.request(req);
|
||||
}
|
||||
|
||||
throw std::runtime_error("No applicable HTTPS implementation found");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "HTTPSClient.h"
|
||||
|
||||
HTTPSClient::Reply request(const HTTPSClient::Request &req);
|
||||
@@ -0,0 +1,37 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include "HTTPSClient.h"
|
||||
|
||||
// This may not be the order you expect, as shorter strings always compare less,
|
||||
// but it's sufficient for our map
|
||||
bool HTTPSClient::ci_string_less::operator()(const std::string &lhs, const std::string &rhs) const
|
||||
{
|
||||
const size_t lhs_size = lhs.size();
|
||||
const size_t rhs_size = rhs.size();
|
||||
const size_t steps = std::min(lhs_size, rhs_size);
|
||||
|
||||
if (lhs_size < rhs_size)
|
||||
return true;
|
||||
else if (lhs_size > rhs_size)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < steps; ++i)
|
||||
{
|
||||
char l = std::tolower(lhs[i]);
|
||||
char r = std::tolower(rhs[i]);
|
||||
if (l < r)
|
||||
return true;
|
||||
else if (l > r)
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HTTPSClient::Request::Request(const std::string &url)
|
||||
: url(url)
|
||||
, method(GET)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
class HTTPSClient
|
||||
{
|
||||
public:
|
||||
struct ci_string_less
|
||||
{
|
||||
bool operator()(const std::string &lhs, const std::string &rhs) const;
|
||||
};
|
||||
using header_map = std::map<std::string, std::string, ci_string_less>;
|
||||
|
||||
struct Request
|
||||
{
|
||||
Request(const std::string &url);
|
||||
|
||||
header_map headers;
|
||||
std::string url;
|
||||
std::string postdata;
|
||||
|
||||
enum Method
|
||||
{
|
||||
GET,
|
||||
POST,
|
||||
} method;
|
||||
};
|
||||
|
||||
struct Reply
|
||||
{
|
||||
header_map headers;
|
||||
std::string body;
|
||||
int responseCode;
|
||||
};
|
||||
|
||||
virtual bool valid() const = 0;
|
||||
virtual Reply request(const Request &req) = 0;
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "config.h"
|
||||
#include <cstring>
|
||||
#ifndef HTTPS_USE_WINSOCK
|
||||
# include <netdb.h>
|
||||
# include <unistd.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
#else
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
#endif // HTTPS_USE_WINSOCK
|
||||
|
||||
#include "PlaintextConnection.h"
|
||||
|
||||
#ifdef HTTPS_USE_WINSOCK
|
||||
static void close(int fd)
|
||||
{
|
||||
closesocket(fd);
|
||||
}
|
||||
#endif // HTTPS_USE_WINSOCK
|
||||
|
||||
PlaintextConnection::PlaintextConnection()
|
||||
: fd(-1)
|
||||
{
|
||||
#ifdef HTTPS_USE_WINSOCK
|
||||
static bool wsaInit = false;
|
||||
if (!wsaInit)
|
||||
{
|
||||
WSADATA data;
|
||||
WSAStartup(MAKEWORD(2, 2), &data);
|
||||
}
|
||||
#endif // HTTPS_USE_WINSOCK
|
||||
}
|
||||
|
||||
PlaintextConnection::~PlaintextConnection()
|
||||
{
|
||||
if (fd != -1)
|
||||
::close(fd);
|
||||
}
|
||||
|
||||
bool PlaintextConnection::connect(const std::string &hostname, uint16_t port)
|
||||
{
|
||||
addrinfo hints;
|
||||
std::memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = hints.ai_protocol = 0;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
addrinfo *addrs = nullptr;
|
||||
std::string portString = std::to_string(port);
|
||||
getaddrinfo(hostname.c_str(), portString.c_str(), &hints, &addrs);
|
||||
|
||||
// Try all addresses returned
|
||||
bool connected = false;
|
||||
for (addrinfo *addr = addrs; !connected && addr; addr = addr->ai_next)
|
||||
{
|
||||
fd = socket(addr->ai_family, SOCK_STREAM, 0);
|
||||
connected = ::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0;
|
||||
if (!connected)
|
||||
::close(fd);
|
||||
}
|
||||
|
||||
freeaddrinfo(addrs);
|
||||
|
||||
if (!connected)
|
||||
{
|
||||
fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t PlaintextConnection::read(char *buffer, size_t size)
|
||||
{
|
||||
auto read = ::recv(fd, buffer, size, 0);
|
||||
if (read < 0)
|
||||
read = 0;
|
||||
return static_cast<size_t>(read);
|
||||
}
|
||||
|
||||
size_t PlaintextConnection::write(const char *buffer, size_t size)
|
||||
{
|
||||
auto written = ::send(fd, buffer, size, 0);
|
||||
if (written < 0)
|
||||
written = 0;
|
||||
return static_cast<size_t>(written);
|
||||
}
|
||||
|
||||
void PlaintextConnection::close()
|
||||
{
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
int PlaintextConnection::getFd() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
class PlaintextConnection : public Connection
|
||||
{
|
||||
public:
|
||||
PlaintextConnection();
|
||||
virtual bool connect(const std::string &hostname, uint16_t port);
|
||||
virtual size_t read(char *buffer, size_t size);
|
||||
virtual size_t write(const char *buffer, size_t size);
|
||||
virtual void close();
|
||||
virtual ~PlaintextConnection();
|
||||
|
||||
int getFd() const;
|
||||
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
#include "CurlClient.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_CURL
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
CurlClient::Curl::Curl()
|
||||
{
|
||||
void *handle = dlopen("libcurl.so", RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
loaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
void (*global_init)() = (void(*)()) dlsym(handle, "curl_global_init");
|
||||
easy_init = (CURL*(*)()) dlsym(handle, "curl_easy_init");
|
||||
easy_cleanup = (void(*)(CURL*)) dlsym(handle, "curl_easy_cleanup");
|
||||
easy_setopt = (CURLcode(*)(CURL*,CURLoption,...)) dlsym(handle, "curl_easy_setopt");
|
||||
easy_perform = (CURLcode(*)(CURL*)) dlsym(handle, "curl_easy_perform");
|
||||
easy_getinfo = (CURLcode(*)(CURL*,CURLINFO,...)) dlsym(handle, "curl_easy_getinfo");
|
||||
slist_append = (curl_slist*(*)(curl_slist*,const char*)) dlsym(handle, "curl_slist_append");
|
||||
slist_free_all = (void(*)(curl_slist*)) dlsym(handle, "curl_slist_free_all");
|
||||
|
||||
loaded = (global_init && easy_init && easy_cleanup && easy_setopt && easy_perform && easy_getinfo && slist_append && slist_free_all);
|
||||
|
||||
if (!loaded)
|
||||
return;
|
||||
|
||||
global_init();
|
||||
}
|
||||
|
||||
static size_t stringstreamWriter(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
std::stringstream *ss = (std::stringstream*) userdata;
|
||||
size_t count = size*nmemb;
|
||||
ss->write(ptr, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static size_t headerWriter(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
std::map<std::string, std::string> &headers = *((std::map<std::string,std::string>*) userdata);
|
||||
size_t count = size*nmemb;
|
||||
std::string line(ptr, count);
|
||||
size_t split = line.find(':');
|
||||
size_t newline = line.find('\r');
|
||||
if (newline == std::string::npos)
|
||||
newline = line.size();
|
||||
|
||||
if (split != std::string::npos)
|
||||
headers[line.substr(0, split)] = line.substr(split+1, newline-split-1);
|
||||
return count;
|
||||
}
|
||||
|
||||
bool CurlClient::valid() const
|
||||
{
|
||||
return curl.loaded;
|
||||
}
|
||||
|
||||
HTTPSClient::Reply CurlClient::request(const HTTPSClient::Request &req)
|
||||
{
|
||||
Reply reply;
|
||||
reply.responseCode = 400;
|
||||
|
||||
CURL *handle = curl.easy_init();
|
||||
if (!handle)
|
||||
throw std::runtime_error("Could not create curl request");
|
||||
|
||||
curl.easy_setopt(handle, CURLOPT_URL, req.url.c_str());
|
||||
curl.easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
|
||||
if (req.method == Request::POST)
|
||||
{
|
||||
curl.easy_setopt(handle, CURLOPT_POST, 1L);
|
||||
curl.easy_setopt(handle, CURLOPT_POSTFIELDS, req.postdata.c_str());
|
||||
curl.easy_setopt(handle, CURLOPT_POSTFIELDSIZE, req.postdata.size());
|
||||
}
|
||||
|
||||
// Curl doesn't copy memory, keep the strings around
|
||||
std::vector<std::string> lines;
|
||||
for (auto &header : req.headers)
|
||||
{
|
||||
std::stringstream line;
|
||||
line << header.first << ": " << header.second;
|
||||
lines.push_back(line.str());
|
||||
}
|
||||
|
||||
curl_slist *sendHeaders = nullptr;
|
||||
for (auto &line : lines)
|
||||
sendHeaders = curl.slist_append(sendHeaders, line.c_str());
|
||||
|
||||
if (sendHeaders)
|
||||
curl.easy_setopt(handle, CURLOPT_HTTPHEADER, sendHeaders);
|
||||
|
||||
std::stringstream body;
|
||||
|
||||
curl.easy_setopt(handle, CURLOPT_WRITEFUNCTION, stringstreamWriter);
|
||||
curl.easy_setopt(handle, CURLOPT_WRITEDATA, &body);
|
||||
|
||||
curl.easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerWriter);
|
||||
curl.easy_setopt(handle, CURLOPT_HEADERDATA, &reply.headers);
|
||||
|
||||
curl.easy_perform(handle);
|
||||
|
||||
if (sendHeaders)
|
||||
curl.slist_free_all(sendHeaders);
|
||||
|
||||
{
|
||||
long responseCode;
|
||||
curl.easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
reply.responseCode = (int) responseCode;
|
||||
}
|
||||
|
||||
reply.body = body.str();
|
||||
|
||||
curl.easy_cleanup(handle);
|
||||
return reply;
|
||||
}
|
||||
|
||||
CurlClient::Curl CurlClient::curl;
|
||||
|
||||
#endif // HTTPS_BACKEND_CURL
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/config.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_CURL
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "../common/HTTPSClient.h"
|
||||
|
||||
class CurlClient : public HTTPSClient
|
||||
{
|
||||
public:
|
||||
virtual bool valid() const override;
|
||||
virtual HTTPSClient::Reply request(const HTTPSClient::Request &req) override;
|
||||
|
||||
private:
|
||||
static struct Curl
|
||||
{
|
||||
Curl();
|
||||
bool loaded;
|
||||
|
||||
CURL *(*easy_init)();
|
||||
void (*easy_cleanup)(CURL *handle);
|
||||
CURLcode (*easy_setopt)(CURL *handle, CURLoption option, ...);
|
||||
CURLcode (*easy_perform)(CURL *easy_handle);
|
||||
CURLcode (*easy_getinfo)(CURL *curl, CURLINFO info, ...);
|
||||
|
||||
curl_slist *(*slist_append)(curl_slist *list, const char *string);
|
||||
void (*slist_free_all)(curl_slist *list);
|
||||
} curl;
|
||||
};
|
||||
|
||||
#endif // HTTPS_BACKEND_CURL
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "OpenSSLConnection.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_OPENSSL
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
// Not present in openssl 1.1 headers
|
||||
#define SSL_CTRL_OPTIONS 32
|
||||
|
||||
template <class T>
|
||||
static inline bool loadSymbol(T &var, void *handle, const char *name)
|
||||
{
|
||||
var = reinterpret_cast<T>(dlsym(handle, name));
|
||||
return var != nullptr;
|
||||
}
|
||||
|
||||
OpenSSLConnection::SSLFuncs::SSLFuncs()
|
||||
{
|
||||
valid = false;
|
||||
|
||||
// Try OpenSSL 1.1
|
||||
void *sslhandle = dlopen("libssl.so.1.1", RTLD_LAZY);
|
||||
void *cryptohandle = dlopen("libcrypto.so.1.1", RTLD_LAZY);
|
||||
// Try OpenSSL 1.0
|
||||
if (!sslhandle || !cryptohandle)
|
||||
{
|
||||
sslhandle = dlopen("libssl.so.1.0.0", RTLD_LAZY);
|
||||
cryptohandle = dlopen("libcrypto.so.1.0.0", RTLD_LAZY);
|
||||
}
|
||||
// Try OpenSSL without version
|
||||
if (!sslhandle || !cryptohandle)
|
||||
{
|
||||
sslhandle = dlopen("libssl.so", RTLD_LAZY);
|
||||
cryptohandle = dlopen("libcrypto.so", RTLD_LAZY);
|
||||
}
|
||||
// Give up
|
||||
if (!sslhandle || !cryptohandle)
|
||||
return;
|
||||
|
||||
valid = true;
|
||||
valid = valid && (loadSymbol(library_init, sslhandle, "SSL_library_init") ||
|
||||
loadSymbol(init_ssl, sslhandle, "OPENSSL_init_ssl"));
|
||||
|
||||
valid = valid && loadSymbol(CTX_new, sslhandle, "SSL_CTX_new");
|
||||
valid = valid && loadSymbol(CTX_ctrl, sslhandle, "SSL_CTX_ctrl");
|
||||
valid = valid && loadSymbol(CTX_set_verify, sslhandle, "SSL_CTX_set_verify");
|
||||
valid = valid && loadSymbol(CTX_set_default_verify_paths, sslhandle, "SSL_CTX_set_default_verify_paths");
|
||||
valid = valid && loadSymbol(CTX_free, sslhandle, "SSL_CTX_free");
|
||||
|
||||
valid = valid && loadSymbol(SSL_new, sslhandle, "SSL_new");
|
||||
valid = valid && loadSymbol(SSL_free, sslhandle, "SSL_free");
|
||||
valid = valid && loadSymbol(set_fd, sslhandle, "SSL_set_fd");
|
||||
valid = valid && loadSymbol(connect, sslhandle, "SSL_connect");
|
||||
valid = valid && loadSymbol(read, sslhandle, "SSL_read");
|
||||
valid = valid && loadSymbol(write, sslhandle, "SSL_write");
|
||||
valid = valid && loadSymbol(shutdown, sslhandle, "SSL_shutdown");
|
||||
valid = valid && loadSymbol(get_verify_result, sslhandle, "SSL_get_verify_result");
|
||||
valid = valid && loadSymbol(get_peer_certificate, sslhandle, "SSL_get_peer_certificate");
|
||||
|
||||
valid = valid && (loadSymbol(SSLv23_method, sslhandle, "SSLv23_method") ||
|
||||
loadSymbol(SSLv23_method, sslhandle, "TLS_method"));
|
||||
|
||||
valid = valid && loadSymbol(check_host, cryptohandle, "X509_check_host");
|
||||
|
||||
if (library_init)
|
||||
library_init();
|
||||
else if(init_ssl)
|
||||
init_ssl(0, nullptr);
|
||||
// else not valid
|
||||
}
|
||||
|
||||
bool OpenSSLConnection::valid()
|
||||
{
|
||||
return ssl.valid;
|
||||
}
|
||||
|
||||
OpenSSLConnection::OpenSSLConnection()
|
||||
: conn(nullptr)
|
||||
{
|
||||
context = ssl.CTX_new(ssl.SSLv23_method());
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
ssl.CTX_ctrl(context, SSL_CTRL_OPTIONS, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3, nullptr);
|
||||
ssl.CTX_set_verify(context, SSL_VERIFY_PEER, nullptr);
|
||||
ssl.CTX_set_default_verify_paths(context);
|
||||
}
|
||||
|
||||
OpenSSLConnection::~OpenSSLConnection()
|
||||
{
|
||||
if (conn)
|
||||
ssl.SSL_free(conn);
|
||||
|
||||
if (context)
|
||||
ssl.CTX_free(context);
|
||||
}
|
||||
|
||||
bool OpenSSLConnection::connect(const std::string &hostname, uint16_t port)
|
||||
{
|
||||
if (!context)
|
||||
return false;
|
||||
|
||||
if (!socket.connect(hostname, port))
|
||||
return false;
|
||||
|
||||
conn = ssl.SSL_new(context);
|
||||
if (!conn)
|
||||
{
|
||||
socket.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
ssl.set_fd(conn, socket.getFd());
|
||||
if (ssl.connect(conn) != 1 || ssl.get_verify_result(conn) != X509_V_OK)
|
||||
{
|
||||
socket.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
X509 *cert = ssl.get_peer_certificate(conn);
|
||||
if (ssl.check_host(cert, hostname.c_str(), hostname.size(), 0, nullptr) != 1)
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t OpenSSLConnection::read(char *buffer, size_t size)
|
||||
{
|
||||
return ssl.read(conn, buffer, (int) size);
|
||||
}
|
||||
|
||||
size_t OpenSSLConnection::write(const char *buffer, size_t size)
|
||||
{
|
||||
return ssl.write(conn, buffer, (int) size);
|
||||
}
|
||||
|
||||
void OpenSSLConnection::close()
|
||||
{
|
||||
ssl.shutdown(conn);
|
||||
socket.close();
|
||||
}
|
||||
|
||||
OpenSSLConnection::SSLFuncs OpenSSLConnection::ssl;
|
||||
|
||||
#endif // HTTPS_BACKEND_OPENSSL
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/config.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_OPENSSL
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "../common/Connection.h"
|
||||
#include "../common/PlaintextConnection.h"
|
||||
|
||||
class OpenSSLConnection : public Connection
|
||||
{
|
||||
public:
|
||||
OpenSSLConnection();
|
||||
virtual bool connect(const std::string &hostname, uint16_t port) override;
|
||||
virtual size_t read(char *buffer, size_t size) override;
|
||||
virtual size_t write(const char *buffer, size_t size) override;
|
||||
virtual void close() override;
|
||||
virtual ~OpenSSLConnection();
|
||||
|
||||
static bool valid();
|
||||
|
||||
private:
|
||||
PlaintextConnection socket;
|
||||
SSL_CTX *context;
|
||||
SSL *conn;
|
||||
|
||||
struct SSLFuncs
|
||||
{
|
||||
SSLFuncs();
|
||||
bool valid;
|
||||
|
||||
int (*library_init)();
|
||||
int (*init_ssl)(uint64_t opts, const void *settings);
|
||||
|
||||
SSL_CTX *(*CTX_new)(const SSL_METHOD *method);
|
||||
long (*CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
|
||||
void (*CTX_set_verify)(SSL_CTX *ctx, int mode, void *verify_callback);
|
||||
int (*CTX_set_default_verify_paths)(SSL_CTX *ctx);
|
||||
void (*CTX_free)(SSL_CTX *ctx);
|
||||
|
||||
SSL *(*SSL_new)(SSL_CTX *ctx);
|
||||
void (*SSL_free)(SSL *ctx);
|
||||
int (*set_fd)(SSL *ssl, int fd);
|
||||
int (*connect)(SSL *ssl);
|
||||
int (*read)(SSL *ssl, void *buf, int num);
|
||||
int (*write)(SSL *ssl, const void *buf, int num);
|
||||
int (*shutdown)(SSL *ssl);
|
||||
long (*get_verify_result)(const SSL *ssl);
|
||||
X509 *(*get_peer_certificate)(const SSL *ssl);
|
||||
|
||||
const SSL_METHOD *(*SSLv23_method)();
|
||||
|
||||
int (*check_host)(X509 *cert, const char *name, size_t namelen, unsigned int flags, char **peername);
|
||||
};
|
||||
static SSLFuncs ssl;
|
||||
};
|
||||
|
||||
#endif // HTTPS_BACKEND_OPENSSL
|
||||
@@ -0,0 +1,119 @@
|
||||
#include <lua.hpp>
|
||||
|
||||
#include "../common/HTTPS.h"
|
||||
#include "../common/config.h"
|
||||
|
||||
static std::string w_checkstring(lua_State *L, int idx)
|
||||
{
|
||||
size_t len;
|
||||
const char *str = luaL_checklstring(L, idx, &len);
|
||||
return std::string(str, len);
|
||||
}
|
||||
|
||||
static void w_pushstring(lua_State *L, const std::string &str)
|
||||
{
|
||||
lua_pushlstring(L, str.data(), str.size());
|
||||
}
|
||||
|
||||
static void w_readheaders(lua_State *L, int idx, HTTPSClient::header_map &headers)
|
||||
{
|
||||
if (idx < 0)
|
||||
idx += lua_gettop(L) + 1;
|
||||
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, idx))
|
||||
{
|
||||
auto header = w_checkstring(L, -2);
|
||||
headers[header] = w_checkstring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static HTTPSClient::Request::Method w_optmethod(lua_State *L, int idx, HTTPSClient::Request::Method defaultMethod)
|
||||
{
|
||||
if (lua_isnoneornil(L, idx))
|
||||
return defaultMethod;
|
||||
|
||||
auto str = w_checkstring(L, idx);
|
||||
if (str == "get")
|
||||
return HTTPSClient::Request::GET;
|
||||
else if (str == "post")
|
||||
return HTTPSClient::Request::POST;
|
||||
else
|
||||
luaL_argerror(L, idx, "expected one of \"get\" or \"set\"");
|
||||
|
||||
return defaultMethod;
|
||||
}
|
||||
|
||||
static int w_request(lua_State *L)
|
||||
{
|
||||
auto url = w_checkstring(L, 1);
|
||||
HTTPSClient::Request req(url);
|
||||
|
||||
bool advanced = false;
|
||||
|
||||
if (lua_istable(L, 2))
|
||||
{
|
||||
advanced = true;
|
||||
|
||||
HTTPSClient::Request::Method defaultMethod = HTTPSClient::Request::GET;
|
||||
|
||||
lua_getfield(L, 2, "data");
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
{
|
||||
req.postdata = w_checkstring(L, -1);
|
||||
defaultMethod = HTTPSClient::Request::POST;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 2, "method");
|
||||
req.method = w_optmethod(L, -1, defaultMethod);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 2, "headers");
|
||||
if (!lua_isnoneornil(L, -1))
|
||||
w_readheaders(L, -1, req.headers);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
HTTPSClient::Reply reply;
|
||||
|
||||
try
|
||||
{
|
||||
reply = request(req);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::string errorMessage = e.what();
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, errorMessage.c_str());
|
||||
return 2;
|
||||
}
|
||||
|
||||
lua_pushinteger(L, reply.responseCode);
|
||||
w_pushstring(L, reply.body);
|
||||
|
||||
if (advanced)
|
||||
{
|
||||
lua_newtable(L);
|
||||
for (const auto &header : reply.headers)
|
||||
{
|
||||
w_pushstring(L, header.first);
|
||||
w_pushstring(L, header.second);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
}
|
||||
|
||||
return advanced ? 3 : 2;
|
||||
}
|
||||
|
||||
extern "C" int HTTPS_DLLEXPORT luaopen_https(lua_State *L)
|
||||
{
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushcfunction(L, w_request);
|
||||
lua_setfield(L, -2, "request");
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
#define SECURITY_WIN32
|
||||
#define NOMINMAX
|
||||
|
||||
#include "SChannelConnection.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_SCHANNEL
|
||||
|
||||
#include <windows.h>
|
||||
#include <security.h>
|
||||
#include <schnlsp.h>
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
|
||||
#ifndef SCH_USE_STRONG_CRYPTO
|
||||
# define SCH_USE_STRONG_CRYPTO 0x00400000
|
||||
#endif
|
||||
#ifndef SP_PROT_TLS1_1_CLIENT
|
||||
# define SP_PROT_TLS1_1_CLIENT 0x00000200
|
||||
#endif
|
||||
#ifndef SP_PROT_TLS1_2_CLIENT
|
||||
# define SP_PROT_TLS1_2_CLIENT 0x00000800
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SCHANNEL
|
||||
#include <iostream>
|
||||
std::ostream &debug = std::cout;
|
||||
#else
|
||||
struct Debug
|
||||
{
|
||||
template<typename T>
|
||||
Debug &operator<<(const T&) { return *this; }
|
||||
} debug;
|
||||
#endif
|
||||
|
||||
static void enqueue(std::vector<char> &buffer, char *data, size_t size)
|
||||
{
|
||||
size_t oldSize = buffer.size();
|
||||
buffer.resize(oldSize + size);
|
||||
memcpy(&buffer[oldSize], data, size);
|
||||
}
|
||||
|
||||
static void enqueue_prepend(std::vector<char> &buffer, char *data, size_t size)
|
||||
{
|
||||
size_t oldSize = buffer.size();
|
||||
buffer.resize(oldSize + size);
|
||||
memmove(&buffer[size], &buffer[0], oldSize);
|
||||
memcpy(&buffer[0], data, size);
|
||||
}
|
||||
|
||||
static size_t dequeue(std::vector<char> &buffer, char *data, size_t size)
|
||||
{
|
||||
size = std::min(size, buffer.size());
|
||||
size_t remaining = buffer.size() - size;
|
||||
|
||||
memcpy(data, &buffer[0], size);
|
||||
memmove(&buffer[0], &buffer[size], remaining);
|
||||
buffer.resize(remaining);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
SChannelConnection::SChannelConnection()
|
||||
: context(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SChannelConnection::~SChannelConnection()
|
||||
{
|
||||
destroyContext();
|
||||
}
|
||||
|
||||
SECURITY_STATUS InitializeSecurityContext(CredHandle *phCredential, std::unique_ptr<CtxtHandle>& phContext, const std::string& szTargetName, ULONG fContextReq, std::vector<char>& inputBuffer, std::vector<char>& outputBuffer, ULONG *pfContextAttr)
|
||||
{
|
||||
std::array<SecBuffer, 1> recvBuffers;
|
||||
recvBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||||
recvBuffers[0].pvBuffer = outputBuffer.data();
|
||||
recvBuffers[0].cbBuffer = outputBuffer.size();
|
||||
|
||||
std::array<SecBuffer, 2> sendBuffers;
|
||||
sendBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||||
sendBuffers[0].pvBuffer = inputBuffer.data();
|
||||
sendBuffers[0].cbBuffer = inputBuffer.size();
|
||||
sendBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||||
sendBuffers[1].pvBuffer = nullptr;
|
||||
sendBuffers[1].cbBuffer = 0;
|
||||
|
||||
SecBufferDesc recvBufferDesc, sendBufferDesc;
|
||||
recvBufferDesc.ulVersion = sendBufferDesc.ulVersion = SECBUFFER_VERSION;
|
||||
recvBufferDesc.pBuffers = &recvBuffers[0];
|
||||
recvBufferDesc.cBuffers = recvBuffers.size();
|
||||
|
||||
if (!inputBuffer.empty())
|
||||
{
|
||||
sendBufferDesc.pBuffers = &sendBuffers[0];
|
||||
sendBufferDesc.cBuffers = sendBuffers.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
sendBufferDesc.pBuffers = nullptr;
|
||||
sendBufferDesc.cBuffers = 0;
|
||||
}
|
||||
|
||||
CtxtHandle* phOldContext = nullptr;
|
||||
CtxtHandle* phNewContext = nullptr;
|
||||
if (!phContext)
|
||||
{
|
||||
phContext = std::make_unique<CtxtHandle>();
|
||||
phNewContext = phContext.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
phOldContext = phContext.get();
|
||||
}
|
||||
|
||||
auto ret = InitializeSecurityContext(phCredential, phOldContext, const_cast<char*>(szTargetName.c_str()), fContextReq, 0, 0, &sendBufferDesc, 0, phNewContext, &recvBufferDesc, pfContextAttr, nullptr);
|
||||
|
||||
outputBuffer.resize(recvBuffers[0].cbBuffer);
|
||||
|
||||
// Clear the input buffer, so the reader can append
|
||||
// If we have unprocessed data, leave it in the buffer
|
||||
size_t unprocessed = 0;
|
||||
if (sendBuffers[1].BufferType == SECBUFFER_EXTRA)
|
||||
unprocessed = sendBuffers[1].cbBuffer;
|
||||
|
||||
if (unprocessed > 0)
|
||||
memmove(inputBuffer.data(), inputBuffer.data() + inputBuffer.size() - unprocessed, unprocessed);
|
||||
|
||||
inputBuffer.resize(unprocessed);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool SChannelConnection::connect(const std::string &hostname, uint16_t port)
|
||||
{
|
||||
debug << "Trying to connect to " << hostname << ":" << port << "\n";
|
||||
if (!socket.connect(hostname, port))
|
||||
return false;
|
||||
debug << "Connected\n";
|
||||
|
||||
SCHANNEL_CRED cred;
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
cred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||
cred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
|
||||
cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS | SCH_USE_STRONG_CRYPTO | SCH_CRED_REVOCATION_CHECK_CHAIN;
|
||||
|
||||
CredHandle credHandle;
|
||||
if (AcquireCredentialsHandle(nullptr, (char*) UNISP_NAME, SECPKG_CRED_OUTBOUND, nullptr, &cred, nullptr, nullptr, &credHandle, nullptr) != SEC_E_OK)
|
||||
{
|
||||
debug << "Failed to acquire handle\n";
|
||||
socket.close();
|
||||
return false;
|
||||
}
|
||||
debug << "Acquired handle\n";
|
||||
|
||||
|
||||
static constexpr size_t bufferSize = 8192;
|
||||
bool done = false, success = false, contextCreated = false;
|
||||
|
||||
ULONG contextAttr;
|
||||
std::unique_ptr<CtxtHandle> context;
|
||||
std::vector<char> inputBuffer;
|
||||
std::vector<char> outputBuffer;
|
||||
|
||||
do
|
||||
{
|
||||
outputBuffer.resize(bufferSize);
|
||||
|
||||
bool recvData = false;
|
||||
bool sendData = false;
|
||||
auto ret = InitializeSecurityContext(&credHandle, context, hostname, ISC_REQ_STREAM, inputBuffer, outputBuffer, &contextAttr);
|
||||
switch (ret)
|
||||
{
|
||||
/*case SEC_I_COMPLETE_NEEDED:
|
||||
case SEC_I_COMPLETE_AND_CONTINUE:
|
||||
if (CompleteAuthToken(context.get(), &outputBuffer) != SEC_E_OK)
|
||||
done = true;
|
||||
else if (ret == SEC_I_COMPLETE_NEEDED)
|
||||
success = done = true;
|
||||
break;*/
|
||||
case SEC_I_CONTINUE_NEEDED:
|
||||
debug << "Initialize: continue needed\n";
|
||||
recvData = true;
|
||||
sendData = true;
|
||||
break;
|
||||
case SEC_E_INCOMPLETE_CREDENTIALS:
|
||||
debug << "Initialize failed: incomplete credentials\n";
|
||||
done = true;
|
||||
break;
|
||||
case SEC_E_INCOMPLETE_MESSAGE:
|
||||
debug << "Initialize: incomplete message\n";
|
||||
recvData = true;
|
||||
break;
|
||||
case SEC_E_OK:
|
||||
debug << "Initialize succeeded\n";
|
||||
success = done = true;
|
||||
sendData = true;
|
||||
break;
|
||||
default:
|
||||
debug << "Initialize done: " << outputBuffer.size() << " bytes of output and unknown status " << ret << "\n";
|
||||
done = true;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!done)
|
||||
contextCreated = true;
|
||||
|
||||
if (sendData && !outputBuffer.empty())
|
||||
{
|
||||
socket.write(outputBuffer.data(), outputBuffer.size());
|
||||
debug << "Sent " << outputBuffer.size() << " bytes of data\n";
|
||||
}
|
||||
|
||||
if (recvData)
|
||||
{
|
||||
size_t unprocessed = inputBuffer.size();
|
||||
inputBuffer.resize(unprocessed + bufferSize);
|
||||
size_t actual = socket.read(inputBuffer.data() + unprocessed, bufferSize);
|
||||
inputBuffer.resize(actual + unprocessed);
|
||||
|
||||
debug << "Received " << actual << " bytes of data\n";
|
||||
if (unprocessed > 0)
|
||||
debug << " had " << unprocessed << " bytes of remaining, unprocessed data\n";
|
||||
|
||||
if (actual + unprocessed == 0)
|
||||
{
|
||||
debug << "No data to submit, break\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
debug << "Done!\n";
|
||||
|
||||
if (success)
|
||||
{
|
||||
SecPkgContext_Flags resultFlags;
|
||||
QueryContextAttributes(context.get(), SECPKG_ATTR_FLAGS, &resultFlags);
|
||||
if (resultFlags.Flags & ISC_REQ_CONFIDENTIALITY == 0)
|
||||
{
|
||||
debug << "Resulting context is not encrypted, marking as failed\n";
|
||||
success = false;
|
||||
}
|
||||
if (resultFlags.Flags & ISC_REQ_INTEGRITY == 0)
|
||||
{
|
||||
debug << "Resulting context is not signed, marking as failed\n";
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
this->context = context.release();
|
||||
else if (contextCreated)
|
||||
DeleteSecurityContext(context.get());
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
size_t SChannelConnection::read(char *buffer, size_t size)
|
||||
{
|
||||
if (decRecvBuffer.size() > 0)
|
||||
{
|
||||
size = dequeue(decRecvBuffer, buffer, size);
|
||||
debug << "Read " << size << " bytes of previously decoded data\n";
|
||||
return size;
|
||||
}
|
||||
else if (encRecvBuffer.size() > 0)
|
||||
{
|
||||
size = dequeue(encRecvBuffer, buffer, size);
|
||||
debug << "Read " << size << " bytes of extra data\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
size = socket.read(buffer, size);
|
||||
debug << "Received " << size << " bytes of data\n";
|
||||
}
|
||||
|
||||
return decrypt(buffer, size);
|
||||
}
|
||||
|
||||
size_t SChannelConnection::decrypt(char *buffer, size_t size, bool recurse)
|
||||
{
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
SecBuffer secBuffers[4];
|
||||
secBuffers[0].cbBuffer = size;
|
||||
secBuffers[0].BufferType = SECBUFFER_DATA;
|
||||
secBuffers[0].pvBuffer = buffer;
|
||||
|
||||
for (size_t i = 1; i < 4; ++i)
|
||||
{
|
||||
secBuffers[i].BufferType = SECBUFFER_EMPTY;
|
||||
secBuffers[i].pvBuffer = nullptr;
|
||||
secBuffers[i].cbBuffer = 0;
|
||||
}
|
||||
|
||||
SecBufferDesc secBufferDesc;
|
||||
secBufferDesc.ulVersion = SECBUFFER_VERSION;
|
||||
secBufferDesc.cBuffers = 4;
|
||||
secBufferDesc.pBuffers = &secBuffers[0];
|
||||
|
||||
auto ret = DecryptMessage(static_cast<CtxtHandle*>(context), &secBufferDesc, 0, nullptr); // FIXME
|
||||
debug << "DecryptMessage returns: " << ret << "\n";
|
||||
switch (ret)
|
||||
{
|
||||
case SEC_E_OK:
|
||||
{
|
||||
void *actualDataStart = buffer;
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
auto &buffer = secBuffers[i];
|
||||
if (buffer.BufferType == SECBUFFER_DATA)
|
||||
{
|
||||
actualDataStart = buffer.pvBuffer;
|
||||
size = buffer.cbBuffer;
|
||||
}
|
||||
else if (buffer.BufferType == SECBUFFER_EXTRA)
|
||||
{
|
||||
debug << "\tExtra data in buffer " << i << " (" << buffer.cbBuffer << " bytes)\n";
|
||||
enqueue(encRecvBuffer, static_cast<char*>(buffer.pvBuffer), buffer.cbBuffer);
|
||||
}
|
||||
else if (buffer.BufferType != SECBUFFER_EMPTY)
|
||||
debug << "\tBuffer of type " << buffer.BufferType << "\n";
|
||||
}
|
||||
|
||||
if (actualDataStart)
|
||||
memmove(buffer, actualDataStart, size);
|
||||
|
||||
break;
|
||||
}
|
||||
case SEC_E_INCOMPLETE_MESSAGE:
|
||||
{
|
||||
// Move all our current data to encRecvBuffer
|
||||
enqueue(encRecvBuffer, buffer, size);
|
||||
|
||||
// Now try to read some more data from the socket
|
||||
size_t bufferSize = encRecvBuffer.size() + 8192;
|
||||
char *recvBuffer = new char[bufferSize];
|
||||
size_t recvd = socket.read(recvBuffer+encRecvBuffer.size(), 8192);
|
||||
debug << recvd << " bytes of extra data read from socket\n";
|
||||
|
||||
if (recvd == 0 && !recurse)
|
||||
{
|
||||
debug << "Recursion prevented, bailing\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fill our buffer with the queued data and the newly received data
|
||||
size_t totalSize = encRecvBuffer.size() + recvd;
|
||||
dequeue(encRecvBuffer, recvBuffer, encRecvBuffer.size());
|
||||
debug << "Trying to decrypt with " << totalSize << " bytes of data\n";
|
||||
|
||||
// Now try to decrypt that
|
||||
size_t decrypted = decrypt(recvBuffer, totalSize, false);
|
||||
debug << "\tObtained " << decrypted << " bytes of decrypted data\n";
|
||||
|
||||
// Copy the first size bytes to the output buffer
|
||||
size = std::min(size, decrypted);
|
||||
memcpy(buffer, recvBuffer, size);
|
||||
|
||||
// And write the remainder to our queued decrypted data...
|
||||
// Note: we prepend, since our recursive call may already have written
|
||||
// something and we can be sure decrypt wasn't called if the buffer was
|
||||
// non-empty in read
|
||||
enqueue_prepend(decRecvBuffer, recvBuffer+size, decrypted-size);
|
||||
debug << "\tStoring " << decrypted-size << " bytes of extra decrypted data\n";
|
||||
return size;
|
||||
}
|
||||
// TODO: More?
|
||||
default:
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
debug << "\tDecrypted " << size << " bytes of data\n";
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t SChannelConnection::write(const char *buffer, size_t size)
|
||||
{
|
||||
static constexpr size_t bufferSize = 8192;
|
||||
assert(size <= bufferSize);
|
||||
|
||||
SecPkgContext_StreamSizes Sizes;
|
||||
QueryContextAttributes(
|
||||
static_cast<CtxtHandle*>(context),
|
||||
SECPKG_ATTR_STREAM_SIZES,
|
||||
&Sizes);
|
||||
debug << "stream sizes:\n\theader: " << Sizes.cbHeader << "\n\tfooter: " << Sizes.cbTrailer << "\n";
|
||||
|
||||
char *sendBuffer = new char[bufferSize + Sizes.cbHeader + Sizes.cbTrailer];
|
||||
memcpy(sendBuffer+Sizes.cbHeader, buffer, size);
|
||||
|
||||
SecBuffer secBuffers[4];
|
||||
secBuffers[0].cbBuffer = Sizes.cbHeader;
|
||||
secBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;
|
||||
secBuffers[0].pvBuffer = sendBuffer;
|
||||
|
||||
secBuffers[1].cbBuffer = size;
|
||||
secBuffers[1].BufferType = SECBUFFER_DATA;
|
||||
secBuffers[1].pvBuffer = sendBuffer+Sizes.cbHeader;
|
||||
|
||||
secBuffers[2].cbBuffer = Sizes.cbTrailer;
|
||||
secBuffers[2].pvBuffer = sendBuffer+Sizes.cbHeader+size;
|
||||
secBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
|
||||
|
||||
secBuffers[3].cbBuffer = 0;
|
||||
secBuffers[3].BufferType = SECBUFFER_EMPTY;
|
||||
secBuffers[3].pvBuffer = nullptr;
|
||||
|
||||
SecBufferDesc secBufferDesc;
|
||||
secBufferDesc.ulVersion = SECBUFFER_VERSION;
|
||||
secBufferDesc.cBuffers = 4;
|
||||
secBufferDesc.pBuffers = secBuffers;
|
||||
|
||||
auto ret = EncryptMessage(static_cast<CtxtHandle*>(context), 0, &secBufferDesc, 0); // FIXME
|
||||
debug << "Send:\n\tHeader size: " << secBuffers[0].cbBuffer << "\n\t\ttype: " << secBuffers[0].BufferType << "\n\tData size: " << secBuffers[1].cbBuffer << "\n\t\ttype: " << secBuffers[1].BufferType << "\n\tFooter size: " << secBuffers[2].cbBuffer << "\n\t\ttype: " << secBuffers[2].BufferType << "\n";
|
||||
|
||||
size_t sendSize = 0;
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
if (secBuffers[i].cbBuffer != bufferSize)
|
||||
sendSize += secBuffers[i].cbBuffer;
|
||||
|
||||
debug << "\tReal length? " << sendSize << "\n";
|
||||
switch (ret)
|
||||
{
|
||||
case SEC_E_OK:
|
||||
socket.write(sendBuffer, sendSize);
|
||||
break;
|
||||
// TODO: More?
|
||||
default:
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
delete[] sendBuffer;
|
||||
return size;
|
||||
}
|
||||
|
||||
void SChannelConnection::destroyContext()
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
DeleteSecurityContext(context);
|
||||
delete context;
|
||||
context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SChannelConnection::close()
|
||||
{
|
||||
destroyContext();
|
||||
socket.close();
|
||||
}
|
||||
|
||||
bool SChannelConnection::valid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // HTTPS_BACKEND_SCHANNEL
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/config.h"
|
||||
|
||||
#ifdef HTTPS_BACKEND_SCHANNEL
|
||||
|
||||
#include "../common/Connection.h"
|
||||
#include "../common/PlaintextConnection.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct _SecHandle;
|
||||
using CtxtHandle = _SecHandle;
|
||||
|
||||
class SChannelConnection : public Connection
|
||||
{
|
||||
public:
|
||||
SChannelConnection();
|
||||
virtual bool connect(const std::string &hostname, uint16_t port) override;
|
||||
virtual size_t read(char *buffer, size_t size) override;
|
||||
virtual size_t write(const char *buffer, size_t size) override;
|
||||
virtual void close() override;
|
||||
virtual ~SChannelConnection();
|
||||
|
||||
static bool valid();
|
||||
|
||||
private:
|
||||
PlaintextConnection socket;
|
||||
CtxtHandle *context;
|
||||
std::vector<char> encRecvBuffer;
|
||||
std::vector<char> decRecvBuffer;
|
||||
|
||||
size_t decrypt(char *buffer, size_t size, bool recurse = true);
|
||||
void destroyContext();
|
||||
};
|
||||
|
||||
#endif // HTTPS_BACKEND_SCHANNEL
|
||||
@@ -173,6 +173,10 @@ extern "C"
|
||||
extern int luaopen_love_arg(lua_State*);
|
||||
extern int luaopen_love_callbacks(lua_State*);
|
||||
extern int luaopen_love_boot(lua_State*);
|
||||
|
||||
#ifdef LOVE_ENABLE_LUAHTTPS
|
||||
extern int luaopen_https(lua_State*);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const luaL_Reg modules[] = {
|
||||
@@ -656,6 +660,9 @@ int luaopen_love(lua_State *L)
|
||||
#ifdef LOVE_ENABLE_LUA53
|
||||
love::luax_preload(L, luaopen_luautf8, "utf8");
|
||||
#endif
|
||||
#ifdef LOVE_ENABLE_LUAHTTPS
|
||||
love::luax_preload(L, luaopen_https, "https");
|
||||
#endif
|
||||
|
||||
#ifdef LOVE_ENABLE_WINDOW
|
||||
// In some environments, LuaJIT is limited to 2GB and LuaJIT sometimes panic when it
|
||||
|
||||
Reference in New Issue
Block a user