XmlRpcClient.cpp
Go to the documentation of this file.
00001 
00002 #include "XmlRpcClient.h"
00003 
00004 #include "XmlRpcSocket.h"
00005 #include "XmlRpc.h"
00006 
00007 #include <stdio.h>
00008 #include <stdlib.h>
00009 #ifndef _WINDOWS
00010         # include <strings.h>
00011 #endif
00012 #include <string.h>
00013 
00014 
00015 using namespace XmlRpc;
00016 
00017 // Static data
00018 const char XmlRpcClient::REQUEST_BEGIN[] = 
00019   "<?xml version=\"1.0\"?>\r\n"
00020   "<methodCall><methodName>";
00021 const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
00022 const char XmlRpcClient::PARAMS_TAG[] = "<params>";
00023 const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
00024 const char XmlRpcClient::PARAM_TAG[] = "<param>";
00025 const char XmlRpcClient::PARAM_ETAG[] =  "</param>";
00026 const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
00027 const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
00028 const char XmlRpcClient::FAULT_TAG[] = "<fault>";
00029 
00030 
00031 
00032 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
00033 {
00034   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
00035 
00036   _host = host;
00037   _port = port;
00038   if (uri)
00039     _uri = uri;
00040   else
00041     _uri = "/RPC2";
00042   _connectionState = NO_CONNECTION;
00043   _executing = false;
00044   _eof = false;
00045 
00046   // Default to keeping the connection open until an explicit close is done
00047   setKeepOpen();
00048 }
00049 
00050 
00051 XmlRpcClient::~XmlRpcClient()
00052 {
00053   this->close();
00054 }
00055 
00056 // Close the owned fd
00057 void
00058 XmlRpcClient::close()
00059 {
00060   XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
00061   _connectionState = NO_CONNECTION;
00062   _disp.exit();
00063   _disp.removeSource(this);
00064   XmlRpcSource::close();
00065 }
00066 
00067 
00068 // Clear the referenced flag even if exceptions or errors occur.
00069 struct ClearFlagOnExit {
00070   ClearFlagOnExit(bool& flag) : _flag(flag) {}
00071   ~ClearFlagOnExit() { _flag = false; }
00072   bool& _flag;
00073 };
00074 
00075 // Execute the named procedure on the remote server.
00076 // Params should be an array of the arguments for the method.
00077 // Returns true if the request was sent and a result received (although the result
00078 // might be a fault).
00079 bool
00080 XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
00081 {
00082   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
00083 
00084   // This is not a thread-safe operation, if you want to do multithreading, use separate
00085   // clients for each thread. If you want to protect yourself from multiple threads
00086   // accessing the same client, replace this code with a real mutex.
00087   if (_executing)
00088     return false;
00089 
00090   _executing = true;
00091   ClearFlagOnExit cf(_executing);
00092 
00093   _sendAttempts = 0;
00094   _isFault = false;
00095 
00096   if ( ! setupConnection())
00097     return false;
00098 
00099   if ( ! generateRequest(method, params))
00100     return false;
00101 
00102   result.clear();
00103   double msTime = -1.0;   // Process until exit is called
00104   _disp.work(msTime);
00105 
00106   if (_connectionState != IDLE || ! parseResponse(result))
00107     return false;
00108 
00109   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
00110   _response = "";
00111   return true;
00112 }
00113 
00114 // Execute the named procedure on the remote server, non-blocking.
00115 // Params should be an array of the arguments for the method.
00116 // Returns true if the request was sent and a result received (although the result
00117 // might be a fault).
00118 bool
00119 XmlRpcClient::executeNonBlock(const char* method, XmlRpcValue const& params)
00120 {
00121   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
00122 
00123   // This is not a thread-safe operation, if you want to do multithreading, use separate
00124   // clients for each thread. If you want to protect yourself from multiple threads
00125   // accessing the same client, replace this code with a real mutex.
00126   if (_executing)
00127     return false;
00128 
00129   _executing = true;
00130   ClearFlagOnExit cf(_executing);
00131 
00132   _sendAttempts = 0;
00133   _isFault = false;
00134 
00135   if ( ! setupConnection())
00136     return false;
00137 
00138   if ( ! generateRequest(method, params))
00139     return false;
00140 
00141   return true;
00142 }
00143 
00144 bool
00145 XmlRpcClient::executeCheckDone(XmlRpcValue& result)
00146 {
00147   result.clear();
00148   // Are we done yet?
00149   if (_connectionState != IDLE)
00150     return false;
00151   if (! parseResponse(result))
00152   {
00153     // Hopefully the caller can determine that parsing failed.
00154   }
00155   //XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
00156   _response = "";
00157   return true;
00158 }
00159 
00160 // XmlRpcSource interface implementation
00161 // Handle server responses. Called by the event dispatcher during execute.
00162 unsigned
00163 XmlRpcClient::handleEvent(unsigned eventType)
00164 {
00165   if (eventType == XmlRpcDispatch::Exception)
00166   {
00167     if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
00168       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).", 
00169                        XmlRpcSocket::getErrorMsg().c_str());
00170     else
00171       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.", 
00172                         _connectionState, XmlRpcSocket::getErrorMsg().c_str());
00173     return 0;
00174   }
00175 
00176   if (_connectionState == WRITE_REQUEST)
00177     if ( ! writeRequest()) return 0;
00178 
00179   if (_connectionState == READ_HEADER)
00180     if ( ! readHeader()) return 0;
00181 
00182   if (_connectionState == READ_RESPONSE)
00183     if ( ! readResponse()) return 0;
00184 
00185   // This should probably always ask for Exception events too
00186   return (_connectionState == WRITE_REQUEST) 
00187         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
00188 }
00189 
00190 
00191 // Create the socket connection to the server if necessary
00192 bool
00193 XmlRpcClient::setupConnection()
00194 {
00195   // If an error occurred last time through, or if the server closed the connection, close our end
00196   if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
00197     close();
00198 
00199   _eof = false;
00200   if (_connectionState == NO_CONNECTION)
00201     if (! doConnect()) 
00202       return false;
00203 
00204   // Prepare to write the request
00205   _connectionState = WRITE_REQUEST;
00206   _bytesWritten = 0;
00207 
00208   // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
00209   _disp.removeSource(this);       // Make sure nothing is left over
00210   _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
00211 
00212   return true;
00213 }
00214 
00215 
00216 // Connect to the xmlrpc server
00217 bool
00218 XmlRpcClient::doConnect()
00219 {
00220   int fd = XmlRpcSocket::socket();
00221   if (fd < 0)
00222   {
00223     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
00224     return false;
00225   }
00226 
00227   XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
00228   this->setfd(fd);
00229 
00230   // Don't block on connect/reads/writes
00231   if ( ! XmlRpcSocket::setNonBlocking(fd))
00232   {
00233     this->close();
00234     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
00235     return false;
00236   }
00237 
00238   if ( ! XmlRpcSocket::connect(fd, _host, _port))
00239   {
00240     this->close();
00241     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
00242     return false;
00243   }
00244 
00245   return true;
00246 }
00247 
00248 // Encode the request to call the specified method with the specified parameters into xml
00249 bool
00250 XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
00251 {
00252   std::string body = REQUEST_BEGIN;
00253   body += methodName;
00254   body += REQUEST_END_METHODNAME;
00255 
00256   // If params is an array, each element is a separate parameter
00257   if (params.valid()) {
00258     body += PARAMS_TAG;
00259     if (params.getType() == XmlRpcValue::TypeArray)
00260     {
00261       for (int i=0; i<params.size(); ++i) {
00262         body += PARAM_TAG;
00263         body += params[i].toXml();
00264         body += PARAM_ETAG;
00265       }
00266     }
00267     else
00268     {
00269       body += PARAM_TAG;
00270       body += params.toXml();
00271       body += PARAM_ETAG;
00272     }
00273       
00274     body += PARAMS_ETAG;
00275   }
00276   body += REQUEST_END;
00277 
00278   std::string header = generateHeader(body);
00279   XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.", 
00280                   header.length(), body.length());
00281 
00282   _request = header + body;
00283   return true;
00284 }
00285 
00286 // Prepend http headers
00287 std::string
00288 XmlRpcClient::generateHeader(std::string const& body)
00289 {
00290   std::string header = 
00291     "POST " + _uri + " HTTP/1.1\r\n"
00292     "User-Agent: ";
00293   header += XMLRPC_VERSION;
00294   header += "\r\nHost: ";
00295   header += _host;
00296 
00297   char buff[40];
00298 #ifdef _MSC_VER
00299   sprintf_s(buff,40,":%d\r\n", _port);
00300 #else
00301   sprintf(buff,":%d\r\n", _port);
00302 #endif
00303 
00304   header += buff;
00305   header += "Content-Type: text/xml\r\nContent-length: ";
00306 
00307 #ifdef _MSC_VER
00308   sprintf_s(buff,40,"%d\r\n\r\n", (int)body.size());
00309 #else
00310   sprintf(buff,"%d\r\n\r\n", (int)body.size());
00311 #endif
00312 
00313   return header + buff;
00314 }
00315 
00316 bool
00317 XmlRpcClient::writeRequest()
00318 {
00319   if (_bytesWritten == 0)
00320     XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
00321 
00322   // Try to write the request
00323   if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
00324     XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
00325     return false;
00326   }
00327     
00328   XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
00329 
00330   // Wait for the result
00331   if (_bytesWritten == int(_request.length())) {
00332     _header = "";
00333     _response = "";
00334     _connectionState = READ_HEADER;
00335   }
00336   return true;
00337 }
00338 
00339 
00340 // Read the header from the response
00341 bool
00342 XmlRpcClient::readHeader()
00343 {
00344   // Read available data
00345   if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
00346        (_eof && _header.length() == 0)) {
00347 
00348     // If we haven't read any data yet and this is a keep-alive connection, the server may
00349     // have timed out, so we try one more time.
00350     if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
00351       XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
00352       XmlRpcSource::close();
00353       _connectionState = NO_CONNECTION;
00354       _eof = false;
00355       return setupConnection();
00356     }
00357 
00358     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
00359                       XmlRpcSocket::getErrorMsg().c_str(), getfd());
00360     return false;
00361   }
00362 
00363   XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
00364 
00365   char *hp = (char*)_header.c_str();  // Start of header
00366   char *ep = hp + _header.length();   // End of string
00367   char *bp = 0;                       // Start of body
00368   char *lp = 0;                       // Start of content-length value
00369 
00370   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
00371     if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
00372       lp = cp + 16;
00373     else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
00374       bp = cp + 4;
00375     else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
00376       bp = cp + 2;
00377   }
00378 
00379   // If we haven't gotten the entire header yet, return (keep reading)
00380   if (bp == 0) {
00381     if (_eof)          // EOF in the middle of a response is an error
00382     {
00383       XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
00384       return false;   // Close the connection
00385     }
00386     
00387     return true;  // Keep reading
00388   }
00389 
00390   // Decode content length
00391   if (lp == 0) {
00392     XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
00393     return false;   // We could try to figure it out by parsing as we read, but for now...
00394   }
00395 
00396   _contentLength = atoi(lp);
00397   if (_contentLength <= 0) {
00398     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
00399     return false;
00400   }
00401         
00402   XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
00403 
00404   // Otherwise copy non-header data to response buffer and set state to read response.
00405   _response = bp;
00406   _header = "";   // should parse out any interesting bits from the header (connection, etc)...
00407   _connectionState = READ_RESPONSE;
00408   return true;    // Continue monitoring this source
00409 }
00410 
00411     
00412 bool
00413 XmlRpcClient::readResponse()
00414 {
00415   // If we dont have the entire response yet, read available data
00416   if (int(_response.length()) < _contentLength) {
00417     if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) {
00418       XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
00419       return false;
00420     }
00421 
00422     // If we haven't gotten the entire _response yet, return (keep reading)
00423     if (int(_response.length()) < _contentLength) {
00424       if (_eof) {
00425         XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
00426         return false;
00427       }
00428       return true;
00429     }
00430   }
00431 
00432   // Otherwise, parse and return the result
00433   XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
00434   XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
00435 
00436   _connectionState = IDLE;
00437 
00438   return false;    // Stop monitoring this source (causes return from work)
00439 }
00440 
00441 
00442 // Convert the response xml into a result value
00443 bool
00444 XmlRpcClient::parseResponse(XmlRpcValue& result)
00445 {
00446   // Parse response xml into result
00447   int offset = 0;
00448   if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
00449     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
00450     return false;
00451   }
00452 
00453   // Expect either <params><param>... or <fault>...
00454   if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
00455        XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
00456       (XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
00457   {
00458     if ( ! result.fromXml(_response, &offset)) {
00459       XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
00460       _response = "";
00461       return false;
00462     }
00463   } else {
00464     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
00465     _response = "";
00466     return false;
00467   }
00468       
00469   _response = "";
00470   return result.valid();
00471 }
00472 


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley
autogenerated on Thu Jun 6 2019 21:10:00