XmlRpcClient.cc

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