2008-05-23 20:56:24 +00:00
|
|
|
/*=============================================================================
|
|
|
|
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.
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
By Bryan Henderson 09.03.22
|
2008-05-23 20:56:24 +00:00
|
|
|
|
|
|
|
Contributed to the public domain by its author.
|
|
|
|
=============================================================================*/
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
#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>
|
2008-05-23 20:56:24 +00:00
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
#include "xmlrpc-c/girerr.hpp"
|
|
|
|
using girerr::throwf;
|
|
|
|
|
|
|
|
#include "xmlrpc-c/server_pstream.hpp"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
namespace xmlrpc_c {
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
struct serverPstream::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;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
serverPstream::constrOpt_impl::constrOpt_impl() {
|
|
|
|
|
|
|
|
this->present.socketFd = false;
|
|
|
|
this->present.registryP = false;
|
|
|
|
this->present.registryPtr = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
serverPstream::constrOpt::constrOpt() {
|
2008-05-23 20:56:24 +00:00
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
this->implP = new serverPstream::constrOpt_impl();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
serverPstream::constrOpt::~constrOpt() {
|
|
|
|
|
|
|
|
delete(this->implP);
|
2008-05-23 20:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream::constrOpt & \
|
|
|
|
serverPstream::constrOpt::OPTION_NAME(TYPE const& arg) { \
|
|
|
|
this->implP->value.OPTION_NAME = arg; \
|
|
|
|
this->implP->present.OPTION_NAME = true; \
|
2008-05-23 20:56:24 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
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() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-05-23 20:56:24 +00:00
|
|
|
void
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream_impl::establishRegistry(
|
|
|
|
serverPstream::constrOpt_impl const& opt) {
|
2008-05-23 20:56:24 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
|
serverPstream::shutdown is a derived class of registry::shutdown. You give
|
|
|
|
it to the registry object to allow XML-RPC method 'system.shutdown' to
|
|
|
|
-----------------------------------------------------------------------------*/
|
2008-05-23 20:56:24 +00:00
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream::shutdown::shutdown(serverPstream * const serverPstreamP) :
|
|
|
|
serverPstreamP(serverPstreamP) {}
|
2008-05-23 20:56:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream::shutdown::~shutdown() {}
|
2008-05-23 20:56:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
void
|
|
|
|
serverPstream::shutdown::doit(string const&,
|
|
|
|
void * const) const {
|
2008-05-23 20:56:24 +00:00
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
this->serverPstreamP->terminate();
|
2008-05-23 20:56:24 +00:00
|
|
|
}
|
2012-10-13 11:37:25 -05:00
|
|
|
/*---------------------------------------------------------------------------*/
|
2008-05-23 20:56:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream::serverPstream(constrOpt const& opt) {
|
2008-05-23 20:56:24 +00:00
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
this->implP = new serverPstream_impl(*opt.implP);
|
2008-05-23 20:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream::~serverPstream() {
|
2008-05-23 20:56:24 +00:00
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
delete(this->implP);
|
|
|
|
}
|
2008-05-23 20:56:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-05-23 20:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream::runSerial() {
|
2008-05-23 20:56:24 +00:00
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
int const interrupt(0); // Never interrupt
|
|
|
|
|
|
|
|
this->runSerial(&interrupt);
|
2008-05-23 20:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2012-10-13 11:37:25 -05:00
|
|
|
serverPstream::terminate() {
|
2008-05-23 20:56:24 +00:00
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
this->implP->termRequested = true;
|
2008-05-23 20:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-10-13 11:37:25 -05:00
|
|
|
callInfo_serverPstream::callInfo_serverPstream(
|
|
|
|
serverPstream * const serverP,
|
|
|
|
struct sockaddr const clientAddr,
|
|
|
|
socklen_t const clientAddrSize) :
|
|
|
|
|
|
|
|
serverP(serverP),
|
|
|
|
clientAddr(clientAddr),
|
|
|
|
clientAddrSize(clientAddrSize)
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-05-23 20:56:24 +00:00
|
|
|
} // namespace
|