Allow using vendored tinyxml2 for XMLRPC

By default, builds will still not have XMLRPC enabled at all and the
configure flag `--with-xmlrpc-tinyxml2` must be specified. If both
xmlrpc-c and tinyxml2 are specified, xmlrpc-c takes precedence.

Basic benchmarks indicate tinyxml2 is 2x faster for small
requests/responses, and that only increases as response sizes get
larger.
This commit is contained in:
kannibalox
2024-10-19 20:23:18 -04:00
committed by Jari Sundell
parent 6ab265ecd4
commit 87c6422052
17 changed files with 6764 additions and 576 deletions
+83
View File
@@ -0,0 +1,83 @@
#include "config.h"
#include <string>
#include <iostream>
#include "command_helpers.h"
#include "rpc/command_map.h"
#include "xmlrpc_test.h"
#include "control.h"
#include "globals.h"
CPPUNIT_TEST_SUITE_REGISTRATION(XmlrpcTest);
torrent::Object xmlprpc_cmd_test_reflect(rpc::target_type t, const torrent::Object& obj) { return obj; }
void initialize_command_dynamic();
#if defined(HAVE_XMLRPC_TINYXML2) && !defined(HAVE_XMLRPC_C)
void
XmlrpcTest::setUp() {
m_commandItr = m_commands;
m_xmlrpc = rpc::XmlRpc();
m_xmlrpc.initialize();
setlocale(LC_ALL, "");
cachedTime = rak::timer::current();
control = new Control;
if (rpc::commands.find("xmlrpc_reflect") == rpc::commands.end()) {
CMD2_ANY("xmlrpc_reflect", &xmlprpc_cmd_test_reflect);
}
}
void
XmlrpcTest::test_basics() {
std::ifstream file; file.open("rpc/xmlrpc_test_data.txt");
CPPUNIT_ASSERT(file.good());
std::vector<std::string> titles;
std::vector<std::string> inputs;
std::vector<std::string> outputs;
std::string line;
int index = 0;
// Read file into inputs/outputs
while (std::getline(file, line)) {
if (line.size() == 0) {
continue;
}
if (line[0] == '#') {
titles.push_back(line);
continue;
}
if (index % 2) {
outputs.push_back(line);
} else {
inputs.push_back(line);
}
index++;
}
// Sanity check the above parser
CPPUNIT_ASSERT_MESSAGE("Could not parse test data", inputs.size() > 0 && inputs.size() == outputs.size() && inputs.size() == titles.size());
for (int i = 0; i < inputs.size(); i++) {
std::cout << titles[i] << "\n";
auto output = std::string("");
m_xmlrpc.process(inputs[i].c_str(), inputs[i].size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;});
CPPUNIT_ASSERT_EQUAL_MESSAGE(titles[i], std::string(outputs[i]), output);
}
}
void
XmlrpcTest::test_invalid_utf8() {
// Surprisingly, this call doesn't fail. TinyXML-2 technically expects
// valid UTF-8, but doesn't check strings, and Object strings are
// just a series of bytes so it reflects just fine.
std::string input = "<?xml version=\"1.0\"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><string>\xc3\x28</string></value></param></params></methodCall>";
std::string expected = "<?xml version=\"1.0\"?><methodReponse><params><param><value><array><value><string>\xc3\x28</string></value></array></value></param></params></methodReponse>";
auto output = std::string("");
m_xmlrpc.process(input.c_str(), input.size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;});
CPPUNIT_ASSERT_EQUAL(expected, output);
}
#else
void XmlrpcTest::test_invalid_utf8() {}
void XmlrpcTest::test_basics() {}
void XmlrpcTest::setUp() {}
#endif
+28
View File
@@ -0,0 +1,28 @@
#include <cppunit/extensions/HelperMacros.h>
#include "rpc/command_map.h"
#include "rpc/xmlrpc.h"
class XmlrpcTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(XmlrpcTest);
CPPUNIT_TEST(test_basics);
CPPUNIT_TEST(test_invalid_utf8);
CPPUNIT_TEST_SUITE_END();
public:
static const int cmd_size = 256;
void setUp();
void tearDown() {}
void test_basics();
void test_invalid_utf8();
private:
rpc::XmlRpc m_xmlrpc;
rpc::CommandMap m_map;
rpc::command_base m_commands[cmd_size];
rpc::command_base* m_commandItr;
};
+55
View File
@@ -0,0 +1,55 @@
# Basic call
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array/></value></param></params></methodReponse>
# UTF-8 string
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><string>чао</string></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><string>чао</string></value></array></value></param></params></methodReponse>
# emoji string
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><string>😊</string></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><string>😊</string></value></array></value></param></params></methodReponse>
# base64 data (which gets returned as a string)
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><base64>Zm9vYmFy</base64></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><string>foobar</string></value></array></value></param></params></methodReponse>
# i4 ints
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i4>41</i4></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><i4>41</i4></value></array></value></param></params></methodReponse>
# i8 ints
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i8>2247483647</i8></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><i8>2247483647</i8></value></array></value></param></params></methodReponse>
# negative i8 ints
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i8>-2347483647</i8></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><i8>-2347483647</i8></value></array></value></param></params></methodReponse>
# Empty array
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><array><data><value><i8>2247483647</i8></value></data></array></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><array><value><i8>2247483647</i8></value></array></value></array></value></param></params></methodReponse>
# Simple array
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><array><data></data></array></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><array/></value></array></value></param></params></methodReponse>
# Empty struct
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><struct></struct></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><struct/></value></array></value></param></params></methodReponse>
# Simple struct
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><struct><member><name>lowerBound</name><value><i4>18</i4></value></member><member><name>upperBound</name><value><i4>139</i4></value></member></struct></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><params><param><value><array><value><struct><member><name>lowerBound</name><value><i4>18</i4></value></member><member><name>upperBound</name><value><i4>139</i4></value></member></struct></value></array></value></param></params></methodReponse>
# Invalid - missing method
<?xml version="1.0"?><methodCall><methodName>no_such_method</methodName><params><param><value><i4>41</i4></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><fault><struct><member><name>faultCode</name><value><int>-506</int></value></member><member><name>faultString</name><value><string>Method 'no_such_method' not defined</string></value></member></struct></fault></methodReponse>
# Invalid - i4 target
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><i4>41</i4></value></param></params></methodCall>
<?xml version="1.0"?><methodReponse><fault><struct><member><name>faultCode</name><value><int>-500</int></value></member><member><name>faultString</name><value><string>invalid parameters: target must be a string</string></value></member></struct></fault></methodReponse>
# Invalid - broken XML
thodCall><methodName>test_a</methodName><params><param><value><i4>41</i4></value></param></params></method
<?xml version="1.0"?><methodReponse><fault><struct><member><name>faultCode</name><value><int>-503</int></value></member><member><name>faultString</name><value><string>Error=XML_ERROR_PARSING_ELEMENT ErrorID=6 (0x6) Line number=1: XMLElement name=method</string></value></member></struct></fault></methodReponse>