FS-2746 --resolve large xmlrpc update thanks garmt

This commit is contained in:
Jeff Lenk
2012-10-13 11:37:25 -05:00
parent 37ecad9903
commit 6b6c83a718
397 changed files with 41822 additions and 33841 deletions
-1
View File
@@ -1 +0,0 @@
cpptest
+130 -49
View File
@@ -13,22 +13,26 @@ default: all
# libxmlrpc_cpp is the legacy C++ wrapper library. The others are the
# more elaborate replacements.
TARGET_LIB_NAMES_PP := \
TARGET_LIB_NAMES_PP = \
libxmlrpc_cpp \
libxmlrpc++ \
lib$(LIBXMLRPCPP_NAME) \
libxmlrpc_server++ \
libxmlrpc_server_abyss++ \
libxmlrpc_server_cgi++ \
libxmlrpc_server_pstream++ \
libxmlrpc_packetsocket \
ifeq ($(ENABLE_ABYSS_SERVER),yes)
TARGET_LIB_NAMES_PP += libxmlrpc_server_abyss++
endif
ifeq ($(MUST_BUILD_CLIENT),yes)
TARGET_LIB_NAMES_PP += libxmlrpc_client++
endif
STATIC_LIBRARIES_TO_INSTALL = $(TARGET_STATIC_LIBRARIES)
SHARED_LIBS_TO_BUILD := $(TARGET_LIB_NAMES_PP)
SHARED_LIBS_TO_INSTALL := $(TARGET_LIB_NAMES_PP)
SHARED_LIBS_TO_BUILD = $(TARGET_LIB_NAMES_PP)
SHARED_LIBS_TO_INSTALL = $(TARGET_LIB_NAMES_PP)
# INCLUDES and DEP_SOURCES are used by dep-common target
INCLUDES = $(BASIC_INCLUDES) $(CLIENT_INCLUDES) $(LIBXML_INCLUDES) \
@@ -45,11 +49,20 @@ else
LIBXML_INCLUDES = -Isrcdir/lib/expat/xmlparse
endif
ifeq ($(ENABLE_LIBXML2_BACKEND),yes)
XML_PARSER_LIBDEP = $(shell xml2-config --libs)
else
XML_PARSER_LIBDEP = \
-L$(BLDDIR)/lib/expat/xmlparse -lxmlrpc_xmlparse \
-L$(BLDDIR)/lib/expat/xmltok -lxmlrpc_xmltok
endif
LIBXMLRPCPP_MODS = \
base64 env_wrap fault girerr girmem outcome param_list value xml
LIBXMLRPC_SERVERPP_MODS = registry
LIBXMLRPC_SERVER_ABYSSPP_MODS = server_abyss
LIBXMLRPC_SERVER_PSTREAMPP_MODS = server_pstream
LIBXMLRPC_SERVER_CGIPP_MODS = server_cgi
LIBXMLRPC_SERVER_PSTREAMPP_MODS = server_pstream_conn server_pstream
LIBXMLRPC_CLIENTPP_MODS = client client_simple curl libwww wininet pstream
LIBXMLRPC_PACKETSOCKET_MODS = packetsocket
@@ -58,12 +71,13 @@ TARGET_MODS_PP = \
$(LIBXMLRPCPP_MODS) \
$(LIBXMLRPC_SERVERPP_MODS) \
$(LIBXMLRPC_SERVER_ABYSSPP_MODS) \
$(LIBXMLRPC_SERVER_CGIPP_MODS) \
$(LIBXMLRPC_SERVER_PSTREAMPP_MODS) \
$(LIBXMLRPC_CLIENTPP_MODS) \
$(LIBXMLRPC_PACKETSOCKET_MODS) \
OMIT_CPP_LIB_RULES = Y
MAJ=4
MAJ = 7
# Major number of shared libraries in this directory
include $(SRCDIR)/common.mk
@@ -104,63 +118,120 @@ all: \
# shlibfn generates e.g. libxmlrpc.so.3.1
# shliblefn generates e.g. libxmlrpc.so
LIBXMLRPC_CPP = $(call shlibfn, libxmlrpc_cpp)
LIBXMLRPC_CPP_SH = $(call shlibfn, libxmlrpc_cpp)
$(LIBXMLRPC_CPP): XmlRpcCpp.osh
$(LIBXMLRPC_CPP): LIBOBJECTS = XmlRpcCpp.osh
LIBXMLRPCPP = $(call shlibfn, libxmlrpc++)
$(LIBXMLRPCPP): $(LIBXMLRPCPP_MODS:%=%.osh) \
$(LIBXMLRPC)
$(LIBXMLRPCPP): LIBOBJECTS = $(LIBXMLRPCPP_MODS:%=%.osh)
$(LIBXMLRPCPP): LIBDEP = -Lblddir/src -lxmlrpc
LIBXMLRPC_SERVERPP = $(call shlibfn, libxmlrpc_server++)
$(LIBXMLRPC_SERVERPP): $(LIBXMLRPC_SERVERPP_MODS:%=%.osh) \
$(LIBXMLRPC_CPP_SH): XmlRpcCpp.osh \
$(LIBXMLRPC_UTIL) \
$(LIBXMLRPC) \
$(LIBXMLRPC_SERVER) \
$(LIBXMLRPC_UTIL)
$(LIBXMLRPC_CPP_SH): LIBOBJECTS = XmlRpcCpp.osh
$(LIBXMLRPC_CPP_SH): LIBDEP = \
-Lblddir/src -lxmlrpc_server -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util
LIBXMLRPCPP_SH = $(call shlibfn, lib$(LIBXMLRPCPP_NAME))
$(LIBXMLRPCPP_SH): $(LIBXMLRPCPP_MODS:%=%.osh) \
$(LIBXMLRPC) \
$(LIBXMLRPC_UTIL)
$(LIBXMLRPCPP_SH): LIBOBJECTS = $(LIBXMLRPCPP_MODS:%=%.osh)
$(LIBXMLRPCPP_SH): LIBDEP = \
-Lblddir/src -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util
LIBXMLRPC_SERVERPP_SH = $(call shlibfn, libxmlrpc_server++)
$(LIBXMLRPC_SERVERPP_SH): $(LIBXMLRPC_SERVERPP_MODS:%=%.osh) \
$(LIBXMLRPC_SERVER) \
$(call shliblefn, lib$(LIBXMLRPCPP_NAME)) \
$(LIBXMLRPC_UTIL) \
$(LIBXMLRPC)
$(LIBXMLRPC_SERVERPP): LIBOBJECTS = $(LIBXMLRPC_SERVERPP_MODS:%=%.osh)
$(LIBXMLRPC_SERVERPP): LIBDEP = -Lblddir/src -lxmlrpc_server -lxmlrpc
$(LIBXMLRPC_SERVERPP_SH): LIBOBJECTS = $(LIBXMLRPC_SERVERPP_MODS:%=%.osh)
$(LIBXMLRPC_SERVERPP_SH): LIBDEP = \
-L. -l$(LIBXMLRPCPP_NAME) \
-Lblddir/src -lxmlrpc_server -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util
LIBXMLRPC_SERVER_ABYSSPP = $(call shlibfn, libxmlrpc_server_abyss++)
LIBXMLRPC_SERVER_ABYSSPP_SH = $(call shlibfn, libxmlrpc_server_abyss++)
$(LIBXMLRPC_SERVER_ABYSSPP): $(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.osh) \
$(LIBXMLRPC_SERVER_ABYSSPP_SH): $(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.osh) \
$(LIBXMLRPC_ABYSS) \
$(LIBXMLRPC_SERVER_ABYSS) \
$(call shliblefn, lib$(LIBXMLRPCPP_NAME)) \
$(call shlibfn, libxmlrpc_server++) \
$(LIBXMLRPC_UTIL) \
$(LIBXMLRPC)
$(LIBXMLRPC_SERVER_ABYSSPP): LIBOBJECTS = $(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.osh)
$(LIBXMLRPC_SERVER_ABYSSPP): LIBDEP = \
-L$(LIBXMLRPC_ABYSS_DIR) -lxmlrpc_abyss -Lblddir/src -lxmlrpc
$(LIBXMLRPC_SERVER_ABYSSPP_SH): LIBOBJECTS = $(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.osh)
$(LIBXMLRPC_SERVER_ABYSSPP_SH): LIBDEP = \
-L. -lxmlrpc_server++ -l$(LIBXMLRPCPP_NAME) \
-Lblddir/src -lxmlrpc_server_abyss -lxmlrpc_server -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_ABYSS_DIR) -lxmlrpc_abyss \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util \
LIBXMLRPC_SERVER_PSTREAMPP = $(call shlibfn, libxmlrpc_server_pstream++)
LIBXMLRPC_SERVER_CGIPP_SH = $(call shlibfn, libxmlrpc_server_cgi++)
$(LIBXMLRPC_SERVER_PSTREAMPP): $(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.osh) \
$(LIBXMLRPC_SERVER_CGIPP_SH): $(LIBXMLRPC_SERVER_CGIPP_MODS:%=%.osh) \
$(call shliblefn, libxmlrpc_server++) \
$(call shliblefn, lib$(LIBXMLRPCPP_NAME)) \
$(LIBXMLRPC)
$(LIBXMLRPC_SERVER_CGIPP_SH): LIBOBJECTS = $(LIBXMLRPC_SERVER_CGIPP_MODS:%=%.osh)
$(LIBXMLRPC_SERVER_CGIPP_SH): LIBDEP = \
-L. -lxmlrpc_server++ -l$(LIBXMLRPCPP_NAME) \
-Lblddir/src -lxmlrpc_server -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util
LIBXMLRPC_SERVER_PSTREAMPP_SH = $(call shlibfn, libxmlrpc_server_pstream++)
$(LIBXMLRPC_SERVER_PSTREAMPP_SH): $(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.osh) \
$(LIBXMLRPC_SERVER) \
$(LIBXMLRPC_PACKETSOCKET)
$(LIBXMLRPC_SERVER_PSTREAMPP): LIBOBJECTS = $(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.osh)
$(LIBXMLRPC_SERVER_PSTREAMPP): LIBDEP = \
-Lblddir/src -lxmlrpc_server -L. -lxmlrpc_packetsocket
$(call shliblefn, lib$(LIBXMLRPCPP_NAME)) \
$(call shliblefn, libxmlrpc_packetsocket) \
$(call shliblefn, libxmlrpc_server++)
$(LIBXMLRPC_SERVER_PSTREAMPP_SH): LIBOBJECTS = $(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.osh)
$(LIBXMLRPC_SERVER_PSTREAMPP_SH): LIBDEP = \
-L. -lxmlrpc_server++ -l$(LIBXMLRPCPP_NAME) -lxmlrpc_packetsocket \
-Lblddir/src -lxmlrpc_server -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util \
LIBXMLRPC_CLIENTPP = $(call shlibfn, libxmlrpc_client++)
LIBXMLRPC_CLIENTPP_SH = $(call shlibfn, libxmlrpc_client++)
$(LIBXMLRPC_CLIENTPP): $(LIBXMLRPC_CLIENTPP_MODS:%=%.osh) \
$(LIBXMLRPCPP) \
$(LIBXMLRPC_CLIENTPP_SH): $(LIBXMLRPC_CLIENTPP_MODS:%=%.osh) \
$(LIBXMLRPC_UTIL) \
$(call shliblefn, libxmlrpc_packetsocket) \
$(LIBXMLRPC) \
$(call shliblefn, lib$(LIBXMLRPCPP_NAME)) \
$(LIBXMLRPC_CLIENT)
$(LIBXMLRPC_CLIENTPP): LIBOBJECTS = $(LIBXMLRPC_CLIENTPP_MODS:%=%.osh)
$(LIBXMLRPC_CLIENTPP): LIBDEP = \
-L. -lxmlrpc++ -Lblddir/src -lxmlrpc_client
$(LIBXMLRPC_CLIENTPP_SH): LIBOBJECTS = $(LIBXMLRPC_CLIENTPP_MODS:%=%.osh)
$(LIBXMLRPC_CLIENTPP_SH): LIBDEP = \
-L. -l$(LIBXMLRPCPP_NAME) -lxmlrpc_packetsocket \
-Lblddir/src -lxmlrpc_client -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util
LIBXMLRPC_PACKETSOCKET = $(call shlibfn, libxmlrpc_packetsocket)
LIBXMLRPC_PACKETSOCKET_SH = $(call shlibfn, libxmlrpc_packetsocket)
$(LIBXMLRPC_PACKETSOCKET): $(LIBXMLRPC_PACKETSOCKET_MODS:%=%.osh)
$(LIBXMLRPC_PACKETSOCKET): LIBOBJECTS = $(LIBXMLRPC_PACKETSOCKET_MODS:%=%.osh)
$(LIBXMLRPC_PACKETSOCKET_SH): $(LIBXMLRPC_PACKETSOCKET_MODS:%=%.osh) \
$(call shliblefn, lib$(LIBXMLRPCPP_NAME))
$(LIBXMLRPC_PACKETSOCKET_SH): LIBOBJECTS = $(LIBXMLRPC_PACKETSOCKET_MODS:%=%.osh)
$(LIBXMLRPC_PACKETSOCKET_SH): LIBDEP = \
-L. -l$(LIBXMLRPCPP_NAME) \
-Lblddir/src -lxmlrpc \
$(XML_PARSER_LIBDEP) \
-L$(LIBXMLRPC_UTIL_DIR) -lxmlrpc_util
libxmlrpc_cpp.a: XmlRpcCpp.o
libxmlrpc_cpp.a: LIBOBJECTS = XmlRpcCpp.o
libxmlrpc++.a: $(LIBXMLRPCPP_MODS:%=%.o)
libxmlrpc++.a: LIBOBJECTS = $(LIBXMLRPCPP_MODS:%=%.o)
lib$(LIBXMLRPCPP_NAME).a: $(LIBXMLRPCPP_MODS:%=%.o)
lib$(LIBXMLRPCPP_NAME).a: LIBOBJECTS = $(LIBXMLRPCPP_MODS:%=%.o)
libxmlrpc_server++.a: $(LIBXMLRPC_SERVERPP_MODS:%=%.o)
libxmlrpc_server++.a: LIBOBJECTS = $(LIBXMLRPC_SERVERPP_MODS:%=%.o)
@@ -168,6 +239,9 @@ libxmlrpc_server++.a: LIBOBJECTS = $(LIBXMLRPC_SERVERPP_MODS:%=%.o)
libxmlrpc_server_abyss++.a: $(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.o)
libxmlrpc_server_abyss++.a: LIBOBJECTS=$(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.o)
libxmlrpc_server_cgi++.a: $(LIBXMLRPC_SERVER_CGIPP_MODS:%=%.o)
libxmlrpc_server_cgi++.a: LIBOBJECTS=$(LIBXMLRPC_SERVER_CGIPP_MODS:%=%.o)
libxmlrpc_server_pstream++.a: $(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.o)
libxmlrpc_server_pstream++.a: LIBOBJECTS=$(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.o)
@@ -197,11 +271,15 @@ $(LIBXMLRPC_SERVERPP_MODS:%=%.osh): \
INCLUDES = $(SERVER_INCLUDES)
$(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.o) \
$(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.o): \
$(LIBXMLRPC_SERVER_ABYSSPP_MODS:%=%.osh): \
INCLUDES = $(SERVER_INCLUDES)
$(LIBXMLRPC_SERVER_CGIPP_MODS:%=%.o) \
$(LIBXMLRPC_SERVER_CGIPP_MODS:%=%.osh): \
INCLUDES = $(SERVER_INCLUDES)
$(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.o) \
$(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.o): \
$(LIBXMLRPC_SERVER_PSTREAMPP_MODS:%=%.osh): \
INCLUDES = $(SERVER_INCLUDES)
$(LIBXMLRPC_CLIENTPP_MODS:%=%.o) \
@@ -212,7 +290,10 @@ $(LIBXMLRPC_PACKETSOCKET_MODS:%=%.o) \
$(LIBXMLRPC_PACKETSOCKET_MODS:%=%.osh): \
INCLUDES = $(BASIC_INCLUDES)
CXXFLAGS = $(CXXFLAGS_COMMON) $(CFLAGS_PERSONAL) $(CADD)
# <sstream> in Glibc 2.2 has some failed inlines, so we disable that warning:
$(LIBXMLRPC_PACKETSOCKET_MODS:%=%.o) \
$(LIBXMLRPC_PACKETSOCKET_MODS:%=%.osh): \
CFLAGS_LOCAL = -Wno-inline
TRANSPORT_CONFIG_USERS = client curl libwww wininet
@@ -242,4 +323,4 @@ distclean-local:
.PHONY: dep
dep: dep-common $(BLDDIR)/transport_config.h
include Makefile.depend
include depend.mk
+1 -1
View File
@@ -77,7 +77,7 @@ public:
assert(bitsInBuffer >= 8);
*outputP = (this->buffer >> (this->bitsInBuffer - 8)) & 0x3f;
*outputP = (this->buffer >> (this->bitsInBuffer - 8)) & 0xff;
this->bitsInBuffer -= 8;
}
+45 -2
View File
@@ -26,6 +26,7 @@ using girmem::autoObject;
#include "xmlrpc-c/transport.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/xml.hpp"
#include "xmlrpc-c/timeout.hpp"
#include "xmlrpc-c/client.hpp"
#include "transport_config.h"
@@ -127,7 +128,7 @@ carriageParmPtr::operator->() const {
carriageParm *
carriageParmPtr::get() const {
return dynamic_cast<carriageParm *>(objectP);
return dynamic_cast<carriageParm *>(this->objectP);
}
@@ -371,6 +372,15 @@ xmlTransaction::finishErr(error const&) const {
void
xmlTransaction::progress(struct xmlrpc_progress_data const&) const {
// This is just the base class method. A derived class may override
// this with something substantial.
}
xmlTransactionPtr::xmlTransactionPtr() {}
@@ -502,6 +512,18 @@ clientXmlTransport::asyncComplete(
void
clientXmlTransport::progress(
struct xmlrpc_call_info * const callInfoP,
struct xmlrpc_progress_data const progressData) {
xmlTranCtl * const xmlTranCtlP = reinterpret_cast<xmlTranCtl *>(callInfoP);
xmlTranCtlP->xmlTranP->progress(progressData);
}
void
clientXmlTransport::setInterrupt(int *) {
@@ -600,7 +622,7 @@ clientXmlTransport_http::start(
this->c_transportP,
carriageParmHttpP->c_serverInfoP,
tranCtlP->callXmlP,
&this->asyncComplete,
&this->asyncComplete, &this->progress,
reinterpret_cast<xmlrpc_call_info *>(tranCtlP));
throwIfError(env);
@@ -1095,8 +1117,20 @@ rpc::notifyComplete() {
}
void
rpc::progress(struct xmlrpc_progress_data const&) const {
/*----------------------------------------------------------------------------
If the user is interested in tracking the progress of the RPC, he will
derive a class from xmlrpc_c::rpc and override this with a progress()
that does something, such as display a progress bar.
-----------------------------------------------------------------------------*/
}
value
rpc::getResult() const {
@@ -1214,6 +1248,15 @@ xmlTransaction_client::finishErr(error const& error) const {
void
xmlTransaction_client::progress(
struct xmlrpc_progress_data const& progressData) const {
this->tranP->progress(progressData);
}
xmlTransaction_clientPtr::xmlTransaction_clientPtr() {}
File diff suppressed because it is too large Load Diff
+127 -20
View File
@@ -127,27 +127,93 @@ carriageParm_curl0Ptr::operator->() const {
clientXmlTransport_curl::constrOpt::constrOpt() {
struct clientXmlTransport_curl::constrOpt_impl {
constrOpt_impl();
struct {
std::string network_interface;
bool no_ssl_verifypeer;
bool no_ssl_verifyhost;
bool dont_advertise;
std::string user_agent;
std::string ssl_cert;
std::string sslcerttype;
std::string sslcertpasswd;
std::string sslkey;
std::string sslkeytype;
std::string sslkeypasswd;
std::string sslengine;
bool sslengine_default;
xmlrpc_sslversion sslversion;
std::string cainfo;
std::string capath;
std::string randomfile;
std::string egdsocket;
std::string ssl_cipher_list;
unsigned int timeout;
std::string proxy;
unsigned int proxy_auth;
unsigned int proxy_port;
std::string proxy_userpwd;
xmlrpc_httpproxytype proxy_type;
} value;
struct {
bool network_interface;
bool no_ssl_verifypeer;
bool no_ssl_verifyhost;
bool dont_advertise;
bool user_agent;
bool ssl_cert;
bool sslcerttype;
bool sslcertpasswd;
bool sslkey;
bool sslkeytype;
bool sslkeypasswd;
bool sslengine;
bool sslengine_default;
bool sslversion;
bool cainfo;
bool capath;
bool randomfile;
bool egdsocket;
bool ssl_cipher_list;
bool timeout;
bool proxy;
bool proxy_auth;
bool proxy_port;
bool proxy_userpwd;
bool proxy_type;
} present;
};
clientXmlTransport_curl::constrOpt_impl::constrOpt_impl() {
present.network_interface = false;
present.no_ssl_verifypeer = false;
present.no_ssl_verifyhost = false;
present.user_agent = false;
present.ssl_cert = false;
present.sslcerttype = false;
present.sslcertpasswd = false;
present.sslkey = false;
present.sslkeytype = false;
present.sslkeypasswd = false;
present.sslengine = false;
present.dont_advertise = false;
present.user_agent = false;
present.ssl_cert = false;
present.sslcerttype = false;
present.sslcertpasswd = false;
present.sslkey = false;
present.sslkeytype = false;
present.sslkeypasswd = false;
present.sslengine = false;
present.sslengine_default = false;
present.sslversion = false;
present.cainfo = false;
present.capath = false;
present.randomfile = false;
present.egdsocket = false;
present.ssl_cipher_list = false;
present.timeout = false;
present.sslversion = false;
present.cainfo = false;
present.capath = false;
present.randomfile = false;
present.egdsocket = false;
present.ssl_cipher_list = false;
present.timeout = false;
present.proxy = false;
present.proxy_port = false;
present.proxy_auth = false;
present.proxy_userpwd = false;
present.proxy_type = false;
}
@@ -155,14 +221,15 @@ clientXmlTransport_curl::constrOpt::constrOpt() {
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
clientXmlTransport_curl::constrOpt & \
clientXmlTransport_curl::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->value.OPTION_NAME = arg; \
this->present.OPTION_NAME = true; \
this->implP->value.OPTION_NAME = arg; \
this->implP->present.OPTION_NAME = true; \
return *this; \
}
DEFINE_OPTION_SETTER(network_interface, string);
DEFINE_OPTION_SETTER(no_ssl_verifypeer, bool);
DEFINE_OPTION_SETTER(no_ssl_verifyhost, bool);
DEFINE_OPTION_SETTER(dont_advertise, bool);
DEFINE_OPTION_SETTER(user_agent, string);
DEFINE_OPTION_SETTER(ssl_cert, string);
DEFINE_OPTION_SETTER(sslcerttype, string);
@@ -179,13 +246,41 @@ DEFINE_OPTION_SETTER(randomfile, string);
DEFINE_OPTION_SETTER(egdsocket, string);
DEFINE_OPTION_SETTER(ssl_cipher_list, string);
DEFINE_OPTION_SETTER(timeout, unsigned int);
DEFINE_OPTION_SETTER(proxy, string);
DEFINE_OPTION_SETTER(proxy_port, unsigned int);
DEFINE_OPTION_SETTER(proxy_auth, unsigned int);
DEFINE_OPTION_SETTER(proxy_userpwd, string);
DEFINE_OPTION_SETTER(proxy_type, xmlrpc_httpproxytype);
#undef DEFINE_OPTION_SETTER
clientXmlTransport_curl::constrOpt::constrOpt() {
this->implP = new clientXmlTransport_curl::constrOpt_impl();
}
clientXmlTransport_curl::constrOpt::~constrOpt() {
delete(this->implP);
}
clientXmlTransport_curl::constrOpt::constrOpt(constrOpt& arg) {
this->implP = new constrOpt_impl(*arg.implP);
}
#if MUST_BUILD_CURL_CLIENT
void
clientXmlTransport_curl::initialize(constrOpt const& opt) {
clientXmlTransport_curl::initialize(constrOpt const& optExt) {
constrOpt_impl const opt(*optExt.implP);
struct xmlrpc_curl_xportparms transportParms;
transportParms.network_interface = opt.present.network_interface ?
@@ -194,6 +289,8 @@ clientXmlTransport_curl::initialize(constrOpt const& opt) {
opt.value.no_ssl_verifypeer : false;
transportParms.no_ssl_verifyhost = opt.present.no_ssl_verifyhost ?
opt.value.no_ssl_verifyhost : false;
transportParms.dont_advertise = opt.present.dont_advertise ?
opt.value.dont_advertise : false;
transportParms.user_agent = opt.present.user_agent ?
opt.value.user_agent.c_str() : NULL;
transportParms.ssl_cert = opt.present.ssl_cert ?
@@ -226,6 +323,16 @@ clientXmlTransport_curl::initialize(constrOpt const& opt) {
opt.value.ssl_cipher_list.c_str() : NULL;
transportParms.timeout = opt.present.timeout ?
opt.value.timeout : 0;
transportParms.proxy = opt.present.proxy ?
opt.value.proxy.c_str() : NULL;
transportParms.proxy_port = opt.present.proxy_port ?
opt.value.proxy_port : 0;
transportParms.proxy_auth = opt.present.proxy_auth ?
opt.value.proxy_auth : XMLRPC_HTTPAUTH_NONE;
transportParms.proxy_userpwd = opt.present.proxy_userpwd ?
opt.value.proxy_userpwd.c_str() : NULL;
transportParms.proxy_type = opt.present.proxy_type ?
opt.value.proxy_type : XMLRPC_HTTPPROXY_HTTP;
this->c_transportOpsP = &xmlrpc_curl_transport_ops;
@@ -233,7 +340,7 @@ clientXmlTransport_curl::initialize(constrOpt const& opt) {
xmlrpc_curl_transport_ops.create(
&env.env_c, 0, "", "",
&transportParms, XMLRPC_CXPSIZE(timeout),
&transportParms, XMLRPC_CXPSIZE(dont_advertise),
&this->c_transportP);
if (env.env_c.fault_occurred)
+2 -1
View File
@@ -1,11 +1,12 @@
#ifndef ENV_INT_HPP_INCLUDED
#define ENV_INT_HPP_INCLUDED
#include "xmlrpc-c/c_util.h"
#include "xmlrpc-c/util.h"
namespace xmlrpc_c {
class env_wrap {
class XMLRPC_DLLEXPORT env_wrap {
/*----------------------------------------------------------------------------
A wrapper to assist in using the Xmlrpc-c C libraries in
Xmlrpc-c C++ code.
+2 -1
View File
@@ -141,7 +141,8 @@ clientXmlTransport_libwww::clientXmlTransport_libwww(
}
#else // MUST_BUILD_LIBWWW_CLIENT
clientXmlTransport_libwww::clientXmlTransport_libwww(string, string) {
clientXmlTransport_libwww::clientXmlTransport_libwww(string const,
string const) {
throw(error("There is no Libwww client XML transport "
"in this XML-RPC client library"));
+409 -144
View File
@@ -37,15 +37,15 @@
All bytes not part of a control word are literal bytes of a packet.
You can create a packet socket from any file descriptor from which
you can read and write a bidirectional character stream. Typically,
it's a TCP socket.
You can create a packet socket from a POSIX stream socket or a
Windows emulation of one.
One use of the NOP control word is to validate that the connection
is still working. You might send one periodically to detect, for
example, an unplugged TCP/IP network cable. It's probably better
to use the TCP keepalive facility for that.
============================================================================*/
#include "xmlrpc_config.h"
#include <cassert>
#include <string>
@@ -54,10 +54,18 @@
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#if MSVCRT
# include <winsock2.h>
# include <io.h>
#else
# include <unistd.h>
# include <poll.h>
# include <sys/socket.h>
#endif
#include <sys/types.h>
#include "c_util.h"
@@ -67,10 +75,266 @@ using girerr::throwf;
#include "xmlrpc-c/packetsocket.hpp"
using namespace std;
#define ESC 0x1B // ASCII Escape character
#define ESC_STR "\x1B"
class XMLRPC_DLLEXPORT socketx {
public:
socketx(int const sockFd);
~socketx();
void
waitForReadable() const;
void
waitForWritable() const;
void
read(unsigned char * const buffer,
size_t const bufferSize,
bool * const wouldblockP,
size_t * const bytesReadP) const;
void
writeWait(const unsigned char * const data,
size_t const size) const;
private:
int fd;
bool fdIsBorrowed;
};
/* Sockets are similar, but not identical between Unix and Windows.
Some Unix socket functions appear to be available on Windows (a
Unix compatibility feature), but work only for file descriptor
numbers < 32, so we don't use those.
*/
socketx::socketx(int const sockFd) {
#if MSVCRT
// We don't have any way to duplicate; we'll just have to borrow.
this->fdIsBorrowed = true;
this->fd = sockFd;
u_long iMode(1); // Nonblocking mode yes
ioctlsocket(this->fd, FIONBIO, &iMode); // Make socket nonblocking
#else
this->fdIsBorrowed = false;
int dupRc;
dupRc = dup(sockFd);
if (dupRc < 0)
throwf("dup() failed. errno=%d (%s)", errno, strerror(errno));
else {
this->fd = dupRc;
fcntl(this->fd, F_SETFL, O_NONBLOCK); // Make socket nonblocking
}
#endif
}
socketx::~socketx() {
if (!this->fdIsBorrowed) {
#if MSVCRT
::closesocket(SOCKET(this->fd));
#else
close(this->fd);
#endif
}
}
void
socketx::waitForReadable() const {
/* Return when there is something to read from the socket
(an EOF indication counts as something to read). Also
return if there is a signal (handled, of course). Rarely,
it is OK to return when there isn't anything to read.
*/
#if MSVCRT
// poll() is not available; settle for select().
// Starting in Windows Vista, there is WSApoll()
fd_set rd_set;
FD_ZERO(&rd_set);
FD_SET(this->fd, &rd_set);
select(this->fd + 1, &rd_set, 0, 0, 0);
#else
// poll() beats select() because higher file descriptor numbers
// work.
struct pollfd pollfds[1];
pollfds[0].fd = this->fd;
pollfds[0].events = POLLIN;
poll(pollfds, ARRAY_SIZE(pollfds), -1);
#endif
}
void
socketx::waitForWritable() const {
/* Return when socket is able to be written to. */
#if MSVCRT
fd_set wr_set;
FD_ZERO(&wr_set);
FD_SET(this->fd, &wr_set);
select(this->fd + 1, 0, &wr_set, 0, 0);
#else
struct pollfd pollfds[1];
pollfds[0].fd = this->fd;
pollfds[0].events = POLLOUT;
poll(pollfds, ARRAY_SIZE(pollfds), -1);
#endif
}
static bool
wouldBlock() {
/*----------------------------------------------------------------------------
The most recently executed system socket function, which we assume failed,
failed because the situation was such that it wanted to block, but the
socket had the nonblocking option.
-----------------------------------------------------------------------------*/
#if MSVCRT
return (WSAGetLastError() == WSAEWOULDBLOCK ||
WSAGetLastError() == WSAEINPROGRESS);
#else
/* EWOULDBLOCK and EAGAIN are normally synonyms, but POSIX allows them
to be separate and allows the OS to return whichever one it wants
for the "would block" condition.
*/
return (errno == EWOULDBLOCK || errno == EAGAIN);
#endif
}
static string
lastErrorDesc() {
/*----------------------------------------------------------------------------
A description suitable for an error message of why the most recent
failed system socket function failed.
-----------------------------------------------------------------------------*/
ostringstream msg;
#if MSVCRT
int const lastError = WSAGetLastError();
msg << "winsock error code " << lastError << " "
<< "(" << strerror(lastError) << ")";
#else
msg << "errno = " << errno << ", (" << strerror(errno);
#endif
return msg.str();
}
void
socketx::read(unsigned char * const buffer,
size_t const bufferSize,
bool * const wouldblockP,
size_t * const bytesReadP) const {
int rc;
// We've seen a Windows library whose recv() expects a char * buffer
// (cf POSIX void *), so we cast.
rc = recv(this->fd, (char *)buffer, bufferSize, 0);
if (rc < 0) {
if (wouldBlock()) {
*wouldblockP = true;
*bytesReadP = 0;
} else
throwf("read() of socket failed with %s", lastErrorDesc().c_str());
} else {
*wouldblockP = false;
*bytesReadP = rc;
}
}
static void
writeFd(int const fd,
const unsigned char * const data,
size_t const size,
size_t * const bytesWrittenP) {
size_t totalBytesWritten;
bool full; // File image is "full" for now - won't take any more data
full = false;
totalBytesWritten = 0;
while (totalBytesWritten < size && !full) {
int rc;
rc = send(fd, (char*)&data[totalBytesWritten],
size - totalBytesWritten, 0);
if (rc < 0) {
if (wouldBlock())
full = true;
else
throwf("write() of socket failed with %s",
lastErrorDesc().c_str());
} else if (rc == 0)
throwf("Zero byte short write.");
else {
size_t const bytesWritten(rc);
totalBytesWritten += bytesWritten;
}
}
*bytesWrittenP = totalBytesWritten;
}
void
socketx::writeWait(const unsigned char * const data,
size_t const size) const {
/*----------------------------------------------------------------------------
Write the 'size' bytes at 'data' to the socket. Wait as long
as it takes for the file image to be able to take all the data.
-----------------------------------------------------------------------------*/
size_t totalBytesWritten;
// We do the first one blind because it will probably just work
// and we don't want to waste the poll() call and buffer arithmetic.
writeFd(this->fd, data, size, &totalBytesWritten);
while (totalBytesWritten < size) {
this->waitForWritable();
size_t bytesWritten;
writeFd(this->fd, &data[totalBytesWritten], size - totalBytesWritten,
&bytesWritten);
totalBytesWritten += bytesWritten;
}
}
namespace xmlrpc_c {
@@ -86,7 +350,7 @@ packet::initialize(const unsigned char * const data,
this->bytes = reinterpret_cast<unsigned char *>(malloc(dataLength));
if (this->bytes == NULL)
throwf("Can't get storage for a %u-byte packet.", dataLength);
throwf("Can't get storage for a %u-byte packet", (unsigned)dataLength);
this->allocSize = dataLength;
@@ -138,7 +402,7 @@ packet::addData(const unsigned char * const data,
realloc(this->bytes, neededSize));
if (this->bytes == NULL)
throwf("Can't get storage for a %u-byte packet.", neededSize);
throwf("Can't get storage for a %u-byte packet", (unsigned)neededSize);
memcpy(this->bytes + this->length, data, dataLength);
@@ -166,33 +430,83 @@ packetPtr::operator->() const {
packetSocket::packetSocket(int const sockFd) {
class packetSocket_impl {
int dupRc;
public:
packetSocket_impl(int const sockFd);
dupRc = dup(sockFd);
if (dupRc < 0)
throwf("dup() failed. errno=%d (%s)", errno, strerror(errno));
else {
this->sockFd = dupRc;
void
writeWait(packetPtr const& packetP) const;
this->inEscapeSeq = false;
this->inPacket = false;
void
read(bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP);
this->escAccum.len = 0;
fcntl(this->sockFd, F_SETFL, O_NONBLOCK);
void
readWait(volatile const int * const interruptP,
bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP);
this->eof = false;
}
}
private:
socketx sock;
// The kernel stream socket we use.
bool eof;
// The packet socket is at end-of-file for reads.
// 'readBuffer' is empty and there won't be any more data to fill
// it because the underlying stream socket is closed.
std::queue<packetPtr> readBuffer;
packetPtr packetAccumP;
// The receive packet we're currently accumulating; it will join
// 'readBuffer' when we've received the whole packet (and we've
// seen the END escape sequence so we know we've received it all).
// If we're not currently accumulating a packet (haven't seen a
// PKT escape sequence), this points to nothing.
bool inEscapeSeq;
// In our trek through the data read from the underlying stream
// socket, we are after an ESC character and before the end of the
// escape sequence. 'escAccum' shows what of the escape sequence
// we've seen so far.
bool inPacket;
// We're now receiving packet data from the underlying stream
// socket. We've seen a complete PKT escape sequence, but have not
// seen a complete END escape sequence since.
struct {
unsigned char bytes[3];
size_t len;
} escAccum;
void
takeSomeEscapeSeq(const unsigned char * const buffer,
size_t const length,
size_t * const bytesTakenP);
void
takeSomePacket(const unsigned char * const buffer,
size_t const length,
size_t * const bytesTakenP);
void
verifyNothingAccumulated();
void
processBytesRead(const unsigned char * const buffer,
size_t const bytesRead);
void
readFromFile();
};
packetSocket::~packetSocket() {
packetSocket_impl::packetSocket_impl(int const sockFd) :
sock(sockFd) {
close(this->sockFd);
this->inEscapeSeq = false;
this->inPacket = false;
this->escAccum.len = 0;
this->eof = false;
}
@@ -229,106 +543,27 @@ packetSocket::~packetSocket() {
-----------------------------------------------------------------------------*/
static void
writeFd(int const fd,
const unsigned char * const data,
size_t const size,
size_t * const bytesWrittenP) {
size_t totalBytesWritten;
bool full; // File image is "full" for now - won't take any more data
full = false;
totalBytesWritten = 0;
while (totalBytesWritten < size && !full) {
ssize_t rc;
rc = write(fd, &data[totalBytesWritten], size - totalBytesWritten);
if (rc < 0) {
if (errno == EAGAIN)
full = true;
else
throwf("write() of socket failed with errno %d (%s)",
errno, strerror(errno));
} else if (rc == 0)
throwf("Zero byte short write.");
else {
size_t const bytesWritten(rc);
totalBytesWritten += bytesWritten;
}
}
*bytesWrittenP = totalBytesWritten;
}
static void
writeFdWait(int const fd,
const unsigned char * const data,
size_t const size) {
/*----------------------------------------------------------------------------
Write the 'size' bytes at 'data' to the file image 'fd'. Wait as long
as it takes for the file image to be able to take all the data.
-----------------------------------------------------------------------------*/
size_t totalBytesWritten;
// We do the first one blind because it will probably just work
// and we don't want to waste the poll() call and buffer arithmetic.
writeFd(fd, data, size, &totalBytesWritten);
while (totalBytesWritten < size) {
struct pollfd pollfds[1];
pollfds[0].fd = fd;
pollfds[0].events = POLLOUT;
poll(pollfds, ARRAY_SIZE(pollfds), -1);
size_t bytesWritten;
writeFd(fd, &data[totalBytesWritten], size - totalBytesWritten,
&bytesWritten);
totalBytesWritten += bytesWritten;
}
}
void
packetSocket::writeWait(packetPtr const& packetP) const {
packetSocket_impl::writeWait(packetPtr const& packetP) const {
const unsigned char * const packetStart(
reinterpret_cast<const unsigned char *>(ESC_STR "PKT"));
const unsigned char * const packetEnd(
reinterpret_cast<const unsigned char *>(ESC_STR "END"));
writeFdWait(this->sockFd, packetStart, 4);
this->sock.writeWait(packetStart, 4);
writeFdWait(this->sockFd, packetP->getBytes(), packetP->getLength());
this->sock.writeWait(packetP->getBytes(), packetP->getLength());
writeFdWait(this->sockFd, packetEnd, 4);
}
static ssize_t
libc_read(int const fd,
void * const buf,
size_t const count) {
return read(fd, buf, count);
this->sock.writeWait(packetEnd, 4);
}
void
packetSocket::takeSomeEscapeSeq(const unsigned char * const buffer,
size_t const length,
size_t * const bytesTakenP) {
packetSocket_impl::takeSomeEscapeSeq(const unsigned char * const buffer,
size_t const length,
size_t * const bytesTakenP) {
/*----------------------------------------------------------------------------
Take and process some bytes from the incoming stream 'buffer',
which contains 'length' bytes, assuming they are within an escape
@@ -378,9 +613,9 @@ packetSocket::takeSomeEscapeSeq(const unsigned char * const buffer,
void
packetSocket::takeSomePacket(const unsigned char * const buffer,
size_t const length,
size_t * const bytesTakenP) {
packetSocket_impl::takeSomePacket(const unsigned char * const buffer,
size_t const length,
size_t * const bytesTakenP) {
assert(!this->inEscapeSeq);
@@ -408,7 +643,7 @@ packetSocket::takeSomePacket(const unsigned char * const buffer,
void
packetSocket::verifyNothingAccumulated() {
packetSocket_impl::verifyNothingAccumulated() {
/*----------------------------------------------------------------------------
Throw an error if there is a partial packet accumulated.
-----------------------------------------------------------------------------*/
@@ -419,14 +654,14 @@ packetSocket::verifyNothingAccumulated() {
if (this->inPacket)
throwf("Stream socket closed in the middle of a packet "
"(%u bytes of packet received; no END marker to mark "
"end of packet)", this->packetAccumP->getLength());
"end of packet)", (unsigned)this->packetAccumP->getLength());
}
void
packetSocket::processBytesRead(const unsigned char * const buffer,
size_t const bytesRead) {
packetSocket_impl::processBytesRead(const unsigned char * const buffer,
size_t const bytesRead) {
unsigned int cursor; // Cursor into buffer[]
cursor = 0;
@@ -456,7 +691,7 @@ packetSocket::processBytesRead(const unsigned char * const buffer,
void
packetSocket::readFromFile() {
packetSocket_impl::readFromFile() {
/*----------------------------------------------------------------------------
Read some data from the underlying stream socket. Read as much as is
available right now, up to 4K. Update 'this' to reflect the data read.
@@ -473,19 +708,11 @@ packetSocket::readFromFile() {
while (this->readBuffer.empty() && !this->eof && !wouldblock) {
unsigned char buffer[4096];
ssize_t rc;
size_t bytesRead;
rc = libc_read(this->sockFd, buffer, sizeof(buffer));
if (rc < 0) {
if (errno == EWOULDBLOCK)
wouldblock = true;
else
throwf("read() of socket failed with errno %d (%s)",
errno, strerror(errno));
} else {
size_t const bytesRead(rc);
this->sock.read(buffer, sizeof(buffer), &wouldblock, &bytesRead);
if (!wouldblock) {
if (bytesRead == 0) {
this->eof = true;
this->verifyNothingAccumulated();
@@ -498,9 +725,9 @@ packetSocket::readFromFile() {
void
packetSocket::read(bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP) {
packetSocket_impl::read(bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP) {
/*----------------------------------------------------------------------------
Read one packet from the socket, through the internal packet buffer.
@@ -535,10 +762,10 @@ packetSocket::read(bool * const eofP,
void
packetSocket::readWait(volatile const int * const interruptP,
bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP) {
packetSocket_impl::readWait(volatile const int * const interruptP,
bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP) {
bool gotPacket;
bool eof;
@@ -547,13 +774,8 @@ packetSocket::readWait(volatile const int * const interruptP,
eof = false;
while (!gotPacket && !eof && !*interruptP) {
struct pollfd pollfds[1];
pollfds[0].fd = this->sockFd;
pollfds[0].events = POLLIN;
poll(pollfds, ARRAY_SIZE(pollfds), -1);
this->sock.waitForReadable();
this->read(&eof, &gotPacket, packetPP);
}
@@ -563,6 +785,49 @@ packetSocket::readWait(volatile const int * const interruptP,
packetSocket::packetSocket(int const sockFd) {
this->implP = new packetSocket_impl(sockFd);
}
packetSocket::~packetSocket() {
delete(this->implP);
}
void
packetSocket::writeWait(packetPtr const& packetP) const {
implP->writeWait(packetP);
}
void
packetSocket::read(bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP) {
this->implP->read(eofP, gotPacketP, packetPP);
}
void
packetSocket::readWait(volatile const int * const interruptP,
bool * const eofP,
bool * const gotPacketP,
packetPtr * const packetPP) {
this->implP->readWait(interruptP, eofP, gotPacketP, packetPP);
}
void
packetSocket::readWait(volatile const int * const interruptP,
bool * const eofP,
@@ -570,7 +835,7 @@ packetSocket::readWait(volatile const int * const interruptP,
bool gotPacket;
this->readWait(interruptP, eofP, &gotPacket, packetPP);
this->implP->readWait(interruptP, eofP, &gotPacket, packetPP);
if (!gotPacket)
throwf("Packet read was interrupted");
+44 -5
View File
@@ -37,9 +37,24 @@ using namespace std;
namespace xmlrpc_c {
clientXmlTransport_pstream::constrOpt::constrOpt() {
present.fd = false;
struct clientXmlTransport_pstream::constrOpt_impl {
constrOpt_impl();
struct {
int fd;
} value;
struct {
bool fd;
} present;
};
clientXmlTransport_pstream::constrOpt_impl::constrOpt_impl() {
this->present.fd = false;
}
@@ -47,8 +62,8 @@ clientXmlTransport_pstream::constrOpt::constrOpt() {
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
clientXmlTransport_pstream::constrOpt & \
clientXmlTransport_pstream::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->value.OPTION_NAME = arg; \
this->present.OPTION_NAME = true; \
this->implP->value.OPTION_NAME = arg; \
this->implP->present.OPTION_NAME = true; \
return *this; \
}
@@ -58,7 +73,31 @@ DEFINE_OPTION_SETTER(fd, xmlrpc_socket);
clientXmlTransport_pstream::clientXmlTransport_pstream(constrOpt const& opt) {
clientXmlTransport_pstream::constrOpt::constrOpt() {
this->implP = new clientXmlTransport_pstream::constrOpt_impl();
}
clientXmlTransport_pstream::constrOpt::~constrOpt() {
delete(this->implP);
}
clientXmlTransport_pstream::constrOpt::constrOpt(constrOpt& arg) {
this->implP = new clientXmlTransport_pstream::constrOpt_impl(*arg.implP);
}
clientXmlTransport_pstream::clientXmlTransport_pstream(
constrOpt const& optExt) {
constrOpt_impl const opt(*optExt.implP);
if (!opt.present.fd)
throwf("You must provide a 'fd' constructor option.");
+147 -23
View File
@@ -59,6 +59,24 @@ methodPtr::operator->() const {
method2::method2() {}
method2::~method2() {}
void
method2::execute(xmlrpc_c::paramList const& paramList,
xmlrpc_c::value * const resultP) {
callInfo const nullCallInfo;
execute(paramList, &nullCallInfo, resultP);
}
defaultMethod::~defaultMethod() {}
@@ -90,7 +108,32 @@ defaultMethodPtr::get() const {
registry::registry() {
struct registry_impl {
xmlrpc_registry * c_registryP;
// Pointer to the C registry object we use to implement this
// object.
std::list<xmlrpc_c::methodPtr> methodList;
// This is a list of all the method objects (actually, pointers
// to them). But since the real registry is the C registry object,
// all this list is for is to maintain references to the objects
// to which the C registry points so that they continue to exist.
xmlrpc_c::defaultMethodPtr defaultMethodP;
// The real identifier of the default method is the C registry
// object; this member exists only to maintain a reference to the
// object to which the C registry points so that it will continue
// to exist.
registry_impl();
~registry_impl();
};
registry_impl::registry_impl() {
env_wrap env;
@@ -101,12 +144,25 @@ registry::registry() {
registry::~registry(void) {
registry_impl::~registry_impl() {
xmlrpc_registry_free(this->c_registryP);
}
registry::registry() {
this->implP = new registry_impl();
}
registry::~registry(void) {
delete(this->implP);
}
registryPtr::registryPtr() {}
@@ -173,7 +229,8 @@ pListFromXmlrpcArray(xmlrpc_value * const arrayP) {
static xmlrpc_value *
c_executeMethod(xmlrpc_env * const envP,
xmlrpc_value * const paramArrayP,
void * const methodPtr) {
void * const methodPtr,
void * const callInfoPtr) {
/*----------------------------------------------------------------------------
This is a function designed to be called via a C registry to
execute an XML-RPC method, but use a C++ method object to do the
@@ -188,18 +245,25 @@ c_executeMethod(xmlrpc_env * const envP,
encounter in processing the result it returns, and turn it into an
XML-RPC method failure. This will cause a leak if the execute()
method actually created a result, since it will not get destroyed.
This function is of type 'xmlrpc_method2'.
-----------------------------------------------------------------------------*/
xmlrpc_c::method * const methodP =
static_cast<xmlrpc_c::method *>(methodPtr);
xmlrpc_c::paramList const paramList(pListFromXmlrpcArray(paramArrayP));
method * const methodP(static_cast<method *>(methodPtr));
paramList const paramList(pListFromXmlrpcArray(paramArrayP));
callInfo * const callInfoP(static_cast<callInfo *>(callInfoPtr));
xmlrpc_value * retval;
retval = NULL; // silence used-before-set warning
try {
xmlrpc_c::value result;
value result;
try {
methodP->execute(paramList, &result);
method2 * const method2P(dynamic_cast<method2 *>(methodP));
if (method2P)
method2P->execute(paramList, callInfoP, &result);
else
methodP->execute(paramList, &result);
} catch (xmlrpc_c::fault const& fault) {
xmlrpc_env_set_fault(envP, fault.getCode(),
fault.getDescription().c_str());
@@ -256,6 +320,7 @@ c_executeDefaultMethod(xmlrpc_env * const envP,
paramList const paramList(pListFromXmlrpcArray(paramArrayP));
xmlrpc_value * retval;
retval = NULL; // silence used-before-set warning
try {
xmlrpc_c::value result;
@@ -296,15 +361,22 @@ void
registry::addMethod(string const name,
methodPtr const methodP) {
this->methodList.push_back(methodP);
this->implP->methodList.push_back(methodP);
struct xmlrpc_method_info3 methodInfo;
env_wrap env;
methodInfo.methodName = name.c_str();
methodInfo.methodFunction = &c_executeMethod;
methodInfo.serverInfo = methodP.get();
methodInfo.stackSize = 0;
string const signatureString(methodP->signature());
methodInfo.signatureString = signatureString.c_str();
string const help(methodP->help());
methodInfo.help = help.c_str();
xmlrpc_registry_add_method_w_doc(
&env.env_c, this->c_registryP, NULL,
name.c_str(), &c_executeMethod,
(void*) methodP.get(),
methodP->signature().c_str(), methodP->help().c_str());
xmlrpc_registry_add_method3(&env.env_c, this->implP->c_registryP,
&methodInfo);
throwIfError(env);
}
@@ -314,12 +386,12 @@ registry::addMethod(string const name,
void
registry::setDefaultMethod(defaultMethodPtr const methodP) {
this->defaultMethodP = methodP;
this->implP->defaultMethodP = methodP;
env_wrap env;
xmlrpc_registry_set_default_method(
&env.env_c, this->c_registryP,
&env.env_c, this->implP->c_registryP,
&c_executeDefaultMethod, (void*) methodP.get());
throwIfError(env);
@@ -330,7 +402,7 @@ registry::setDefaultMethod(defaultMethodPtr const methodP) {
void
registry::disableIntrospection() {
xmlrpc_registry_disable_introspection(this->c_registryP);
xmlrpc_registry_disable_introspection(this->implP->c_registryP);
}
@@ -362,7 +434,7 @@ registry::setShutdown(const registry::shutdown * const shutdownP) {
void * const context(const_cast<registry::shutdown *>(shutdownP));
xmlrpc_registry_set_shutdown(this->c_registryP,
xmlrpc_registry_set_shutdown(this->implP->c_registryP,
&shutdownServer,
context);
}
@@ -374,13 +446,53 @@ registry::setDialect(xmlrpc_dialect const dialect) {
env_wrap env;
xmlrpc_registry_set_dialect(&env.env_c, this->c_registryP, dialect);
xmlrpc_registry_set_dialect(&env.env_c, this->implP->c_registryP, dialect);
throwIfError(env);
}
void
registry::processCall(string const& callXml,
const callInfo * const callInfoP,
string * const responseXmlP) const {
/*----------------------------------------------------------------------------
Process an XML-RPC call whose XML is 'callXml'.
Return the response XML as *responseXmlP.
If we are unable to execute the call, we throw an error. But if
the call executes and the method merely fails in an XML-RPC sense, we
don't. In that case, *responseXmlP indicates the failure.
-----------------------------------------------------------------------------*/
env_wrap env;
xmlrpc_mem_block * response;
// For the pure C++ version, this will have to parse 'callXml'
// into a method name and parameters, look up the method name in
// the registry, call the method's execute() method, then marshall
// the result into XML and return it as *responseXmlP. It will
// also have to execute system methods (e.g. introspection)
// itself. This will be more or less like what
// xmlrpc_registry_process_call() does.
xmlrpc_registry_process_call2(
&env.env_c, this->implP->c_registryP,
callXml.c_str(), callXml.length(),
const_cast<callInfo *>(callInfoP),
&response);
throwIfError(env);
*responseXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, response),
XMLRPC_MEMBLOCK_SIZE(char, response));
xmlrpc_mem_block_free(response);
}
void
registry::processCall(string const& callXml,
string * const responseXmlP) const {
@@ -405,7 +517,7 @@ registry::processCall(string const& callXml,
// xmlrpc_registry_process_call() does.
output = xmlrpc_registry_process_call(
&env.env_c, this->c_registryP, NULL,
&env.env_c, this->implP->c_registryP, NULL,
callXml.c_str(), callXml.length());
throwIfError(env);
@@ -416,12 +528,24 @@ registry::processCall(string const& callXml,
xmlrpc_mem_block_free(output);
}
xmlrpc_registry *
registry::c_registry() const {
return this->c_registryP;
#define PROCESS_CALL_STACK_SIZE 256
// This is our liberal estimate of how much stack space
// registry::processCall() needs, not counting what
// the call the to C registry uses.
size_t
registry::maxStackSize() const {
return xmlrpc_registry_max_stackSize(this->implP->c_registryP) +
PROCESS_CALL_STACK_SIZE;
}
} // namespace
+457 -114
View File
@@ -1,10 +1,11 @@
#include "xmlrpc_config.h"
#include <cstdlib>
#include <string>
#include <memory>
#include <signal.h>
#include <errno.h>
#include <iostream>
#ifndef _WIN32
#if !MSVCRT
#include <sys/wait.h>
#endif
@@ -14,9 +15,13 @@
using girerr::error;
using girerr::throwf;
#include "xmlrpc-c/base.h"
#include "xmlrpc-c/util.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/abyss.h"
#include "xmlrpc-c/server_abyss.h"
#include "xmlrpc-c/registry.hpp"
#include "env_wrap.hpp"
#include "xmlrpc-c/server_abyss.hpp"
using namespace std;
@@ -43,14 +48,13 @@ sigchld(int const ASSERT_ONLY_ARG(signalClass)) {
This is a signal handler for a SIGCHLD signal (which informs us that
one of our child processes has terminated).
We respond by reaping the zombie process.
Implementation note: In some systems, just setting the signal handler
to SIG_IGN (ignore signal) does this. In some, the system does this
automatically if the signal is blocked.
The only child processes we have are those that belong to the Abyss
server (and then only if the Abyss server was configured to use
forking as a threading mechanism), so we respond by passing the
signal on to the Abyss server. And reaping the dead child.
-----------------------------------------------------------------------------*/
#ifndef _WIN32
/* Reap zombie children until there aren't any more. */
// Reap zombie children / report to Abyss until there aren't any more.
bool zombiesExist;
bool error;
@@ -71,7 +75,8 @@ sigchld(int const ASSERT_ONLY_ARG(signalClass)) {
// This is OK - it's a ptrace notification
} else
error = true;
}
} else
ServerHandleSigchld(pid);
}
#endif /* _WIN32 */
}
@@ -160,41 +165,78 @@ public:
serverAbyss::shutdown::shutdown(serverAbyss * const serverAbyssP) :
serverAbyssP(serverAbyssP) {}
callInfo_serverAbyss::callInfo_serverAbyss(
serverAbyss * const serverAbyssP,
TSession * const abyssSessionP) :
serverAbyssP(serverAbyssP), abyssSessionP(abyssSessionP) {}
serverAbyss::shutdown::~shutdown() {}
struct serverAbyss::constrOpt_impl {
constrOpt_impl();
struct value {
xmlrpc_c::registryPtr registryPtr;
const xmlrpc_c::registry * registryP;
XMLRPC_SOCKET socketFd;
unsigned int portNumber;
std::string logFileName;
unsigned int keepaliveTimeout;
unsigned int keepaliveMaxConn;
unsigned int timeout;
bool dontAdvertise;
std::string uriPath;
bool chunkResponse;
std::string allowOrigin;
unsigned int accessCtlMaxAge;
bool serverOwnsSignals;
bool expectSigchld;
} value;
struct {
bool registryPtr;
bool registryP;
bool socketFd;
bool portNumber;
bool logFileName;
bool keepaliveTimeout;
bool keepaliveMaxConn;
bool timeout;
bool dontAdvertise;
bool uriPath;
bool chunkResponse;
bool allowOrigin;
bool accessCtlMaxAge;
bool serverOwnsSignals;
bool expectSigchld;
} present;
};
void
serverAbyss::shutdown::doit(string const&,
void * const) const {
this->serverAbyssP->terminate();
}
serverAbyss::constrOpt::constrOpt() {
present.registryPtr = false;
present.registryP = false;
present.socketFd = false;
present.portNumber = false;
present.logFileName = false;
present.keepaliveTimeout = false;
present.keepaliveMaxConn = false;
present.timeout = false;
present.dontAdvertise = false;
present.uriPath = false;
present.chunkResponse = false;
serverAbyss::constrOpt_impl::constrOpt_impl() {
present.registryPtr = false;
present.registryP = false;
present.socketFd = false;
present.portNumber = false;
present.logFileName = false;
present.keepaliveTimeout = false;
present.keepaliveMaxConn = false;
present.timeout = false;
present.dontAdvertise = false;
present.uriPath = false;
present.chunkResponse = false;
present.allowOrigin = false;
present.accessCtlMaxAge = false;
present.serverOwnsSignals = false;
present.expectSigchld = false;
// Set default values
value.dontAdvertise = false;
value.uriPath = string("/RPC2");
value.chunkResponse = false;
value.dontAdvertise = false;
value.uriPath = string("/RPC2");
value.chunkResponse = false;
value.serverOwnsSignals = true;
value.expectSigchld = false;
}
@@ -202,40 +244,40 @@ serverAbyss::constrOpt::constrOpt() {
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
serverAbyss::constrOpt & \
serverAbyss::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->value.OPTION_NAME = arg; \
this->present.OPTION_NAME = true; \
this->implP->value.OPTION_NAME = arg; \
this->implP->present.OPTION_NAME = true; \
return *this; \
}
DEFINE_OPTION_SETTER(registryPtr, xmlrpc_c::registryPtr);
DEFINE_OPTION_SETTER(registryP, const registry *);
DEFINE_OPTION_SETTER(socketFd, XMLRPC_SOCKET);
DEFINE_OPTION_SETTER(portNumber, unsigned int);
DEFINE_OPTION_SETTER(logFileName, string);
DEFINE_OPTION_SETTER(keepaliveTimeout, unsigned int);
DEFINE_OPTION_SETTER(keepaliveMaxConn, unsigned int);
DEFINE_OPTION_SETTER(timeout, unsigned int);
DEFINE_OPTION_SETTER(dontAdvertise, bool);
DEFINE_OPTION_SETTER(uriPath, string);
DEFINE_OPTION_SETTER(chunkResponse, bool);
DEFINE_OPTION_SETTER(registryPtr, xmlrpc_c::registryPtr);
DEFINE_OPTION_SETTER(registryP, const registry *);
DEFINE_OPTION_SETTER(socketFd, XMLRPC_SOCKET);
DEFINE_OPTION_SETTER(portNumber, unsigned int);
DEFINE_OPTION_SETTER(logFileName, string);
DEFINE_OPTION_SETTER(keepaliveTimeout, unsigned int);
DEFINE_OPTION_SETTER(keepaliveMaxConn, unsigned int);
DEFINE_OPTION_SETTER(timeout, unsigned int);
DEFINE_OPTION_SETTER(dontAdvertise, bool);
DEFINE_OPTION_SETTER(uriPath, string);
DEFINE_OPTION_SETTER(chunkResponse, bool);
DEFINE_OPTION_SETTER(allowOrigin, string);
DEFINE_OPTION_SETTER(accessCtlMaxAge, unsigned int);
DEFINE_OPTION_SETTER(serverOwnsSignals, bool);
DEFINE_OPTION_SETTER(expectSigchld, bool);
#undef DEFINE_OPTION_SETTER
serverAbyss::constrOpt::constrOpt() {
this->implP = new serverAbyss::constrOpt_impl();
}
void
serverAbyss::setAdditionalServerParms(constrOpt const& opt) {
serverAbyss::constrOpt::~constrOpt() {
/* The following ought to be parameters on ServerCreate(), but it
looks like plugging them straight into the TServer structure is
the only way to set them.
*/
if (opt.present.keepaliveTimeout)
ServerSetKeepaliveTimeout(&this->cServer, opt.value.keepaliveTimeout);
if (opt.present.keepaliveMaxConn)
ServerSetKeepaliveMaxConn(&this->cServer, opt.value.keepaliveMaxConn);
if (opt.present.timeout)
ServerSetTimeout(&this->cServer, opt.value.timeout);
ServerSetAdvertise(&this->cServer, !opt.value.dontAdvertise);
delete(this->implP);
}
@@ -280,11 +322,156 @@ createServer(bool const logFileNameGiven,
void
serverAbyss::initialize(constrOpt const& opt) {
struct serverAbyss_impl {
registryPtr regPtr;
// This just holds a reference to the registry so that it may
// get destroyed when the serverAbyss gets destroyed. If the
// creator of the serverAbyss is managing lifetime himself,
// this is a null pointer. 'registryP' is what you really use
// to access the registry.
const registry * registryP;
TServer cServer;
serverAbyss_impl(serverAbyss::constrOpt_impl const& opt,
serverAbyss * const serverAbyssP);
~serverAbyss_impl();
void
setAdditionalServerParms(serverAbyss::constrOpt_impl const& opt);
void
setHttpReqHandlers(string const& uriPath,
bool const chunkResponse,
bool const doHttpAccessControl,
string const& allowOrigin,
bool const accessCtlExpires,
unsigned int const accessCtlMaxAge);
void
run();
void
processCall(std::string const& call,
TSession * const abyssSessionP,
std::string * const responseP);
serverAbyss * const serverAbyssP;
// The server for which we are the implementation.
bool expectSigchld;
bool serverOwnsSignals;
};
static void
processXmlrpcCall(xmlrpc_env * const envP,
void * const arg,
const char * const callXml,
size_t const callXmlLen,
TSession * const abyssSessionP,
xmlrpc_mem_block ** const responseXmlPP) {
/*----------------------------------------------------------------------------
This is an XML-RPC XML call processor, as called by the HTTP request
handler of the libxmlrpc_server_abyss C library.
'callXml'/'callXmlLen' is the XML text of a supposed XML-RPC call.
We execute the RPC and return the XML text of the XML-RPC response
as *responseXmlPP.
'arg' carries the information that tells us how to do that; e.g.
what XML-RPC methods are defined.
-----------------------------------------------------------------------------*/
serverAbyss_impl * const implP(
static_cast<serverAbyss_impl *>(arg));
try {
string const call(callXml, callXmlLen);
string response;
implP->processCall(call, abyssSessionP, &response);
xmlrpc_mem_block * responseMbP;
responseMbP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
if (!envP->fault_occurred) {
XMLRPC_MEMBLOCK_APPEND(char, envP, responseMbP,
response.c_str(), response.length());
*responseXmlPP = responseMbP;
}
} catch (exception const& e) {
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR, e.what());
}
}
void
serverAbyss_impl::setAdditionalServerParms(
serverAbyss::constrOpt_impl const& opt) {
// The following ought to be parameters on ServerCreate().
if (opt.present.keepaliveTimeout)
ServerSetKeepaliveTimeout(&this->cServer, opt.value.keepaliveTimeout);
if (opt.present.keepaliveMaxConn)
ServerSetKeepaliveMaxConn(&this->cServer, opt.value.keepaliveMaxConn);
if (opt.present.timeout)
ServerSetTimeout(&this->cServer, opt.value.timeout);
ServerSetAdvertise(&this->cServer, !opt.value.dontAdvertise);
if (opt.value.expectSigchld)
ServerUseSigchld(&this->cServer);
}
void
serverAbyss_impl::setHttpReqHandlers(string const& uriPath,
bool const chunkResponse,
bool const doHttpAccessControl,
string const& allowOrigin,
bool const accessCtlExpires,
unsigned int const accessCtlMaxAge) {
/*----------------------------------------------------------------------------
This is a constructor helper. Don't assume *this is complete.
-----------------------------------------------------------------------------*/
env_wrap env;
xmlrpc_server_abyss_handler_parms parms;
parms.xml_processor = &processXmlrpcCall;
parms.xml_processor_arg = this;
parms.xml_processor_max_stack = this->registryP->maxStackSize();
parms.uri_path = uriPath.c_str();
parms.chunk_response = chunkResponse;
parms.allow_origin = doHttpAccessControl ? allowOrigin.c_str() : NULL;
parms.access_ctl_expires = accessCtlExpires;
parms.access_ctl_max_age = accessCtlMaxAge;
xmlrpc_server_abyss_set_handler3(
&env.env_c, &this->cServer,
&parms, XMLRPC_AHPSIZE(access_ctl_max_age));
if (env.env_c.fault_occurred)
throwf("Failed to register the HTTP handler for XML-RPC "
"with the underlying Abyss HTTP server. "
"xmlrpc_server_abyss_set_handler3() failed with: %s",
env.env_c.fault_string);
xmlrpc_server_abyss_set_default_handler(&this->cServer);
}
serverAbyss_impl::serverAbyss_impl(
serverAbyss::constrOpt_impl const& opt,
serverAbyss * const serverAbyssP) :
serverAbyssP(serverAbyssP) {
if (!opt.present.registryP && !opt.present.registryPtr)
throwf("You must specify the 'registryP' or 'registryPtr' option");
else if (opt.present.registryP && opt.present.registryPtr)
@@ -292,15 +479,21 @@ serverAbyss::initialize(constrOpt const& opt) {
"the 'registryPtr' options");
else {
if (opt.present.registryP)
registryP = opt.value.registryP;
this->registryP = opt.value.registryP;
else {
this->registryPtr = opt.value.registryPtr;
registryP = this->registryPtr.get();
this->regPtr = opt.value.registryPtr;
this->registryP = this->regPtr.get();
}
}
if (opt.present.portNumber && opt.present.socketFd)
throwf("You can't specify both portNumber and socketFd options");
this->serverOwnsSignals = opt.value.serverOwnsSignals;
if (opt.value.serverOwnsSignals && opt.value.expectSigchld)
throwf("You can't specify both expectSigchld "
"and serverOwnsSignals options");
DateInit();
createServer(opt.present.logFileName, opt.value.logFileName,
@@ -309,18 +502,16 @@ serverAbyss::initialize(constrOpt const& opt) {
&this->cServer);
try {
setAdditionalServerParms(opt);
this->setAdditionalServerParms(opt);
this->setHttpReqHandlers(opt.value.uriPath,
opt.value.chunkResponse,
opt.present.allowOrigin,
opt.value.allowOrigin,
opt.present.accessCtlMaxAge,
opt.value.accessCtlMaxAge);
// chunked response implementation is incomplete. We must
// eventually get away from libxmlrpc_server_abyss and
// register our own handler with the Abyss server. At that
// time, we'll have some place to pass
// opt.value.chunkResponse.
xmlrpc_c::server_abyss_set_handlers(&this->cServer,
registryP,
opt.value.uriPath);
if (opt.present.portNumber || opt.present.socketFd)
ServerInit(&this->cServer);
} catch (...) {
@@ -331,6 +522,94 @@ serverAbyss::initialize(constrOpt const& opt) {
serverAbyss_impl::~serverAbyss_impl() {
ServerFree(&this->cServer);
}
static void
setupSignalsAndRunAbyss(TServer * const abyssServerP) {
/* We do some pretty ugly stuff for an object method: we set signal
handlers, which are process-global.
One example of where this can be hairy is: Caller has a child
process unrelated to the Abyss server. That child dies. We
get his death of a child signal and Caller never knows.
We really expect to be the only thing in the process, at least
for the time we're running. If you want the Abyss Server
to behave more like an object and own the signals yourself,
use runOnce() in a loop instead of run().
*/
signalHandlers oldHandlers;
setupSignalHandlers(&oldHandlers);
ServerUseSigchld(abyssServerP);
ServerRun(abyssServerP);
restoreSignalHandlers(oldHandlers);
}
void
serverAbyss_impl::run() {
if (this->serverOwnsSignals)
setupSignalsAndRunAbyss(&this->cServer);
else {
if (this->expectSigchld)
ServerUseSigchld(&this->cServer);
ServerRun(&this->cServer);
}
}
void
serverAbyss_impl::processCall(string const& call,
TSession * const abyssSessionP,
string * const responseP) {
callInfo_serverAbyss const callInfo(this->serverAbyssP, abyssSessionP);
this->registryP->processCall(call, &callInfo, responseP);
}
serverAbyss::shutdown::shutdown(serverAbyss * const serverAbyssP) :
serverAbyssP(serverAbyssP) {}
serverAbyss::shutdown::~shutdown() {}
void
serverAbyss::shutdown::doit(string const&,
void * const) const {
this->serverAbyssP->terminate();
}
void
serverAbyss::initialize(constrOpt const& opt) {
this->implP = new serverAbyss_impl(*opt.implP, this);
}
serverAbyss::serverAbyss(constrOpt const& opt) {
initialize(opt);
@@ -376,7 +655,7 @@ serverAbyss::serverAbyss(
serverAbyss::~serverAbyss() {
ServerFree(&this->cServer);
delete(this->implP);
}
@@ -384,25 +663,7 @@ serverAbyss::~serverAbyss() {
void
serverAbyss::run() {
/* We do some pretty ugly stuff for an object method: we set signal
handlers, which are process-global.
One example of where this can be hairy is: Caller has a child
process unrelated to the Abyss server. That child dies. We
get his death of a child signal and Caller never knows.
We really expect to be the only thing in the process, at least
for the time we're running. If you want the Abyss Server
to behave more like an object and own the signals yourself,
use runOnce() in a loop instead of run().
*/
signalHandlers oldHandlers;
setupSignalHandlers(&oldHandlers);
ServerRun(&this->cServer);
restoreSignalHandlers(oldHandlers);
this->implP->run();
}
@@ -410,7 +671,7 @@ serverAbyss::run() {
void
serverAbyss::runOnce() {
ServerRunOnce(&this->cServer);
ServerRunOnce(&this->implP->cServer);
}
@@ -418,49 +679,131 @@ serverAbyss::runOnce() {
void
serverAbyss::runConn(int const socketFd) {
ServerRunConn(&this->cServer, socketFd);
ServerRunConn(&this->implP->cServer, socketFd);
}
#ifndef WIN32
void
serverAbyss::sigchld(pid_t const pid) {
// There's a hole in the design here, because the Abyss server uses
// a process-global list of children (so there can't be more than one
// Abyss object in the process), so while this is an object method,
// it doesn't really refer to the object at all.
// We might conceivably fix Abyss some day, then this method would do
// what you expect -- affect only its own object. But forking Abyss is
// obsolete anyway, so we just don't worry about it.
ServerHandleSigchld(pid);
}
#endif
void
serverAbyss::terminate() {
ServerTerminate(&this->cServer);
ServerTerminate(&this->implP->cServer);
}
callInfo_abyss::callInfo_abyss(TSession * const abyssSessionP) :
abyssSessionP(abyssSessionP) {}
void
server_abyss_set_handlers(TServer * const srvP,
processXmlrpcCall2(xmlrpc_env * const envP,
void * const arg,
const char * const callXml,
size_t const callXmlLen,
TSession * const abyssSessionP,
xmlrpc_mem_block ** const responseXmlPP) {
/*----------------------------------------------------------------------------
This is an XML-RPC XML call processor, as called by the HTTP request
handler of the libxmlrpc_server_abyss C library.
'callXml'/'callXmlLen' is the XML text of a supposed XML-RPC call.
We execute the RPC and return the XML text of the XML-RPC response
as *responseXmlPP.
'arg' carries the information that tells us how to do that; e.g.
what XML-RPC methods are defined.
-----------------------------------------------------------------------------*/
const registry * const registryP(static_cast<registry *>(arg));
try {
string const call(callXml, callXmlLen);
callInfo_abyss const callInfo(abyssSessionP);
string response;
registryP->processCall(call, &callInfo, &response);
xmlrpc_mem_block * responseMbP;
responseMbP = XMLRPC_MEMBLOCK_NEW(char, envP, response.length());
if (!envP->fault_occurred) {
XMLRPC_MEMBLOCK_APPEND(char, envP, responseMbP,
response.c_str(), response.length());
*responseXmlPP = responseMbP;
}
} catch (exception const& e) {
xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR, e.what());
}
}
static void
setHandlers(TServer * const serverP,
string const& uriPath,
registry const& registry) {
xmlrpc_server_abyss_set_handler2(
serverP, uriPath.c_str(),
processXmlrpcCall2,
const_cast<xmlrpc_c::registry *>(&registry),
registry.maxStackSize(),
false);
xmlrpc_server_abyss_set_default_handler(serverP);
}
void
server_abyss_set_handlers(TServer * const serverP,
registry const& registry,
string const& uriPath) {
xmlrpc_server_abyss_set_handlers2(srvP,
uriPath.c_str(),
registry.c_registry());
setHandlers(serverP, uriPath, registry);
}
void
server_abyss_set_handlers(TServer * const srvP,
server_abyss_set_handlers(TServer * const serverP,
const registry * const registryP,
string const& uriPath) {
xmlrpc_server_abyss_set_handlers2(srvP,
uriPath.c_str(),
registryP->c_registry());
setHandlers(serverP, uriPath, *registryP);
}
void
server_abyss_set_handlers(TServer * const srvP,
server_abyss_set_handlers(TServer * const serverP,
registryPtr const registryPtr,
string const& uriPath) {
xmlrpc_server_abyss_set_handlers2(srvP,
uriPath.c_str(),
registryPtr->c_registry());
setHandlers(serverP, uriPath, *registryPtr.get());
}
+354
View File
@@ -0,0 +1,354 @@
/*=============================================================================
server_cgi
===============================================================================
This is the definition of the xmlrpc_c::server_cgi class. An object of
this class is the guts of a CGI-based XML-RPC server. It runs inside
a CGI script and gets the XML-RPC call from and delivers the XML-RPC
response to the CGI environment.
By Bryan Henderson 08.09.17.
Contributed to the public domain by its author.
=============================================================================*/
#include "xmlrpc_config.h"
#if MSVCRT
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <io.h>
#include <fcntl.h>
#endif
#include <cstdlib> // for getenv
#include <memory>
#include <stdio.h>
#include "xmlrpc-c/girerr.hpp"
using girerr::throwf;
#include "xmlrpc-c/server_cgi.hpp"
using namespace std;
namespace {
class httpInfo {
public:
string requestMethod;
bool contentTypePresent;
string contentType;
unsigned int contentLength;
bool contentLengthPresent;
bool authCookiePresent;
string authCookie;
httpInfo() {
const char * const requestMethodC = getenv("REQUEST_METHOD");
const char * const contentTypeC = getenv("CONTENT_TYPE");
const char * const contentLengthC = getenv("CONTENT_LENGTH");
const char * const authCookieC = getenv("HTTP_COOKIE_AUTH");
if (requestMethodC)
this->requestMethod = string(requestMethodC);
else
throwf("Invalid CGI environment; environment variable "
"REQUEST_METHOD is not set");
if (contentTypeC) {
this->contentTypePresent = true;
this->contentType = string(contentTypeC);
} else
this->contentTypePresent = false;
if (contentLengthC) {
this->contentLengthPresent = true;
int const lengthAtoi(atoi(string(contentLengthC).c_str()));
if (lengthAtoi < 0)
throwf("Content-length HTTP header value is negative");
else if (lengthAtoi == 0)
throwf("Content-length HTTP header value is zero");
else
this->contentLength = lengthAtoi;
} else
this->contentLengthPresent = false;
if (authCookieC) {
this->authCookie = string(authCookieC);
this->authCookiePresent = true;
} else
this->authCookiePresent = false;
}
};
class httpError {
public:
int const code;
string const msg;
httpError(int const code,
string const& msg) :
code(code), msg(msg) {}
};
} // namespace
namespace xmlrpc_c {
struct serverCgi_impl {
// 'registryP' is what we actually use; 'registryHolder' just holds a
// reference to 'registryP' so the registry doesn't disappear while
// this server exists. But note that if the creator doesn't supply
// a registryPtr, 'registryHolder' is just a placeholder variable and
// the creator is responsible for making sure the registry doesn't
// go anywhere while the server exists.
registryPtr registryHolder;
const registry * registryP;
serverCgi_impl(serverCgi::constrOpt const& opt);
void
establishRegistry(serverCgi::constrOpt const& opt);
void
tryToProcessCall();
};
void
serverCgi_impl::establishRegistry(serverCgi::constrOpt const& opt) {
if (!opt.present.registryP && !opt.present.registryPtr)
throwf("You must specify the 'registryP' or 'registryPtr' option");
else if (opt.present.registryP && opt.present.registryPtr)
throwf("You may not specify both the 'registryP' and "
"the 'registryPtr' options");
else {
if (opt.present.registryP)
this->registryP = opt.value.registryP;
else {
this->registryHolder = opt.value.registryPtr;
this->registryP = opt.value.registryPtr.get();
}
}
}
serverCgi_impl::serverCgi_impl(serverCgi::constrOpt const& opt) {
this->establishRegistry(opt);
}
serverCgi::constrOpt::constrOpt() {
present.registryP = false;
present.registryPtr = false;
}
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
serverCgi::constrOpt & \
serverCgi::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->value.OPTION_NAME = arg; \
this->present.OPTION_NAME = true; \
return *this; \
}
DEFINE_OPTION_SETTER(registryP, const registry *);
DEFINE_OPTION_SETTER(registryPtr, xmlrpc_c::registryPtr);
#undef DEFINE_OPTION_SETTER
serverCgi::serverCgi(constrOpt const& opt) {
this->implP = new serverCgi_impl(opt);
}
serverCgi::~serverCgi() {
delete(this->implP);
}
#if MSVCRT
#define FILEVAR fileP
#else
#define FILEVAR
#endif
static void
setModeBinary(FILE * const FILEVAR) {
#if MSVCRT
/* Fix from Jeff Stewart: NT opens stdin and stdout in text mode
by default, badly confusing our length calculations. So we need
to set the file handle to binary.
*/
_setmode(_fileno(FILEVAR), _O_BINARY);
#endif
}
static string
getHttpBody(FILE * const fileP,
size_t const length) {
setModeBinary(fileP);
char * const buffer(new char[length]);
auto_ptr<char> p(buffer); // To make it go away when we leave
size_t count;
count = fread(buffer, sizeof(buffer[0]), length, fileP);
if (count < length)
throwf("Expected %lu bytes, received %lu",
(unsigned long) length, (unsigned long) count);
return string(buffer, length);
}
static void
writeNormalHttpResp(FILE * const fileP,
bool const sendCookie,
string const& authCookie,
string const& httpBody) {
setModeBinary(fileP);
// HTTP headers
fprintf(fileP, "Status: 200 OK\n");
if (sendCookie)
fprintf(fileP, "Set-Cookie: auth=%s\n", authCookie.c_str());
fprintf(fileP, "Content-type: text/xml; charset=\"utf-8\"\n");
fprintf(fileP, "Content-length: %u\n", (unsigned)httpBody.size());
fprintf(fileP, "\n");
// HTTP body
fwrite(httpBody.c_str(), sizeof(char), httpBody.size(), fileP);
}
void
processCall2(const registry * const registryP,
FILE * const callFileP,
unsigned int const callSize,
bool const sendCookie,
string const& authCookie,
FILE * const respFileP) {
if (callSize > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
throw(xmlrpc_c::fault(string("XML-RPC call is too large"),
fault::CODE_LIMIT_EXCEEDED));
else {
string const callXml(getHttpBody(callFileP, callSize));
string responseXml;
try {
registryP->processCall(callXml, &responseXml);
} catch (exception const& e) {
throw(httpError(500, e.what()));
}
writeNormalHttpResp(respFileP, sendCookie, authCookie, responseXml);
}
}
static void
sendHttpErrorResp(FILE * const fileP,
httpError const& e) {
setModeBinary(fileP);
// HTTP headers
fprintf(fileP, "Status: %d %s\n", e.code, e.msg.c_str());
fprintf(fileP, "Content-type: text/html\n");
fprintf(fileP, "\n");
// HTTP body: HTML error message
fprintf(fileP, "<title>%d %s</title>\n", e.code, e.msg.c_str());
fprintf(fileP, "<h1>%d %s</h1>\n", e.code, e.msg.c_str());
fprintf(fileP, "<p>The Xmlrpc-c CGI server was unable to process "
"your request. It could not process it even enough to generate "
"an XML-RPC fault response.</p>\n");
}
void
serverCgi_impl::tryToProcessCall() {
httpInfo httpInfo;
if (httpInfo.requestMethod != string("POST"))
throw(httpError(405, "Method must be POST"));
if (!httpInfo.contentTypePresent)
throw(httpError(400, "Must have content-type header"));
if (httpInfo.contentType != string("text/xml"))
throw(httpError(400, string("ContentType must be 'text/xml', not '") +
httpInfo.contentType + string("'")));
if (!httpInfo.contentLengthPresent)
throw(httpError(411, "Content-length required"));
processCall2(this->registryP, stdin, httpInfo.contentLength,
httpInfo.authCookiePresent, httpInfo.authCookie, stdout);
}
void
serverCgi::processCall() {
/*----------------------------------------------------------------------------
Get the XML-RPC call from Standard Input and environment variables,
parse it, find the right method, call it, prepare an XML-RPC
response with the result, and write it to Standard Output.
-----------------------------------------------------------------------------*/
try {
this->implP->tryToProcessCall();
} catch (httpError const e) {
sendHttpErrorResp(stdout, e);
}
}
} // namespace
+200 -109
View File
@@ -9,24 +9,27 @@
is an Xmlrpc-c invention. It is an almost trivial representation of
a sequence of packets on a byte stream.
You can create a pstream server from any file descriptor from which
you can read and write a bidirectional character stream. Typically,
it's a TCP socket. Such a server talks to one client its entire life.
Some day, we'll also have a version that you create from a "listening"
socket, which can talk to multiple clients serially (a client connects,
does some RPCs, and disconnects).
By Bryan Henderson 07.05.12.
By Bryan Henderson 09.03.22
Contributed to the public domain by its author.
=============================================================================*/
#include "xmlrpc_config.h"
#if MSVCRT
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <winsock.h>
typedef int socklen_t;
#else
#include <sys/socket.h>
#endif
#include <errno.h>
#include <cstring>
#include <memory>
#include "xmlrpc-c/girerr.hpp"
using girerr::throwf;
#include "xmlrpc-c/packetsocket.hpp"
#include "xmlrpc-c/server_pstream.hpp"
@@ -35,20 +38,52 @@ using namespace std;
namespace xmlrpc_c {
serverPstreamConn::constrOpt::constrOpt() {
struct serverPstream::constrOpt_impl {
present.socketFd = false;
present.registryP = false;
present.registryPtr = false;
constrOpt_impl();
struct value {
xmlrpc_c::registryPtr registryPtr;
const xmlrpc_c::registry * registryP;
XMLRPC_SOCKET socketFd;
} value;
struct {
bool registryPtr;
bool registryP;
bool socketFd;
} present;
};
serverPstream::constrOpt_impl::constrOpt_impl() {
this->present.socketFd = false;
this->present.registryP = false;
this->present.registryPtr = false;
}
serverPstream::constrOpt::constrOpt() {
this->implP = new serverPstream::constrOpt_impl();
}
serverPstream::constrOpt::~constrOpt() {
delete(this->implP);
}
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
serverPstreamConn::constrOpt & \
serverPstreamConn::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->value.OPTION_NAME = arg; \
this->present.OPTION_NAME = true; \
serverPstream::constrOpt & \
serverPstream::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->implP->value.OPTION_NAME = arg; \
this->implP->present.OPTION_NAME = true; \
return *this; \
}
@@ -60,8 +95,63 @@ DEFINE_OPTION_SETTER(registryPtr, xmlrpc_c::registryPtr);
struct serverPstream_impl {
serverPstream_impl(serverPstream::constrOpt_impl const& opt);
~serverPstream_impl();
void
establishRegistry(serverPstream::constrOpt_impl const& opt);
// 'registryP' is what we actually use; 'registryHolder' just holds a
// reference to 'registryP' so the registry doesn't disappear while
// this server exists. But note that if the creator doesn't supply
// a registryPtr, 'registryHolder' is just a placeholder variable and
// the creator is responsible for making sure the registry doesn't
// go anywhere while the server exists.
registryPtr registryHolder;
const registry * registryP;
XMLRPC_SOCKET listenSocketFd;
// The socket on which we accept connections from clients. This comes
// to us from the creator, already bound and in listen mode. That
// way, this object doesn't have to know anything about socket
// addresses or listen parameters such as the maximum connection
// backlog size.
bool termRequested;
// User has requested that the run method return ASAP; i.e. that
// the server cease servicing RPCs.
};
serverPstream_impl::serverPstream_impl(
serverPstream::constrOpt_impl const& opt) {
this->establishRegistry(opt);
if (!opt.present.socketFd)
throwf("You must provide a 'socketFd' constructor option.");
this->listenSocketFd = opt.value.socketFd;
this->termRequested = false;
}
serverPstream_impl::~serverPstream_impl() {
}
void
serverPstreamConn::establishRegistry(constrOpt const& opt) {
serverPstream_impl::establishRegistry(
serverPstream::constrOpt_impl const& opt) {
if (!opt.present.registryP && !opt.present.registryPtr)
throwf("You must specify the 'registryP' or 'registryPtr' option");
@@ -79,93 +169,75 @@ serverPstreamConn::establishRegistry(constrOpt const& opt) {
}
void
serverPstreamConn::establishPacketSocket(constrOpt const& opt) {
if (!opt.present.socketFd)
throwf("You must provide a 'socketFd' constructor option.");
auto_ptr<packetSocket> packetSocketAP;
try {
auto_ptr<packetSocket> p(new packetSocket(opt.value.socketFd));
packetSocketAP = p;
} catch (exception const& e) {
throwf("Unable to create packet socket out of file descriptor %d. %s",
opt.value.socketFd, e.what());
}
this->packetSocketP = packetSocketAP.get();
packetSocketAP.release();
}
serverPstreamConn::serverPstreamConn(constrOpt const& opt) {
this->establishRegistry(opt);
this->establishPacketSocket(opt);
}
serverPstreamConn::~serverPstreamConn() {
delete(this->packetSocketP);
}
void
processCall(const registry * const registryP,
packetPtr const& callPacketP,
packetPtr * const responsePacketPP) {
string const callXml(reinterpret_cast<char *>(callPacketP->getBytes()),
callPacketP->getLength());
string responseXml;
registryP->processCall(callXml, &responseXml);
*responsePacketPP = packetPtr(new packet(responseXml.c_str(),
responseXml.length()));
}
void
serverPstreamConn::runOnce(volatile const int * const interruptP,
bool * const eofP) {
/*----------------------------------------------------------------------------
Get and execute one RPC from the client.
Unless *interruptP gets set nonzero first.
/*-----------------------------------------------------------------------------
serverPstream::shutdown is a derived class of registry::shutdown. You give
it to the registry object to allow XML-RPC method 'system.shutdown' to
-----------------------------------------------------------------------------*/
bool gotPacket;
packetPtr callPacketP;
try {
this->packetSocketP->readWait(interruptP, eofP, &gotPacket,
&callPacketP);
} catch (exception const& e) {
throwf("Error reading a packet from the packet socket. %s",
e.what());
}
if (gotPacket) {
packetPtr responsePacketP;
try {
processCall(this->registryP, callPacketP, &responsePacketP);
} catch (exception const& e) {
throwf("Error executing received packet as an XML-RPC RPC. %s",
e.what());
}
try {
this->packetSocketP->writeWait(responsePacketP);
} catch (exception const& e) {
throwf("Failed to write the response to the packet socket. %s",
e.what());
serverPstream::shutdown::shutdown(serverPstream * const serverPstreamP) :
serverPstreamP(serverPstreamP) {}
serverPstream::shutdown::~shutdown() {}
void
serverPstream::shutdown::doit(string const&,
void * const) const {
this->serverPstreamP->terminate();
}
/*---------------------------------------------------------------------------*/
serverPstream::serverPstream(constrOpt const& opt) {
this->implP = new serverPstream_impl(*opt.implP);
}
serverPstream::~serverPstream() {
delete(this->implP);
}
void
serverPstream::runSerial(volatile const int * const interruptP) {
while (!this->implP->termRequested && !*interruptP) {
struct sockaddr peerAddr;
socklen_t size = sizeof(peerAddr);
int rc;
rc = accept(this->implP->listenSocketFd, &peerAddr, &size);
if (!*interruptP) {
if (rc < 0)
if (errno == EINTR) {
// system call was interrupted, but user doesn't want
// to interrupt the server, so just keep trying
} else
throwf("Failed to accept a connection "
"on the listening socket. accept() failed "
"with errno %d (%s)", errno, strerror(errno));
else {
int const acceptedFd = rc;
serverPstreamConn connectionServer(
xmlrpc_c::serverPstreamConn::constrOpt()
.socketFd(acceptedFd)
.registryP(this->implP->registryP));
callInfo_serverPstream callInfo(this, peerAddr, size);
connectionServer.run(&callInfo, interruptP);
}
}
}
}
@@ -173,15 +245,34 @@ serverPstreamConn::runOnce(volatile const int * const interruptP,
void
serverPstreamConn::runOnce(bool * const eofP) {
/*----------------------------------------------------------------------------
Get and execute one RPC from the client.
-----------------------------------------------------------------------------*/
serverPstream::runSerial() {
int const interrupt(0); // Never interrupt
this->runOnce(&interrupt, eofP);
this->runSerial(&interrupt);
}
void
serverPstream::terminate() {
this->implP->termRequested = true;
}
callInfo_serverPstream::callInfo_serverPstream(
serverPstream * const serverP,
struct sockaddr const clientAddr,
socklen_t const clientAddrSize) :
serverP(serverP),
clientAddr(clientAddr),
clientAddrSize(clientAddrSize)
{}
} // namespace
@@ -0,0 +1,364 @@
/*=============================================================================
server_pstream
===============================================================================
RPC server based on a very simple byte stream and XML-RPC XML
(But this is not an XML-RPC server because it doesn't use HTTP).
The protocol we use is the "packet socket" protocol, which
is an Xmlrpc-c invention. It is an almost trivial representation of
a sequence of packets on a byte stream.
You can create a pstream server from any file descriptor from which
you can read and write a bidirectional character stream. Typically,
it's a TCP socket. Such a server talks to one client its entire life.
Some day, we'll also have a version that you create from a "listening"
socket, which can talk to multiple clients serially (a client connects,
does some RPCs, and disconnects).
By Bryan Henderson 07.05.12.
Contributed to the public domain by its author.
=============================================================================*/
#include <memory>
#include "xmlrpc-c/girerr.hpp"
using girerr::throwf;
#include "xmlrpc-c/packetsocket.hpp"
#include "xmlrpc-c/server_pstream.hpp"
using namespace std;
namespace xmlrpc_c {
struct serverPstreamConn::constrOpt_impl {
constrOpt_impl();
struct value {
xmlrpc_c::registryPtr registryPtr;
const xmlrpc_c::registry * registryP;
XMLRPC_SOCKET socketFd;
} value;
struct {
bool registryPtr;
bool registryP;
bool socketFd;
} present;
};
serverPstreamConn::constrOpt_impl::constrOpt_impl() {
this->present.socketFd = false;
this->present.registryP = false;
this->present.registryPtr = false;
}
serverPstreamConn::constrOpt::constrOpt() {
this->implP = new constrOpt_impl();
}
serverPstreamConn::constrOpt::~constrOpt() {
delete(this->implP);
}
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
serverPstreamConn::constrOpt & \
serverPstreamConn::constrOpt::OPTION_NAME(TYPE const& arg) { \
this->implP->value.OPTION_NAME = arg; \
this->implP->present.OPTION_NAME = true; \
return *this; \
}
DEFINE_OPTION_SETTER(socketFd, XMLRPC_SOCKET);
DEFINE_OPTION_SETTER(registryP, const registry *);
DEFINE_OPTION_SETTER(registryPtr, xmlrpc_c::registryPtr);
#undef DEFINE_OPTION_SETTER
struct serverPstreamConn_impl {
serverPstreamConn_impl(serverPstreamConn::constrOpt_impl const& opt);
~serverPstreamConn_impl();
void
establishRegistry(serverPstreamConn::constrOpt_impl const& opt);
void
establishPacketSocket(serverPstreamConn::constrOpt_impl const& opt);
void
processRecdPacket(packetPtr const callPacketP,
callInfo * const callInfoP);
// 'registryP' is what we actually use; 'registryHolder' just holds a
// reference to 'registryP' so the registry doesn't disappear while
// this server exists. But note that if the creator doesn't supply
// a registryPtr, 'registryHolder' is just a placeholder variable and
// the creator is responsible for making sure the registry doesn't
// go anywhere while the server exists.
registryPtr registryHolder;
const registry * registryP;
packetSocket * packetSocketP;
// The packet socket over which we received RPCs.
// This is permanently connected to our fixed client.
};
serverPstreamConn_impl::serverPstreamConn_impl(
serverPstreamConn::constrOpt_impl const& opt) {
this->establishRegistry(opt);
this->establishPacketSocket(opt);
}
serverPstreamConn_impl::~serverPstreamConn_impl() {
delete(this->packetSocketP);
}
void
serverPstreamConn_impl::establishRegistry(
serverPstreamConn::constrOpt_impl const& opt) {
if (!opt.present.registryP && !opt.present.registryPtr)
throwf("You must specify the 'registryP' or 'registryPtr' option");
else if (opt.present.registryP && opt.present.registryPtr)
throwf("You may not specify both the 'registryP' and "
"the 'registryPtr' options");
else {
if (opt.present.registryP)
this->registryP = opt.value.registryP;
else {
this->registryHolder = opt.value.registryPtr;
this->registryP = opt.value.registryPtr.get();
}
}
}
void
serverPstreamConn_impl::establishPacketSocket(
serverPstreamConn::constrOpt_impl const& opt) {
if (!opt.present.socketFd)
throwf("You must provide a 'socketFd' constructor option.");
auto_ptr<packetSocket> packetSocketAP;
try {
auto_ptr<packetSocket> p(new packetSocket(opt.value.socketFd));
packetSocketAP = p;
} catch (exception const& e) {
throwf("Unable to create packet socket out of file descriptor %d. %s",
opt.value.socketFd, e.what());
}
this->packetSocketP = packetSocketAP.get();
packetSocketAP.release();
}
serverPstreamConn::serverPstreamConn(constrOpt const& opt) {
this->implP = new serverPstreamConn_impl(*opt.implP);
}
serverPstreamConn::~serverPstreamConn() {
delete(this->implP);
}
static void
processCall(const registry * const registryP,
packetPtr const& callPacketP,
callInfo * const callInfoP,
packetPtr * const responsePacketPP) {
string const callXml(reinterpret_cast<char *>(callPacketP->getBytes()),
callPacketP->getLength());
string responseXml;
registryP->processCall(callXml, callInfoP, &responseXml);
*responsePacketPP = packetPtr(new packet(responseXml.c_str(),
responseXml.length()));
}
void
serverPstreamConn_impl::processRecdPacket(packetPtr const callPacketP,
callInfo * const callInfoP) {
packetPtr responsePacketP;
try {
processCall(this->registryP, callPacketP, callInfoP, &responsePacketP);
} catch (exception const& e) {
throwf("Error executing received packet as an XML-RPC RPC. %s",
e.what());
}
try {
this->packetSocketP->writeWait(responsePacketP);
} catch (exception const& e) {
throwf("Failed to write the response to the packet socket. %s",
e.what());
}
}
void
serverPstreamConn::runOnce(callInfo * const callInfoP,
volatile const int * const interruptP,
bool * const eofP) {
/*----------------------------------------------------------------------------
Get and execute one RPC from the client.
Unless *interruptP gets set nonzero first.
-----------------------------------------------------------------------------*/
bool gotPacket;
packetPtr callPacketP;
try {
this->implP->packetSocketP->readWait(interruptP, eofP, &gotPacket,
&callPacketP);
} catch (exception const& e) {
throwf("Error reading a packet from the packet socket. %s",
e.what());
}
if (gotPacket)
this->implP->processRecdPacket(callPacketP, callInfoP);
}
void
serverPstreamConn::runOnce(volatile const int * const interruptP,
bool * const eofP) {
this->runOnce(NULL, interruptP, eofP);
}
void
serverPstreamConn::runOnce(bool * const eofP) {
/*----------------------------------------------------------------------------
Get and execute one RPC from the client.
-----------------------------------------------------------------------------*/
int const interrupt(0); // Never interrupt
this->runOnce(&interrupt, eofP);
}
void
serverPstreamConn::runOnceNoWait(callInfo * const callInfoP,
bool * const eofP,
bool * const didOneP) {
/*----------------------------------------------------------------------------
Get and execute one RPC from the client, unless none has been
received yet. Return as *didOneP whether or not one has been
received. Unless didOneP is NULL.
-----------------------------------------------------------------------------*/
bool gotPacket;
packetPtr callPacketP;
try {
this->implP->packetSocketP->read(eofP, &gotPacket, &callPacketP);
} catch (exception const& e) {
throwf("Error reading a packet from the packet socket. %s",
e.what());
}
if (gotPacket)
this->implP->processRecdPacket(callPacketP, callInfoP);
if (didOneP)
*didOneP = gotPacket;
}
void
serverPstreamConn::runOnceNoWait(bool * const eofP,
bool * const didOneP) {
this->runOnceNoWait(NULL, eofP, didOneP);
}
void
serverPstreamConn::runOnceNoWait(bool * const eofP) {
/*----------------------------------------------------------------------------
Get and execute one RPC from the client, unless none has been
received yet.
-----------------------------------------------------------------------------*/
this->runOnceNoWait(eofP, NULL);
}
void
serverPstreamConn::run(callInfo * const callInfoP,
volatile const int * const interruptP) {
for (bool clientHasDisconnected = false;
!clientHasDisconnected && !*interruptP;)
this->runOnce(callInfoP, interruptP, &clientHasDisconnected);
}
void
serverPstreamConn::run(volatile const int * const interruptP) {
this->run(NULL, interruptP);
}
void
serverPstreamConn::run() {
int const interrupt(0); // Never interrupt
this->run(&interrupt);
}
} // namespace
+19 -14
View File
@@ -17,20 +17,16 @@ all: $(PROGS)
XMLRPC_C_CONFIG = $(BLDDIR)/xmlrpc-c-config.test
CXXFLAGS = $(CXXFLAGS_COMMON) $(CFLAGS_PERSONAL) $(CADD)
LDFLAGS += $(shell $(XMLRPC_C_CONFIG) client --ldadd)
LIBS := $(shell $(XMLRPC_C_CONFIG) client --ldadd)
ifeq ($(MUST_BUILD_CURL_CLIENT),yes)
LDFLAGS += $(shell curl-config --libs)
LIBS += $(shell curl-config --libs)
endif
ifeq ($(MUST_BUILD_LIBWWW_CLIENT),yes)
LDFLAGS += $(shell libwww-config --libs)
LIBS += $(shell libwww-config --libs)
endif
LDFLAGS += "-lpthread"
LDFLAGS += $(LADD)
LIBS += -lpthread
INCLUDES = -Isrcdir/include -I$(BLDDIR) -Isrcdir -Isrcdir/lib/util/include
@@ -42,7 +38,15 @@ Makefile: srcdir
include $(SRCDIR)/common.mk
TEST_OBJS = test.o registry.o server_abyss.o server_pstream.o tools.o
TEST_OBJS = \
test.o \
base64.o \
registry.o \
server_abyss.o \
server_pstream.o \
tools.o \
value.o \
xml.o \
ifeq ($(MUST_BUILD_CLIENT),yes)
TEST_OBJS += testclient.o
@@ -69,16 +73,17 @@ TEST_LIBS = \
ifneq ($(ENABLE_LIBXML2_BACKEND),yes)
# We're using the internal Expat XML parser
TEST_LIBS += $(LIBXMLRPC_XMLPARSE_A) $(LIBXMLRPC_XMLTOK_A)
LDADD_XML =
LIB_XML =
registry.o: D_INTERNAL_EXPAT=-DINTERNAL_EXPAT
else
LDADD_XML = $(shell xml2-config --libs)
LIB_XML = $(shell xml2-config --libs)
endif
test: $(TEST_OBJS) $(TEST_LIBS)
$(CXXLD) -o $@ $(LDFLAGS) $(LDADD_XML) $^
$(CXXLD) -o $@ $(LDFLAGS) $(LADD) $^ $(LIB_XML) $(LIBS)
%.o:%.cpp
$(CXX) -c $(INCLUDES) $(CXXFLAGS) $<
$(CXX) -c $(INCLUDES) $(CXXFLAGS_ALL) $(D_INTERNAL_EXPAT) $<
# Note the difference between 'check' and 'runtests'. 'check' means to check
# our own correctness. 'runtests' means to run the tests that check our
@@ -104,4 +109,4 @@ distclean: clean distclean-common
.PHONY: dep
dep: dep-common
include Makefile.depend
include depend.mk
+54
View File
@@ -0,0 +1,54 @@
#include <string>
#include <iostream>
#include <vector>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "xmlrpc-c/base64.hpp"
#include "tools.hpp"
#include "base64.hpp"
using namespace xmlrpc_c;
using namespace std;
string
base64TestSuite::suiteName() {
return "base64TestSuite";
}
void
base64TestSuite::runtests(unsigned int const) {
unsigned char const bytes0Data[] = "This is a test";
vector<unsigned char> bytes0(&bytes0Data[0],
&bytes0Data[sizeof(bytes0Data)]);
string const base64_0("VGhpcyBpcyBhIHRlc3QA");
string const expectedBase64_0(base64_0 + "\r\n");
TEST(base64FromBytes(bytes0) == expectedBase64_0);
TEST(bytesFromBase64(base64_0) == bytes0);
unsigned char const bytes1Data[] = {0x80, 0xff};
vector<unsigned char> bytes1(&bytes1Data[0],
&bytes1Data[sizeof(bytes1Data)]);
string const base64_1("gP8=");
string const expectedBase64_1(base64_1 + "\r\n");
TEST(base64FromBytes(bytes1) == expectedBase64_1);
TEST(bytesFromBase64(base64_1) == bytes1);
}
+9
View File
@@ -0,0 +1,9 @@
#include "tools.hpp"
class base64TestSuite : public testSuite {
public:
virtual std::string suiteName();
virtual void runtests(unsigned int const indentation);
};
+229 -43
View File
@@ -20,11 +20,19 @@ using namespace xmlrpc_c;
using namespace std;
string const xmlPrologue("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
namespace {
static string const
xmlPrologue("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
static string const
apacheUrl("http://ws.apache.org/xmlrpc/namespaces/extensions");
static string const
xmlnsApache("xmlns:ex=\"" + apacheUrl + "\"");
string const noElementFoundXml(
xmlPrologue +
"<methodResponse>\r\n"
@@ -41,6 +49,22 @@ string const noElementFoundXml(
"</methodResponse>\r\n"
);
string const invalidXMLCall(
xmlPrologue +
"<methodResponse>\r\n"
"<fault>\r\n"
"<value><struct>\r\n"
"<member><name>faultCode</name>\r\n"
"<value><i4>-503</i4></value></member>\r\n"
"<member><name>faultString</name>\r\n"
"<value><string>Call XML not a proper XML-RPC call. "
"Call is not valid XML. XML parsing failed</string></value>"
"</member>\r\n"
"</struct></value>\r\n"
"</fault>\r\n"
"</methodResponse>\r\n"
);
string const sampleAddGoodCallXml(
xmlPrologue +
"<methodCall>\r\n"
@@ -86,6 +110,24 @@ string const sampleAddBadResponseXml(
"</methodResponse>\r\n"
);
string const testCallInfoCallXml(
xmlPrologue +
"<methodCall>\r\n"
"<methodName>test.callinfo</methodName>\r\n"
"<params>\r\n"
"</params>\r\n"
"</methodCall>\r\n"
);
string const testCallInfoResponseXml(
xmlPrologue +
"<methodResponse>\r\n"
"<params>\r\n"
"<param><value><string>this is a test callInfo</string></value>"
"</param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n"
);
string const nonexistentMethodCallXml(
xmlPrologue +
@@ -123,31 +165,30 @@ string const nonexistentMethodNoDefResponseXml(
"</methodResponse>\r\n"
);
} // namespace
string const echoI8ApacheCall(
xmlPrologue +
"<methodCall>\r\n"
"<methodCall " + xmlnsApache + ">\r\n"
"<methodName>echo</methodName>\r\n"
"<params>\r\n"
"<param><value><ex.i8>5</ex.i8></value></param>\r\n"
"<param><value><ex:i8>5</ex:i8></value></param>\r\n"
"</params>\r\n"
"</methodCall>\r\n"
);
string const echoI8ApacheResponse(
xmlPrologue +
"<methodResponse>\r\n"
"<methodResponse " + xmlnsApache + ">\r\n"
"<params>\r\n"
"<param><value><ex.i8>5</ex.i8></value></param>\r\n"
"<param><value><ex:i8>5</ex:i8></value></param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n"
);
string const echoNilApacheCall(
xmlPrologue +
"<methodCall>\r\n"
"<methodCall " + xmlnsApache + ">\r\n"
"<methodName>echo</methodName>\r\n"
"<params>\r\n"
"<param><value><nil/></value></param>\r\n"
@@ -157,14 +198,26 @@ string const echoNilApacheCall(
string const echoNilApacheResponse(
xmlPrologue +
"<methodResponse>\r\n"
"<methodResponse " + xmlnsApache + ">\r\n"
"<params>\r\n"
"<param><value><ex.nil/></value></param>\r\n"
"<param><value><ex:nil/></value></param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n"
);
class callInfo_test : public callInfo {
public:
callInfo_test() : data("this is a test callInfo") {}
callInfo_test(string const& data) : data(data) {};
string data;
};
class sampleAddMethod : public method {
public:
sampleAddMethod() {
@@ -186,6 +239,51 @@ public:
class sampleAddMethod2 : public method2 {
public:
sampleAddMethod2() {
this->_signature = "i:ii";
this->_help = "This method adds two integers together";
}
void
execute(xmlrpc_c::paramList const& paramList,
const callInfo * const,
value * const retvalP) {
int const addend(paramList.getInt(0));
int const adder(paramList.getInt(1));
paramList.verifyEnd(2);
*retvalP = value_int(addend + adder);
}
};
class testCallInfoMethod : public method2 {
public:
testCallInfoMethod() {
this->_signature = "s:";
}
void
execute(xmlrpc_c::paramList const& paramList,
const callInfo * const callInfoPtr,
value * const retvalP) {
const callInfo_test * const callInfoP(
dynamic_cast<const callInfo_test *>(callInfoPtr));
TEST(callInfoP != NULL);
paramList.verifyEnd(0);
*retvalP = value_string(callInfoP->data);
}
};
class nameMethod : public defaultMethod {
void
@@ -213,6 +311,22 @@ public:
static void
testEmptyXmlDocCall(xmlrpc_c::registry const& myRegistry) {
string response;
myRegistry.processCall("", &response);
#ifdef INTERNAL_EXPAT
TEST(response == noElementFoundXml);
#else
// This is what we get with libxml2
TEST(response == invalidXMLCall);
#endif
}
class registryRegMethodTestSuite : public testSuite {
public:
@@ -227,11 +341,7 @@ public:
xmlrpc_c::methodPtr(new sampleAddMethod));
myRegistry.disableIntrospection();
{
string response;
myRegistry.processCall("", &response);
TEST(response == noElementFoundXml);
}
testEmptyXmlDocCall(myRegistry);
{
string response;
myRegistry.processCall(sampleAddGoodCallXml, &response);
@@ -242,6 +352,12 @@ public:
myRegistry.processCall(sampleAddBadCallXml, &response);
TEST(response == sampleAddBadResponseXml);
}
{
string response;
callInfo const callInfo;
myRegistry.processCall(sampleAddBadCallXml, &callInfo, &response);
TEST(response == sampleAddBadResponseXml);
}
}
};
@@ -284,6 +400,93 @@ public:
class method2TestSuite : public testSuite {
public:
virtual string suiteName() {
return "method2TestSuite";
}
virtual void runtests(unsigned int const) {
xmlrpc_c::registry myRegistry;
myRegistry.addMethod("sample.add",
xmlrpc_c::methodPtr(new sampleAddMethod2));
myRegistry.addMethod("test.callinfo",
xmlrpc_c::methodPtr(new testCallInfoMethod));
{
string response;
myRegistry.processCall(sampleAddGoodCallXml, &response);
TEST(response == sampleAddGoodResponseXml);
}
{
string response;
myRegistry.processCall(sampleAddBadCallXml, &response);
TEST(response == sampleAddBadResponseXml);
}
{
string response;
callInfo_test const callInfo;
myRegistry.processCall(testCallInfoCallXml, &callInfo, &response);
TEST(response == testCallInfoResponseXml);
}
}
};
class dialectTestSuite : public testSuite {
public:
virtual string suiteName() {
return "dialectTestSuite";
}
virtual void runtests(unsigned int const) {
registry myRegistry;
string response;
myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod));
myRegistry.addMethod("echo", methodPtr(new echoMethod));
myRegistry.setDialect(xmlrpc_dialect_i8);
myRegistry.setDialect(xmlrpc_dialect_apache);
myRegistry.processCall(echoI8ApacheCall, &response);
TEST(response == echoI8ApacheResponse);
myRegistry.processCall(echoNilApacheCall, &response);
TEST(response == echoNilApacheResponse);
EXPECT_ERROR( // invalid dialect
myRegistry.setDialect(static_cast<xmlrpc_dialect>(300));
);
}
};
class testShutdown : public xmlrpc_c::registry::shutdown {
/*----------------------------------------------------------------------------
This class is logically local to
registryShutdownTestSuite::runtests(), but if we declare it that
way, gcc 2.95.3 fails with some bogus messages about undefined
references from random functions when we do that.
-----------------------------------------------------------------------------*/
public:
void doit(string const&,
void * const) const {
}
};
class registryShutdownTestSuite : public testSuite {
public:
@@ -294,15 +497,7 @@ public:
xmlrpc_c::registry myRegistry;
class myshutdown : public xmlrpc_c::registry::shutdown {
public:
void doit(string const&,
void * const) const {
}
};
myshutdown shutdown;
testShutdown shutdown;
myRegistry.setShutdown(&shutdown);
}
@@ -310,6 +505,10 @@ public:
} // unnamed namespace
string
registryTestSuite::suiteName() {
return "registryTestSuite";
@@ -327,32 +526,19 @@ registryTestSuite::runtests(unsigned int const indentation) {
}
registryRegMethodTestSuite().run(indentation+1);
registryDefaultMethodTestSuite().run(indentation+1);
registry myRegistry;
myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod));
myRegistry.addMethod("echo", methodPtr(new echoMethod));
method2TestSuite().run(indentation+1);
string response;
registry myRegistry;
myRegistry.disableIntrospection();
myRegistry.setDialect(xmlrpc_dialect_i8);
myRegistry.setDialect(xmlrpc_dialect_apache);
dialectTestSuite().run(indentation+1);
registryShutdownTestSuite().run(indentation+1);
myRegistry.processCall(echoI8ApacheCall, &response);
TEST(myRegistry.maxStackSize() >= 256);
TEST(response == echoI8ApacheResponse);
myRegistry.processCall(echoNilApacheCall, &response);
TEST(response == echoNilApacheResponse);
EXPECT_ERROR( // invalid dialect
myRegistry.setDialect(static_cast<xmlrpc_dialect>(300));
);
}
+60 -1
View File
@@ -10,6 +10,8 @@
#include <vector>
#include <sstream>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <time.h>
#ifdef WIN32
#include <winsock.h>
@@ -17,6 +19,7 @@
#include <sys/unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "xmlrpc-c/girerr.hpp"
@@ -34,6 +37,9 @@ using namespace xmlrpc_c;
using namespace std;
namespace {
static void
closesock(int const fd) {
#ifdef WIN32
@@ -75,7 +81,7 @@ public:
closesock(this->fd);
}
int fd;
XMLRPC_SOCKET fd;
};
@@ -259,6 +265,10 @@ public:
.timeout(20)
.dontAdvertise(true)
.uriPath("/xmlrpc")
.chunkResponse(true)
.allowOrigin("*")
.serverOwnsSignals(false)
.expectSigchld(true)
);
}
@@ -274,6 +284,54 @@ public:
class testCallInfoMethod : public method2 {
public:
void
execute(paramList const& paramList,
const callInfo * const callInfoPtr,
value * const retvalP) {
const callInfo_serverAbyss * const callInfoP(
dynamic_cast<const callInfo_serverAbyss *>(callInfoPtr));
TEST(callInfoP != NULL);
paramList.verifyEnd(0);
TEST(callInfoP->serverAbyssP != NULL);
TEST(callInfoP->abyssSessionP != NULL);
*retvalP = value_nil();
}
};
class callInfoTestSuite : public testSuite {
public:
virtual string suiteName() {
return "callInfoTestSuite";
}
virtual void runtests(unsigned int const) {
registry myRegistry;
myRegistry.addMethod("sample.add", methodPtr(new testCallInfoMethod));
serverAbyss abyssServer(serverAbyss::constrOpt()
.registryP(&myRegistry)
.portNumber(12345)
);
}
};
} // unnamed namespace
string
serverAbyssTestSuite::suiteName() {
return "serverAbyssTestSuite";
@@ -289,4 +347,5 @@ serverAbyssTestSuite::runtests(unsigned int const indentation) {
createTestSuite().run(indentation+1);
callInfoTestSuite().run(indentation+1);
}
+711 -68
View File
@@ -4,16 +4,37 @@
Test the pstream server C++ facilities of XML-RPC for C/C++.
=============================================================================*/
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "xmlrpc_config.h"
#if MSVCRT
#include <winsock2.h>
#include <io.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#endif
#include <errno.h>
#include <string>
#include <cstring>
#include <fcntl.h>
#include "xmlrpc-c/config.h"
#if MSVCRT
int
xmlrpc_win32_socketpair(int const domain,
int const type,
int const protocol,
SOCKET socks[2]);
#endif
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
using girerr::throwf;
#include "xmlrpc-c/sleep_int.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/registry.hpp"
#include "xmlrpc-c/server_pstream.hpp"
@@ -25,6 +46,41 @@ using namespace xmlrpc_c;
using namespace std;
namespace {
static void
setNonBlocking(XMLRPC_SOCKET const socket) {
#if MSVCRT
u_long iMode = 1;
ioctlsocket(socket, FIONBIO, &iMode);
#else
fcntl(socket, F_SETFL, O_NONBLOCK);
#endif
}
#define ESC_STR "\x1B"
static string const
xmlPrologue("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
static string const
packetStart(ESC_STR "PKT");
static string const
packetEnd(ESC_STR "END");
class callInfo_test : public callInfo {
public:
callInfo_test() : info("this is a test") {}
string const info;
};
class sampleAddMethod : public method {
public:
@@ -45,51 +101,542 @@ public:
}
};
string const sampleAddCallXml(
xmlPrologue +
"<methodCall>\r\n"
"<methodName>sample.add</methodName>\r\n"
"<params>\r\n"
"<param><value><i4>5</i4></value></param>\r\n"
"<param><value><i4>7</i4></value></param>\r\n"
"</params>\r\n"
"</methodCall>\r\n"
);
string const sampleAddResponseXml(
xmlPrologue +
"<methodResponse>\r\n"
"<params>\r\n"
"<param><value><i4>12</i4></value></param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n"
);
class testCallInfoMethod : public method2 {
public:
virtual void
execute(paramList const& paramList,
const callInfo * const callInfoPtr,
value * const retvalP) {
const callInfo_test * const callInfoP(
dynamic_cast<const callInfo_test *>(callInfoPtr));
TEST(callInfoP != NULL);
paramList.verifyEnd(0);
TEST(callInfoP->info == string("this is a test"));
*retvalP = value_nil();
}
};
string const testCallInfoCallXml(
xmlPrologue +
"<methodCall>\r\n"
"<methodName>test.callinfo</methodName>\r\n"
"<params>\r\n"
"</params>\r\n"
"</methodCall>\r\n"
);
string const testCallInfoResponseXml(
xmlPrologue +
"<methodResponse>\r\n"
"<params>\r\n"
"<param><value><nil/></value>"
"</param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n"
);
static void
createTestFile(string const& contents,
int * const fdP) {
waitForNetworkTransport() {
/*----------------------------------------------------------------------------
Wait for a message to travel through the network.
string const filename("/tmp/xmlrpc_test_pstream");
unlink(filename.c_str());
This is part of our hack to allow us to test client/server communication
without the bother of a separate thread for each. One party writes
to a socket, causing the OS to buffer the message, then the other party
reads from the socket, getting the buffered message. We never wait
to send or receive, because with only one thread to do both, we would
deadlock. Instead, we just count on the buffer being big enough.
But on some systems, the message doesn't immediately travel like this. It
takes action by an independent thread (provided by the OS) to move the
message. In particular, we've seen this behavior on Windows (2010.10).
So we just sleep for a small amount of time to let the message move.
-----------------------------------------------------------------------------*/
// xmlrpc_millisecond_sleep() is allowed to return early, and on Windows
// it does that in preference to returning late insofar as the clock
// resolution doesn't allow returning at the exact time. It is rumored
// that Windows clock period may be as long as 40 milliseconds.
xmlrpc_millisecond_sleep(50);
}
class client {
/*----------------------------------------------------------------------------
This is an object you can use as a client to test a packet stream
server.
You attach the 'serverFd' member to your packet stream server, then
call the 'sendCall' method to send a call to your server, then call
the 'recvResp' method to get the response.
Destroying the object closes the connection.
We rely on typical, though unguaranteed socket function: we need to
be able to write 'contents' to the socket in a single write()
system call before the other side reads anything -- i.e. the socket
has to have a buffer that big. We do this because we're lazy; doing
it right would require forking a writer process.
-----------------------------------------------------------------------------*/
public:
client();
~client();
void
sendCall(string const& callBytes) const;
void
hangup();
void
recvResp(string * const respBytesP) const;
int serverFd;
private:
int clientFd;
};
client::client() {
enum {
SERVER = 0,
CLIENT = 1,
};
XMLRPC_SOCKET sockets[2];
int rc;
rc = open(filename.c_str(), O_RDWR | O_CREAT);
unlink(filename.c_str());
rc = XMLRPC_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, sockets);
if (rc < 0)
throwf("Failed to create file '%s' as a test tool. errno=%d (%s)",
filename.c_str(), errno, strerror(errno));
throwf("Failed to create UNIX domain stream socket pair "
"as test tool. errno=%d (%s)",
errno, strerror(errno));
else {
int const fd(rc);
setNonBlocking(sockets[CLIENT]);
int rc;
rc = write(fd, contents.c_str(), contents.length());
if (rc < 0)
throwf("write() of test file failed, errno=%d (%s)",
errno, strerror(errno));
else {
unsigned int bytesWritten(rc);
if (bytesWritten != contents.length())
throwf("Short write");
else {
int rc;
rc = lseek(fd, 0, SEEK_SET);
if (rc < 0)
throwf("lseek(0) of test file failed, errno=%d (%s)",
errno, strerror(errno));
}
}
*fdP = fd;
this->serverFd = sockets[SERVER];
this->clientFd = sockets[CLIENT];
}
}
client::~client() {
XMLRPC_CLOSESOCKET(this->clientFd);
XMLRPC_CLOSESOCKET(this->serverFd);
}
void
client::sendCall(string const& packetBytes) const {
int rc;
rc = send(this->clientFd, packetBytes.c_str(), packetBytes.length(), 0);
waitForNetworkTransport();
if (rc < 0)
throwf("send() of test data to socket failed, errno=%d (%s)",
errno, strerror(errno));
else {
unsigned int bytesWritten(rc);
if (bytesWritten != packetBytes.length())
throwf("Short write to socket");
}
}
void
client::hangup() {
// Closing the socket (close()) would be a better simulation of the
// real world, and easier, but we shut down just the client->server
// half of the socket and remain open to receive an RPC response.
// That's because this test program is lazy and does the client and
// server in the same thread, depending on socket buffering on the
// receive side to provide parallelism. We need to be able to do the
// following sequence:
//
// - Client sends call
// - Client hangs up
// - Server gets call
// - Server sends response
// - Client gets response
// - Server notices hangup
shutdown(this->clientFd, 1); // Shutdown for transmission only
}
void
client::recvResp(string * const packetBytesP) const {
char buffer[4096];
int rc;
waitForNetworkTransport();
rc = recv(this->clientFd, buffer, sizeof(buffer), 0);
if (rc < 0)
throwf("recv() from socket failed, errno=%d (%s)",
errno, strerror(errno));
else {
unsigned int bytesReceived(rc);
*packetBytesP = string(buffer, bytesReceived);
}
}
static void
testEmptyStream(registry const& myRegistry) {
/*----------------------------------------------------------------------------
Here we send the pstream server an empty stream; i.e. we close the
socket from the client end without sending anything.
This should cause the server to recognize EOF.
-----------------------------------------------------------------------------*/
client client;
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(client.serverFd));
client.hangup();
bool eof;
server.runOnce(&eof);
TEST(eof);
}
static void
testBrokenPacket(registry const& myRegistry) {
/*----------------------------------------------------------------------------
Here we send a stream that is not a legal packetsocket stream: it
doesn't have any control word.
-----------------------------------------------------------------------------*/
client client;
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(client.serverFd));
client.sendCall("junk");
client.hangup();
bool eof;
EXPECT_ERROR(
server.runOnce(&eof);
);
}
static void
testEmptyPacket(registry const& myRegistry) {
/*----------------------------------------------------------------------------
Here we send the pstream server one empty packet. It should respond
with one packet, being an XML-RPC fault response complaining that the
call is not valid XML.
-----------------------------------------------------------------------------*/
client client;
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(client.serverFd));
client.sendCall(packetStart + packetEnd);
bool eof;
server.runOnce(&eof);
TEST(!eof);
string response;
client.recvResp(&response);
// We ought to validate that the response is a complaint about
// the empty call
client.hangup();
server.runOnce(&eof);
TEST(eof);
}
static void
testCallInfo(client * const clientP,
serverPstreamConn * const serverP) {
string const testCallInfoCallStream(
packetStart + testCallInfoCallXml + packetEnd
);
string const testCallInfoResponseStream(
packetStart + testCallInfoResponseXml + packetEnd
);
clientP->sendCall(testCallInfoCallStream);
callInfo_test callInfo;
int nointerrupt(0);
bool eof;
serverP->runOnce(&callInfo, &nointerrupt, &eof);
TEST(!eof);
string response;
clientP->recvResp(&response);
TEST(response == testCallInfoResponseStream);
}
static void
testNormalCall(registry const& myRegistry) {
string const sampleAddGoodCallStream(
packetStart + sampleAddCallXml + packetEnd
);
string const sampleAddGoodResponseStream(
packetStart + sampleAddResponseXml + packetEnd
);
client client;
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(client.serverFd));
client.sendCall(sampleAddGoodCallStream);
bool eof;
int interrupt(1);
server.runOnce(&interrupt, &eof); // returns without reading socket
TEST(!eof);
server.runOnce(&eof);
TEST(!eof);
string response;
client.recvResp(&response);
TEST(response == sampleAddGoodResponseStream);
testCallInfo(&client, &server);
client.hangup();
server.runOnce(&eof);
TEST(eof);
}
static void
testNoWaitCall(registry const& myRegistry) {
string const sampleAddGoodCallStream(
packetStart +
xmlPrologue +
"<methodCall>\r\n"
"<methodName>sample.add</methodName>\r\n"
"<params>\r\n"
"<param><value><i4>5</i4></value></param>\r\n"
"<param><value><i4>7</i4></value></param>\r\n"
"</params>\r\n"
"</methodCall>\r\n" +
packetEnd
);
string const sampleAddGoodResponseStream(
packetStart +
xmlPrologue +
"<methodResponse>\r\n"
"<params>\r\n"
"<param><value><i4>12</i4></value></param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n" +
packetEnd
);
client client;
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(client.serverFd));
bool eof;
bool gotOne;
string response;
server.runOnceNoWait(&eof, &gotOne);
TEST(!eof);
TEST(!gotOne);
server.runOnceNoWait(&eof);
TEST(!eof);
client.sendCall(sampleAddGoodCallStream);
server.runOnceNoWait(&eof, &gotOne);
TEST(!eof);
TEST(gotOne);
client.recvResp(&response);
TEST(response == sampleAddGoodResponseStream);
client.sendCall(sampleAddGoodCallStream);
server.runOnce(&eof);
TEST(!eof);
client.recvResp(&response);
TEST(response == sampleAddGoodResponseStream);
client.hangup();
server.runOnce(&eof);
TEST(eof);
}
static void
testMultiRpcRunNoRpc(registry const& myRegistry) {
client client;
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(client.serverFd));
client.hangup();
server.run();
}
static void
testMultiRpcRunOneRpc(registry const& myRegistry) {
string const sampleAddGoodCallStream(
packetStart +
xmlPrologue +
"<methodCall>\r\n"
"<methodName>sample.add</methodName>\r\n"
"<params>\r\n"
"<param><value><i4>5</i4></value></param>\r\n"
"<param><value><i4>7</i4></value></param>\r\n"
"</params>\r\n"
"</methodCall>\r\n" +
packetEnd
);
string const sampleAddGoodResponseStream(
packetStart +
xmlPrologue +
"<methodResponse>\r\n"
"<params>\r\n"
"<param><value><i4>12</i4></value></param>\r\n"
"</params>\r\n"
"</methodResponse>\r\n" +
packetEnd
);
client client;
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(client.serverFd));
client.sendCall(sampleAddGoodCallStream);
client.hangup();
int interrupt;
interrupt = 1;
server.run(&interrupt); // Returns without reading socket
interrupt = 0;
server.run(&interrupt); // Does the buffered RPC
string response;
client.recvResp(&response);
TEST(response == sampleAddGoodResponseStream);
}
class serverPstreamConnTestSuite : public testSuite {
public:
@@ -97,14 +644,12 @@ public:
return "serverPstreamConnTestSuite";
}
virtual void runtests(unsigned int const) {
int const devNullFd(open("/dev/null", 0));
if (devNullFd < 0)
throwf("Failed to open /dev/null, needed for test.");
registry myRegistry;
myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod));
myRegistry.addMethod("sample.add",
methodPtr(new sampleAddMethod));
myRegistry.addMethod("test.callinfo",
methodPtr(new testCallInfoMethod));
registryPtr myRegistryP(new registry);
@@ -125,43 +670,140 @@ public:
.registryP(&myRegistry));
);
EXPECT_ERROR( // No such file descriptor
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(37));
);
{
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(devNullFd));
testEmptyStream(myRegistry);
bool eof;
server.runOnce(&eof);
TEST(eof);
}
{
int fd;
createTestFile("junk", &fd);
testBrokenPacket(myRegistry);
serverPstreamConn server(serverPstreamConn::constrOpt()
.registryP(&myRegistry)
.socketFd(fd));
testEmptyPacket(myRegistry);
bool eof;
testNormalCall(myRegistry);
EXPECT_ERROR( // EOF in the middle of a packet
server.runOnce(&eof);
);
close(fd);
}
testNoWaitCall(myRegistry);
close(devNullFd);
testMultiRpcRunNoRpc(myRegistry);
testMultiRpcRunOneRpc(myRegistry);
}
};
static void
testMultiConnInterrupt(registry const& myRegistry) {
// We use a nonexistent file descriptor, but the server won't
// ever access it, so it won't know.
serverPstream server(serverPstream::constrOpt()
.registryP(&myRegistry)
.socketFd(37));
int interrupt(1); // interrupt immediately
server.runSerial(&interrupt);
}
class derivedServer : public xmlrpc_c::serverPstream {
public:
derivedServer(serverPstream::constrOpt const& constrOpt) :
serverPstream(constrOpt),
info("this is my derived server") {}
string const info;
};
class multiTestCallInfoMethod : public method2 {
// The test isn't sophisticated enough actually to do an RPC, so this
// code never runs. We just want to see if it compiles.
public:
virtual void
execute(paramList const& paramList,
const callInfo * const callInfoPtr,
value * const retvalP) {
const callInfo_serverPstream * const callInfoP(
dynamic_cast<const callInfo_serverPstream *>(callInfoPtr));
TEST(callInfoP != NULL);
paramList.verifyEnd(0);
derivedServer * const derivedServerP(
dynamic_cast<derivedServer *>(callInfoP->serverP));
TEST(derivedServerP->info == string("this is my derived server"));
TEST(callInfoP->clientAddr.sa_family == AF_INET);
TEST(callInfoP->clientAddrSize >= sizeof(struct sockaddr_in));
*retvalP = value_nil();
}
};
static void
testMultiConnCallInfo() {
registry myRegistry;
myRegistry.addMethod("testCallInfo",
methodPtr(new multiTestCallInfoMethod));
derivedServer server(serverPstream::constrOpt()
.registryP(&myRegistry)
.socketFd(37));
}
class multiConnServerTestSuite : public testSuite {
public:
virtual string suiteName() {
return "multiConnServerTestSuite";
}
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( // Empty options
serverPstream::constrOpt opt;
serverPstream server(opt);
);
EXPECT_ERROR( // No registry
serverPstream server(serverPstream::constrOpt()
.socketFd(3));
);
EXPECT_ERROR( // No socket fd
serverPstream server(serverPstream::constrOpt()
.registryP(&myRegistry));
);
testMultiConnInterrupt(myRegistry);
testMultiConnCallInfo();
}
};
} // unnamed namespace
string
serverPstreamTestSuite::suiteName() {
return "serverPstreamTestSuite";
@@ -173,5 +815,6 @@ serverPstreamTestSuite::runtests(unsigned int const indentation) {
serverPstreamConnTestSuite().run(indentation + 1);
multiConnServerTestSuite().run(indentation + 1);
}
+73 -324
View File
@@ -4,6 +4,7 @@
#include <vector>
#include <sstream>
#include <memory>
#include <cstring>
#include <time.h>
#include "xmlrpc-c/girerr.hpp"
@@ -13,6 +14,9 @@ using girerr::error;
#include "xmlrpc-c/oldcppwrapper.hpp"
#include "xmlrpc-c/registry.hpp"
#include "base64.hpp"
#include "xml.hpp"
#include "value.hpp"
#include "testclient.hpp"
#include "registry.hpp"
#include "server_abyss.hpp"
@@ -189,7 +193,7 @@ void test_value (void) {
XmlRpcValue::makeArray().getArray();
XmlRpcValue::makeStruct().getStruct();
// Test Base64 values.
// Test byte string values.
const unsigned char *b64_data;
size_t b64_len;
XmlRpcValue val6 = XmlRpcValue::makeBase64((unsigned char*) "a\0\0b", 4);
@@ -216,7 +220,7 @@ void test_value (void) {
TEST(strct.structSize() == 2);
TEST(strct.structHasKey("bar"));
TEST(!strct.structHasKey("nosuch"));
for (size_t i = 0; i < strct.structSize(); i++) {
for (int i = 0; i < (int)strct.structSize(); ++i) {
string key;
XmlRpcValue value;
strct.structGetKeyAndValue(i, key, value);
@@ -240,285 +244,66 @@ testXmlRpcCpp() {
class intTestSuite : public testSuite {
public:
virtual string suiteName() {
return "intTestSuite";
}
virtual void runtests(unsigned int const) {
value_int int1(7);
TEST(static_cast<int>(int1) == 7);
value_int int2(-7);
TEST(static_cast<int>(int2) == -7);
value val1(int1);
TEST(val1.type() == value::TYPE_INT);
value_int int3(val1);
TEST(static_cast<int>(int3) == 7);
try {
value_int int4(value_double(3.7));
TEST_FAILED("invalid cast double-int suceeded");
} catch (error) {}
}
};
static void
buildParamListWithAdd(paramList * const paramListP,
time_t const timeFuture) {
paramListP->add(value_int(7));
paramListP->add(value_boolean(true)).add(value_double(3.14));
time_t const timeZero(0);
paramListP->add(value_datetime(timeZero));
paramListP->add(value_datetime(timeFuture));
paramListP->add(value_string("hello world"));
unsigned char bytestringArray[] = {0x10, 0x11, 0x12, 0x13, 0x14};
vector<unsigned char>
bytestringData(&bytestringArray[0], &bytestringArray[4]);
paramListP->add(value_bytestring(bytestringData));
vector<value> arrayData;
arrayData.push_back(value_int(7));
arrayData.push_back(value_double(2.78));
arrayData.push_back(value_string("hello world"));
paramListP->add(value_array(arrayData));
map<string, value> structData;
pair<string, value> member("the_integer", value_int(9));
structData.insert(member);
paramListP->add(value_struct(structData));
paramListP->add(value_nil());
paramListP->add(value_i8((xmlrpc_int64)UINT_MAX + 1));
}
class doubleTestSuite : public testSuite {
public:
virtual string suiteName() {
return "doubleTestSuite";
}
virtual void runtests(unsigned int const) {
value_double double1(3.14);
TEST(static_cast<double>(double1) == 3.14);
value val1(double1);
TEST(val1.type() == value::TYPE_DOUBLE);
value_double double2(val1);
TEST(static_cast<double>(double2) == 3.14);
try {
value_double double4(value_int(4));
TEST_FAILED("invalid cast int-double suceeded");
} catch (error) {}
}
};
static void
verifyParamList(paramList const& paramList,
time_t const timeFuture) {
TEST(paramList.size() == 11);
TEST(paramList.getInt(0) == 7);
TEST(paramList.getInt(0, 7) == 7);
TEST(paramList.getInt(0, -5, 7) == 7);
TEST(paramList.getBoolean(1) == true);
TEST(paramList.getDouble(2) == 3.14);
TEST(paramList.getDouble(2, 1) == 3.14);
TEST(paramList.getDouble(2, 1, 4) == 3.14);
time_t const timeZero(0);
TEST(paramList.getDatetime_sec(3) == timeZero);
TEST(paramList.getDatetime_sec(3, paramList::TC_ANY) == timeZero);
TEST(paramList.getDatetime_sec(3, paramList::TC_NO_FUTURE)
== timeZero);
TEST(paramList.getDatetime_sec(4, paramList::TC_NO_PAST)
== timeFuture);
TEST(paramList.getString(5) == "hello world");
TEST(paramList.getBytestring(6)[0] == 0x10);
TEST(paramList.getArray(7).size() == 3);
TEST(paramList.getArray(7, 3).size() == 3);
TEST(paramList.getArray(7, 1, 3).size() == 3);
paramList.getStruct(8)["the_integer"];
paramList.getNil(9);
TEST(paramList.getI8(10) == (xmlrpc_int64)UINT_MAX + 1);
paramList.verifyEnd(11);
}
class booleanTestSuite : public testSuite {
public:
virtual string suiteName() {
return "booleanTestSuite";
}
virtual void runtests(unsigned int const) {
value_boolean boolean1(true);
TEST(static_cast<bool>(boolean1) == true);
value_boolean boolean2(false);
TEST(static_cast<bool>(boolean2) == false);
value val1(boolean1);
TEST(val1.type() == value::TYPE_BOOLEAN);
value_boolean boolean3(val1);
TEST(static_cast<bool>(boolean3) == true);
try {
value_boolean boolean4(value_int(4));
TEST_FAILED("invalid cast int-boolean suceeded");
} catch (error) {}
}
};
class datetimeTestSuite : public testSuite {
public:
virtual string suiteName() {
return "datetimeTestSuite";
}
virtual void runtests(unsigned int const) {
time_t const testTime(900684535);
value_datetime datetime1("19980717T14:08:55");
TEST(static_cast<time_t>(datetime1) == testTime);
value_datetime datetime2(testTime);
TEST(static_cast<time_t>(datetime2) == testTime);
value val1(datetime1);
TEST(val1.type() == value::TYPE_DATETIME);
value_datetime datetime3(val1);
TEST(static_cast<time_t>(datetime3) == testTime);
try {
value_datetime datetime4(value_int(4));
TEST_FAILED("invalid cast int-datetime suceeded");
} catch (error) {}
}
};
class stringTestSuite : public testSuite {
public:
virtual string suiteName() {
return "stringTestSuite";
}
virtual void runtests(unsigned int const) {
value_string string1("hello world");
TEST(static_cast<string>(string1) == "hello world");
value_string string2("embedded\0null");
TEST(static_cast<string>(string2) == "embedded\0null");
value val1(string1);
TEST(val1.type() == value::TYPE_STRING);
value_string string3(val1);
TEST(static_cast<string>(string3) == "hello world");
try {
value_string string4(value_int(4));
TEST_FAILED("invalid cast int-string succeeded");
} catch (error) {}
value_string string5("hello world", value_string::nlCode_all);
TEST(static_cast<string>(string5) == "hello world");
value_string string6("hello\nthere\rworld\r\n\n",
value_string::nlCode_all);
TEST(static_cast<string>(string6) == "hello\nthere\nworld\n\n");
TEST(string6.crlfValue() == "hello\r\nthere\r\nworld\r\n\r\n");
value_string string7("hello\nthere\rworld\r\n\n",
value_string::nlCode_lf);
TEST(static_cast<string>(string7) == "hello\nthere\rworld\r\n\n");
}
};
class bytestringTestSuite : public testSuite {
public:
virtual string suiteName() {
return "bytestringTestSuite";
}
virtual void runtests(unsigned int const) {
unsigned char bytestringArray[] = {0x10, 0x11, 0x12, 0x13, 0x14};
vector<unsigned char>
bytestringData(&bytestringArray[0], &bytestringArray[4]);
value_bytestring bytestring1(bytestringData);
vector<unsigned char> const dataReadBack1(
bytestring1.vectorUcharValue());
TEST(dataReadBack1 == bytestringData);
value val1(bytestring1);
TEST(val1.type() == value::TYPE_BYTESTRING);
value_bytestring bytestring2(val1);
vector<unsigned char> const dataReadBack2(
bytestring2.vectorUcharValue());
TEST(dataReadBack2 == bytestringData);
try {
value_bytestring bytestring4(value_int(4));
TEST_FAILED("invalid cast int-bytestring suceeded");
} catch (error) {}
}
};
class nilTestSuite : public testSuite {
public:
virtual string suiteName() {
return "nilTestSuite";
}
virtual void runtests(unsigned int const) {
value_nil nil1;
value val1(nil1);
TEST(val1.type() == value::TYPE_NIL);
value_nil nil2(val1);
try {
value_nil nil4(value_int(4));
TEST_FAILED("invalid cast int-nil suceeded");
} catch (error) {}
}
};
class i8TestSuite : public testSuite {
public:
virtual string suiteName() {
return "i8TestSuite";
}
virtual void runtests(unsigned int const) {
value_i8 int1(7);
TEST(static_cast<xmlrpc_int64>(int1) == 7);
value_i8 int2(-7);
TEST(static_cast<xmlrpc_int64>(int2) == -7);
value_i8 int5(1ull << 40);
TEST(static_cast<xmlrpc_int64>(int5) == (1ull << 40));
value val1(int1);
TEST(val1.type() == value::TYPE_I8);
value_i8 int3(val1);
TEST(static_cast<xmlrpc_int64>(int3) == 7);
try {
value_i8 int4(value_double(3.7));
TEST_FAILED("invalid cast double-i8 suceeded");
} catch (error) {}
}
};
class structTestSuite : public testSuite {
public:
virtual string suiteName() {
return "structTestSuite";
}
virtual void runtests(unsigned int const) {
map<string, value> structData;
pair<string, value> member("the_integer", value_int(9));
structData.insert(member);
value_struct struct1(structData);
map<string, value> dataReadBack(struct1);
TEST(static_cast<int>(value_int(dataReadBack["the_integer"])) == 9);
value val1(struct1);
TEST(val1.type() == value::TYPE_STRUCT);
value_struct struct2(val1);
try {
value_struct struct4(value_int(4));
TEST_FAILED("invalid cast int-struct suceeded");
} catch (error) {}
}
};
class arrayTestSuite : public testSuite {
public:
virtual string suiteName() {
return "arrayTestSuite";
}
virtual void runtests(unsigned int const) {
vector<value> arrayData;
arrayData.push_back(value_int(7));
arrayData.push_back(value_double(2.78));
arrayData.push_back(value_string("hello world"));
value_array array1(arrayData);
TEST(array1.size() == 3);
vector<value> dataReadBack1(array1.vectorValueValue());
TEST(dataReadBack1[0].type() == value::TYPE_INT);
TEST(static_cast<int>(value_int(dataReadBack1[0])) == 7);
TEST(dataReadBack1[1].type() == value::TYPE_DOUBLE);
TEST(static_cast<double>(value_double(dataReadBack1[1])) == 2.78);
TEST(dataReadBack1[2].type() == value::TYPE_STRING);
TEST(static_cast<string>(value_string(dataReadBack1[2])) ==
"hello world");
value val1(array1);
TEST(val1.type() == value::TYPE_ARRAY);
value_array array2(val1);
TEST(array2.size() == 3);
try {
value_array array4(value_int(4));
TEST_FAILED("invalid cast int-array suceeded");
} catch (error) {}
}
};
class valueTestSuite : public testSuite {
public:
virtual string suiteName() {
return "valueTestSuite";
}
virtual void runtests(unsigned int const indentation) {
intTestSuite().run(indentation+1);
doubleTestSuite().run(indentation+1);
booleanTestSuite().run(indentation+1);
datetimeTestSuite().run(indentation+1);
stringTestSuite().run(indentation+1);
bytestringTestSuite().run(indentation+1);
nilTestSuite().run(indentation+1);
i8TestSuite().run(indentation+1);
structTestSuite().run(indentation+1);
arrayTestSuite().run(indentation+1);
}
};
class paramListTestSuite : public testSuite {
@@ -529,60 +314,24 @@ public:
}
virtual void runtests(unsigned int const) {
time_t const timeFuture(time(NULL)+100);
paramList paramList1;
TEST(paramList1.size() == 0);
paramList1.add(value_int(7));
paramList1.add(value_boolean(true));
paramList1.add(value_double(3.14));
time_t const timeZero(0);
paramList1.add(value_datetime(timeZero));
time_t const timeFuture(time(NULL)+100);
paramList1.add(value_datetime(timeFuture));
paramList1.add(value_string("hello world"));
unsigned char bytestringArray[] = {0x10, 0x11, 0x12, 0x13, 0x14};
vector<unsigned char>
bytestringData(&bytestringArray[0], &bytestringArray[4]);
paramList1.add(value_bytestring(bytestringData));
vector<value> arrayData;
arrayData.push_back(value_int(7));
arrayData.push_back(value_double(2.78));
arrayData.push_back(value_string("hello world"));
paramList1.add(value_array(arrayData));
map<string, value> structData;
pair<string, value> member("the_integer", value_int(9));
structData.insert(member);
paramList1.add(value_struct(structData));
paramList1.add(value_nil());
paramList1.add(value_i8((xmlrpc_int64)UINT_MAX + 1));
buildParamListWithAdd(&paramList1, timeFuture);
TEST(paramList1.size() == 11);
TEST(paramList1.getInt(0) == 7);
TEST(paramList1.getInt(0, 7) == 7);
TEST(paramList1.getInt(0, -5, 7) == 7);
TEST(paramList1.getBoolean(1) == true);
TEST(paramList1.getDouble(2) == 3.14);
TEST(paramList1.getDouble(2, 1) == 3.14);
TEST(paramList1.getDouble(2, 1, 4) == 3.14);
TEST(paramList1.getDatetime_sec(3) == 0);
TEST(paramList1.getDatetime_sec(3, paramList::TC_ANY) == timeZero);
TEST(paramList1.getDatetime_sec(3, paramList::TC_NO_FUTURE)
== timeZero);
TEST(paramList1.getDatetime_sec(4, paramList::TC_NO_PAST)
== timeFuture);
TEST(paramList1.getString(5) == "hello world");
TEST(paramList1.getBytestring(6)[0] == 0x10);
TEST(paramList1.getArray(7).size() == 3);
TEST(paramList1.getArray(7, 3).size() == 3);
TEST(paramList1.getArray(7, 1, 3).size() == 3);
paramList1.getStruct(8)["the_integer"];
paramList1.getNil(9);
TEST(paramList1.getI8(10) == (xmlrpc_int64)UINT_MAX + 1);
paramList1.verifyEnd(11);
verifyParamList(paramList1, timeFuture);
paramList paramList2(5);
TEST(paramList2.size() == 0);
paramList2.addc(7);
paramList2.addc(true).addc(3.14);
TEST(paramList2.size() == 3);
TEST(paramList2.getInt(0) == 7);
TEST(paramList2.getBoolean(1) == true);
TEST(paramList2.getDouble(2) == 3.14);
}
};
@@ -604,13 +353,13 @@ main(int argc, char**) {
try {
// Add your test suites here.
base64TestSuite().run(0);
xmlTestSuite().run(0);
valueTestSuite().run(0);
paramListTestSuite().run(0);
registryTestSuite().run(0);
serverAbyssTestSuite().run(0);
#ifndef WIN32
serverPstreamTestSuite().run(0);
#endif
clientTestSuite().run(0);
testXmlRpcCpp();
+80 -1
View File
@@ -32,10 +32,12 @@ using namespace std;
namespace {
class sampleAddMethod : public method {
public:
sampleAddMethod() {
this->_signature = "ii";
this->_signature = "i:ii";
this->_help = "This method adds two integers together";
}
void
@@ -78,7 +80,15 @@ public:
class clientXmlTransport_direct : public clientXmlTransport {
/*----------------------------------------------------------------------------
Though this fills the shoes of a client XML transport, it's really a
simulation, because it doesn't transport anything anywhere. The call()
method, which is supposed to transport a call to a server and then
transport the response back, actually just executes the method itself.
Part of a carriage parameter for a transport of this class is the handle
of a method registry, so the transport knows how to execute the method.
-----------------------------------------------------------------------------*/
public:
void
call(xmlrpc_c::carriageParm * const carriageParmP,
@@ -158,6 +168,64 @@ public:
class MyRpc : public rpc {
public:
MyRpc(std::string const methodName,
paramList const& paramList) :
rpc(methodName, paramList) {}
void
progress(struct xmlrpc_progress_data const& data) const {
// The way the tests are currently written, this never actually
// runs; we're just testing for ability to compile.
cout << "Progress of " << this << ": "
<< data.call.total << " "
<< data.call.now << " "
<< data.response.total << " "
<< data.response.now
<< endl;
}
};
class clientDerivedRpcTestSuite : public testSuite {
/*----------------------------------------------------------------------------
The object of this class tests the ability to derive a class
from xmlrpc_c::rpc in order to override certain methods.
-----------------------------------------------------------------------------*/
public:
virtual string suiteName() {
return "clientDerivedRpcTestSuite";
}
virtual void runtests(unsigned int const ) {
registry myRegistry;
myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod));
carriageParm_direct carriageParmDirect(&myRegistry);
clientXmlTransport_direct transportDirect;
client_xml clientDirect(&transportDirect);
paramList paramListSampleAdd;
paramListSampleAdd.add(value_int(5));
paramListSampleAdd.add(value_int(7));
{
/* Test a successful RPC */
rpcPtr rpcSampleAddP(new MyRpc("sample.add", paramListSampleAdd));
rpcSampleAddP->call(&clientDirect, &carriageParmDirect);
TEST(rpcSampleAddP->isFinished());
TEST(rpcSampleAddP->isSuccessful());
value_int const resultDirect(rpcSampleAddP->getResult());
TEST(static_cast<int>(resultDirect) == 12);
}
}
};
class clientDirectTestSuite : public testSuite {
/*----------------------------------------------------------------------------
The object of this class tests the client facilities by using a
@@ -241,6 +309,8 @@ public:
// Same as above
clientDirectAsyncTestSuite().run(indentation+1);
clientDerivedRpcTestSuite().run(indentation+1);
}
};
@@ -263,6 +333,7 @@ public:
.network_interface("eth0")
.no_ssl_verifypeer(true)
.no_ssl_verifyhost(true)
.dont_advertise(true)
.user_agent("my user agent")
.ssl_cert("/etc/sslcert")
.sslcerttype("PEM")
@@ -278,6 +349,10 @@ public:
.randomfile("/dev/random")
.egdsocket("/tmp/egdsocket")
.ssl_cipher_list("RC4-SHA:DEFAULT")
.proxy("example.com")
.proxy_port(8080)
.proxy_userpwd("password")
.proxy_type(XMLRPC_HTTPPROXY_SOCKS5)
);
clientXmlTransport_curl transport5(
@@ -831,6 +906,10 @@ public:
} // unnamed namespace
string
clientTestSuite::suiteName() {
return "clientTestSuite";
+453
View File
@@ -0,0 +1,453 @@
#include <string>
#include <iostream>
#include <iomanip>
#include <vector>
#include <sstream>
#include <memory>
#include <cstring>
#include <time.h>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
#include "transport_config.h"
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/oldcppwrapper.hpp"
#include "xmlrpc-c/registry.hpp"
#include "c_util.h"
#include "tools.hpp"
#include "value.hpp"
using namespace xmlrpc_c;
using namespace std;
namespace {
class intTestSuite : public testSuite {
public:
virtual string suiteName() {
return "intTestSuite";
}
virtual void runtests(unsigned int const) {
value_int int1(7);
TEST(static_cast<int>(int1) == 7);
value_int int2(-7);
TEST(static_cast<int>(int2) == -7);
value val1(int1);
TEST(val1.type() == value::TYPE_INT);
value_int int3(val1);
TEST(static_cast<int>(int3) == 7);
try {
value_int int4(value_double(3.7));
TEST_FAILED("invalid cast double-int suceeded");
} catch (error) {}
value const int1x(toValue(7));
TEST(int1x.type() == value::TYPE_INT);
TEST(static_cast<int>(value_int(int1x)) == 7);
int test1x;
fromValue(test1x, int1x);
TEST(test1x == 7);
}
};
class doubleTestSuite : public testSuite {
public:
virtual string suiteName() {
return "doubleTestSuite";
}
virtual void runtests(unsigned int const) {
value_double double1(3.14);
TEST(static_cast<double>(double1) == 3.14);
value val1(double1);
TEST(val1.type() == value::TYPE_DOUBLE);
value_double double2(val1);
TEST(static_cast<double>(double2) == 3.14);
try {
value_double double4(value_int(4));
TEST_FAILED("invalid cast int-double suceeded");
} catch (error) {}
value const double1x(toValue(3.14));
TEST(double1x.type() == value::TYPE_DOUBLE);
TEST(static_cast<double>(value_double(double1x)) == 3.14);
double test1x;
fromValue(test1x, double1x);
TEST(test1x == 3.14);
}
};
class booleanTestSuite : public testSuite {
public:
virtual string suiteName() {
return "booleanTestSuite";
}
virtual void runtests(unsigned int const) {
value_boolean boolean1(true);
TEST(static_cast<bool>(boolean1) == true);
value_boolean boolean2(false);
TEST(static_cast<bool>(boolean2) == false);
value val1(boolean1);
TEST(val1.type() == value::TYPE_BOOLEAN);
value_boolean boolean3(val1);
TEST(static_cast<bool>(boolean3) == true);
try {
value_boolean boolean4(value_int(4));
TEST_FAILED("invalid cast int-boolean suceeded");
} catch (error) {}
value const boolean1x(toValue(true));
TEST(boolean1x.type() == value::TYPE_BOOLEAN);
TEST(static_cast<bool>(value_boolean(boolean1x)) == true);
bool test1x;
fromValue(test1x, boolean1x);
TEST(test1x == true);
}
};
#if XMLRPC_HAVE_TIMEVAL
static struct timeval
makeTv(time_t const secs,
unsigned int const usecs) {
struct timeval retval;
retval.tv_sec = secs;
retval.tv_usec = usecs;
return retval;
}
static bool
tvIsEqual(struct timeval const comparand,
struct timeval const comparator) {
return
comparand.tv_sec == comparator.tv_sec &&
comparand.tv_usec == comparator.tv_usec;
}
#endif
#if XMLRPC_HAVE_TIMESPEC
static struct timespec
makeTs(time_t const secs,
unsigned int const usecs) {
struct timespec retval;
retval.tv_sec = secs;
retval.tv_nsec = usecs * 1000;
return retval;
}
static bool
tsIsEqual(struct timespec const comparand,
struct timespec const comparator) {
return
comparand.tv_sec == comparator.tv_sec &&
comparand.tv_nsec == comparator.tv_nsec;
}
#endif
class datetimeTestSuite : public testSuite {
public:
virtual string suiteName() {
return "datetimeTestSuite";
}
virtual void runtests(unsigned int const) {
time_t const testTime(900684535);
value_datetime datetime1("19980717T14:08:55");
TEST(static_cast<time_t>(datetime1) == testTime);
value_datetime datetime2(testTime);
TEST(static_cast<time_t>(datetime2) == testTime);
value val1(datetime1);
TEST(val1.type() == value::TYPE_DATETIME);
value_datetime datetime3(val1);
TEST(static_cast<time_t>(datetime3) == testTime);
#if XMLRPC_HAVE_TIMEVAL
struct timeval const testTimeTv(makeTv(testTime, 0));
value_datetime datetime4(testTimeTv);
TEST(static_cast<time_t>(datetime4) == testTime);
TEST(tvIsEqual(static_cast<timeval>(datetime4), testTimeTv));
#endif
#if XMLRPC_HAVE_TIMESPEC
struct timespec const testTimeTs(makeTs(testTime, 0));
value_datetime datetime5(testTimeTs);
TEST(static_cast<time_t>(datetime5) == testTime);
TEST(tsIsEqual(static_cast<timespec>(datetime5), testTimeTs));
#endif
try {
value_datetime datetime4(value_int(4));
TEST_FAILED("invalid cast int-datetime suceeded");
} catch (error) {}
}
};
class stringTestSuite : public testSuite {
public:
virtual string suiteName() {
return "stringTestSuite";
}
virtual void runtests(unsigned int const) {
value_string string1("hello world");
TEST(static_cast<string>(string1) == "hello world");
value_string string2("embedded\0null");
TEST(static_cast<string>(string2) == "embedded\0null");
value val1(string1);
TEST(val1.type() == value::TYPE_STRING);
value_string string3(val1);
TEST(static_cast<string>(string3) == "hello world");
try {
value_string string4(value_int(4));
TEST_FAILED("invalid cast int-string succeeded");
} catch (error) {}
value_string string5("hello world", value_string::nlCode_all);
TEST(static_cast<string>(string5) == "hello world");
value_string string6("hello\nthere\rworld\r\n\n",
value_string::nlCode_all);
TEST(static_cast<string>(string6) == "hello\nthere\nworld\n\n");
TEST(string6.crlfValue() == "hello\r\nthere\r\nworld\r\n\r\n");
value_string string7("hello\nthere\rworld\r\n\n",
value_string::nlCode_lf);
TEST(static_cast<string>(string7) == "hello\nthere\rworld\r\n\n");
value const string1x(toValue("hello world"));
TEST(string1x.type() == value::TYPE_STRING);
TEST(static_cast<string>(value_string(string1x)) == "hello world");
string test1x;
fromValue(test1x, string1x);
TEST(test1x == "hello world");
value const string2x(toValue(string("hello world")));
TEST(string2x.type() == value::TYPE_STRING);
TEST(static_cast<string>(value_string(string2x)) == "hello world");
}
};
class bytestringTestSuite : public testSuite {
public:
virtual string suiteName() {
return "bytestringTestSuite";
}
virtual void runtests(unsigned int const) {
unsigned char bytestringArray[] = {0x10, 0x11, 0x12, 0x13, 0x14};
cbytestring
bytestringData(&bytestringArray[0], &bytestringArray[4]);
value_bytestring bytestring1(bytestringData);
cbytestring const dataReadBack1(bytestring1.vectorUcharValue());
TEST(dataReadBack1 == bytestringData);
value val1(bytestring1);
TEST(val1.type() == value::TYPE_BYTESTRING);
value_bytestring bytestring2(val1);
vector<unsigned char> const dataReadBack2(
bytestring2.vectorUcharValue());
TEST(dataReadBack2 == bytestringData);
try {
value_bytestring bytestring4(value_int(4));
TEST_FAILED("invalid cast int-bytestring suceeded");
} catch (error) {}
value const bytestring1x(toValue(bytestringData));
TEST(bytestring1x.type() == value::TYPE_BYTESTRING);
vector<unsigned char> const dataReadBack1x(
value_bytestring(bytestring1x).vectorUcharValue());
TEST(dataReadBack1x == bytestringData);
vector<unsigned char> test1x;
fromValue(test1x, bytestring1x);
TEST(test1x == bytestringData);
}
};
class nilTestSuite : public testSuite {
public:
virtual string suiteName() {
return "nilTestSuite";
}
virtual void runtests(unsigned int const) {
value_nil nil1;
value val1(nil1);
TEST(val1.type() == value::TYPE_NIL);
value_nil nil2(val1);
try {
value_nil nil4(value_int(4));
TEST_FAILED("invalid cast int-nil suceeded");
} catch (error) {}
}
};
class i8TestSuite : public testSuite {
public:
virtual string suiteName() {
return "i8TestSuite";
}
virtual void runtests(unsigned int const) {
value_i8 int1(7);
TEST(static_cast<xmlrpc_int64>(int1) == 7);
value_i8 int2(-7);
TEST(static_cast<xmlrpc_int64>(int2) == -7);
value_i8 int5(1ull << 40);
TEST(static_cast<xmlrpc_int64>(int5) == (1ull << 40));
value val1(int1);
TEST(val1.type() == value::TYPE_I8);
value_i8 int3(val1);
TEST(static_cast<xmlrpc_int64>(int3) == 7);
try {
value_i8 int4(value_double(3.7));
TEST_FAILED("invalid cast double-i8 suceeded");
} catch (error) {}
}
};
class structTestSuite : public testSuite {
public:
virtual string suiteName() {
return "structTestSuite";
}
virtual void runtests(unsigned int const) {
cstruct structData;
pair<string, value> member("the_integer", value_int(9));
structData.insert(member);
value_struct struct1(structData);
map<string, value> dataReadBack(struct1);
TEST(static_cast<int>(value_int(dataReadBack["the_integer"])) == 9);
value val1(struct1);
TEST(val1.type() == value::TYPE_STRUCT);
value_struct struct2(val1);
try {
value_struct struct4(value_int(4));
TEST_FAILED("invalid cast int-struct suceeded");
} catch (error) {}
map<string, int> structDatax;
structDatax["one"] = 1;
structDatax["two"] = 2;
value const struct5(toValue(structDatax));
TEST(struct5.type() == value::TYPE_STRUCT);
map<string, value> dataReadBackx;
dataReadBackx = value_struct(struct5);
TEST(static_cast<int>(value_int(dataReadBackx["two"])) == 2);
map<string, int> test5x;
fromValue(test5x, struct5);
TEST(test5x["two"] == 2);
}
};
class arrayTestSuite : public testSuite {
public:
virtual string suiteName() {
return "arrayTestSuite";
}
virtual void runtests(unsigned int const) {
carray arrayData;
arrayData.push_back(value_int(7));
arrayData.push_back(value_double(2.78));
arrayData.push_back(value_string("hello world"));
value_array array1(arrayData);
TEST(array1.size() == 3);
vector<value> dataReadBack1(array1.vectorValueValue());
TEST(dataReadBack1[0].type() == value::TYPE_INT);
TEST(static_cast<int>(value_int(dataReadBack1[0])) == 7);
TEST(dataReadBack1[1].type() == value::TYPE_DOUBLE);
TEST(static_cast<double>(value_double(dataReadBack1[1])) == 2.78);
TEST(dataReadBack1[2].type() == value::TYPE_STRING);
TEST(static_cast<string>(value_string(dataReadBack1[2])) ==
"hello world");
value val1(array1);
TEST(val1.type() == value::TYPE_ARRAY);
value_array array2(val1);
TEST(array2.size() == 3);
try {
value_array array4(value_int(4));
TEST_FAILED("invalid cast int-array suceeded");
} catch (error) {}
int const arrayDatax[] = {7, 4};
value const array5(
arrayValueArray(arrayDatax, ARRAY_SIZE(arrayDatax)));
TEST(array5.type() == value::TYPE_ARRAY);
TEST(value_array(array5).size() == 2);
vector<value> dataReadBackx(value_array(array5).vectorValueValue());
TEST(dataReadBackx.size() == 2);
TEST(static_cast<int>(value_int(dataReadBackx[0])) == 7);
vector<int> test5x;
fromValue(test5x, array5);
TEST(test5x[1] == 4);
vector<string> arrayDataVec;
arrayDataVec.push_back("hello world");
value const array6(toValue(arrayDataVec));
TEST(array6.type() == value::TYPE_ARRAY);
TEST(value_array(array6).size() == 1);
}
};
} // unnamed namespace
string
valueTestSuite::suiteName() {
return "valueTestSuite";
}
void
valueTestSuite::runtests(unsigned int const indentation) {
intTestSuite().run(indentation+1);
doubleTestSuite().run(indentation+1);
booleanTestSuite().run(indentation+1);
datetimeTestSuite().run(indentation+1);
stringTestSuite().run(indentation+1);
bytestringTestSuite().run(indentation+1);
nilTestSuite().run(indentation+1);
i8TestSuite().run(indentation+1);
structTestSuite().run(indentation+1);
arrayTestSuite().run(indentation+1);
}
+9
View File
@@ -0,0 +1,9 @@
#include "tools.hpp"
class valueTestSuite : public testSuite {
public:
virtual std::string suiteName();
virtual void runtests(unsigned int const indentation);
};
+100
View File
@@ -0,0 +1,100 @@
/*=============================================================================
xml
===============================================================================
Test the XML generator and parser C++ facilities of XML-RPC for C/C++.
=============================================================================*/
#include <string>
#include "xmlrpc-c/girerr.hpp"
using girerr::error;
using girerr::throwf;
#include "xmlrpc-c/base.hpp"
#include "xmlrpc-c/xml.hpp"
#include "tools.hpp"
#include "xml.hpp"
using namespace xmlrpc_c;
using namespace std;
namespace {
class callTestSuite : public testSuite {
public:
virtual string suiteName() {
return "callTestSuite";
}
virtual void runtests(unsigned int const) {
string callXml;
string const methodName0("myMethod");
paramList const paramList0;
xml::generateCall(methodName0, paramList(), &callXml);
string methodName;
paramList paramList;
xml::parseCall(callXml, &methodName, &paramList);
TEST(methodName == methodName0);
TEST(paramList.size() == paramList0.size());
}
};
class responseTestSuite : public testSuite {
public:
virtual string suiteName() {
return "responseTestSuite";
}
virtual void runtests(unsigned int const) {
string respXml;
rpcOutcome outcome0(value_int(7));
xml::generateResponse(outcome0, &respXml);
rpcOutcome outcome;
xml::parseResponse(respXml, &outcome);
TEST((int)value_int(outcome.getResult()) ==
(int)value_int(outcome0.getResult()));
value result;
xml::parseSuccessfulResponse(respXml, &result);
TEST((int)value_int(result) == (int)value_int(outcome0.getResult()));
}
};
} // unnamed namespace
string
xmlTestSuite::suiteName() {
return "XMLTestSuite";
}
void
xmlTestSuite::runtests(unsigned int const indentation) {
callTestSuite().run(indentation+1);
responseTestSuite().run(indentation+1);
}
+9
View File
@@ -0,0 +1,9 @@
#include "tools.hpp"
class xmlTestSuite : public testSuite {
public:
virtual std::string suiteName();
virtual void runtests(unsigned int const indentation);
};
+208 -24
View File
@@ -64,6 +64,22 @@ public:
this->valueP = xmlrpc_datetime_new_sec(&env.env_c, cppvalue);
throwIfError(env);
}
#if XMLRPC_HAVE_TIMEVAL
cDatetimeValueWrapper(struct timeval const cppvalue) {
env_wrap env;
this->valueP = xmlrpc_datetime_new_timeval(&env.env_c, cppvalue);
throwIfError(env);
}
#endif
#if XMLRPC_HAVE_TIMESPEC
cDatetimeValueWrapper(struct timespec const cppvalue) {
env_wrap env;
this->valueP = xmlrpc_datetime_new_timespec(&env.env_c, cppvalue);
throwIfError(env);
}
#endif
~cDatetimeValueWrapper() {
xmlrpc_DECREF(this->valueP);
}
@@ -93,13 +109,13 @@ public:
namespace xmlrpc_c {
value::value() {
value::value() { // default constructor
this->cValueP = NULL;
}
value::value(xmlrpc_value * const valueP) { // default constructor
value::value(xmlrpc_value * const valueP) {
this->instantiate(valueP);
}
@@ -135,7 +151,7 @@ value::~value() {
bool
value::isInstantiated() const {
/*----------------------------------------------------------------------------
Return whether the value is actually a value, as opposed to a placeholder
Return whether the object is actually a value, as opposed to a placeholder
variable waiting to be assigned a value.
-----------------------------------------------------------------------------*/
return (this->cValueP != NULL);
@@ -143,6 +159,20 @@ value::isInstantiated() const {
void
value::validateInstantiated() const { // private
/*----------------------------------------------------------------------------
Throw an exception if the object is just a placeholder, rather than an
actual XML-RPC value.
-----------------------------------------------------------------------------*/
if (!this->cValueP)
throw(error("Reference to xmlrpc_c::value that has not been "
"instantiated. (xmlrpc_c::value::isInstantiated may be "
"useful in diagnosing)"));
}
void
value::instantiate(xmlrpc_value * const valueP) {
@@ -168,6 +198,8 @@ value::appendToCArray(xmlrpc_value * const arrayP) const {
/*----------------------------------------------------------------------------
Append this value to the C array 'arrayP'.
----------------------------------------------------------------------------*/
this->validateInstantiated();
env_wrap env;
xmlrpc_array_append_item(&env.env_c, arrayP, this->cValueP);
@@ -183,6 +215,8 @@ value::addToCStruct(xmlrpc_value * const structP,
/*----------------------------------------------------------------------------
Add this value to the C array 'arrayP' with key 'key'.
----------------------------------------------------------------------------*/
this->validateInstantiated();
env_wrap env;
xmlrpc_struct_set_value_n(&env.env_c, structP,
@@ -196,8 +230,11 @@ value::addToCStruct(xmlrpc_value * const structP,
value::type_t
value::type() const {
this->validateInstantiated();
/* You'd think we could just cast from xmlrpc_type to
value:type_t, but Gcc warns if we do that. So we have to do this
value::type_t, but Gcc warns if we do that. So we have to do this
even messier union nonsense.
*/
union {
@@ -212,6 +249,15 @@ value::type() const {
ostream& operator<<(ostream& out, value::type_t const& type) {
string typeName;
return out << string(xmlrpc_type_name((xmlrpc_type)type));
}
value_int::value_int(int const cppvalue) {
class cWrapper {
@@ -249,6 +295,8 @@ value_int::value_int(xmlrpc_c::value const baseValue) {
value_int::operator int() const {
this->validateInstantiated();
int retval;
env_wrap env;
@@ -260,6 +308,14 @@ value_int::operator int() const {
int
value_int::cvalue() const {
return static_cast<int>(*this);
}
value_double::value_double(double const cppvalue) {
class cWrapper {
@@ -295,6 +351,8 @@ value_double::value_double(xmlrpc_c::value const baseValue) {
value_double::operator double() const {
this->validateInstantiated();
double retval;
env_wrap env;
@@ -307,6 +365,14 @@ value_double::operator double() const {
double
value_double::cvalue() const {
return static_cast<double>(*this);
}
value_boolean::value_boolean(bool const cppvalue) {
class cWrapper {
@@ -331,8 +397,21 @@ value_boolean::value_boolean(bool const cppvalue) {
value_boolean::value_boolean(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_BOOLEAN)
throw(error("Not boolean type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_boolean::operator bool() const {
this->validateInstantiated();
xmlrpc_bool retval;
env_wrap env;
@@ -345,13 +424,10 @@ value_boolean::operator bool() const {
value_boolean::value_boolean(xmlrpc_c::value const baseValue) {
bool
value_boolean::cvalue() const {
if (baseValue.type() != xmlrpc_c::value::TYPE_BOOLEAN)
throw(error("Not boolean type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
return static_cast<bool>(*this);
}
@@ -393,7 +469,7 @@ value_datetime::value_datetime(time_t const cppvalue) {
#if XMLRPC_HAVE_TIMEVAL
value_datetime::value_datetime(struct timeval const& cppvalue) {
cDatetimeValueWrapper wrapper(cppvalue.tv_sec);
cDatetimeValueWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
@@ -404,7 +480,7 @@ value_datetime::value_datetime(struct timeval const& cppvalue) {
#if XMLRPC_HAVE_TIMESPEC
value_datetime::value_datetime(struct timespec const& cppvalue) {
cDatetimeValueWrapper wrapper(cppvalue.tv_sec);
cDatetimeValueWrapper wrapper(cppvalue);
this->instantiate(wrapper.valueP);
}
@@ -425,6 +501,8 @@ value_datetime::value_datetime(xmlrpc_c::value const baseValue) {
value_datetime::operator time_t() const {
this->validateInstantiated();
time_t retval;
env_wrap env;
@@ -436,6 +514,50 @@ value_datetime::operator time_t() const {
#if XMLRPC_HAVE_TIMEVAL
value_datetime::operator timeval() const {
this->validateInstantiated();
struct timeval retval;
env_wrap env;
xmlrpc_read_datetime_timeval(&env.env_c, this->cValueP, &retval);
throwIfError(env);
return retval;
}
#endif
#if XMLRPC_HAVE_TIMESPEC
value_datetime::operator timespec() const {
this->validateInstantiated();
struct timespec retval;
env_wrap env;
xmlrpc_read_datetime_timespec(&env.env_c, this->cValueP, &retval);
throwIfError(env);
return retval;
}
#endif
time_t
value_datetime::cvalue() const {
return static_cast<time_t>(*this);
}
class cNewStringWrapper {
public:
xmlrpc_value * valueP;
@@ -517,6 +639,8 @@ value_string::crlfValue() const {
}
};
this->validateInstantiated();
cWrapper wrapper(this->cValueP);
return string(wrapper.str, wrapper.length);
@@ -526,7 +650,7 @@ value_string::crlfValue() const {
value_string::operator string() const {
env_wrap env;
this->validateInstantiated();
cStringWrapper adapter(this->cValueP);
@@ -535,6 +659,14 @@ value_string::operator string() const {
std::string
value_string::cvalue() const {
return static_cast<std::string>(*this);
}
value_bytestring::value_bytestring(
vector<unsigned char> const& cppvalue) {
@@ -561,6 +693,17 @@ value_bytestring::value_bytestring(
value_bytestring::value_bytestring(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_BYTESTRING)
throw(error("Not byte string type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
vector<unsigned char>
value_bytestring::vectorUcharValue() const {
@@ -580,6 +723,8 @@ value_bytestring::vectorUcharValue() const {
}
};
this->validateInstantiated();
cWrapper wrapper(this->cValueP);
return vector<unsigned char>(&wrapper.contents[0],
@@ -588,9 +733,19 @@ value_bytestring::vectorUcharValue() const {
vector<unsigned char>
value_bytestring::cvalue() const {
return this->vectorUcharValue();
}
size_t
value_bytestring::length() const {
this->validateInstantiated();
env_wrap env;
size_t length;
@@ -602,17 +757,6 @@ value_bytestring::length() const {
value_bytestring::value_bytestring(xmlrpc_c::value const baseValue) {
if (baseValue.type() != xmlrpc_c::value::TYPE_BYTESTRING)
throw(error("Not byte string type. See type() method"));
else {
this->instantiate(baseValue.cValueP);
}
}
value_array::value_array(vector<xmlrpc_c::value> const& cppvalue) {
class cWrapper {
@@ -655,6 +799,8 @@ value_array::value_array(xmlrpc_c::value const baseValue) {
vector<xmlrpc_c::value>
value_array::vectorValueValue() const {
this->validateInstantiated();
env_wrap env;
unsigned int arraySize;
@@ -693,9 +839,19 @@ value_array::vectorValueValue() const {
vector<xmlrpc_c::value>
value_array::cvalue() const {
return this->vectorValueValue();
}
size_t
value_array::size() const {
this->validateInstantiated();
env_wrap env;
unsigned int arraySize;
@@ -752,6 +908,8 @@ value_struct::value_struct(xmlrpc_c::value const baseValue) {
value_struct::operator map<string, xmlrpc_c::value>() const {
this->validateInstantiated();
env_wrap env;
unsigned int structSize;
@@ -796,6 +954,14 @@ value_struct::operator map<string, xmlrpc_c::value>() const {
map<string, xmlrpc_c::value>
value_struct::cvalue() const {
return static_cast<map<string, xmlrpc_c::value> >(*this);
}
value_nil::value_nil() {
class cWrapper {
@@ -831,6 +997,14 @@ value_nil::value_nil(xmlrpc_c::value const baseValue) {
void *
value_nil::cvalue() const {
return NULL;
}
value_i8::value_i8(xmlrpc_int64 const cppvalue) {
class cWrapper {
@@ -868,6 +1042,8 @@ value_i8::value_i8(xmlrpc_c::value const baseValue) {
value_i8::operator xmlrpc_int64() const {
this->validateInstantiated();
xmlrpc_int64 retval;
env_wrap env;
@@ -879,4 +1055,12 @@ value_i8::operator xmlrpc_int64() const {
xmlrpc_int64
value_i8::cvalue() const {
return static_cast<xmlrpc_int64>(*this);
}
} // namespace
+142 -13
View File
@@ -21,14 +21,38 @@ class cValueWrapper {
/*----------------------------------------------------------------------------
Use an object of this class to set up to remove a reference to an
xmlrpc_value object (a C object with manual reference management)
at then end of a scope -- even if the scope ends with a throw.
at the end of a scope -- even if the scope ends with a throw.
-----------------------------------------------------------------------------*/
public:
xmlrpc_value * valueP;
xmlrpc_value * const valueP;
cValueWrapper(xmlrpc_value * valueP) : valueP(valueP) {}
~cValueWrapper() { xmlrpc_DECREF(valueP); }
};
class cStringWrapper {
public:
const char * const cString;
cStringWrapper(const char * const cString) : cString(cString) {}
~cStringWrapper() { xmlrpc_strfree(cString); }
};
class memblockWrapper {
xmlrpc_mem_block * const memblockP;
public:
memblockWrapper(xmlrpc_mem_block * const memblockP) :
memblockP(memblockP) {}
~memblockWrapper() {
XMLRPC_MEMBLOCK_FREE(char, memblockP);
}
};
xmlrpc_value *
cArrayFromParamList(paramList const& paramList) {
@@ -53,6 +77,42 @@ cArrayFromParamList(paramList const& paramList) {
return paramArrayP;
}
paramList const
paramListFromCArray(xmlrpc_value * const cArrayP) {
paramList retval;
env_wrap env;
unsigned int const nParam(xmlrpc_array_size(&env.env_c, cArrayP));
if (!env.env_c.fault_occurred) {
for (unsigned int i = 0;
i < nParam && !env.env_c.fault_occurred;
++i) {
xmlrpc_value * cParamP;
xmlrpc_array_read_item(&env.env_c, cArrayP, i, &cParamP);
if (!env.env_c.fault_occurred) {
cValueWrapper const paramAuto(cParamP);
// Causes xmlrpc_DECREF(cParamP) at end of scope
retval.add(cParamP);
}
}
}
if (env.env_c.fault_occurred)
throw(error(env.env_c.fault_string));
return retval;
}
} // namespace
@@ -71,17 +131,6 @@ generateCall(string const& methodName,
Use dialect 'dialect' of XML-RPC.
-----------------------------------------------------------------------------*/
class memblockWrapper {
xmlrpc_mem_block * const memblockP;
public:
memblockWrapper(xmlrpc_mem_block * const memblockP) :
memblockP(memblockP) {}
~memblockWrapper() {
XMLRPC_MEMBLOCK_FREE(char, memblockP);
}
};
xmlrpc_mem_block * callXmlMP;
env_wrap env;
@@ -117,6 +166,86 @@ generateCall(string const& methodName,
void
parseCall(string const& callXml,
string * const methodNameP,
paramList * const paramListP) {
env_wrap env;
const char * c_methodName;
xmlrpc_value * c_paramArrayP;
xmlrpc_parse_call(&env.env_c, callXml.c_str(), callXml.size(),
&c_methodName, &c_paramArrayP);
if (env.env_c.fault_occurred)
throw(error(env.env_c.fault_string));
else {
cValueWrapper const paramListAuto(c_paramArrayP);
// Causes XMLRPC_decref(c_paramArrayP) at end of scope
cStringWrapper const methodNameAuto(c_methodName);
// Causes xmlrpc_strfree(c_methodName) at end of scope
*paramListP = paramListFromCArray(c_paramArrayP);
*methodNameP = string(c_methodName);
}
}
void
generateResponse(rpcOutcome const& outcome,
xmlrpc_dialect const dialect,
string * const respXmlP) {
/*----------------------------------------------------------------------------
Generate the XML for an XML-RPC resp, given the RPC outcome.
Use dialect 'dialect' of XML-RPC.
-----------------------------------------------------------------------------*/
xmlrpc_mem_block * respXmlMP;
env_wrap env;
respXmlMP = XMLRPC_MEMBLOCK_NEW(char, &env.env_c, 0);
if (!env.env_c.fault_occurred) {
memblockWrapper respXmlAuto(respXmlMP);
// Makes respXmlMP get freed at end of scope
if (outcome.succeeded()) {
cValueWrapper cResult(outcome.getResult().cValue());
xmlrpc_serialize_response2(&env.env_c, respXmlMP,
cResult.valueP, dialect);
*respXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, respXmlMP),
XMLRPC_MEMBLOCK_SIZE(char, respXmlMP));
} else {
env_wrap cFault;
xmlrpc_env_set_fault(&cFault.env_c, outcome.getFault().getCode(),
outcome.getFault().getDescription().c_str());
xmlrpc_serialize_fault(&env.env_c, respXmlMP, &cFault.env_c);
*respXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, respXmlMP),
XMLRPC_MEMBLOCK_SIZE(char, respXmlMP));
}
}
if (env.env_c.fault_occurred)
throw(error(env.env_c.fault_string));
}
void
generateResponse(rpcOutcome const& outcome,
string * const respXmlP) {
generateResponse(outcome, xmlrpc_dialect_i8, respXmlP);
}
void
parseResponse(string const& responseXml,
rpcOutcome * const outcomeP) {