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

Generated on Tue Nov 29 23:14:44 2016 for BOSS_7.0.2 by  doxygen 1.4.7