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:
Alex Szpakowski
2022-01-30 13:21:33 -04:00
parent f4a85f42df
commit d7522c88af
29 changed files with 2229 additions and 3 deletions
+68
View File
@@ -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 */,
+1
View File
@@ -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
+26
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
src/android/java
+17
View File
@@ -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
+119
View File
@@ -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
+7
View File
@@ -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