/*============================================================================= server_abyss =============================================================================== Test the Abyss server C++ facilities of XML-RPC for C/C++. =============================================================================*/ #include #include #include #include #include #include #include #ifdef WIN32 #include #else #include #include #include #endif #include "xmlrpc-c/girerr.hpp" using girerr::error; using girerr::throwf; #include "xmlrpc-c/base.hpp" #include "xmlrpc-c/registry.hpp" #include "xmlrpc-c/server_abyss.hpp" #include "xmlrpc-c/abyss.h" #include "tools.hpp" #include "server_abyss.hpp" using namespace xmlrpc_c; using namespace std; static void closesock(int const fd) { #ifdef WIN32 closesocket(fd); #else close(fd); #endif } class boundSocket { public: boundSocket(short const portNumber) { this->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (this->fd < 0) throwf("socket() failed. errno=%d (%s)", errno, strerror(errno)); struct sockaddr_in sockAddr; int rc; sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(portNumber); sockAddr.sin_addr.s_addr = 0; rc = bind(this->fd, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); if (rc != 0) { closesock(this->fd); throwf("Couldn't bind. bind() failed with errno=%d (%s)", errno, strerror(errno)); } } ~boundSocket() { closesock(this->fd); } int fd; }; class sampleAddMethod : public method { public: sampleAddMethod() { this->_signature = "i:ii"; this->_help = "This method adds two integers together"; } void execute(xmlrpc_c::paramList const& paramList, value * const retvalP) { int const addend(paramList.getInt(0)); int const adder(paramList.getInt(1)); paramList.verifyEnd(2); *retvalP = value_int(addend + adder); } }; // We need 'global' because methods of class addHandlerTestSuite call // functions in the Abyss C library. By virtue of global's static // storage class, the program loader will call its constructor and // destructor and thus initialize and terminate the Abyss C library. static class abyssGlobalState { public: abyssGlobalState() { const char * error; AbyssInit(&error); if (error) { string const e(error); free(const_cast(error)); throwf("AbyssInit() failed. %s", e.c_str()); } } ~abyssGlobalState() { AbyssTerm(); } } const global; class addHandlerTestSuite : public testSuite { public: virtual string suiteName() { return "addHandlerTestSuite"; } virtual void runtests(unsigned int const) { TServer abyssServer; ServerCreate(&abyssServer, "testserver", 8080, NULL, NULL); registry myRegistry; myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod)); registryPtr myRegistryP(new registry); myRegistryP->addMethod("sample.add", methodPtr(new sampleAddMethod)); server_abyss_set_handlers(&abyssServer, myRegistry); server_abyss_set_handlers(&abyssServer, &myRegistry); server_abyss_set_handlers(&abyssServer, myRegistryP); server_abyss_set_handlers(&abyssServer, myRegistry, "/RPC3"); server_abyss_set_handlers(&abyssServer, &myRegistry, "/RPC3"); server_abyss_set_handlers(&abyssServer, myRegistryP, "/RPC3"); ServerFree(&abyssServer); } }; class setShutdownTestSuite : public testSuite { public: virtual string suiteName() { return "setShutdownTestSuite"; } virtual void runtests(unsigned int const) { registry myRegistry; serverAbyss myServer(serverAbyss::constrOpt() .registryP(&myRegistry) .portNumber(12345) ); serverAbyss::shutdown shutdown(&myServer); myRegistry.setShutdown(&shutdown); } }; class createTestSuite : public testSuite { public: virtual string suiteName() { return "createTestSuite"; } virtual void runtests(unsigned int const) { registry myRegistry; myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod)); registryPtr myRegistryP(new registry); myRegistryP->addMethod("sample.add", methodPtr(new sampleAddMethod)); EXPECT_ERROR( // No registry serverAbyss::constrOpt opt; serverAbyss abyssServer(opt); ); EXPECT_ERROR( // Both portNumber and socketFd serverAbyss abyssServer(serverAbyss::constrOpt() .portNumber(8080) .socketFd(3)); ); // Due to the vagaries of Abyss, construction of the following // objects may exit the program if it detects an error, such as // port number already in use. We need to fix Abyss some day. { serverAbyss abyssServer(serverAbyss::constrOpt() .registryP(&myRegistry) .portNumber(12345) ); } { serverAbyss abyssServer(serverAbyss::constrOpt() .registryPtr(myRegistryP) .portNumber(12345) ); EXPECT_ERROR( // Both registryP and registryPtr serverAbyss abyssServer(serverAbyss::constrOpt() .registryPtr(myRegistryP) .registryP(&myRegistry) .portNumber(12345) ); ); } { boundSocket socket(12345); serverAbyss abyssServer(serverAbyss::constrOpt() .registryP(&myRegistry) .socketFd(socket.fd) ); } { serverAbyss abyssServer(serverAbyss::constrOpt() .registryP(&myRegistry) ); } { // Test all the options serverAbyss abyssServer(serverAbyss::constrOpt() .registryPtr(myRegistryP) .portNumber(12345) .logFileName("/tmp/logfile") .keepaliveTimeout(5) .keepaliveMaxConn(4) .timeout(20) .dontAdvertise(true) .uriPath("/xmlrpc") ); } { serverAbyss abyssServer( myRegistry, 12345, // TCP port on which to listen "/tmp/xmlrpc_log" // Log file ); } } }; string serverAbyssTestSuite::suiteName() { return "serverAbyssTestSuite"; } void serverAbyssTestSuite::runtests(unsigned int const indentation) { addHandlerTestSuite().run(indentation+1); setShutdownTestSuite().run(indentation+1); createTestSuite().run(indentation+1); }