Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

XmlRpc::XmlRpcClient Class Reference

A class to send XML RPC requests to a server and return the results. More...

#include <XmlRpcClient.h>

Inheritance diagram for XmlRpc::XmlRpcClient:

XmlRpc::XmlRpcSource List of all members.

Public Member Functions

virtual void close ()
 Close the connection.
bool execute (const char *method, XmlRpcValue const &params, XmlRpcValue &result)
int getfd () const
 Return the file descriptor being monitored.
bool getKeepOpen () const
 Return whether the file descriptor should be kept open if it is no longer monitored.
virtual unsigned handleEvent (unsigned eventType)
bool isFault () const
 Returns true if the result of the last execute() was a fault response.
void setfd (int fd)
 Specify the file descriptor to monitor.
void setKeepOpen (bool b=true)
 Specify whether the file descriptor should be kept open if it is no longer monitored.
 XmlRpcClient (const char *host, int port, const char *uri=0)
virtual ~XmlRpcClient ()
 Destructor.

Static Public Attributes

const char FAULT_TAG [] = "<fault>"
const char METHODRESPONSE_TAG [] = "<methodResponse>"
const char PARAM_ETAG [] = "</param>"
const char PARAM_TAG [] = "<param>"
const char PARAMS_ETAG [] = "</params>"
const char PARAMS_TAG [] = "<params>"
const char REQUEST_BEGIN []
const char REQUEST_END [] = "</methodCall>\r\n"
const char REQUEST_END_METHODNAME [] = "</methodName>\r\n"

Protected Types

enum  ClientConnectionState {
  NO_CONNECTION, CONNECTING, WRITE_REQUEST, READ_HEADER,
  READ_RESPONSE, IDLE
}

Protected Member Functions

virtual bool doConnect ()
virtual std::string generateHeader (std::string const &body)
virtual bool generateRequest (const char *method, XmlRpcValue const &params)
virtual bool parseResponse (XmlRpcValue &result)
virtual bool readHeader ()
virtual bool readResponse ()
virtual bool setupConnection ()
virtual bool writeRequest ()

Protected Attributes

int _bytesWritten
ClientConnectionState _connectionState
int _contentLength
XmlRpcDispatch _disp
bool _eof
bool _executing
std::string _header
std::string _host
bool _isFault
int _port
std::string _request
std::string _response
int _sendAttempts
std::string _uri

Detailed Description

A class to send XML RPC requests to a server and return the results.


Member Enumeration Documentation

enum XmlRpc::XmlRpcClient::ClientConnectionState [protected]
 

Enumeration values:
NO_CONNECTION 
CONNECTING 
WRITE_REQUEST 
READ_HEADER 
READ_RESPONSE 
IDLE 
00086 { NO_CONNECTION, CONNECTING, WRITE_REQUEST, READ_HEADER, READ_RESPONSE, IDLE };


Constructor & Destructor Documentation

XmlRpcClient::XmlRpcClient const char *  host,
int  port,
const char *  uri = 0
 

Construct a client to connect to the server at the specified host:port address

Parameters:
host The name of the remote machine hosting the server
port The port on the remote machine where the server is listening
uri An optional string to be sent as the URI in the HTTP GET header
00030 {
00031   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
00032 
00033   _host = host;
00034   _port = port;
00035   if (uri)
00036     _uri = uri;
00037   else
00038     _uri = "/RPC2";
00039   _connectionState = NO_CONNECTION;
00040   _executing = false;
00041   _eof = false;
00042 
00043   // Default to keeping the connection open until an explicit close is done
00044   setKeepOpen();
00045 }

XmlRpcClient::~XmlRpcClient  )  [virtual]
 

Destructor.

00049 {
00050 }


Member Function Documentation

void XmlRpcClient::close  )  [virtual]
 

Close the connection.

Reimplemented from XmlRpc::XmlRpcSource.

00055 {
00056   XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
00057   _connectionState = NO_CONNECTION;
00058   _disp.exit();
00059   _disp.removeSource(this);
00060   XmlRpcSource::close();
00061 }

bool XmlRpcClient::doConnect  )  [protected, virtual]
 

