Home | History | Annotate | Download | only in src
      1 
      2 #include "XmlRpcClient.h"
      3 
      4 #include "XmlRpcSocket.h"
      5 #include "XmlRpc.h"
      6 
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 
     10 
     11 using namespace XmlRpc;
     12 
     13 // Static data
     14 const char XmlRpcClient::REQUEST_BEGIN[] =
     15   "<?xml version=\"1.0\"?>\r\n"
     16   "<methodCall><methodName>";
     17 const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
     18 const char XmlRpcClient::PARAMS_TAG[] = "<params>";
     19 const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
     20 const char XmlRpcClient::PARAM_TAG[] = "<param>";
     21 const char XmlRpcClient::PARAM_ETAG[] =  "</param>";
     22 const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
     23 const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
     24 const char XmlRpcClient::FAULT_TAG[] = "<fault>";
     25 
     26 
     27 
     28 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
     29 {
     30   XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
     31 
     32   _host = host;
     33   _port = port;
     34   if (uri)
     35     _uri = uri;
     36   else
     37     _uri = "/RPC2";
     38   _connectionState = NO_CONNECTION;
     39   _executing = false;
     40   _eof = false;
     41 
     42   // Default to keeping the connection open until an explicit close is done
     43   setKeepOpen();
     44 }
     45 
     46 
     47 XmlRpcClient::~XmlRpcClient()
     48 {
     49 }
     50 
     51 // Close the owned fd
     52 void
     53 XmlRpcClient::close()
     54 {
     55   XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
     56   _connectionState = NO_CONNECTION;
     57   _disp.exit();
     58   _disp.removeSource(this);
     59   XmlRpcSource::close();
     60 }
     61 
     62 
     63 // Clear the referenced flag even if exceptions or errors occur.
     64 struct ClearFlagOnExit {
     65   ClearFlagOnExit(bool& flag) : _flag(flag) {}
     66   ~ClearFlagOnExit() { _flag = false; }
     67   bool& _flag;
     68 };
     69 
     70 // Execute the named procedure on the remote server.
     71 // Params should be an array of the arguments for the method.
     72 // Returns true if the request was sent and a result received (although the result
     73 // might be a fault).
     74 bool
     75 XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
     76 {
     77   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
     78 
     79   // This is not a thread-safe operation, if you want to do multithreading, use separate
     80   // clients for each thread. If you want to protect yourself from multiple threads
     81   // accessing the same client, replace this code with a real mutex.
     82   if (_executing)
     83     return false;
     84 
     85   _executing = true;
     86   ClearFlagOnExit cf(_executing);
     87 
     88   _sendAttempts = 0;
     89   _isFault = false;
     90 
     91   if ( ! setupConnection())
     92     return false;
     93 
     94   if ( ! generateRequest(method, params))
     95     return false;
     96 
     97   result.clear();
     98   double msTime = -1.0;   // Process until exit is called
     99   _disp.work(msTime);
    100 
    101   if (_connectionState != IDLE || ! parseResponse(result))
    102     return false;
    103 
    104   XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
    105   _response = "";
    106   return true;
    107 }
    108 
    109 // XmlRpcSource interface implementation
    110 // Handle server responses. Called by the event dispatcher during execute.
    111 unsigned
    112 XmlRpcClient::handleEvent(unsigned eventType)
    113 {
    114   if (eventType == XmlRpcDispatch::Exception)
    115   {
    116     if (_connectionState == WRITE_REQUEST && _bytesWritten == 0)
    117       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
    118                        XmlRpcSocket::getErrorMsg().c_str());
    119     else
    120       XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
    121                         _connectionState, XmlRpcSocket::getErrorMsg().c_str());
    122     return 0;
    123   }
    124 
    125   if (_connectionState == WRITE_REQUEST)
    126     if ( ! writeRequest()) return 0;
    127 
    128   if (_connectionState == READ_HEADER)
    129     if ( ! readHeader()) return 0;
    130 
    131   if (_connectionState == READ_RESPONSE)
    132     if ( ! readResponse()) return 0;
    133 
    134   // This should probably always ask for Exception events too
    135   return (_connectionState == WRITE_REQUEST)
    136         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
    137 }
    138 
    139 
    140 // Create the socket connection to the server if necessary
    141 bool
    142 XmlRpcClient::setupConnection()
    143 {
    144   // If an error occurred last time through, or if the server closed the connection, close our end
    145   if ((_connectionState != NO_CONNECTION && _connectionState != IDLE) || _eof)
    146     close();
    147 
    148   _eof = false;
    149   if (_connectionState == NO_CONNECTION)
    150     if (! doConnect())
    151       return false;
    152 
    153   // Prepare to write the request
    154   _connectionState = WRITE_REQUEST;
    155   _bytesWritten = 0;
    156 
    157   // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
    158   _disp.removeSource(this);       // Make sure nothing is left over
    159   _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
    160 
    161   return true;
    162 }
    163 
    164 
    165 // Connect to the xmlrpc server
    166 bool
    167 XmlRpcClient::doConnect()
    168 {
    169   int fd = XmlRpcSocket::socket();
    170   if (fd < 0)
    171   {
    172     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
    173     return false;
    174   }
    175 
    176   XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
    177   this->setfd(fd);
    178 
    179   // Don't block on connect/reads/writes
    180   if ( ! XmlRpcSocket::setNonBlocking(fd))
    181   {
    182     this->close();
    183     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
    184     return false;
    185   }
    186 
    187   if ( ! XmlRpcSocket::connect(fd, _host, _port))
    188   {
    189     this->close();
    190     XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
    191     return false;
    192   }
    193 
    194   return true;
    195 }
    196 
    197 // Encode the request to call the specified method with the specified parameters into xml
    198 bool
    199 XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
    200 {
    201   std::string body = REQUEST_BEGIN;
    202   body += methodName;
    203   body += REQUEST_END_METHODNAME;
    204 
    205   // If params is an array, each element is a separate parameter
    206   if (params.valid()) {
    207     body += PARAMS_TAG;
    208     if (params.getType() == XmlRpcValue::TypeArray)
    209     {
    210       for (int i=0; i<params.size(); ++i) {
    211         body += PARAM_TAG;
    212         body += params[i].toXml();
    213         body += PARAM_ETAG;
    214       }
    215     }
    216     else
    217     {
    218       body += PARAM_TAG;
    219       body += params.toXml();
    220       body += PARAM_ETAG;
    221     }
    222 
    223     body += PARAMS_ETAG;
    224   }
    225   body += REQUEST_END;
    226 
    227   std::string header = generateHeader(body);
    228   XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
    229                   header.length(), body.length());
    230 
    231   _request = header + body;
    232   return true;
    233 }
    234 
    235 // Prepend http headers
    236 std::string
    237 XmlRpcClient::generateHeader(std::string const& body)
    238 {
    239   std::string header =
    240     "POST " + _uri + " HTTP/1.1\r\n"
    241     "User-Agent: ";
    242   header += XMLRPC_VERSION;
    243   header += "\r\nHost: ";
    244   header += _host;
    245 
    246   char buff[40];
    247   sprintf(buff,":%d\r\n", _port);
    248 
    249   header += buff;
    250   header += "Content-Type: text/xml\r\nContent-length: ";
    251 
    252   sprintf(buff,"%d\r\n\r\n", (int)body.size());
    253 
    254   return header + buff;
    255 }
    256 
    257 bool
    258 XmlRpcClient::writeRequest()
    259 {
    260   if (_bytesWritten == 0)
    261     XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
    262 
    263   // Try to write the request
    264   if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
    265     XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
    266     return false;
    267   }
    268 
    269   XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
    270 
    271   // Wait for the result
    272   if (_bytesWritten == int(_request.length())) {
    273     _header = "";
    274     _response = "";
    275     _connectionState = READ_HEADER;
    276   }
    277   return true;
    278 }
    279 
    280 
    281 // Read the header from the response
    282 bool
    283 XmlRpcClient::readHeader()
    284 {
    285   // Read available data
    286   if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
    287        (_eof && _header.length() == 0)) {
    288 
    289     // If we haven't read any data yet and this is a keep-alive connection, the server may
    290     // have timed out, so we try one more time.
    291     if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
    292       XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
    293       XmlRpcSource::close();
    294       _connectionState = NO_CONNECTION;
    295       _eof = false;
    296       return setupConnection();
    297     }
    298 
    299     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
    300                       XmlRpcSocket::getErrorMsg().c_str(), getfd());
    301     return false;
    302   }
    303 
    304   XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
    305 
    306   char *hp = (char*)_header.c_str();  // Start of header
    307   char *ep = hp + _header.length();   // End of string
    308   char *bp = 0;                       // Start of body
    309   char *lp = 0;                       // Start of content-length value
    310 
    311   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
    312     if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
    313       lp = cp + 16;
    314     else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
    315       bp = cp + 4;
    316     else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
    317       bp = cp + 2;
    318   }
    319 
    320   // If we haven't gotten the entire header yet, return (keep reading)
    321   if (bp == 0) {
    322     if (_eof)          // EOF in the middle of a response is an error
    323     {
    324       XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
    325       return false;   // Close the connection
    326     }
    327 
    328     return true;  // Keep reading
    329   }
    330 
    331   // Decode content length
    332   if (lp == 0) {
    333     XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
    334     return false;   // We could try to figure it out by parsing as we read, but for now...
    335   }
    336 
    337   _contentLength = atoi(lp);
    338   if (_contentLength <= 0) {
    339     XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
    340     return false;
    341   }
    342 
    343   XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
    344 
    345   // Otherwise copy non-header data to response buffer and set state to read response.
    346   _response = bp;
    347   _header = "";   // should parse out any interesting bits from the header (connection, etc)...
    348   _connectionState = READ_RESPONSE;
    349   return true;    // Continue monitoring this source
    350 }
    351 
    352 
    353 bool
    354 XmlRpcClient::readResponse()
    355 {
    356   // If we dont have the entire response yet, read available data
    357   if (int(_response.length()) < _contentLength) {
    358     if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) {
    359       XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
    360       return false;
    361     }
    362 
    363     // If we haven't gotten the entire _response yet, return (keep reading)
    364     if (int(_response.length()) < _contentLength) {
    365       if (_eof) {
    366         XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
    367         return false;
    368       }
    369       return true;
    370     }
    371   }
    372 
    373   // Otherwise, parse and return the result
    374   XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
    375   XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
    376 
    377   _connectionState = IDLE;
    378 
    379   return false;    // Stop monitoring this source (causes return from work)
    380 }
    381 
    382 
    383 // Convert the response xml into a result value
    384 bool
    385 XmlRpcClient::parseResponse(XmlRpcValue& result)
    386 {
    387   // Parse response xml into result
    388   int offset = 0;
    389   if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
    390     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
    391     return false;
    392   }
    393 
    394   // Expect either <params><param>... or <fault>...
    395   if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
    396        XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
    397       (XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
    398   {
    399     if ( ! result.fromXml(_response, &offset)) {
    400       XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
    401       _response = "";
    402       return false;
    403     }
    404   } else {
    405     XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
    406     _response = "";
    407     return false;
    408   }
    409 
    410   _response = "";
    411   return result.valid();
    412 }
    413 
    414