#include <XmlRpcClient.h>
Inheritance diagram for XmlRpc::XmlRpcClient:
Public Member Functions | |
virtual void | close () |
Close the connection. | |
bool | execute (const char *method, XmlRpcValue const ¶ms, 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 ¶ms) |
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 |
|
00086 { NO_CONNECTION, CONNECTING, WRITE_REQUEST, READ_HEADER, READ_RESPONSE, IDLE };
|
|
Construct a client to connect to the server at the specified host:port address
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 }
|
|
Destructor.
00049 { 00050 }
|
|
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 }
|
|
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 }
|
|
Execute the named procedure on the remote server.
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 }
|
|
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 }
|
|
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 }
|
|
Return the file descriptor being monitored.
00025 { return _fd; }
|
|
Return whether the file descriptor should be kept open if it is no longer monitored.
00030 { return _keepOpen; }
|
|
Handle server responses. Called by the event dispatcher during execute.
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 }
|
|
Returns true if the result of the last execute() was a fault response.
00061 { return _isFault; }
|
|
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 }
|
|
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 }
|
|
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 }
|
|
Specify the file descriptor to monitor.
00027 { _fd = fd; }
|
|
Specify whether the file descriptor should be kept open if it is no longer monitored.
00032 { _keepOpen = b; }
|
|
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 }
|
|
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 }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Initial value: "<?xml version=\"1.0\"?>\r\n" "<methodCall><methodName>" |
|
|
|
|