00170 {
00171   int fd = XmlRpcSocket::socket();
00172   if (fd < 0)
00173   {
00174     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
00175     return false;
00176   }
00177 
00178   XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
00179   this->setfd(fd);
00180 
00181   // Don't block on connect/reads/writes
00182   if ( ! XmlRpcSocket::setNonBlocking(fd))
00183   {
00184     this->close();
00185     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00186     return false;
00187   }
00188 
00189   if ( ! XmlRpcSocket::connect(fd, _host, _port))
00190   {
00191     this->close();
00192     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
00193     return false;
00194   }
00195 
00196   return true;
00197 }

bool XmlRpcClient::execute const char *  method,
XmlRpcValue const &  params,
XmlRpcValue result
 

Execute the named procedure on the remote server.

Parameters:
method The name of the remote procedure to execute
params An array of the arguments for the method
result The result value to be returned to the client
Returns:
true if the request was sent and a result received (although the result might be a fault).
Currently this is a synchronous (blocking) implementation (execute does not return until it receives a response or an error). Use isFault() to determine whether the result is a fault response.
00077 {
00078   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
00079   //std::cout<< "_connectionState   "<<_connectionState << std::endl;//yzhang debug
00080   // This is not a thread-safe operation, if you want to do multithreading, use separate
00081   // clients for each thread. If you want to protect yourself from multiple threads
00082   // accessing the same client, replace this code with a real mutex.
00083   if (_executing)
00084     return false;
00085 
00086   _executing = true;
00087   ClearFlagOnExit cf(_executing);
00088 
00089   _sendAttempts = 0;
00090   _isFault = false;
00091 
00092   if ( ! setupConnection())
00093     return false;
00094 
00095   if ( ! generateRequest(method, params))
00096     return false;
00097   result.clear();
00098   double msTime = 5.;   // Process until exit is called //yzhang change
00099   //double msTime = -1.0;   // Process until exit is called //yzhang delete
00100   _disp.work(msTime);
00101 
00102   if (_connectionState != IDLE )//yzhang add
00103   //if (_connectionState != IDLE || ! parseResponse(result))//yzhang delete
00104     return false; //yzhang delete
00105 
00106   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
00107   _response = "";
00108   return true;
00109 }

std::string XmlRpcClient::generateHeader std::string const &  body  )  [protected, virtual]
 

00240 {
00241   std::string header = 
00242     "POST " + _uri + " HTTP/1.1\r\n"
00243     "User-Agent: ";
00244   header += XMLRPC_VERSION;
00245   header += "\r\nHost: ";
00246   header += _host;
00247 
00248   char buff[40];
00249   sprintf(buff,":%d\r\n", _port);
00250 
00251   header += buff;
00252   header += "Content-Type: text/xml\r\nContent-length: ";
00253 
00254   sprintf(buff,"%d\r\n\r\n", body.size());
00255 
00256   return header + buff;
00257 }

bool XmlRpcClient::generateRequest const char *  method,
XmlRpcValue const &  params
[protected, virtual]
 

00202 {
00203   std::string body = REQUEST_BEGIN;
00204   body += methodName;
00205   body += REQUEST_END_METHODNAME;
00206 
00207   // If params is an array, each element is a separate parameter
00208   if (params.valid()) {
00209     body += PARAMS_TAG;
00210     if (params.getType() == XmlRpcValue::TypeArray)
00211     {
00212       for (int i=0; i<params.size(); ++i) {
00213         body += PARAM_TAG;
00214         body += params[i].toXml();
00215         body += PARAM_ETAG;
00216       }
00217     }
00218     else
00219     {
00220       body += PARAM_TAG;
00221       body += params.toXml();
00222       body += PARAM_ETAG;
00223     }
00224       
00225     body += PARAMS_ETAG;
00226   }
00227   body += REQUEST_END;
00228 
00229   std::string header = generateHeader(body);
00230   XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.", 
00231                   header.length(), body.length());
00232 
00233   _request = header + body;
00234   return true;
00235 }

int XmlRpc::XmlRpcSource::getfd  )  const [inline, inherited]
 

Return the file descriptor being monitored.

00025 { return _fd; }

bool XmlRpc::XmlRpcSource::getKeepOpen  )  const [inline, inherited]
 

