XmlRpcClient.cpp
Go to the documentation of this file.
1 
3 
5 #include "xmlrpcpp/XmlRpcUtil.h"
6 #include "xmlrpcpp/XmlRpcValue.h"
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #ifndef _WINDOWS
11  # include <strings.h>
12 #endif
13 #include <string.h>
14 #include <climits>
15 
16 
17 using namespace XmlRpc;
18 
19 // Static data
20 const char XmlRpcClient::REQUEST_BEGIN[] =
21  "<?xml version=\"1.0\"?>\r\n"
22  "<methodCall><methodName>";
23 const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
24 const char XmlRpcClient::PARAMS_TAG[] = "<params>";
25 const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
26 const char XmlRpcClient::PARAM_TAG[] = "<param>";
27 const char XmlRpcClient::PARAM_ETAG[] = "</param>";
28 const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
29 const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
30 const char XmlRpcClient::FAULT_TAG[] = "<fault>";
31 
32 
34  switch(state) {
35  case NO_CONNECTION:
36  return "NO_CONNECTION";
37  case CONNECTING:
38  return "CONNECTING";
39  case WRITE_REQUEST:
40  return "WRITE_REQUEST";
41  case READ_HEADER:
42  return "READ_HEADER";
43  case READ_RESPONSE:
44  return "READ_RESPONSE";
45  case IDLE:
46  return "IDLE";
47  default:
48  return "UNKNOWN";
49  }
50 }
51 
52 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
54  _host(host),
55  _port(port),
56  _sendAttempts(0),
57  _bytesWritten(0),
58  _executing(false),
59  _eof(false),
60  _isFault(false),
62 {
63  XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
64 
65  if (uri)
66  _uri = uri;
67  else
68  _uri = "/RPC2";
69 
70  // Default to keeping the connection open until an explicit close is done
71  setKeepOpen();
72 }
73 
74 
76 {
77  this->close();
78 }
79 
80 // Close the owned fd
81 void
83 {
84  XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
86  _disp.exit();
87  _disp.removeSource(this);
89 }
90 
91 
92 // Clear the referenced flag even if exceptions or errors occur.
94  ClearFlagOnExit(bool& flag) : _flag(flag) {}
95  ~ClearFlagOnExit() { _flag = false; }
96  bool& _flag;
97 };
98 
99 // Execute the named procedure on the remote server.
100 // Params should be an array of the arguments for the method.
101 // Returns true if the request was sent and a result received (although the result
102 // might be a fault).
103 bool
104 XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
105 {
106  XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %s).", method, connectionStateStr(_connectionState));
107 
108  // This is not a thread-safe operation, if you want to do multithreading, use separate
109  // clients for each thread. If you want to protect yourself from multiple threads
110  // accessing the same client, replace this code with a real mutex.
111  if (_executing)
112  return false;
113 
114  _executing = true;
116 
117  _sendAttempts = 0;
118  _isFault = false;
119 
120  if ( ! setupConnection())
121  return false;
122 
123  if ( ! generateRequest(method, params))
124  return false;
125 
126  result.clear();
127  double msTime = -1.0; // Process until exit is called
128  _disp.work(msTime);
129 
130  if (_connectionState != IDLE || ! parseResponse(result))
131  return false;
132 
133  XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
134  _response = "";
135  return true;
136 }
137 
138 // Execute the named procedure on the remote server, non-blocking.
139 // Params should be an array of the arguments for the method.
140 // Returns true if the request was sent and a result received (although the result
141 // might be a fault).
142 bool
143 XmlRpcClient::executeNonBlock(const char* method, XmlRpcValue const& params)
144 {
145  XmlRpcUtil::log(1, "XmlRpcClient::executeNonBlock: method %s (_connectionState %s).", method, connectionStateStr(_connectionState));
146 
147  // This is not a thread-safe operation, if you want to do multithreading, use separate
148  // clients for each thread. If you want to protect yourself from multiple threads
149  // accessing the same client, replace this code with a real mutex.
150  if (_executing)
151  return false;
152 
153  _executing = true;
155 
156  _sendAttempts = 0;
157  _isFault = false;
158 
159  if ( ! setupConnection())
160  return false;
161 
162  if ( ! generateRequest(method, params))
163  return false;
164 
165  return true;
166 }
167 
168 bool
170 {
171  result.clear();
172  // Are we done yet?
173  // If we lost connection, the call failed.
175  return true;
176  }
177 
178  // Otherwise, assume the call is still in progress.
179  if (_connectionState != IDLE) {
180  return false;
181  }
182 
183  if (! parseResponse(result))
184  {
185  // Hopefully the caller can determine that parsing failed.
186  }
187  //XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
188  _response = "";
189  return true;
190 }
191 
192 // XmlRpcSource interface implementation
193 // Handle server responses. Called by the event dispatcher during execute.
194 unsigned
195 XmlRpcClient::handleEvent(unsigned eventType)
196 {
197  if (eventType == XmlRpcDispatch::Exception)
198  {
200  XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
201  XmlRpcSocket::getErrorMsg().c_str());
202  else
203  XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %s): %s.",
205  XmlRpcSocket::getErrorMsg().c_str());
206  return 0;
207  }
208 
210  if ( ! writeRequest()) return 0;
211 
213  if ( ! readHeader()) return 0;
214 
216  if ( ! readResponse()) return 0;
217 
218  // This should probably always ask for Exception events too
219  return (_connectionState == WRITE_REQUEST)
221 }
222 
223 
224 // Create the socket connection to the server if necessary
225 bool
227 {
228  // If an error occurred last time through, or if the server closed the connection, close our end
230  close();
231 
232  _eof = false;
234  if (! doConnect())
235  return false;
236 
237  // Prepare to write the request
239  _bytesWritten = 0;
240 
241  // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
242  _disp.removeSource(this); // Make sure nothing is left over
244 
245  return true;
246 }
247 
248 
249 // Connect to the xmlrpc server
250 bool
252 {
253  int fd = XmlRpcSocket::socket();
254  if (fd < 0)
255  {
256  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
257  return false;
258  }
259 
260  XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
261  this->setfd(fd);
262 
263  // Don't block on connect/reads/writes
264  if ( ! XmlRpcSocket::setNonBlocking(fd))
265  {
266  this->close();
267  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
268  return false;
269  }
270 
271  if ( ! XmlRpcSocket::connect(fd, _host, _port))
272  {
273  this->close();
274  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
275  return false;
276  }
277 
278  return true;
279 }
280 
281 // Encode the request to call the specified method with the specified parameters into xml
282 bool
283 XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
284 {
285  std::string body = REQUEST_BEGIN;
286  body += methodName;
287  body += REQUEST_END_METHODNAME;
288 
289  // If params is an array, each element is a separate parameter
290  if (params.valid()) {
291  body += PARAMS_TAG;
292  if (params.getType() == XmlRpcValue::TypeArray)
293  {
294  for (int i=0; i<params.size(); ++i) {
295  body += PARAM_TAG;
296  body += params[i].toXml();
297  body += PARAM_ETAG;
298  }
299  }
300  else
301  {
302  body += PARAM_TAG;
303  body += params.toXml();
304  body += PARAM_ETAG;
305  }
306 
307  body += PARAMS_ETAG;
308  }
309  body += REQUEST_END;
310 
311  std::string header = generateHeader(body.length());
312  XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
313  header.length(), body.length());
314 
315  _request = header + body;
316  // Limit the size of the request to avoid integer overruns
317  if (_request.length() > size_t(INT_MAX)) {
318  XmlRpcUtil::error("XmlRpcClient::generateRequest: request length (%u) exceeds maximum allowed size (%u).",
319  _request.length(), INT_MAX);
320  _request.clear();
321  return false;
322  }
323  return true;
324 }
325 
326 // Prepend http headers
327 std::string
328 XmlRpcClient::generateHeader(size_t length) const
329 {
330  std::string header =
331  "POST " + _uri + " HTTP/1.1\r\n"
332  "User-Agent: ";
333  header += XMLRPC_VERSION;
334  header += "\r\nHost: ";
335  header += _host;
336 
337  char buff[40];
338  std::snprintf(buff,40,":%d\r\n", _port);
339 
340  header += buff;
341  header += "Content-Type: text/xml\r\nContent-length: ";
342 
343  std::snprintf(buff,40,"%zu\r\n\r\n", length);
344 
345  return header + buff;
346 }
347 
348 bool
350 {
351  if (_bytesWritten == 0)
352  XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
353 
354  // Try to write the request
355  if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
356  XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
357  // If the write fails, we had an unrecoverable error. Close the socket.
358  close();
359  return false;
360  }
361 
362  XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
363 
364  // Wait for the result
365  if (_bytesWritten == int(_request.length())) {
366  _header = "";
367  _response = "";
369  } else {
370  // On partial write, remove the portion of the output that was written from
371  // the request buffer.
372  _request = _request.substr(_bytesWritten);
373  _bytesWritten = 0;
374  }
375  return true;
376 }
377 
378 
379 // Read the header from the response
380 bool
382 {
383  // Read available data
384  if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
385  (_eof && _header.length() == 0)) {
386 
387  // If we haven't read any data yet and this is a keep-alive connection, the server may
388  // have timed out, so we try one more time.
389  if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
390  XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
393  _eof = false;
394  return setupConnection();
395  }
396 
397  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading "
398  "header (%s) on fd %d.",
399  XmlRpcSocket::getErrorMsg().c_str(), getfd());
400  // Read failed; this means the socket is in an unrecoverable state.
401  // Close the socket.
402  close();
403  return false;
404  }
405 
406  XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
407 
408  char *hp = (char*)_header.c_str(); // Start of header
409  char *ep = hp + _header.length(); // End of string
410  char *bp = 0; // Start of body
411  char *lp = 0; // Start of content-length value
412 
413  for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
414  if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
415  lp = cp + 16;
416  else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
417  bp = cp + 4;
418  else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
419  bp = cp + 2;
420  }
421 
422  // If we haven't gotten the entire header yet, return (keep reading)
423  if (bp == 0) {
424  if (_eof) // EOF in the middle of a response is an error
425  {
426  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
427  close();
428  return false; // Close the connection
429  }
430 
431  return true; // Keep reading
432  }
433 
434  // Decode content length
435  if (lp == 0) {
436  XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
437  // Close the socket because we can't make further use of it.
438  close();
439  return false; // We could try to figure it out by parsing as we read, but for now...
440  }
441 
442  // avoid overly large or improperly formatted content-length
443  long int clength = 0;
444  clength = strtol(lp, nullptr, 10);
445  if ((clength <= 0) || (clength > INT_MAX)) {
446  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified.");
447  // Close the socket because we can't make further use of it.
448  close();
449  return false;
450  }
451  _contentLength = int(clength);
452 
453  XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
454 
455  // Otherwise copy non-header data to response buffer and set state to read response.
456  _response = bp;
457  _header = ""; // should parse out any interesting bits from the header (connection, etc)...
459  return true; // Continue monitoring this source
460 }
461 
462 
463 bool
465 {
466  // If we dont have the entire response yet, read available data
467  if (int(_response.length()) < _contentLength) {
468  std::string buff;
469  if ( ! XmlRpcSocket::nbRead(this->getfd(), buff, &_eof)) {
470  XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
471  // nbRead returned an error, indicating that the socket is in a bad state.
472  // close it and stop monitoring this client.
473  close();
474  return false;
475  }
476  _response += buff;
477 
478  // Avoid an overly large response
479  if (_response.length() > size_t(INT_MAX)) {
480  XmlRpcUtil::error("XmlRpcClient::readResponse: response length (%u) exceeds the maximum allowed size (%u).",
481  _response.length(), INT_MAX);
482  _response.clear();
483  close();
484  return false;
485  }
486  // If we haven't gotten the entire _response yet, return (keep reading)
487  if (int(_response.length()) < _contentLength) {
488  if (_eof) {
489  XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
490  // nbRead returned an eof, indicating that the socket is disconnected.
491  // close it and stop monitoring this client.
492  close();
493  return false;
494  }
495  return true;
496  }
497  }
498 
499  // Otherwise, parse and return the result
500  XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
501  XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
502 
504 
505  return false; // Stop monitoring this source (causes return from work)
506 }
507 
508 
509 // Convert the response xml into a result value
510 bool
512 {
513  // Parse response xml into result
514  int offset = 0;
516  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
517  return false;
518  }
519 
520  // Expect either <params><param>... or <fault>...
523  (XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
524  {
525  if ( ! result.fromXml(_response, &offset)) {
526  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
527  _response = "";
528  return false;
529  }
530  } else {
531  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
532  _response = "";
533  return false;
534  }
535 
536  _response = "";
537  return result.valid();
538 }
539 
static const char REQUEST_BEGIN[]
Definition: XmlRpcClient.h:29
virtual bool generateRequest(const char *method, XmlRpcValue const &params)
virtual bool parseResponse(XmlRpcValue &result)
int getfd() const
Return the file descriptor being monitored.
Definition: XmlRpcSource.h:27
int size() const
Return the size for string, base64, array, and struct values.
RPC method arguments and results are represented by Values.
Definition: XmlRpcValue.h:24
XMLRPCPP_DECL const char XMLRPC_VERSION[]
Version identifier.
Definition: XmlRpcUtil.cpp:25
connected/data can be written without blocking
static const char PARAMS_TAG[]
Definition: XmlRpcClient.h:31
virtual bool readHeader()
void removeSource(XmlRpcSource *source)
static const char REQUEST_END[]
Definition: XmlRpcClient.h:35
bool valid() const
Return true if the value has been set to something.
Definition: XmlRpcValue.h:121
void setKeepOpen(bool b=true)
Specify whether the file descriptor should be kept open if it is no longer monitored.
Definition: XmlRpcSource.h:34
static void error(const char *fmt,...)
Dump error messages somewhere.
Definition: XmlRpcUtil.cpp:96
static bool findTag(const char *tag, std::string const &xml, int *offset)
Returns true if the tag is found and updates offset to the char after the tag.
Definition: XmlRpcUtil.cpp:130
static bool nbRead(int socket, std::string &s, bool *eof)
Read text from the specified socket. Returns false on error.
std::string _request
Definition: XmlRpcClient.h:105
void clear()
Erase the current value.
Definition: XmlRpcValue.h:79
static std::string getErrorMsg()
Returns message corresponding to last error.
virtual void close()
Close the connection.
XmlRpcDispatch _disp
Definition: XmlRpcClient.h:131
static const char PARAM_ETAG[]
Definition: XmlRpcClient.h:34
static bool nbWrite(int socket, const std::string &s, int *bytesSoFar)
Write text to the specified socket. Returns false on error.
static const char REQUEST_END_METHODNAME[]
Definition: XmlRpcClient.h:30
ClientConnectionState _connectionState
Definition: XmlRpcClient.h:91
virtual void close()
Close the owned fd. If deleteOnClose was specified at construction, the object is deleted...
void setfd(int fd)
Specify the file descriptor to monitor.
Definition: XmlRpcSource.h:29
Type const & getType() const
Return the type of the value stored.
Definition: XmlRpcValue.h:124
void work(double msTime)
static bool connect(int socket, const std::string &host, int port)
Connect a socket to a server (from a client)
static const char PARAMS_ETAG[]
Definition: XmlRpcClient.h:32
XmlRpcClient(const char *host, int port, const char *uri=0)
virtual ~XmlRpcClient()
Destructor.
static const char * connectionStateStr(ClientConnectionState state)
virtual bool doConnect()
static const char PARAM_TAG[]
Definition: XmlRpcClient.h:33
bool fromXml(std::string const &valueXml, int *offset)
Decode xml. Destroys any existing value.
static int socket()
Creates a stream (TCP) socket. Returns -1 on failure.
virtual unsigned handleEvent(unsigned eventType)
std::string toXml() const
Encode the Value in xml.
static const char METHODRESPONSE_TAG[]
Definition: XmlRpcClient.h:37
bool execute(const char *method, XmlRpcValue const &params, XmlRpcValue &result)
virtual std::string generateHeader(size_t length) const
void addSource(XmlRpcSource *source, unsigned eventMask)
bool getKeepOpen() const
Return whether the file descriptor should be kept open if it is no longer monitored.
Definition: XmlRpcSource.h:32
std::string _response
Definition: XmlRpcClient.h:107
bool executeNonBlock(const char *method, XmlRpcValue const &params)
out-of-band data has arrived
const std::string header
void exit()
Exit from work routine.
static bool setNonBlocking(int socket)
Sets a stream (TCP) socket to perform non-blocking IO. Returns false on failure.
virtual bool setupConnection()
virtual bool readResponse()
bool executeCheckDone(XmlRpcValue &result)
virtual bool writeRequest()
static void log(int level, const char *fmt,...)
Dump messages somewhere.
Definition: XmlRpcUtil.cpp:81
ClearFlagOnExit(bool &flag)
static const char FAULT_TAG[]
Definition: XmlRpcClient.h:38
static bool nextTagIs(const char *tag, std::string const &xml, int *offset)
Definition: XmlRpcUtil.cpp:146


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley, Austin Hendrix, Dirk Thomas
autogenerated on Wed Apr 28 2021 02:23:00