mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-23 20:51:58 +00:00
FS-2746 --resolve large xmlrpc update thanks garmt
This commit is contained in:
@@ -1 +0,0 @@
|
||||
cpptest
|
||||
+130
-49
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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 *>(®istry),
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "tools.hpp"
|
||||
|
||||
class base64TestSuite : public testSuite {
|
||||
|
||||
public:
|
||||
virtual std::string suiteName();
|
||||
virtual void runtests(unsigned int const indentation);
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(¶mList1, 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();
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "tools.hpp"
|
||||
|
||||
class valueTestSuite : public testSuite {
|
||||
|
||||
public:
|
||||
virtual std::string suiteName();
|
||||
virtual void runtests(unsigned int const indentation);
|
||||
};
|
||||
|
||||
@@ -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, ¶mList);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user