Return whether the file descriptor should be kept open if it is no longer monitored.

00030 { return _keepOpen; }

unsigned XmlRpcClient::handleEvent unsigned  eventType  )  [virtual]
 

Handle server responses. Called by the event dispatcher during execute.

Parameters:
eventType The type of event that occurred.
See also:
XmlRpcDispatch::EventType

Implements XmlRpc::XmlRpcSource.

00115 {
00116   if (eventType == XmlRpcDispatch::Exception)
00117   {
00118     if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
00119       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).", 
00120                        XmlRpcSocket::getErrorMsg().c_str());
00121     else
00122       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.", 
00123                         _connectionState, XmlRpcSocket::getErrorMsg().c_str());
00124     return 0;
00125   }
00126 
00127   if (_connectionState == WRITE_REQUEST)
00128     if ( ! writeRequest()) return 0;
00129 
00130   if (_connectionState == READ_HEADER)
00131     if ( ! readHeader()) return 0;
00132 
00133   if (_connectionState == READ_RESPONSE)
00134     if ( ! readResponse()) return 0;
00135 
00136   // This should probably always ask for Exception events too
00137   return (_connectionState == WRITE_REQUEST) 
00138         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
00139 }

bool XmlRpc::XmlRpcClient::isFault  )  const [inline]
 

Returns true if the result of the last execute() was a fault response.

00061 { return _isFault; }

bool XmlRpcClient::parseResponse XmlRpcValue result  )  [protected, virtual]
 

00388 {
00389   // Parse response xml into result
00390   int offset = 0;
00391   if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
00392     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
00393     return false;
00394   }
00395 
00396   
00397   // Expect either <params><param>... or <fault>...
00398   if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
00399        XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) 
00400        // || XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) //yzhang delete
00401       && (_isFault = true))
00402   {
00403     if ( ! result.fromXml(_response, &offset)) {
00404       XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
00405       _response = "";
00406       return false;
00407     }
00408   } else {
00409     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
00410     _response = "";
00411     return false;
00412   }
00413       
00414   _response = "";
00415   return result.valid();
00416 }

bool XmlRpcClient::readHeader  )  [protected, virtual]
 

00286 {
00287   // Read available data
00288   if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
00289        (_eof && _header.length() == 0)) {
00290 
00291     // If we haven't read any data yet and this is a keep-alive connection, the server may
00292     // have timed out, so we try one more time.
00293     if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
00294       XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
00295       XmlRpcSource::close();
00296       _connectionState = NO_CONNECTION;
00297       _eof = false;
00298       return setupConnection();
00299     }
00300 
00301     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
00302                       XmlRpcSocket::getErrorMsg().c_str(), getfd());
00303     return false;
00304   }
00305 
00306   XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
00307 
00308   char *hp = (char*)_header.c_str();  // Start of header
00309   char *ep = hp + _header.length();   // End of string
00310   char *bp = 0;                       // Start of body
00311   char *lp = 0;                       // Start of content-length value
00312 
00313   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
00314     if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
00315       lp = cp + 16;
00316     else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
00317       bp = cp + 4;
00318     else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
00319       bp = cp + 2;
00320   }
00321 
00322   // If we haven't gotten the entire header yet, return (keep reading)
00323   if (bp == 0) {
00324     if (_eof)          // EOF in the middle of a response is an error
00325     {
00326       XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
00327       return false;   // Close the connection
00328     }
00329     
00330     return true;  // Keep reading
00331   }
00332 
00333   // Decode content length
00334   if (lp == 0) {
00335     XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
00336     return false;   // We could try to figure it out by parsing as we read, but for now...
00337   }
00338 
00339   _contentLength = atoi(lp);
00340   if (_contentLength <= 0) {
00341     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
00342     return false;
00343   }
00344         
00345   XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
00346 
00347   // Otherwise copy non-header data to response buffer and set state to read response.
00348   _response = bp;
00349   _header = "";   // should parse out any interesting bits from the header (connection, etc)...
00350   _connectionState = READ_RESPONSE;
00351   return true;    // Continue monitoring this source
00352 }

bool XmlRpcClient::readResponse  )  [protected, virtual]
 

