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
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
00025 const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
00026 const char XmlRpcClient::FAULT_TAG[] = "<fault>";
00027
00028
00029
00030 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri)
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
00045 setKeepOpen();
00046 }
00047
00048
00049 XmlRpcClient::~XmlRpcClient()
00050 {
00051 }
00052
00053
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
00066 struct ClearFlagOnExit {
00067 ClearFlagOnExit(bool& flag) : _flag(flag) {}
00068 ~ClearFlagOnExit() { _flag = false; }
00069 bool& _flag;
00070 };
00071
00072
00073
00074
00075
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
00081
00082
00083
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.;
00100
00101 _disp.work(msTime);
00102
00103 if (_connectionState != IDLE )
00104
00105 return false;
00106
00107 XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
00108 _response = "";
00109 return true;
00110 }
00111
00112
00113
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
00138 return (_connectionState == WRITE_REQUEST)
00139 ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
00140 }
00141
00142
00143
00144 bool
00145 XmlRpcClient::setupConnection()
00146 {
00147
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
00157 _connectionState = WRITE_REQUEST;
00158 _bytesWritten = 0;
00159
00160
00161 _disp.removeSource(this);
00162 _disp.addSource(this, XmlRpcDispatch::WritableEvent | XmlRpcDispatch::Exception);
00163
00164 return true;
00165 }
00166
00167
00168
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
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
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
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
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
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
00275 if (_bytesWritten == int(_request.length())) {
00276 _header = "";
00277 _response = "";
00278 _connectionState = READ_HEADER;
00279 }
00280 return true;
00281 }
00282
00283
00284
00285 bool
00286 XmlRpcClient::readHeader()
00287 {
00288
00289 if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
00290 (_eof && _header.length() == 0)) {
00291
00292
00293
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();
00310 char *ep = hp + _header.length();
00311 char *bp = 0;
00312 char *lp = 0;
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
00324 if (bp == 0) {
00325 if (_eof)
00326 {
00327 XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
00328 return false;
00329 }
00330
00331 return true;
00332 }
00333
00334
00335 if (lp == 0) {
00336 XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
00337 return false;
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
00349 _response = bp;
00350 _header = "";
00351 _connectionState = READ_RESPONSE;
00352 return true;
00353 }
00354
00355
00356 bool
00357 XmlRpcClient::readResponse()
00358 {
00359
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
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
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;
00383 }
00384
00385
00386
00387 bool
00388 XmlRpcClient::parseResponse(XmlRpcValue& result)
00389 {
00390
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
00399 if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
00400 XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset))
00401
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