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
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)
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
00047 setKeepOpen();
00048 }
00049
00050
00051 XmlRpcClient::~XmlRpcClient()
00052 {
00053 this->close();
00054 }
00055
00056
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
00069 struct ClearFlagOnExit {
00070 ClearFlagOnExit(bool& flag) : _flag(flag) {}
00071 ~ClearFlagOnExit() { _flag = false; }
00072 bool& _flag;
00073 };
00074
00075
00076
00077
00078
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
00085
00086
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;
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
00115
00116
00117
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
00124
00125
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
00149 if (_connectionState != IDLE)
00150 return false;
00151 if (! parseResponse(result))
00152 {
00153
00154 }
00155
00156 _response = "";
00157 return true;
00158 }
00159
00160
00161
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
00186 return (_connectionState == WRITE_REQUEST)
00187 ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
00188 }
00189
00190
00191
00192 bool
00193 XmlRpcClient::setupConnection()
00194 {
00195
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
00205 _connectionState = WRITE_REQUEST;
00206 _bytesWritten = 0;
00207
00208
00209 _disp.removeSource(this);
00210 _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
00211
00212 return true;
00213 }
00214
00215
00216
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
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
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
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
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
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
00331 if (_bytesWritten == int(_request.length())) {
00332 _header = "";
00333 _response = "";
00334 _connectionState = READ_HEADER;
00335 }
00336 return true;
00337 }
00338
00339
00340
00341 bool
00342 XmlRpcClient::readHeader()
00343 {
00344
00345 if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
00346 (_eof && _header.length() == 0)) {
00347
00348
00349
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();
00366 char *ep = hp + _header.length();
00367 char *bp = 0;
00368 char *lp = 0;
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
00380 if (bp == 0) {
00381 if (_eof)
00382 {
00383 XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
00384 return false;
00385 }
00386
00387 return true;
00388 }
00389
00390
00391 if (lp == 0) {
00392 XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
00393 return false;
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
00405 _response = bp;
00406 _header = "";
00407 _connectionState = READ_RESPONSE;
00408 return true;
00409 }
00410
00411
00412 bool
00413 XmlRpcClient::readResponse()
00414 {
00415
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
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
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;
00439 }
00440
00441
00442
00443 bool
00444 XmlRpcClient::parseResponse(XmlRpcValue& result)
00445 {
00446
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
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