00357 {
00358   // If we dont have the entire response yet, read available data
00359   if (int(_response.length()) < _contentLength) {
00360     if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) {
00361       XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
00362       return false;
00363     }
00364 
00365     // If we haven't gotten the entire _response yet, return (keep reading)
00366     if (int(_response.length()) < _contentLength) {
00367       if (_eof) {
00368         XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
00369         return false;
00370       }
00371       return true;
00372     }
00373   }
00374 
00375   // Otherwise, parse and return the result
00376   XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
00377   XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
00378 
00379   _connectionState = IDLE;
00380 
00381   return false;    // Stop monitoring this source (causes return from work)
00382 }

void XmlRpc::XmlRpcSource::setfd int  fd  )  [inline, inherited]
 

Specify the file descriptor to monitor.

00027 { _fd = fd; }

void XmlRpc::XmlRpcSource::setKeepOpen bool  b = true  )  [inline, inherited]
 

Specify whether the file descriptor should be kept open if it is no longer monitored.

00032 { _keepOpen = b; }

bool XmlRpcClient::setupConnection  )  [protected, virtual]
 

00145 {
00146   // If an error occurred last time through, or if the server closed the connection, close our end
00147   if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
00148     close();
00149 
00150   _eof = false;
00151   if (_connectionState == NO_CONNECTION)
00152     if (! doConnect()) 
00153       return false;
00154 
00155   // Prepare to write the request
00156   _connectionState = WRITE_REQUEST;
00157   _bytesWritten = 0;
00158 
00159   // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
00160   _disp.removeSource(this);       // Make sure nothing is left over
00161   _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
00162 
00163   return true;
00164 }

bool XmlRpcClient::writeRequest  )  [protected, virtual]
 

00261 {
00262   if (_bytesWritten == 0)
00263     XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
00264 
00265   // Try to write the request
00266   if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
00267     XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
00268     return false;
00269   }
00270     
00271   XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
00272 
00273   // Wait for the result
00274   if (_bytesWritten == int(_request.length())) {
00275     _header = "";
00276     _response = "";
00277     _connectionState = READ_HEADER;
00278   }
00279   return true;
00280 }


Member Data Documentation

int XmlRpc::XmlRpcClient::_bytesWritten [protected]
 

ClientConnectionState XmlRpc::XmlRpcClient::_connectionState [protected]
 

int XmlRpc::XmlRpcClient::_contentLength [protected]
 

XmlRpcDispatch XmlRpc::XmlRpcClient::_disp [protected]
 

bool XmlRpc::XmlRpcClient::_eof [protected]
 

bool XmlRpc::XmlRpcClient::_executing [protected]
 

std::string XmlRpc::XmlRpcClient::_header [protected]
 

std::string XmlRpc::XmlRpcClient::_host [protected]
 

bool XmlRpc::XmlRpcClient::_isFault [protected]
 

int XmlRpc::XmlRpcClient::_port [protected]
 

std::string XmlRpc::XmlRpcClient::_request [protected]
 

std::string XmlRpc::XmlRpcClient::_response [protected]
 

int XmlRpc::XmlRpcClient::_sendAttempts [protected]
 

std::string XmlRpc::XmlRpcClient::_uri [protected]
 

const char XmlRpcClient::FAULT_TAG = "<fault>" [static]
 

const char XmlRpcClient::METHODRESPONSE_TAG = "<methodResponse>" [static]
 

const char XmlRpcClient::PARAM_ETAG = "</param>" [static]
 

const char XmlRpcClient::PARAM_TAG = "<param>" [static]
 

const char XmlRpcClient::PARAMS_ETAG = "</params>" [static]
 

const char XmlRpcClient::PARAMS_TAG = "<params>" [static]
 

const char XmlRpcClient::REQUEST_BEGIN [static]
 

Initial value:

 
  "<?xml version=\"1.0\"?>\r\n"
  "<methodCall><methodName>"

const char XmlRpcClient::REQUEST_END = "</methodCall>\r\n" [static]
 

const char XmlRpcClient::REQUEST_END_METHODNAME = "</methodName>\r\n" [static]
 


The documentation for this class was generated from the following files:
Generated on Wed Feb 2 19:25:16 2011 for BOSS6.5.5 by  doxygen 1.3.9.1