v01
This commit is contained in:
1041
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/Doxyfile
vendored
Normal file
1041
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/Doxyfile
vendored
Normal file
File diff suppressed because it is too large
Load Diff
472
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcClient.cpp
vendored
Normal file
472
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcClient.cpp
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
|
||||
#include "xmlrpcpp/XmlRpcClient.h"
|
||||
|
||||
#include "xmlrpcpp/XmlRpcSocket.h"
|
||||
#include "xmlrpcpp/XmlRpc.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef _WINDOWS
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
|
||||
using namespace XmlRpc;
|
||||
|
||||
// Static data
|
||||
const char XmlRpcClient::REQUEST_BEGIN[] =
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<methodCall><methodName>";
|
||||
const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
|
||||
const char XmlRpcClient::PARAMS_TAG[] = "<params>";
|
||||
const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
|
||||
const char XmlRpcClient::PARAM_TAG[] = "<param>";
|
||||
const char XmlRpcClient::PARAM_ETAG[] = "</param>";
|
||||
const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
|
||||
const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
|
||||
const char XmlRpcClient::FAULT_TAG[] = "<fault>";
|
||||
|
||||
|
||||
|
||||
XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
|
||||
{
|
||||
XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
|
||||
|
||||
_host = host;
|
||||
_port = port;
|
||||
if (uri)
|
||||
_uri = uri;
|
||||
else
|
||||
_uri = "/RPC2";
|
||||
_connectionState = NO_CONNECTION;
|
||||
_executing = false;
|
||||
_eof = false;
|
||||
|
||||
// Default to keeping the connection open until an explicit close is done
|
||||
setKeepOpen();
|
||||
}
|
||||
|
||||
|
||||
XmlRpcClient::~XmlRpcClient()
|
||||
{
|
||||
this->close();
|
||||
}
|
||||
|
||||
// Close the owned fd
|
||||
void
|
||||
XmlRpcClient::close()
|
||||
{
|
||||
XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
|
||||
_connectionState = NO_CONNECTION;
|
||||
_disp.exit();
|
||||
_disp.removeSource(this);
|
||||
XmlRpcSource::close();
|
||||
}
|
||||
|
||||
|
||||
// Clear the referenced flag even if exceptions or errors occur.
|
||||
struct ClearFlagOnExit {
|
||||
ClearFlagOnExit(bool& flag) : _flag(flag) {}
|
||||
~ClearFlagOnExit() { _flag = false; }
|
||||
bool& _flag;
|
||||
};
|
||||
|
||||
// Execute the named procedure on the remote server.
|
||||
// Params should be an array of the arguments for the method.
|
||||
// Returns true if the request was sent and a result received (although the result
|
||||
// might be a fault).
|
||||
bool
|
||||
XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
|
||||
{
|
||||
XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
|
||||
|
||||
// This is not a thread-safe operation, if you want to do multithreading, use separate
|
||||
// clients for each thread. If you want to protect yourself from multiple threads
|
||||
// accessing the same client, replace this code with a real mutex.
|
||||
if (_executing)
|
||||
return false;
|
||||
|
||||
_executing = true;
|
||||
ClearFlagOnExit cf(_executing);
|
||||
|
||||
_sendAttempts = 0;
|
||||
_isFault = false;
|
||||
|
||||
if ( ! setupConnection())
|
||||
return false;
|
||||
|
||||
if ( ! generateRequest(method, params))
|
||||
return false;
|
||||
|
||||
result.clear();
|
||||
double msTime = -1.0; // Process until exit is called
|
||||
_disp.work(msTime);
|
||||
|
||||
if (_connectionState != IDLE || ! parseResponse(result))
|
||||
return false;
|
||||
|
||||
XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
|
||||
_response = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Execute the named procedure on the remote server, non-blocking.
|
||||
// Params should be an array of the arguments for the method.
|
||||
// Returns true if the request was sent and a result received (although the result
|
||||
// might be a fault).
|
||||
bool
|
||||
XmlRpcClient::executeNonBlock(const char* method, XmlRpcValue const& params)
|
||||
{
|
||||
XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
|
||||
|
||||
// This is not a thread-safe operation, if you want to do multithreading, use separate
|
||||
// clients for each thread. If you want to protect yourself from multiple threads
|
||||
// accessing the same client, replace this code with a real mutex.
|
||||
if (_executing)
|
||||
return false;
|
||||
|
||||
_executing = true;
|
||||
ClearFlagOnExit cf(_executing);
|
||||
|
||||
_sendAttempts = 0;
|
||||
_isFault = false;
|
||||
|
||||
if ( ! setupConnection())
|
||||
return false;
|
||||
|
||||
if ( ! generateRequest(method, params))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
XmlRpcClient::executeCheckDone(XmlRpcValue& result)
|
||||
{
|
||||
result.clear();
|
||||
// Are we done yet?
|
||||
if (_connectionState != IDLE)
|
||||
return false;
|
||||
if (! parseResponse(result))
|
||||
{
|
||||
// Hopefully the caller can determine that parsing failed.
|
||||
}
|
||||
//XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
|
||||
_response = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
// XmlRpcSource interface implementation
|
||||
// Handle server responses. Called by the event dispatcher during execute.
|
||||
unsigned
|
||||
XmlRpcClient::handleEvent(unsigned eventType)
|
||||
{
|
||||
if (eventType == XmlRpcDispatch::Exception)
|
||||
{
|
||||
if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
|
||||
XmlRpcSocket::getErrorMsg().c_str());
|
||||
else
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
|
||||
_connectionState, XmlRpcSocket::getErrorMsg().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_connectionState == WRITE_REQUEST)
|
||||
if ( ! writeRequest()) return 0;
|
||||
|
||||
if (_connectionState == READ_HEADER)
|
||||
if ( ! readHeader()) return 0;
|
||||
|
||||
if (_connectionState == READ_RESPONSE)
|
||||
if ( ! readResponse()) return 0;
|
||||
|
||||
// This should probably always ask for Exception events too
|
||||
return (_connectionState == WRITE_REQUEST)
|
||||
? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
|
||||
}
|
||||
|
||||
|
||||
// Create the socket connection to the server if necessary
|
||||
bool
|
||||
XmlRpcClient::setupConnection()
|
||||
{
|
||||
// If an error occurred last time through, or if the server closed the connection, close our end
|
||||
if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
|
||||
close();
|
||||
|
||||
_eof = false;
|
||||
if (_connectionState == NO_CONNECTION)
|
||||
if (! doConnect())
|
||||
return false;
|
||||
|
||||
// Prepare to write the request
|
||||
_connectionState = WRITE_REQUEST;
|
||||
_bytesWritten = 0;
|
||||
|
||||
// Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
|
||||
_disp.removeSource(this); // Make sure nothing is left over
|
||||
_disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Connect to the xmlrpc server
|
||||
bool
|
||||
XmlRpcClient::doConnect()
|
||||
{
|
||||
int fd = XmlRpcSocket::socket();
|
||||
if (fd < 0)
|
||||
{
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
|
||||
this->setfd(fd);
|
||||
|
||||
// Don't block on connect/reads/writes
|
||||
if ( ! XmlRpcSocket::setNonBlocking(fd))
|
||||
{
|
||||
this->close();
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! XmlRpcSocket::connect(fd, _host, _port))
|
||||
{
|
||||
this->close();
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Encode the request to call the specified method with the specified parameters into xml
|
||||
bool
|
||||
XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
|
||||
{
|
||||
std::string body = REQUEST_BEGIN;
|
||||
body += methodName;
|
||||
body += REQUEST_END_METHODNAME;
|
||||
|
||||
// If params is an array, each element is a separate parameter
|
||||
if (params.valid()) {
|
||||
body += PARAMS_TAG;
|
||||
if (params.getType() == XmlRpcValue::TypeArray)
|
||||
{
|
||||
for (int i=0; i<params.size(); ++i) {
|
||||
body += PARAM_TAG;
|
||||
body += params[i].toXml();
|
||||
body += PARAM_ETAG;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
body += PARAM_TAG;
|
||||
body += params.toXml();
|
||||
body += PARAM_ETAG;
|
||||
}
|
||||
|
||||
body += PARAMS_ETAG;
|
||||
}
|
||||
body += REQUEST_END;
|
||||
|
||||
std::string header = generateHeader(body);
|
||||
XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
|
||||
header.length(), body.length());
|
||||
|
||||
_request = header + body;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepend http headers
|
||||
std::string
|
||||
XmlRpcClient::generateHeader(std::string const& body)
|
||||
{
|
||||
std::string header =
|
||||
"POST " + _uri + " HTTP/1.1\r\n"
|
||||
"User-Agent: ";
|
||||
header += XMLRPC_VERSION;
|
||||
header += "\r\nHost: ";
|
||||
header += _host;
|
||||
|
||||
char buff[40];
|
||||
#ifdef _MSC_VER
|
||||
sprintf_s(buff,40,":%d\r\n", _port);
|
||||
#else
|
||||
sprintf(buff,":%d\r\n", _port);
|
||||
#endif
|
||||
|
||||
header += buff;
|
||||
header += "Content-Type: text/xml\r\nContent-length: ";
|
||||
|
||||
#ifdef _MSC_VER
|
||||
sprintf_s(buff,40,"%d\r\n\r\n", (int)body.size());
|
||||
#else
|
||||
sprintf(buff,"%d\r\n\r\n", (int)body.size());
|
||||
#endif
|
||||
|
||||
return header + buff;
|
||||
}
|
||||
|
||||
bool
|
||||
XmlRpcClient::writeRequest()
|
||||
{
|
||||
if (_bytesWritten == 0)
|
||||
XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
|
||||
|
||||
// Try to write the request
|
||||
if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
|
||||
|
||||
// Wait for the result
|
||||
if (_bytesWritten == int(_request.length())) {
|
||||
_header = "";
|
||||
_response = "";
|
||||
_connectionState = READ_HEADER;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Read the header from the response
|
||||
bool
|
||||
XmlRpcClient::readHeader()
|
||||
{
|
||||
// Read available data
|
||||
if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
|
||||
(_eof && _header.length() == 0)) {
|
||||
|
||||
// If we haven't read any data yet and this is a keep-alive connection, the server may
|
||||
// have timed out, so we try one more time.
|
||||
if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
|
||||
XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
|
||||
XmlRpcSource::close();
|
||||
_connectionState = NO_CONNECTION;
|
||||
_eof = false;
|
||||
return setupConnection();
|
||||
}
|
||||
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
|
||||
XmlRpcSocket::getErrorMsg().c_str(), getfd());
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
|
||||
|
||||
char *hp = (char*)_header.c_str(); // Start of header
|
||||
char *ep = hp + _header.length(); // End of string
|
||||
char *bp = 0; // Start of body
|
||||
char *lp = 0; // Start of content-length value
|
||||
|
||||
for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
|
||||
if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
|
||||
lp = cp + 16;
|
||||
else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
|
||||
bp = cp + 4;
|
||||
else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
|
||||
bp = cp + 2;
|
||||
}
|
||||
|
||||
// If we haven't gotten the entire header yet, return (keep reading)
|
||||
if (bp == 0) {
|
||||
if (_eof) // EOF in the middle of a response is an error
|
||||
{
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
|
||||
return false; // Close the connection
|
||||
}
|
||||
|
||||
return true; // Keep reading
|
||||
}
|
||||
|
||||
// Decode content length
|
||||
if (lp == 0) {
|
||||
XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
|
||||
return false; // We could try to figure it out by parsing as we read, but for now...
|
||||
}
|
||||
|
||||
_contentLength = atoi(lp);
|
||||
if (_contentLength <= 0) {
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
|
||||
|
||||
// Otherwise copy non-header data to response buffer and set state to read response.
|
||||
_response = bp;
|
||||
_header = ""; // should parse out any interesting bits from the header (connection, etc)...
|
||||
_connectionState = READ_RESPONSE;
|
||||
return true; // Continue monitoring this source
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
XmlRpcClient::readResponse()
|
||||
{
|
||||
// If we dont have the entire response yet, read available data
|
||||
if (int(_response.length()) < _contentLength) {
|
||||
if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) {
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we haven't gotten the entire _response yet, return (keep reading)
|
||||
if (int(_response.length()) < _contentLength) {
|
||||
if (_eof) {
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, parse and return the result
|
||||
XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
|
||||
XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
|
||||
|
||||
_connectionState = IDLE;
|
||||
|
||||
return false; // Stop monitoring this source (causes return from work)
|
||||
}
|
||||
|
||||
|
||||
// Convert the response xml into a result value
|
||||
bool
|
||||
XmlRpcClient::parseResponse(XmlRpcValue& result)
|
||||
{
|
||||
// Parse response xml into result
|
||||
int offset = 0;
|
||||
if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Expect either <params><param>... or <fault>...
|
||||
if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
|
||||
XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
|
||||
(XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
|
||||
{
|
||||
if ( ! result.fromXml(_response, &offset)) {
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
|
||||
_response = "";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
|
||||
_response = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
_response = "";
|
||||
return result.valid();
|
||||
}
|
||||
|
||||
231
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcDispatch.cpp
vendored
Normal file
231
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcDispatch.cpp
vendored
Normal file
@@ -0,0 +1,231 @@
|
||||
|
||||
#include "xmlrpcpp/XmlRpcDispatch.h"
|
||||
#include "xmlrpcpp/XmlRpcSource.h"
|
||||
#include "xmlrpcpp/XmlRpcUtil.h"
|
||||
|
||||
#include "ros/time.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/poll.h>
|
||||
|
||||
#if defined(_WINDOWS)
|
||||
# include <winsock2.h>
|
||||
static inline int poll( struct pollfd *pfd, int nfds, int timeout)
|
||||
{
|
||||
return WSAPoll(pfd, nfds, timeout);
|
||||
}
|
||||
|
||||
# define USE_FTIME
|
||||
# if defined(_MSC_VER)
|
||||
# define timeb _timeb
|
||||
# define ftime _ftime_s
|
||||
# endif
|
||||
#else
|
||||
# include <sys/time.h>
|
||||
#endif // _WINDOWS
|
||||
|
||||
|
||||
using namespace XmlRpc;
|
||||
|
||||
|
||||
XmlRpcDispatch::XmlRpcDispatch()
|
||||
{
|
||||
_endTime = -1.0;
|
||||
_doClear = false;
|
||||
_inWork = false;
|
||||
}
|
||||
|
||||
|
||||
XmlRpcDispatch::~XmlRpcDispatch()
|
||||
{
|
||||
}
|
||||
|
||||
// Monitor this source for the specified events and call its event handler
|
||||
// when the event occurs
|
||||
void
|
||||
XmlRpcDispatch::addSource(XmlRpcSource* source, unsigned mask)
|
||||
{
|
||||
_sources.push_back(MonitoredSource(source, mask));
|
||||
}
|
||||
|
||||
// Stop monitoring this source. Does not close the source.
|
||||
void
|
||||
XmlRpcDispatch::removeSource(XmlRpcSource* source)
|
||||
{
|
||||
for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
|
||||
if (it->getSource() == source)
|
||||
{
|
||||
_sources.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Modify the types of events to watch for on this source
|
||||
void
|
||||
XmlRpcDispatch::setSourceEvents(XmlRpcSource* source, unsigned eventMask)
|
||||
{
|
||||
for (SourceList::iterator it=_sources.begin(); it!=_sources.end(); ++it)
|
||||
if (it->getSource() == source)
|
||||
{
|
||||
it->getMask() = eventMask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Watch current set of sources and process events
|
||||
void
|
||||
XmlRpcDispatch::work(double timeout)
|
||||
{
|
||||
// Loosely based on `man select` > Correspondence between select() and poll() notifications
|
||||
// and cloudius-systems/osv#35, cloudius-systems/osv@b53d39a using poll to emulate select
|
||||
const unsigned POLLIN_REQ = POLLIN; // Request read
|
||||
const unsigned POLLIN_CHK = (POLLIN | POLLHUP | POLLERR); // Readable or connection lost
|
||||
const unsigned POLLOUT_REQ = POLLOUT; // Request write
|
||||
const unsigned POLLOUT_CHK = (POLLOUT | POLLERR); // Writable or connection lost
|
||||
const unsigned POLLEX_CHK = (POLLPRI | POLLNVAL); // Exception or invalid fd
|
||||
|
||||
// Compute end time
|
||||
_endTime = (timeout < 0.0) ? -1.0 : (getTime() + timeout);
|
||||
_doClear = false;
|
||||
_inWork = true;
|
||||
int timeout_ms = static_cast<int>(floor(timeout * 1000.));
|
||||
|
||||
// Only work while there is something to monitor
|
||||
while (_sources.size() > 0) {
|
||||
|
||||
// Construct the sets of descriptors we are interested in
|
||||
const unsigned source_cnt = _sources.size();
|
||||
pollfd fds[source_cnt];
|
||||
XmlRpcSource * sources[source_cnt];
|
||||
|
||||
SourceList::iterator it;
|
||||
std::size_t i = 0;
|
||||
for (it=_sources.begin(); it!=_sources.end(); ++it, ++i) {
|
||||
sources[i] = it->getSource();
|
||||
fds[i].fd = sources[i]->getfd();
|
||||
fds[i].revents = 0; // some platforms may not clear this in poll()
|
||||
fds[i].events = 0;
|
||||
if (it->getMask() & ReadableEvent) fds[i].events |= POLLIN_REQ;
|
||||
if (it->getMask() & WritableEvent) fds[i].events |= POLLOUT_REQ;
|
||||
}
|
||||
|
||||
// Check for events
|
||||
int nEvents = poll(fds, source_cnt, (timeout_ms < 0) ? -1 : timeout_ms);
|
||||
|
||||
if (nEvents < 0)
|
||||
{
|
||||
if(errno != EINTR)
|
||||
XmlRpcUtil::error("Error in XmlRpcDispatch::work: error in poll (%d).", nEvents);
|
||||
_inWork = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Process events
|
||||
for (i=0; i < source_cnt; ++i)
|
||||
{
|
||||
XmlRpcSource* src = sources[i];
|
||||
pollfd & pfd = fds[i];
|
||||
unsigned newMask = (unsigned) -1;
|
||||
// Only handle requested events to avoid being prematurely removed from dispatch
|
||||
bool readable = (pfd.events & POLLIN_REQ) == POLLIN_REQ;
|
||||
bool writable = (pfd.events & POLLOUT_REQ) == POLLOUT_REQ;
|
||||
if (readable && (pfd.revents & POLLIN_CHK))
|
||||
newMask &= src->handleEvent(ReadableEvent);
|
||||
if (writable && (pfd.revents & POLLOUT_CHK))
|
||||
newMask &= src->handleEvent(WritableEvent);
|
||||
if (pfd.revents & POLLEX_CHK)
|
||||
newMask &= src->handleEvent(Exception);
|
||||
|
||||
// Find the source iterator. It may have moved as a result of the way
|
||||
// that sources are removed and added in the call stack starting
|
||||
// from the handleEvent() calls above.
|
||||
SourceList::iterator thisIt;
|
||||
for (thisIt = _sources.begin(); thisIt != _sources.end(); thisIt++)
|
||||
{
|
||||
if(thisIt->getSource() == src)
|
||||
break;
|
||||
}
|
||||
if(thisIt == _sources.end())
|
||||
{
|
||||
XmlRpcUtil::error("Error in XmlRpcDispatch::work: couldn't find source iterator");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! newMask) {
|
||||
_sources.erase(thisIt); // Stop monitoring this one
|
||||
if ( ! src->getKeepOpen())
|
||||
src->close();
|
||||
} else if (newMask != (unsigned) -1) {
|
||||
thisIt->getMask() = newMask;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether to clear all sources
|
||||
if (_doClear)
|
||||
{
|
||||
SourceList closeList = _sources;
|
||||
_sources.clear();
|
||||
for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it) {
|
||||
XmlRpcSource *src = it->getSource();
|
||||
src->close();
|
||||
}
|
||||
|
||||
_doClear = false;
|
||||
}
|
||||
|
||||
// Check whether end time has passed
|
||||
if (0 <= _endTime && getTime() > _endTime)
|
||||
break;
|
||||
}
|
||||
|
||||
_inWork = false;
|
||||
}
|
||||
|
||||
|
||||
// Exit from work routine. Presumably this will be called from
|
||||
// one of the source event handlers.
|
||||
void
|
||||
XmlRpcDispatch::exit()
|
||||
{
|
||||
_endTime = 0.0; // Return from work asap
|
||||
}
|
||||
|
||||
// Clear all sources from the monitored sources list
|
||||
void
|
||||
XmlRpcDispatch::clear()
|
||||
{
|
||||
if (_inWork)
|
||||
_doClear = true; // Finish reporting current events before clearing
|
||||
else
|
||||
{
|
||||
SourceList closeList = _sources;
|
||||
_sources.clear();
|
||||
for (SourceList::iterator it=closeList.begin(); it!=closeList.end(); ++it)
|
||||
it->getSource()->close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
XmlRpcDispatch::getTime()
|
||||
{
|
||||
#ifdef USE_FTIME
|
||||
struct timeb tbuff;
|
||||
|
||||
ftime(&tbuff);
|
||||
return ((double) tbuff.time + ((double)tbuff.millitm / 1000.0) +
|
||||
((double) tbuff.timezone * 60));
|
||||
#else
|
||||
uint32_t sec, nsec;
|
||||
|
||||
ros::ros_steadytime(sec, nsec);
|
||||
return ((double)sec + (double)nsec / 1e9);
|
||||
#endif /* USE_FTIME */
|
||||
}
|
||||
|
||||
|
||||
290
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcServer.cpp
vendored
Normal file
290
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcServer.cpp
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
// this file modified by Morgan Quigley on 22 Apr 2008.
|
||||
// added features: server can be opened on port 0 and you can read back
|
||||
// what port the OS gave you
|
||||
|
||||
#include "xmlrpcpp/XmlRpcServer.h"
|
||||
#include "xmlrpcpp/XmlRpcServerConnection.h"
|
||||
#include "xmlrpcpp/XmlRpcServerMethod.h"
|
||||
#include "xmlrpcpp/XmlRpcSocket.h"
|
||||
#include "xmlrpcpp/XmlRpcUtil.h"
|
||||
#include "xmlrpcpp/XmlRpcException.h"
|
||||
|
||||
|
||||
using namespace XmlRpc;
|
||||
|
||||
|
||||
XmlRpcServer::XmlRpcServer()
|
||||
{
|
||||
_introspectionEnabled = false;
|
||||
_listMethods = 0;
|
||||
_methodHelp = 0;
|
||||
_port = 0;
|
||||
}
|
||||
|
||||
|
||||
XmlRpcServer::~XmlRpcServer()
|
||||
{
|
||||
this->shutdown();
|
||||
_methods.clear();
|
||||
delete _listMethods;
|
||||
delete _methodHelp;
|
||||
}
|
||||
|
||||
|
||||
// Add a command to the RPC server
|
||||
void
|
||||
XmlRpcServer::addMethod(XmlRpcServerMethod* method)
|
||||
{
|
||||
_methods[method->name()] = method;
|
||||
}
|
||||
|
||||
// Remove a command from the RPC server
|
||||
void
|
||||
XmlRpcServer::removeMethod(XmlRpcServerMethod* method)
|
||||
{
|
||||
MethodMap::iterator i = _methods.find(method->name());
|
||||
if (i != _methods.end())
|
||||
_methods.erase(i);
|
||||
}
|
||||
|
||||
// Remove a command from the RPC server by name
|
||||
void
|
||||
XmlRpcServer::removeMethod(const std::string& methodName)
|
||||
{
|
||||
MethodMap::iterator i = _methods.find(methodName);
|
||||
if (i != _methods.end())
|
||||
_methods.erase(i);
|
||||
}
|
||||
|
||||
|
||||
// Look up a method by name
|
||||
XmlRpcServerMethod*
|
||||
XmlRpcServer::findMethod(const std::string& name) const
|
||||
{
|
||||
MethodMap::const_iterator i = _methods.find(name);
|
||||
if (i == _methods.end())
|
||||
return 0;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
|
||||
// Create a socket, bind to the specified port, and
|
||||
// set it in listen mode to make it available for clients.
|
||||
bool
|
||||
XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
|
||||
{
|
||||
int fd = XmlRpcSocket::socket();
|
||||
if (fd < 0)
|
||||
{
|
||||
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
this->setfd(fd);
|
||||
|
||||
// Don't block on reads/writes
|
||||
if ( ! XmlRpcSocket::setNonBlocking(fd))
|
||||
{
|
||||
this->close();
|
||||
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow this port to be re-bound immediately so server re-starts are not delayed
|
||||
if ( ! XmlRpcSocket::setReuseAddr(fd))
|
||||
{
|
||||
this->close();
|
||||
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind to the specified port on the default interface
|
||||
if ( ! XmlRpcSocket::bind(fd, port))
|
||||
{
|
||||
this->close();
|
||||
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set in listening mode
|
||||
if ( ! XmlRpcSocket::listen(fd, backlog))
|
||||
{
|
||||
this->close();
|
||||
XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_port = XmlRpcSocket::get_port(fd);
|
||||
|
||||
XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", _port, fd);
|
||||
|
||||
// Notify the dispatcher to listen on this source when we are in work()
|
||||
_disp.addSource(this, XmlRpcDispatch::ReadableEvent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Process client requests for the specified time
|
||||
void
|
||||
XmlRpcServer::work(double msTime)
|
||||
{
|
||||
XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
|
||||
_disp.work(msTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Handle input on the server socket by accepting the connection
|
||||
// and reading the rpc request.
|
||||
unsigned
|
||||
XmlRpcServer::handleEvent(unsigned)
|
||||
{
|
||||
acceptConnection();
|
||||
return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd
|
||||
}
|
||||
|
||||
|
||||
// Accept a client connection request and create a connection to
|
||||
// handle method calls from the client.
|
||||
void
|
||||
XmlRpcServer::acceptConnection()
|
||||
{
|
||||
int s = XmlRpcSocket::accept(this->getfd());
|
||||
XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
|
||||
if (s < 0)
|
||||
{
|
||||
//this->close();
|
||||
XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
}
|
||||
else if ( ! XmlRpcSocket::setNonBlocking(s))
|
||||
{
|
||||
XmlRpcSocket::close(s);
|
||||
XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
|
||||
}
|
||||
else // Notify the dispatcher to listen for input on this source when we are in work()
|
||||
{
|
||||
XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
|
||||
_disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create a new connection object for processing requests from a specific client.
|
||||
XmlRpcServerConnection*
|
||||
XmlRpcServer::createConnection(int s)
|
||||
{
|
||||
// Specify that the connection object be deleted when it is closed
|
||||
return new XmlRpcServerConnection(s, this, true);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
|
||||
{
|
||||
_disp.removeSource(sc);
|
||||
}
|
||||
|
||||
|
||||
// Stop processing client requests
|
||||
void
|
||||
XmlRpcServer::exit()
|
||||
{
|
||||
_disp.exit();
|
||||
}
|
||||
|
||||
|
||||
// Close the server socket file descriptor and stop monitoring connections
|
||||
void
|
||||
XmlRpcServer::shutdown()
|
||||
{
|
||||
// This closes and destroys all connections as well as closing this socket
|
||||
_disp.clear();
|
||||
}
|
||||
|
||||
|
||||
// Introspection support
|
||||
static const std::string LIST_METHODS("system.listMethods");
|
||||
static const std::string METHOD_HELP("system.methodHelp");
|
||||
static const std::string MULTICALL("system.multicall");
|
||||
|
||||
|
||||
// List all methods available on a server
|
||||
class ListMethods : public XmlRpcServerMethod
|
||||
{
|
||||
public:
|
||||
ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
|
||||
|
||||
void execute(XmlRpcValue&, XmlRpcValue& result)
|
||||
{
|
||||
_server->listMethods(result);
|
||||
}
|
||||
|
||||
std::string help() { return std::string("List all methods available on a server as an array of strings"); }
|
||||
};
|
||||
|
||||
|
||||
// Retrieve the help string for a named method
|
||||
class MethodHelp : public XmlRpcServerMethod
|
||||
{
|
||||
public:
|
||||
MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
|
||||
|
||||
void execute(XmlRpcValue& params, XmlRpcValue& result)
|
||||
{
|
||||
if (params[0].getType() != XmlRpcValue::TypeString)
|
||||
throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
|
||||
|
||||
XmlRpcServerMethod* m = _server->findMethod(params[0]);
|
||||
if ( ! m)
|
||||
throw XmlRpcException(METHOD_HELP + ": Unknown method name");
|
||||
|
||||
result = m->help();
|
||||
}
|
||||
|
||||
std::string help() { return std::string("Retrieve the help string for a named method"); }
|
||||
};
|
||||
|
||||
|
||||
// Specify whether introspection is enabled or not. Default is enabled.
|
||||
void
|
||||
XmlRpcServer::enableIntrospection(bool enabled)
|
||||
{
|
||||
if (_introspectionEnabled == enabled)
|
||||
return;
|
||||
|
||||
_introspectionEnabled = enabled;
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
if ( ! _listMethods)
|
||||
{
|
||||
_listMethods = new ListMethods(this);
|
||||
_methodHelp = new MethodHelp(this);
|
||||
} else {
|
||||
addMethod(_listMethods);
|
||||
addMethod(_methodHelp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
removeMethod(LIST_METHODS);
|
||||
removeMethod(METHOD_HELP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XmlRpcServer::listMethods(XmlRpcValue& result)
|
||||
{
|
||||
int i = 0;
|
||||
result.setSize(_methods.size()+1);
|
||||
for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
|
||||
result[i++] = it->first;
|
||||
|
||||
// Multicall support is built into XmlRpcServerConnection
|
||||
result[i] = MULTICALL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
381
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcServerConnection.cpp
vendored
Normal file
381
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcServerConnection.cpp
vendored
Normal file
@@ -0,0 +1,381 @@
|
||||
|
||||
#include "xmlrpcpp/XmlRpcServerConnection.h"
|
||||
|
||||
#include "xmlrpcpp/XmlRpcSocket.h"
|
||||
#include "xmlrpcpp/XmlRpc.h"
|
||||
#ifndef MAKEDEPEND
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
#ifndef _WINDOWS
|
||||
# include <strings.h>
|
||||
#endif
|
||||
# include <string.h>
|
||||
#endif
|
||||
|
||||
using namespace XmlRpc;
|
||||
|
||||
// Static data
|
||||
const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>";
|
||||
const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>";
|
||||
const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>";
|
||||
const char XmlRpcServerConnection::PARAM_TAG[] = "<param>";
|
||||
const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>";
|
||||
|
||||
const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall";
|
||||
const std::string XmlRpcServerConnection::METHODNAME = "methodName";
|
||||
const std::string XmlRpcServerConnection::PARAMS = "params";
|
||||
|
||||
const std::string XmlRpcServerConnection::FAULTCODE = "faultCode";
|
||||
const std::string XmlRpcServerConnection::FAULTSTRING = "faultString";
|
||||
|
||||
|
||||
|
||||
// The server delegates handling client requests to a serverConnection object.
|
||||
XmlRpcServerConnection::XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose /*= false*/) :
|
||||
XmlRpcSource(fd, deleteOnClose)
|
||||
{
|
||||
XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
|
||||
_server = server;
|
||||
_connectionState = READ_HEADER;
|
||||
_contentLength = 0;
|
||||
_bytesWritten = 0;
|
||||
_keepAlive = true;
|
||||
}
|
||||
|
||||
|
||||
XmlRpcServerConnection::~XmlRpcServerConnection()
|
||||
{
|
||||
XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
|
||||
_server->removeConnection(this);
|
||||
}
|
||||
|
||||
|
||||
// Handle input on the server socket by accepting the connection
|
||||
// and reading the rpc request. Return true to continue to monitor
|
||||
// the socket for events, false to remove it from the dispatcher.
|
||||
unsigned
|
||||
XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
|
||||
{
|
||||
if (_connectionState == READ_HEADER)
|
||||
if ( ! readHeader()) return 0;
|
||||
|
||||
if (_connectionState == READ_REQUEST)
|
||||
if ( ! readRequest()) return 0;
|
||||
|
||||
if (_connectionState == WRITE_RESPONSE)
|
||||
if ( ! writeResponse()) return 0;
|
||||
|
||||
return (_connectionState == WRITE_RESPONSE)
|
||||
? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
XmlRpcServerConnection::readHeader()
|
||||
{
|
||||
// Read available data
|
||||
bool eof;
|
||||
if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof)) {
|
||||
// Its only an error if we already have read some data
|
||||
if (_header.length() > 0)
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
|
||||
char *hp = (char*)_header.c_str(); // Start of header
|
||||
char *ep = hp + _header.length(); // End of string
|
||||
char *bp = 0; // Start of body
|
||||
char *lp = 0; // Start of content-length value
|
||||
char *kp = 0; // Start of connection value
|
||||
|
||||
for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
|
||||
if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
|
||||
lp = cp + 16;
|
||||
else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
|
||||
kp = cp + 12;
|
||||
else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
|
||||
bp = cp + 4;
|
||||
else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
|
||||
bp = cp + 2;
|
||||
}
|
||||
|
||||
// If we haven't gotten the entire header yet, return (keep reading)
|
||||
if (bp == 0) {
|
||||
// EOF in the middle of a request is an error, otherwise its ok
|
||||
if (eof) {
|
||||
XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
|
||||
if (_header.length() > 0)
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
|
||||
return false; // Either way we close the connection
|
||||
}
|
||||
|
||||
return true; // Keep reading
|
||||
}
|
||||
|
||||
// Decode content length
|
||||
if (lp == 0) {
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
|
||||
return false; // We could try to figure it out by parsing as we read, but for now...
|
||||
}
|
||||
|
||||
_contentLength = atoi(lp);
|
||||
if (_contentLength <= 0) {
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
|
||||
|
||||
// Otherwise copy non-header data to request buffer and set state to read request.
|
||||
_request = bp;
|
||||
|
||||
// Parse out any interesting bits from the header (HTTP version, connection)
|
||||
_keepAlive = true;
|
||||
if (_header.find("HTTP/1.0") != std::string::npos) {
|
||||
if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
|
||||
_keepAlive = false; // Default for HTTP 1.0 is to close the connection
|
||||
} else {
|
||||
if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
|
||||
_keepAlive = false;
|
||||
}
|
||||
XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
|
||||
|
||||
|
||||
_header = "";
|
||||
_connectionState = READ_REQUEST;
|
||||
return true; // Continue monitoring this source
|
||||
}
|
||||
|
||||
bool
|
||||
XmlRpcServerConnection::readRequest()
|
||||
{
|
||||
// If we dont have the entire request yet, read available data
|
||||
if (int(_request.length()) < _contentLength) {
|
||||
bool eof;
|
||||
if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) {
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we haven't gotten the entire request yet, return (keep reading)
|
||||
if (int(_request.length()) < _contentLength) {
|
||||
if (eof) {
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
|
||||
return false; // Either way we close the connection
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, parse and dispatch the request
|
||||
XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
|
||||
//XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
|
||||
|
||||
_connectionState = WRITE_RESPONSE;
|
||||
|
||||
return true; // Continue monitoring this source
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
XmlRpcServerConnection::writeResponse()
|
||||
{
|
||||
if (_response.length() == 0) {
|
||||
executeRequest();
|
||||
_bytesWritten = 0;
|
||||
if (_response.length() == 0) {
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to write the response
|
||||
if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) {
|
||||
XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
|
||||
return false;
|
||||
}
|
||||
XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
|
||||
|
||||
// Prepare to read the next request
|
||||
if (_bytesWritten == int(_response.length())) {
|
||||
_header = "";
|
||||
_request = "";
|
||||
_response = "";
|
||||
_connectionState = READ_HEADER;
|
||||
}
|
||||
|
||||
return _keepAlive; // Continue monitoring this source if true
|
||||
}
|
||||
|
||||
// Run the method, generate _response string
|
||||
void
|
||||
XmlRpcServerConnection::executeRequest()
|
||||
{
|
||||
XmlRpcValue params, resultValue;
|
||||
std::string methodName = parseRequest(params);
|
||||
XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'",
|
||||
methodName.c_str());
|
||||
|
||||
try {
|
||||
|
||||
if ( ! executeMethod(methodName, params, resultValue) &&
|
||||
! executeMulticall(methodName, params, resultValue))
|
||||
generateFaultResponse(methodName + ": unknown method name");
|
||||
else
|
||||
generateResponse(resultValue.toXml());
|
||||
|
||||
} catch (const XmlRpcException& fault) {
|
||||
XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.",
|
||||
fault.getMessage().c_str());
|
||||
generateFaultResponse(fault.getMessage(), fault.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the method name and the argument values from the request.
|
||||
std::string
|
||||
XmlRpcServerConnection::parseRequest(XmlRpcValue& params)
|
||||
{
|
||||
int offset = 0; // Number of chars parsed from the request
|
||||
|
||||
std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset);
|
||||
|
||||
if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset))
|
||||
{
|
||||
int nArgs = 0;
|
||||
while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) {
|
||||
params[nArgs++] = XmlRpcValue(_request, &offset);
|
||||
(void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset);
|
||||
}
|
||||
|
||||
(void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset);
|
||||
}
|
||||
|
||||
return methodName;
|
||||
}
|
||||
|
||||
// Execute a named method with the specified params.
|
||||
bool
|
||||
XmlRpcServerConnection::executeMethod(const std::string& methodName,
|
||||
XmlRpcValue& params, XmlRpcValue& result)
|
||||
{
|
||||
XmlRpcServerMethod* method = _server->findMethod(methodName);
|
||||
|
||||
if ( ! method) return false;
|
||||
|
||||
method->execute(params, result);
|
||||
|
||||
// Ensure a valid result value
|
||||
if ( ! result.valid())
|
||||
result = std::string();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Execute multiple calls and return the results in an array.
|
||||
bool
|
||||
XmlRpcServerConnection::executeMulticall(const std::string& methodName,
|
||||
XmlRpcValue& params, XmlRpcValue& result)
|
||||
{
|
||||
if (methodName != SYSTEM_MULTICALL) return false;
|
||||
|
||||
// There ought to be 1 parameter, an array of structs
|
||||
if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
|
||||
throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)");
|
||||
|
||||
int nc = params[0].size();
|
||||
result.setSize(nc);
|
||||
|
||||
for (int i=0; i<nc; ++i) {
|
||||
|
||||
if ( ! params[0][i].hasMember(METHODNAME) ||
|
||||
! params[0][i].hasMember(PARAMS)) {
|
||||
result[i][FAULTCODE] = -1;
|
||||
result[i][FAULTSTRING] = SYSTEM_MULTICALL +
|
||||
": Invalid argument (expected a struct with members methodName and params)";
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string& methodName = params[0][i][METHODNAME];
|
||||
XmlRpcValue& methodParams = params[0][i][PARAMS];
|
||||
|
||||
XmlRpcValue resultValue;
|
||||
resultValue.setSize(1);
|
||||
try {
|
||||
if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
|
||||
! executeMulticall(methodName, params, resultValue[0]))
|
||||
{
|
||||
result[i][FAULTCODE] = -1;
|
||||
result[i][FAULTSTRING] = methodName + ": unknown method name";
|
||||
}
|
||||
else
|
||||
result[i] = resultValue;
|
||||
|
||||
} catch (const XmlRpcException& fault) {
|
||||
result[i][FAULTCODE] = fault.getCode();
|
||||
result[i][FAULTSTRING] = fault.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Create a response from results xml
|
||||
void
|
||||
XmlRpcServerConnection::generateResponse(std::string const& resultXml)
|
||||
{
|
||||
const char RESPONSE_1[] =
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<methodResponse><params><param>\r\n\t";
|
||||
const char RESPONSE_2[] =
|
||||
"\r\n</param></params></methodResponse>\r\n";
|
||||
|
||||
std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
|
||||
std::string header = generateHeader(body);
|
||||
|
||||
_response = header + body;
|
||||
XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str());
|
||||
}
|
||||
|
||||
// Prepend http headers
|
||||
std::string
|
||||
XmlRpcServerConnection::generateHeader(std::string const& body)
|
||||
{
|
||||
std::string header =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: ";
|
||||
header += XMLRPC_VERSION;
|
||||
header += "\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"Content-length: ";
|
||||
|
||||
char buffLen[40];
|
||||
#ifdef _MSC_VER
|
||||
sprintf_s(buffLen,40,"%d\r\n\r\n", (int)body.size());
|
||||
#else
|
||||
sprintf(buffLen,"%d\r\n\r\n", (int)body.size());
|
||||
#endif
|
||||
|
||||
return header + buffLen;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode)
|
||||
{
|
||||
const char RESPONSE_1[] =
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<methodResponse><fault>\r\n\t";
|
||||
const char RESPONSE_2[] =
|
||||
"\r\n</fault></methodResponse>\r\n";
|
||||
|
||||
XmlRpcValue faultStruct;
|
||||
faultStruct[FAULTCODE] = errorCode;
|
||||
faultStruct[FAULTSTRING] = errorMsg;
|
||||
std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
|
||||
std::string header = generateHeader(body);
|
||||
|
||||
_response = header + body;
|
||||
}
|
||||
|
||||
21
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcServerMethod.cpp
vendored
Normal file
21
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcServerMethod.cpp
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
#include "xmlrpcpp/XmlRpcServerMethod.h"
|
||||
#include "xmlrpcpp/XmlRpcServer.h"
|
||||
|
||||
namespace XmlRpc {
|
||||
|
||||
|
||||
XmlRpcServerMethod::XmlRpcServerMethod(std::string const& name, XmlRpcServer* server)
|
||||
{
|
||||
_name = name;
|
||||
_server = server;
|
||||
if (_server) _server->addMethod(this);
|
||||
}
|
||||
|
||||
XmlRpcServerMethod::~XmlRpcServerMethod()
|
||||
{
|
||||
if (_server) _server->removeMethod(this);
|
||||
}
|
||||
|
||||
|
||||
} // namespace XmlRpc
|
||||
389
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcSocket.cpp
vendored
Normal file
389
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcSocket.cpp
vendored
Normal file
@@ -0,0 +1,389 @@
|
||||
// this file modified by Morgan Quigley on 22 Apr 2008.
|
||||
// added features: server can be opened on port 0 and you can read back
|
||||
// what port the OS gave you
|
||||
|
||||
#include "xmlrpcpp/XmlRpcSocket.h"
|
||||
#include "xmlrpcpp/XmlRpcUtil.h"
|
||||
|
||||
#ifndef MAKEDEPEND
|
||||
|
||||
#if defined(_WINDOWS)
|
||||
# include <stdio.h>
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
//# pragma lib(WS2_32.lib)
|
||||
|
||||
// MS VS10 actually has these definitions (as opposed to earlier versions),
|
||||
// so if present, temporarily disable them and reset to WSA codes for this file only.
|
||||
#ifdef EAGAIN
|
||||
#undef EAGAIN
|
||||
#endif
|
||||
#ifdef EINTR
|
||||
#undef EINTR
|
||||
#endif
|
||||
#ifdef EINPROGRESS
|
||||
#undef EINPROGRESS
|
||||
#endif
|
||||
#ifdef EWOULDBLOCK
|
||||
#undef EWOULDBLOCK
|
||||
#endif
|
||||
#ifdef ETIMEDOUT
|
||||
#undef ETIMEDOUT
|
||||
#endif
|
||||
# define EAGAIN WSATRY_AGAIN
|
||||
# define EINTR WSAEINTR
|
||||
# define EINPROGRESS WSAEINPROGRESS
|
||||
# define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
# define ETIMEDOUT WSAETIMEDOUT
|
||||
#else
|
||||
extern "C" {
|
||||
# include <unistd.h>
|
||||
# include <stdio.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netdb.h>
|
||||
# include <errno.h>
|
||||
# include <fcntl.h>
|
||||
# include <string.h>
|
||||
# include <stdlib.h>
|
||||
# include <arpa/inet.h>
|
||||
}
|
||||
#endif // _WINDOWS
|
||||
|
||||
#endif // MAKEDEPEND
|
||||
|
||||
// MSG_NOSIGNAL does not exists on OS X
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
# ifndef MSG_NOSIGNAL
|
||||
# define MSG_NOSIGNAL SO_NOSIGPIPE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
using namespace XmlRpc;
|
||||
|
||||
|
||||
bool XmlRpcSocket::s_use_ipv6_ = false;
|
||||
|
||||
#if defined(_WINDOWS)
|
||||
|
||||
static void initWinSock()
|
||||
{
|
||||
static bool wsInit = false;
|
||||
if (! wsInit)
|
||||
{
|
||||
WORD wVersionRequested = MAKEWORD( 2, 0 );
|
||||
WSADATA wsaData;
|
||||
WSAStartup(wVersionRequested, &wsaData);
|
||||
wsInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define initWinSock()
|
||||
|
||||
#endif // _WINDOWS
|
||||
|
||||
|
||||
// These errors are not considered fatal for an IO operation; the operation will be re-tried.
|
||||
static inline bool
|
||||
nonFatalError()
|
||||
{
|
||||
int err = XmlRpcSocket::getError();
|
||||
return (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK || err == EINTR);
|
||||
}
|
||||
|
||||
int
|
||||
XmlRpcSocket::socket()
|
||||
{
|
||||
initWinSock();
|
||||
return (int) ::socket(s_use_ipv6_ ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XmlRpcSocket::close(int fd)
|
||||
{
|
||||
XmlRpcUtil::log(4, "XmlRpcSocket::close: fd %d.", fd);
|
||||
#if defined(_WINDOWS)
|
||||
closesocket(fd);
|
||||
#else
|
||||
::close(fd);
|
||||
#endif // _WINDOWS
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool
|
||||
XmlRpcSocket::setNonBlocking(int fd)
|
||||
{
|
||||
#if defined(_WINDOWS)
|
||||
unsigned long flag = 1;
|
||||
return (ioctlsocket((SOCKET)fd, FIONBIO, &flag) == 0);
|
||||
#else
|
||||
return (fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
||||
#endif // _WINDOWS
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
XmlRpcSocket::setReuseAddr(int fd)
|
||||
{
|
||||
// Allow this port to be re-bound immediately so server re-starts are not delayed
|
||||
int sflag = 1;
|
||||
return (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&sflag, sizeof(sflag)) == 0);
|
||||
}
|
||||
|
||||
|
||||
// Bind to a specified port
|
||||
bool
|
||||
XmlRpcSocket::bind(int fd, int port)
|
||||
{
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len;
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
|
||||
if (s_use_ipv6_)
|
||||
{
|
||||
sockaddr_in6 *address = (sockaddr_in6 *)&ss;
|
||||
ss_len = sizeof(sockaddr_in6);
|
||||
|
||||
address->sin6_family = AF_INET6;
|
||||
address->sin6_addr = in6addr_any;
|
||||
address->sin6_port = htons((u_short) port);
|
||||
}
|
||||
else
|
||||
{
|
||||
sockaddr_in *address = (sockaddr_in *)&ss;
|
||||
ss_len = sizeof(sockaddr_in);
|
||||
|
||||
address->sin_family = AF_INET;
|
||||
address->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
address->sin_port = htons((u_short) port);
|
||||
}
|
||||
|
||||
return (::bind(fd, (sockaddr*)&ss, ss_len) == 0);
|
||||
}
|
||||
|
||||
|
||||
// Set socket in listen mode
|
||||
bool
|
||||
XmlRpcSocket::listen(int fd, int backlog)
|
||||
{
|
||||
return (::listen(fd, backlog) == 0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
XmlRpcSocket::accept(int fd)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
#if defined(_WINDOWS)
|
||||
int
|
||||
#else
|
||||
socklen_t
|
||||
#endif
|
||||
addrlen = sizeof(addr);
|
||||
// accept will truncate the address if the buffer is too small.
|
||||
// As we are not using it, no special case for IPv6
|
||||
// has to be made.
|
||||
return (int) ::accept(fd, (struct sockaddr*)&addr, &addrlen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Connect a socket to a server (from a client)
|
||||
bool
|
||||
XmlRpcSocket::connect(int fd, std::string& host, int port)
|
||||
{
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len;
|
||||
memset(&ss, 0, sizeof(ss));
|
||||
|
||||
struct addrinfo* addr;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
if (getaddrinfo(host.c_str(), NULL, &hints, &addr) != 0)
|
||||
{
|
||||
fprintf(stderr, "Couldn't find an %s address for [%s]\n", s_use_ipv6_ ? "AF_INET6" : "AF_INET", host.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
struct addrinfo* it = addr;
|
||||
|
||||
for (; it; it = it->ai_next)
|
||||
{
|
||||
if (!s_use_ipv6_ && it->ai_family == AF_INET)
|
||||
{
|
||||
sockaddr_in *address = (sockaddr_in *)&ss;
|
||||
ss_len = sizeof(sockaddr_in);
|
||||
|
||||
memcpy(address, it->ai_addr, it->ai_addrlen);
|
||||
address->sin_family = it->ai_family;
|
||||
address->sin_port = htons((u_short) port);
|
||||
|
||||
XmlRpcUtil::log(5, "found host as %s\n", inet_ntoa(address->sin_addr));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (s_use_ipv6_ && it->ai_family == AF_INET6)
|
||||
{
|
||||
sockaddr_in6 *address = (sockaddr_in6 *)&ss;
|
||||
ss_len = sizeof(sockaddr_in6);
|
||||
|
||||
memcpy(address, it->ai_addr, it->ai_addrlen);
|
||||
address->sin6_family = it->ai_family;
|
||||
address->sin6_port = htons((u_short) port);
|
||||
|
||||
char buf[128];
|
||||
// TODO IPV6: check if this also works under Windows
|
||||
XmlRpcUtil::log(5, "found ipv6 host as %s\n", inet_ntop(AF_INET6, (void*)&(address->sin6_addr), buf, sizeof(buf)));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
printf("Couldn't find an %s address for [%s]\n", s_use_ipv6_ ? "AF_INET6" : "AF_INET", host.c_str());
|
||||
freeaddrinfo(addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For asynch operation, this will return EWOULDBLOCK (windows) or
|
||||
// EINPROGRESS (linux) and we just need to wait for the socket to be writable...
|
||||
int result = ::connect(fd, (sockaddr*)&ss, ss_len);
|
||||
if (result != 0 ) {
|
||||
int error = getError();
|
||||
if ( (error != EINPROGRESS) && error != EWOULDBLOCK) { // actually, should probably do a platform check here, EWOULDBLOCK on WIN32 and EINPROGRESS otherwise
|
||||
printf("::connect error = %d\n", getError());
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(addr);
|
||||
|
||||
return result == 0 || nonFatalError();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Read available text from the specified socket. Returns false on error.
|
||||
bool
|
||||
XmlRpcSocket::nbRead(int fd, std::string& s, bool *eof)
|
||||
{
|
||||
const int READ_SIZE = 4096; // Number of bytes to attempt to read at a time
|
||||
char readBuf[READ_SIZE];
|
||||
|
||||
bool wouldBlock = false;
|
||||
*eof = false;
|
||||
|
||||
while ( ! wouldBlock && ! *eof) {
|
||||
#if defined(_WINDOWS)
|
||||
int n = recv(fd, readBuf, READ_SIZE-1, 0);
|
||||
#else
|
||||
int n = read(fd, readBuf, READ_SIZE-1);
|
||||
#endif
|
||||
XmlRpcUtil::log(5, "XmlRpcSocket::nbRead: read/recv returned %d.", n);
|
||||
|
||||
if (n > 0) {
|
||||
readBuf[n] = 0;
|
||||
s.append(readBuf, n);
|
||||
} else if (n == 0) {
|
||||
*eof = true;
|
||||
} else if (nonFatalError()) {
|
||||
wouldBlock = true;
|
||||
} else {
|
||||
return false; // Error
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Write text to the specified socket. Returns false on error.
|
||||
bool
|
||||
XmlRpcSocket::nbWrite(int fd, std::string& s, int *bytesSoFar)
|
||||
{
|
||||
int nToWrite = int(s.length()) - *bytesSoFar;
|
||||
char *sp = const_cast<char*>(s.c_str()) + *bytesSoFar;
|
||||
bool wouldBlock = false;
|
||||
|
||||
while ( nToWrite > 0 && ! wouldBlock ) {
|
||||
#if defined(_WINDOWS)
|
||||
int n = send(fd, sp, nToWrite, 0);
|
||||
#else
|
||||
int n = write(fd, sp, nToWrite);
|
||||
#endif
|
||||
XmlRpcUtil::log(5, "XmlRpcSocket::nbWrite: send/write returned %d.", n);
|
||||
|
||||
if (n > 0) {
|
||||
sp += n;
|
||||
*bytesSoFar += n;
|
||||
nToWrite -= n;
|
||||
} else if (nonFatalError()) {
|
||||
wouldBlock = true;
|
||||
} else {
|
||||
return false; // Error
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Returns last errno
|
||||
int
|
||||
XmlRpcSocket::getError()
|
||||
{
|
||||
#if defined(_WINDOWS)
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Returns message corresponding to last errno
|
||||
std::string
|
||||
XmlRpcSocket::getErrorMsg()
|
||||
{
|
||||
return getErrorMsg(getError());
|
||||
}
|
||||
|
||||
// Returns message corresponding to errno... well, it should anyway
|
||||
std::string
|
||||
XmlRpcSocket::getErrorMsg(int error)
|
||||
{
|
||||
char err[60];
|
||||
#ifdef _MSC_VER
|
||||
strerror_s(err,60,error);
|
||||
#else
|
||||
snprintf(err,sizeof(err),"%s",strerror(error));
|
||||
#endif
|
||||
return std::string(err);
|
||||
}
|
||||
|
||||
int XmlRpcSocket::get_port(int socket)
|
||||
{
|
||||
sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof(ss);
|
||||
getsockname(socket, (sockaddr *)&ss, &ss_len);
|
||||
|
||||
sockaddr_in *sin = (sockaddr_in *)&ss;
|
||||
sockaddr_in6 *sin6 = (sockaddr_in6 *)&ss;
|
||||
|
||||
switch (ss.ss_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return ntohs(sin->sin_port);
|
||||
case AF_INET6:
|
||||
return ntohs(sin6->sin6_port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
35
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcSource.cpp
vendored
Normal file
35
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcSource.cpp
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
#include "xmlrpcpp/XmlRpcSource.h"
|
||||
#include "xmlrpcpp/XmlRpcSocket.h"
|
||||
#include "xmlrpcpp/XmlRpcUtil.h"
|
||||
|
||||
namespace XmlRpc {
|
||||
|
||||
|
||||
XmlRpcSource::XmlRpcSource(int fd /*= -1*/, bool deleteOnClose /*= false*/)
|
||||
: _fd(fd), _deleteOnClose(deleteOnClose), _keepOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
XmlRpcSource::~XmlRpcSource()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XmlRpcSource::close()
|
||||
{
|
||||
if (_fd != -1) {
|
||||
XmlRpcUtil::log(2,"XmlRpcSource::close: closing socket %d.", _fd);
|
||||
XmlRpcSocket::close(_fd);
|
||||
XmlRpcUtil::log(2,"XmlRpcSource::close: done closing socket %d.", _fd);
|
||||
_fd = -1;
|
||||
}
|
||||
if (_deleteOnClose) {
|
||||
XmlRpcUtil::log(2,"XmlRpcSource::close: deleting this");
|
||||
_deleteOnClose = false;
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace XmlRpc
|
||||
261
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcUtil.cpp
vendored
Normal file
261
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcUtil.cpp
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
|
||||
#include "xmlrpcpp/XmlRpcUtil.h"
|
||||
|
||||
#ifndef MAKEDEPEND
|
||||
# include <ctype.h>
|
||||
# include <iostream>
|
||||
# include <stdarg.h>
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
#endif
|
||||
|
||||
#include "xmlrpcpp/XmlRpc.h"
|
||||
|
||||
using namespace XmlRpc;
|
||||
|
||||
|
||||
//#define USE_WINDOWS_DEBUG // To make the error and log messages go to VC++ debug output
|
||||
#ifdef USE_WINDOWS_DEBUG
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// Version id
|
||||
const char XmlRpc::XMLRPC_VERSION[] = "XMLRPC++ 0.7";
|
||||
|
||||
// Default log verbosity: 0 for no messages through 5 (writes everything)
|
||||
int XmlRpcLogHandler::_verbosity = 0;
|
||||
|
||||
// Default log handler
|
||||
static class DefaultLogHandler : public XmlRpcLogHandler {
|
||||
public:
|
||||
|
||||
void log(int level, const char* msg) {
|
||||
#ifdef USE_WINDOWS_DEBUG
|
||||
if (level <= _verbosity) { OutputDebugString(msg); OutputDebugString("\n"); }
|
||||
#else
|
||||
if (level <= _verbosity) std::cout << msg << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
} defaultLogHandler;
|
||||
|
||||
// Message log singleton
|
||||
XmlRpcLogHandler* XmlRpcLogHandler::_logHandler = &defaultLogHandler;
|
||||
|
||||
|
||||
// Default error handler
|
||||
static class DefaultErrorHandler : public XmlRpcErrorHandler {
|
||||
public:
|
||||
|
||||
#ifdef USE_WINDOWS_DEBUG
|
||||
void error(const char* msg) {
|
||||
OutputDebugString(msg); OutputDebugString("\n");
|
||||
#else
|
||||
void error(const char*) {
|
||||
#endif
|
||||
// As far as I can tell, throwing an exception here is a bug, unless
|
||||
// the intention is that the program should exit. Throughout the code,
|
||||
// calls to error() are followed by cleanup code that does things like
|
||||
// closing a failed socket. Thus it would seem that it should be
|
||||
// possible to continue execution. But if the user just catches the
|
||||
// exception that's thrown here, the library ends up in a bogus state.
|
||||
// So I'm commenting out the throw. - BPG
|
||||
//
|
||||
//throw std::runtime_error(msg);
|
||||
}
|
||||
} defaultErrorHandler;
|
||||
|
||||
|
||||
// Error handler singleton
|
||||
XmlRpcErrorHandler* XmlRpcErrorHandler::_errorHandler = &defaultErrorHandler;
|
||||
|
||||
|
||||
// Easy API for log verbosity
|
||||
int XmlRpc::getVerbosity() { return XmlRpcLogHandler::getVerbosity(); }
|
||||
void XmlRpc::setVerbosity(int level) { XmlRpcLogHandler::setVerbosity(level); }
|
||||
|
||||
|
||||
|
||||
void XmlRpcUtil::log(int level, const char* fmt, ...)
|
||||
{
|
||||
if (level <= XmlRpcLogHandler::getVerbosity())
|
||||
{
|
||||
va_list va;
|
||||
char buf[1024];
|
||||
va_start( va, fmt);
|
||||
vsnprintf(buf,sizeof(buf)-1,fmt,va);
|
||||
va_end(va);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
XmlRpcLogHandler::getLogHandler()->log(level, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void XmlRpcUtil::error(const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
char buf[1024];
|
||||
vsnprintf(buf,sizeof(buf)-1,fmt,va);
|
||||
va_end(va);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
XmlRpcErrorHandler::getErrorHandler()->error(buf);
|
||||
}
|
||||
|
||||
|
||||
// Returns contents between <tag> and </tag>, updates offset to char after </tag>
|
||||
std::string
|
||||
XmlRpcUtil::parseTag(const char* tag, std::string const& xml, int* offset)
|
||||
{
|
||||
if (*offset >= int(xml.length())) return std::string();
|
||||
size_t istart = xml.find(tag, *offset);
|
||||
if (istart == std::string::npos) return std::string();
|
||||
istart += strlen(tag);
|
||||
std::string etag = "</";
|
||||
etag += tag + 1;
|
||||
size_t iend = xml.find(etag, istart);
|
||||
if (iend == std::string::npos) return std::string();
|
||||
|
||||
*offset = int(iend + etag.length());
|
||||
return xml.substr(istart, iend-istart);
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the tag is found and updates offset to the char after the tag
|
||||
bool
|
||||
XmlRpcUtil::findTag(const char* tag, std::string const& xml, int* offset)
|
||||
{
|
||||
if (*offset >= int(xml.length())) return false;
|
||||
size_t istart = xml.find(tag, *offset);
|
||||
if (istart == std::string::npos)
|
||||
return false;
|
||||
|
||||
*offset = int(istart + strlen(tag));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the tag is found at the specified offset (modulo any whitespace)
|
||||
// and updates offset to the char after the tag
|
||||
bool
|
||||
XmlRpcUtil::nextTagIs(const char* tag, std::string const& xml, int* offset)
|
||||
{
|
||||
if (*offset >= int(xml.length())) return false;
|
||||
const char* cp = xml.c_str() + *offset;
|
||||
int nc = 0;
|
||||
while (*cp && isspace(*cp)) {
|
||||
++cp;
|
||||
++nc;
|
||||
}
|
||||
|
||||
int len = int(strlen(tag));
|
||||
if (*cp && (strncmp(cp, tag, len) == 0)) {
|
||||
*offset += nc + len;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the next tag and updates offset to the char after the tag, or empty string
|
||||
// if the next non-whitespace character is not '<'
|
||||
std::string
|
||||
XmlRpcUtil::getNextTag(std::string const& xml, int* offset)
|
||||
{
|
||||
if (*offset >= int(xml.length())) return std::string();
|
||||
|
||||
size_t pos = *offset;
|
||||
const char* cp = xml.c_str() + pos;
|
||||
while (*cp && isspace(*cp)) {
|
||||
++cp;
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (*cp != '<') return std::string();
|
||||
|
||||
std::string s;
|
||||
do {
|
||||
s += *cp;
|
||||
++pos;
|
||||
} while (*cp++ != '>' && *cp != 0);
|
||||
|
||||
*offset = int(pos);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// xml encodings (xml-encoded entities are preceded with '&')
|
||||
static const char AMP = '&';
|
||||
static const char rawEntity[] = { '<', '>', '&', '\'', '\"', 0 };
|
||||
static const char* xmlEntity[] = { "lt;", "gt;", "amp;", "apos;", "quot;", 0 };
|
||||
static const int xmlEntLen[] = { 3, 3, 4, 5, 5 };
|
||||
|
||||
|
||||
// Replace xml-encoded entities with the raw text equivalents.
|
||||
|
||||
std::string
|
||||
XmlRpcUtil::xmlDecode(const std::string& encoded)
|
||||
{
|
||||
std::string::size_type iAmp = encoded.find(AMP);
|
||||
if (iAmp == std::string::npos)
|
||||
return encoded;
|
||||
|
||||
std::string decoded(encoded, 0, iAmp);
|
||||
std::string::size_type iSize = encoded.size();
|
||||
decoded.reserve(iSize);
|
||||
|
||||
const char* ens = encoded.c_str();
|
||||
while (iAmp != iSize) {
|
||||
if (encoded[iAmp] == AMP && iAmp+1 < iSize) {
|
||||
int iEntity;
|
||||
for (iEntity=0; xmlEntity[iEntity] != 0; ++iEntity)
|
||||
//if (encoded.compare(iAmp+1, xmlEntLen[iEntity], xmlEntity[iEntity]) == 0)
|
||||
if (strncmp(ens+iAmp+1, xmlEntity[iEntity], xmlEntLen[iEntity]) == 0)
|
||||
{
|
||||
decoded += rawEntity[iEntity];
|
||||
iAmp += xmlEntLen[iEntity]+1;
|
||||
break;
|
||||
}
|
||||
if (xmlEntity[iEntity] == 0) // unrecognized sequence
|
||||
decoded += encoded[iAmp++];
|
||||
|
||||
} else {
|
||||
decoded += encoded[iAmp++];
|
||||
}
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
|
||||
// Replace raw text with xml-encoded entities.
|
||||
|
||||
std::string
|
||||
XmlRpcUtil::xmlEncode(const std::string& raw)
|
||||
{
|
||||
std::string::size_type iRep = raw.find_first_of(rawEntity);
|
||||
if (iRep == std::string::npos)
|
||||
return raw;
|
||||
|
||||
std::string encoded(raw, 0, iRep);
|
||||
std::string::size_type iSize = raw.size();
|
||||
|
||||
while (iRep != iSize) {
|
||||
int iEntity;
|
||||
for (iEntity=0; rawEntity[iEntity] != 0; ++iEntity)
|
||||
if (raw[iRep] == rawEntity[iEntity])
|
||||
{
|
||||
encoded += AMP;
|
||||
encoded += xmlEntity[iEntity];
|
||||
break;
|
||||
}
|
||||
if (rawEntity[iEntity] == 0)
|
||||
encoded += raw[iRep];
|
||||
++iRep;
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
636
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcValue.cpp
vendored
Normal file
636
thirdparty/ros/ros_comm/utilities/xmlrpcpp/src/XmlRpcValue.cpp
vendored
Normal file
@@ -0,0 +1,636 @@
|
||||
|
||||
#include "xmlrpcpp/XmlRpcValue.h"
|
||||
#include "xmlrpcpp/XmlRpcException.h"
|
||||
#include "xmlrpcpp/XmlRpcUtil.h"
|
||||
#include "xmlrpcpp/base64.h"
|
||||
|
||||
#ifndef MAKEDEPEND
|
||||
# include <iostream>
|
||||
# include <ostream>
|
||||
# include <stdlib.h>
|
||||
# include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace XmlRpc {
|
||||
|
||||
|
||||
static const char VALUE_TAG[] = "<value>";
|
||||
static const char VALUE_ETAG[] = "</value>";
|
||||
|
||||
static const char BOOLEAN_TAG[] = "<boolean>";
|
||||
static const char BOOLEAN_ETAG[] = "</boolean>";
|
||||
static const char DOUBLE_TAG[] = "<double>";
|
||||
static const char DOUBLE_ETAG[] = "</double>";
|
||||
static const char INT_TAG[] = "<int>";
|
||||
static const char I4_TAG[] = "<i4>";
|
||||
static const char I4_ETAG[] = "</i4>";
|
||||
static const char STRING_TAG[] = "<string>";
|
||||
static const char DATETIME_TAG[] = "<dateTime.iso8601>";
|
||||
static const char DATETIME_ETAG[] = "</dateTime.iso8601>";
|
||||
static const char BASE64_TAG[] = "<base64>";
|
||||
static const char BASE64_ETAG[] = "</base64>";
|
||||
|
||||
static const char ARRAY_TAG[] = "<array>";
|
||||
static const char DATA_TAG[] = "<data>";
|
||||
static const char DATA_ETAG[] = "</data>";
|
||||
static const char ARRAY_ETAG[] = "</array>";
|
||||
|
||||
static const char STRUCT_TAG[] = "<struct>";
|
||||
static const char MEMBER_TAG[] = "<member>";
|
||||
static const char NAME_TAG[] = "<name>";
|
||||
static const char NAME_ETAG[] = "</name>";
|
||||
static const char MEMBER_ETAG[] = "</member>";
|
||||
static const char STRUCT_ETAG[] = "</struct>";
|
||||
|
||||
|
||||
|
||||
// Format strings
|
||||
std::string XmlRpcValue::_doubleFormat("%.16g");
|
||||
|
||||
|
||||
|
||||
// Clean up
|
||||
void XmlRpcValue::invalidate()
|
||||
{
|
||||
switch (_type) {
|
||||
case TypeString: delete _value.asString; break;
|
||||
case TypeDateTime: delete _value.asTime; break;
|
||||
case TypeBase64: delete _value.asBinary; break;
|
||||
case TypeArray: delete _value.asArray; break;
|
||||
case TypeStruct: delete _value.asStruct; break;
|
||||
default: break;
|
||||
}
|
||||
_type = TypeInvalid;
|
||||
_value.asBinary = 0;
|
||||
}
|
||||
|
||||
|
||||
// Type checking
|
||||
void XmlRpcValue::assertTypeOrInvalid(Type t)
|
||||
{
|
||||
if (_type == TypeInvalid)
|
||||
{
|
||||
_type = t;
|
||||
switch (_type) { // Ensure there is a valid value for the type
|
||||
case TypeString: _value.asString = new std::string(); break;
|
||||
case TypeDateTime: _value.asTime = new struct tm(); break;
|
||||
case TypeBase64: _value.asBinary = new BinaryData(); break;
|
||||
case TypeArray: _value.asArray = new ValueArray(); break;
|
||||
case TypeStruct: _value.asStruct = new ValueStruct(); break;
|
||||
default: _value.asBinary = 0; break;
|
||||
}
|
||||
}
|
||||
else if (_type != t)
|
||||
throw XmlRpcException("type error");
|
||||
}
|
||||
|
||||
void XmlRpcValue::assertArray(int size) const
|
||||
{
|
||||
if (_type != TypeArray)
|
||||
throw XmlRpcException("type error: expected an array");
|
||||
else if (int(_value.asArray->size()) < size)
|
||||
throw XmlRpcException("range error: array index too large");
|
||||
}
|
||||
|
||||
|
||||
void XmlRpcValue::assertArray(int size)
|
||||
{
|
||||
if (_type == TypeInvalid) {
|
||||
_type = TypeArray;
|
||||
_value.asArray = new ValueArray(size);
|
||||
} else if (_type == TypeArray) {
|
||||
if (int(_value.asArray->size()) < size)
|
||||
_value.asArray->resize(size);
|
||||
} else
|
||||
throw XmlRpcException("type error: expected an array");
|
||||
}
|
||||
|
||||
void XmlRpcValue::assertStruct()
|
||||
{
|
||||
if (_type == TypeInvalid) {
|
||||
_type = TypeStruct;
|
||||
_value.asStruct = new ValueStruct();
|
||||
} else if (_type != TypeStruct)
|
||||
throw XmlRpcException("type error: expected a struct");
|
||||
}
|
||||
|
||||
|
||||
// Operators
|
||||
XmlRpcValue& XmlRpcValue::operator=(XmlRpcValue const& rhs)
|
||||
{
|
||||
if (this != &rhs)
|
||||
{
|
||||
invalidate();
|
||||
_type = rhs._type;
|
||||
switch (_type) {
|
||||
case TypeBoolean: _value.asBool = rhs._value.asBool; break;
|
||||
case TypeInt: _value.asInt = rhs._value.asInt; break;
|
||||
case TypeDouble: _value.asDouble = rhs._value.asDouble; break;
|
||||
case TypeDateTime: _value.asTime = new struct tm(*rhs._value.asTime); break;
|
||||
case TypeString: _value.asString = new std::string(*rhs._value.asString); break;
|
||||
case TypeBase64: _value.asBinary = new BinaryData(*rhs._value.asBinary); break;
|
||||
case TypeArray: _value.asArray = new ValueArray(*rhs._value.asArray); break;
|
||||
case TypeStruct: _value.asStruct = new ValueStruct(*rhs._value.asStruct); break;
|
||||
default: _value.asBinary = 0; break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// Predicate for tm equality
|
||||
static bool tmEq(struct tm const& t1, struct tm const& t2) {
|
||||
return t1.tm_sec == t2.tm_sec && t1.tm_min == t2.tm_min &&
|
||||
t1.tm_hour == t2.tm_hour && t1.tm_mday == t2.tm_mday &&
|
||||
t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year;
|
||||
}
|
||||
|
||||
bool XmlRpcValue::operator==(XmlRpcValue const& other) const
|
||||
{
|
||||
if (_type != other._type)
|
||||
return false;
|
||||
|
||||
switch (_type) {
|
||||
case TypeBoolean: return ( !_value.asBool && !other._value.asBool) ||
|
||||
( _value.asBool && other._value.asBool);
|
||||
case TypeInt: return _value.asInt == other._value.asInt;
|
||||
case TypeDouble: return _value.asDouble == other._value.asDouble;
|
||||
case TypeDateTime: return tmEq(*_value.asTime, *other._value.asTime);
|
||||
case TypeString: return *_value.asString == *other._value.asString;
|
||||
case TypeBase64: return *_value.asBinary == *other._value.asBinary;
|
||||
case TypeArray: return *_value.asArray == *other._value.asArray;
|
||||
|
||||
// The map<>::operator== requires the definition of value< for kcc
|
||||
case TypeStruct: //return *_value.asStruct == *other._value.asStruct;
|
||||
{
|
||||
if (_value.asStruct->size() != other._value.asStruct->size())
|
||||
return false;
|
||||
|
||||
ValueStruct::const_iterator it1=_value.asStruct->begin();
|
||||
ValueStruct::const_iterator it2=other._value.asStruct->begin();
|
||||
while (it1 != _value.asStruct->end()) {
|
||||
const XmlRpcValue& v1 = it1->second;
|
||||
const XmlRpcValue& v2 = it2->second;
|
||||
if ( ! (v1 == v2))
|
||||
return false;
|
||||
it1++;
|
||||
it2++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
return true; // Both invalid values ...
|
||||
}
|
||||
|
||||
bool XmlRpcValue::operator!=(XmlRpcValue const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
// Works for strings, binary data, arrays, and structs.
|
||||
int XmlRpcValue::size() const
|
||||
{
|
||||
switch (_type) {
|
||||
case TypeString: return int(_value.asString->size());
|
||||
case TypeBase64: return int(_value.asBinary->size());
|
||||
case TypeArray: return int(_value.asArray->size());
|
||||
case TypeStruct: return int(_value.asStruct->size());
|
||||
default: break;
|
||||
}
|
||||
|
||||
throw XmlRpcException("type error");
|
||||
}
|
||||
|
||||
// Checks for existence of struct member
|
||||
bool XmlRpcValue::hasMember(const std::string& name) const
|
||||
{
|
||||
return _type == TypeStruct && _value.asStruct->find(name) != _value.asStruct->end();
|
||||
}
|
||||
|
||||
// Set the value from xml. The chars at *offset into valueXml
|
||||
// should be the start of a <value> tag. Destroys any existing value.
|
||||
bool XmlRpcValue::fromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
int savedOffset = *offset;
|
||||
|
||||
invalidate();
|
||||
if ( ! XmlRpcUtil::nextTagIs(VALUE_TAG, valueXml, offset))
|
||||
return false; // Not a value, offset not updated
|
||||
|
||||
int afterValueOffset = *offset;
|
||||
std::string typeTag = XmlRpcUtil::getNextTag(valueXml, offset);
|
||||
bool result = false;
|
||||
if (typeTag == BOOLEAN_TAG)
|
||||
result = boolFromXml(valueXml, offset);
|
||||
else if (typeTag == I4_TAG || typeTag == INT_TAG)
|
||||
result = intFromXml(valueXml, offset);
|
||||
else if (typeTag == DOUBLE_TAG)
|
||||
result = doubleFromXml(valueXml, offset);
|
||||
else if (typeTag.empty() || typeTag == STRING_TAG)
|
||||
result = stringFromXml(valueXml, offset);
|
||||
else if (typeTag == DATETIME_TAG)
|
||||
result = timeFromXml(valueXml, offset);
|
||||
else if (typeTag == BASE64_TAG)
|
||||
result = binaryFromXml(valueXml, offset);
|
||||
else if (typeTag == ARRAY_TAG)
|
||||
result = arrayFromXml(valueXml, offset);
|
||||
else if (typeTag == STRUCT_TAG)
|
||||
result = structFromXml(valueXml, offset);
|
||||
// Watch for empty/blank strings with no <string>tag
|
||||
else if (typeTag == VALUE_ETAG)
|
||||
{
|
||||
*offset = afterValueOffset; // back up & try again
|
||||
result = stringFromXml(valueXml, offset);
|
||||
}
|
||||
|
||||
if (result) // Skip over the </value> tag
|
||||
XmlRpcUtil::findTag(VALUE_ETAG, valueXml, offset);
|
||||
else // Unrecognized tag after <value>
|
||||
*offset = savedOffset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Encode the Value in xml
|
||||
std::string XmlRpcValue::toXml() const
|
||||
{
|
||||
switch (_type) {
|
||||
case TypeBoolean: return boolToXml();
|
||||
case TypeInt: return intToXml();
|
||||
case TypeDouble: return doubleToXml();
|
||||
case TypeString: return stringToXml();
|
||||
case TypeDateTime: return timeToXml();
|
||||
case TypeBase64: return binaryToXml();
|
||||
case TypeArray: return arrayToXml();
|
||||
case TypeStruct: return structToXml();
|
||||
default: break;
|
||||
}
|
||||
return std::string(); // Invalid value
|
||||
}
|
||||
|
||||
|
||||
// Boolean
|
||||
bool XmlRpcValue::boolFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
const char* valueStart = valueXml.c_str() + *offset;
|
||||
char* valueEnd;
|
||||
long ivalue = strtol(valueStart, &valueEnd, 10);
|
||||
if (valueEnd == valueStart || (ivalue != 0 && ivalue != 1))
|
||||
return false;
|
||||
|
||||
_type = TypeBoolean;
|
||||
_value.asBool = (ivalue == 1);
|
||||
*offset += int(valueEnd - valueStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string XmlRpcValue::boolToXml() const
|
||||
{
|
||||
std::string xml = VALUE_TAG;
|
||||
xml += BOOLEAN_TAG;
|
||||
xml += (_value.asBool ? "1" : "0");
|
||||
xml += BOOLEAN_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
// Int
|
||||
bool XmlRpcValue::intFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
const char* valueStart = valueXml.c_str() + *offset;
|
||||
char* valueEnd;
|
||||
long ivalue = strtol(valueStart, &valueEnd, 10);
|
||||
if (valueEnd == valueStart)
|
||||
return false;
|
||||
|
||||
_type = TypeInt;
|
||||
_value.asInt = int(ivalue);
|
||||
*offset += int(valueEnd - valueStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string XmlRpcValue::intToXml() const
|
||||
{
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf)-1, "%d", _value.asInt);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
std::string xml = VALUE_TAG;
|
||||
xml += I4_TAG;
|
||||
xml += buf;
|
||||
xml += I4_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
// Double
|
||||
bool XmlRpcValue::doubleFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
const char* valueStart = valueXml.c_str() + *offset;
|
||||
char* valueEnd;
|
||||
|
||||
// ticket #2438
|
||||
// push/pop the locale here. Value 123.45 can get read by strtod
|
||||
// as '123', if the locale expects a comma instead of dot.
|
||||
// if there are locale problems, silently continue.
|
||||
std::string tmplocale;
|
||||
char* locale_cstr = setlocale(LC_NUMERIC, 0);
|
||||
if (locale_cstr)
|
||||
{
|
||||
tmplocale = locale_cstr;
|
||||
setlocale(LC_NUMERIC, "POSIX");
|
||||
}
|
||||
|
||||
double dvalue = strtod(valueStart, &valueEnd);
|
||||
|
||||
if (tmplocale.size() > 0) {
|
||||
setlocale(LC_NUMERIC, tmplocale.c_str());
|
||||
}
|
||||
|
||||
if (valueEnd == valueStart)
|
||||
return false;
|
||||
|
||||
_type = TypeDouble;
|
||||
_value.asDouble = dvalue;
|
||||
*offset += int(valueEnd - valueStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string XmlRpcValue::doubleToXml() const
|
||||
{
|
||||
// ticket #2438
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic()); // ensure we're using "C" locale for formatting floating-point (1.4 vs. 1,4, etc.)
|
||||
ss.precision(17);
|
||||
ss << _value.asDouble;
|
||||
|
||||
std::string xml = VALUE_TAG;
|
||||
xml += DOUBLE_TAG;
|
||||
xml += ss.str();
|
||||
xml += DOUBLE_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
// String
|
||||
bool XmlRpcValue::stringFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
size_t valueEnd = valueXml.find('<', *offset);
|
||||
if (valueEnd == std::string::npos)
|
||||
return false; // No end tag;
|
||||
|
||||
_type = TypeString;
|
||||
_value.asString = new std::string(XmlRpcUtil::xmlDecode(valueXml.substr(*offset, valueEnd-*offset)));
|
||||
*offset += int(_value.asString->length());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string XmlRpcValue::stringToXml() const
|
||||
{
|
||||
std::string xml = VALUE_TAG;
|
||||
//xml += STRING_TAG; optional
|
||||
xml += XmlRpcUtil::xmlEncode(*_value.asString);
|
||||
//xml += STRING_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
// DateTime (stored as a struct tm)
|
||||
bool XmlRpcValue::timeFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
size_t valueEnd = valueXml.find('<', *offset);
|
||||
if (valueEnd == std::string::npos)
|
||||
return false; // No end tag;
|
||||
|
||||
std::string stime = valueXml.substr(*offset, valueEnd-*offset);
|
||||
|
||||
struct tm t;
|
||||
#ifdef _MSC_VER
|
||||
if (sscanf_s(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6)
|
||||
#else
|
||||
if (sscanf(stime.c_str(),"%4d%2d%2dT%2d:%2d:%2d",&t.tm_year,&t.tm_mon,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec) != 6)
|
||||
#endif
|
||||
return false;
|
||||
|
||||
t.tm_isdst = -1;
|
||||
_type = TypeDateTime;
|
||||
_value.asTime = new struct tm(t);
|
||||
*offset += int(stime.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string XmlRpcValue::timeToXml() const
|
||||
{
|
||||
struct tm* t = _value.asTime;
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
|
||||
t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
|
||||
std::string xml = VALUE_TAG;
|
||||
xml += DATETIME_TAG;
|
||||
xml += buf;
|
||||
xml += DATETIME_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
||||
// Base64
|
||||
bool XmlRpcValue::binaryFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
size_t valueEnd = valueXml.find('<', *offset);
|
||||
if (valueEnd == std::string::npos)
|
||||
return false; // No end tag;
|
||||
|
||||
_type = TypeBase64;
|
||||
std::string asString = valueXml.substr(*offset, valueEnd-*offset);
|
||||
_value.asBinary = new BinaryData();
|
||||
// check whether base64 encodings can contain chars xml encodes...
|
||||
|
||||
// convert from base64 to binary
|
||||
int iostatus = 0;
|
||||
base64<char> decoder;
|
||||
std::back_insert_iterator<BinaryData> ins = std::back_inserter(*(_value.asBinary));
|
||||
decoder.get(asString.begin(), asString.end(), ins, iostatus);
|
||||
|
||||
*offset += int(asString.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::string XmlRpcValue::binaryToXml() const
|
||||
{
|
||||
// convert to base64
|
||||
std::vector<char> base64data;
|
||||
int iostatus = 0;
|
||||
base64<char> encoder;
|
||||
std::back_insert_iterator<std::vector<char> > ins = std::back_inserter(base64data);
|
||||
encoder.put(_value.asBinary->begin(), _value.asBinary->end(), ins, iostatus, base64<>::crlf());
|
||||
|
||||
// Wrap with xml
|
||||
std::string xml = VALUE_TAG;
|
||||
xml += BASE64_TAG;
|
||||
xml.append(base64data.begin(), base64data.end());
|
||||
xml += BASE64_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
||||
// Array
|
||||
bool XmlRpcValue::arrayFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
if ( ! XmlRpcUtil::nextTagIs(DATA_TAG, valueXml, offset))
|
||||
return false;
|
||||
|
||||
_type = TypeArray;
|
||||
_value.asArray = new ValueArray;
|
||||
XmlRpcValue v;
|
||||
while (v.fromXml(valueXml, offset))
|
||||
_value.asArray->push_back(v); // copy...
|
||||
|
||||
// Skip the trailing </data>
|
||||
(void) XmlRpcUtil::nextTagIs(DATA_ETAG, valueXml, offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// In general, its preferable to generate the xml of each element of the
|
||||
// array as it is needed rather than glomming up one big string.
|
||||
std::string XmlRpcValue::arrayToXml() const
|
||||
{
|
||||
std::string xml = VALUE_TAG;
|
||||
xml += ARRAY_TAG;
|
||||
xml += DATA_TAG;
|
||||
|
||||
int s = int(_value.asArray->size());
|
||||
for (int i=0; i<s; ++i)
|
||||
xml += _value.asArray->at(i).toXml();
|
||||
|
||||
xml += DATA_ETAG;
|
||||
xml += ARRAY_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
||||
// Struct
|
||||
bool XmlRpcValue::structFromXml(std::string const& valueXml, int* offset)
|
||||
{
|
||||
_type = TypeStruct;
|
||||
_value.asStruct = new ValueStruct;
|
||||
|
||||
while (XmlRpcUtil::nextTagIs(MEMBER_TAG, valueXml, offset)) {
|
||||
// name
|
||||
const std::string name = XmlRpcUtil::parseTag(NAME_TAG, valueXml, offset);
|
||||
// value
|
||||
XmlRpcValue val(valueXml, offset);
|
||||
if ( ! val.valid()) {
|
||||
invalidate();
|
||||
return false;
|
||||
}
|
||||
const std::pair<const std::string, XmlRpcValue> p(name, val);
|
||||
_value.asStruct->insert(p);
|
||||
|
||||
(void) XmlRpcUtil::nextTagIs(MEMBER_ETAG, valueXml, offset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// In general, its preferable to generate the xml of each element
|
||||
// as it is needed rather than glomming up one big string.
|
||||
std::string XmlRpcValue::structToXml() const
|
||||
{
|
||||
std::string xml = VALUE_TAG;
|
||||
xml += STRUCT_TAG;
|
||||
|
||||
ValueStruct::const_iterator it;
|
||||
for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it) {
|
||||
xml += MEMBER_TAG;
|
||||
xml += NAME_TAG;
|
||||
xml += XmlRpcUtil::xmlEncode(it->first);
|
||||
xml += NAME_ETAG;
|
||||
xml += it->second.toXml();
|
||||
xml += MEMBER_ETAG;
|
||||
}
|
||||
|
||||
xml += STRUCT_ETAG;
|
||||
xml += VALUE_ETAG;
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Write the value without xml encoding it
|
||||
std::ostream& XmlRpcValue::write(std::ostream& os) const {
|
||||
switch (_type) {
|
||||
default: break;
|
||||
case TypeBoolean: os << _value.asBool; break;
|
||||
case TypeInt: os << _value.asInt; break;
|
||||
case TypeDouble: os << _value.asDouble; break;
|
||||
case TypeString: os << *_value.asString; break;
|
||||
case TypeDateTime:
|
||||
{
|
||||
struct tm* t = _value.asTime;
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf)-1, "%4d%02d%02dT%02d:%02d:%02d",
|
||||
t->tm_year,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
os << buf;
|
||||
break;
|
||||
}
|
||||
case TypeBase64:
|
||||
{
|
||||
int iostatus = 0;
|
||||
std::ostreambuf_iterator<char> out(os);
|
||||
base64<char> encoder;
|
||||
encoder.put(_value.asBinary->begin(), _value.asBinary->end(), out, iostatus, base64<>::crlf());
|
||||
break;
|
||||
}
|
||||
case TypeArray:
|
||||
{
|
||||
int s = int(_value.asArray->size());
|
||||
os << '{';
|
||||
for (int i=0; i<s; ++i)
|
||||
{
|
||||
if (i > 0) os << ',';
|
||||
_value.asArray->at(i).write(os);
|
||||
}
|
||||
os << '}';
|
||||
break;
|
||||
}
|
||||
case TypeStruct:
|
||||
{
|
||||
os << '[';
|
||||
ValueStruct::const_iterator it;
|
||||
for (it=_value.asStruct->begin(); it!=_value.asStruct->end(); ++it)
|
||||
{
|
||||
if (it!=_value.asStruct->begin()) os << ',';
|
||||
os << it->first << ':';
|
||||
it->second.write(os);
|
||||
}
|
||||
os << ']';
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace XmlRpc
|
||||
|
||||
|
||||
// ostream
|
||||
std::ostream& operator<<(std::ostream& os, const XmlRpc::XmlRpcValue& v)
|
||||
{
|
||||
// If you want to output in xml format:
|
||||
//return os << v.toXml();
|
||||
return v.write(os);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user