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  _header = "";
132  return false;
133  }
134 
135  // close() if server does not supports HTTP1.1
136  // otherwise, reusing the socket to write leads to a SIGPIPE because
137  // the remote server could shut down the corresponding socket.
138  if (_header.find("HTTP/1.1 200 OK", 0, 15) != 0) {
139  close();
140  }
141 
142  XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
143  _header = "";
144  _response = "";
145  return true;
146 }
147 
148 // Execute the named procedure on the remote server, non-blocking.
149 // Params should be an array of the arguments for the method.
150 // Returns true if the request was sent and a result received (although the result
151 // might be a fault).
152 bool
153 XmlRpcClient::executeNonBlock(const char* method, XmlRpcValue const& params)
154 {
155  XmlRpcUtil::log(1, "XmlRpcClient::executeNonBlock: method %s (_connectionState %s).", method, connectionStateStr(_connectionState));
156 
157  // This is not a thread-safe operation, if you want to do multithreading, use separate
158  // clients for each thread. If you want to protect yourself from multiple threads
159  // accessing the same client, replace this code with a real mutex.
160  if (_executing)
161  return false;
162 
163  _executing = true;
165 
166  _sendAttempts = 0;
167  _isFault = false;
168 
169  if ( ! setupConnection())
170  return false;
171 
172  if ( ! generateRequest(method, params))
173  return false;
174 
175  return true;
176 }
177 
178 bool
180 {
181  result.clear();
182  // Are we done yet?
183  // If we lost connection, the call failed.
185  return true;
186  }
187 
188  // Otherwise, assume the call is still in progress.
189  if (_connectionState != IDLE) {
190  return false;
191  }
192 
193  if (! parseResponse(result))
194  {
195  // Hopefully the caller can determine that parsing failed.
196  }
197  //XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
198  _response = "";
199  return true;
200 }
201 
202 // XmlRpcSource interface implementation
203 // Handle server responses. Called by the event dispatcher during execute.
204 unsigned
205 XmlRpcClient::handleEvent(unsigned eventType)
206 {
207  if (eventType == XmlRpcDispatch::Exception)
208  {
210  XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
211  XmlRpcSocket::getErrorMsg().c_str());
212  else
213  XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %s): %s.",
215  XmlRpcSocket::getErrorMsg().c_str());
216  return 0;
217  }
218 
220  if ( ! writeRequest()) return 0;
221 
223  if ( ! readHeader()) return 0;
224 
226  if ( ! readResponse()) return 0;
227 
228  // This should probably always ask for Exception events too
229  return (_connectionState == WRITE_REQUEST)
231 }
232 
233 
234 // Create the socket connection to the server if necessary
235 bool
237 {
238  // If an error occurred last time through, or if the server closed the connection, close our end
240  close();
241 
242  _eof = false;
244  if (! doConnect())
245  return false;
246 
247  // Prepare to write the request
249  _bytesWritten = 0;
250 
251  // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
252  _disp.removeSource(this); // Make sure nothing is left over
254 
255  return true;
256 }
257 
258 
259 // Connect to the xmlrpc server
260 bool
262 {
263  int fd = XmlRpcSocket::socket();
264  if (fd < 0)
265  {
266  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
267  return false;
268  }
269 
270  XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
271  this->setfd(fd);
272 
273  // Don't block on connect/reads/writes
274  if ( ! XmlRpcSocket::setNonBlocking(fd))
275  {
276  this->close();
277  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
278  return false;
279  }
280 
281  if ( ! XmlRpcSocket::connect(fd, _host, _port))
282  {
283  this->close();
284  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
285  return false;
286  }
287 
288  return true;
289 }
290 
291 // Encode the request to call the specified method with the specified parameters into xml
292 bool
293 XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
294 {
295  std::string body = REQUEST_BEGIN;
296  body += methodName;
297  body += REQUEST_END_METHODNAME;
298 
299  // If params is an array, each element is a separate parameter
300  if (params.valid()) {
301  body += PARAMS_TAG;
302  if (params.getType() == XmlRpcValue::TypeArray)
303  {
304  for (int i=0; i<params.size(); ++i) {
305  body += PARAM_TAG;
306  body += params[i].toXml();
307  body += PARAM_ETAG;
308  }
309  }
310  else
311  {
312  body += PARAM_TAG;
313  body += params.toXml();
314  body += PARAM_ETAG;
315  }
316 
317  body += PARAMS_ETAG;
318  }
319  body += REQUEST_END;
320 
321  std::string header = generateHeader(body.length());
322  XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
323  header.length(), body.length());
324 
325  _request = header + body;
326  // Limit the size of the request to avoid integer overruns
327  if (_request.length() > size_t(INT_MAX)) {
328  XmlRpcUtil::error("XmlRpcClient::generateRequest: request length (%u) exceeds maximum allowed size (%u).",
329  _request.length(), INT_MAX);
330  _request.clear();
331  return false;
332  }
333  return true;
334 }
335 
336 // Prepend http headers
337 std::string
338 XmlRpcClient::generateHeader(size_t length) const
339 {
340  std::string header =
341  "POST " + _uri + " HTTP/1.1\r\n"
342  "User-Agent: ";
343  header += XMLRPC_VERSION;
344  header += "\r\nHost: ";
345  header += _host;
346 
347  char buff[40];
348  std::snprintf(buff,40,":%d\r\n", _port);
349 
350  header += buff;
351  header += "Content-Type: text/xml\r\nContent-length: ";
352 
353  std::snprintf(buff,40,"%zu\r\n\r\n", length);
354 
355  return header + buff;
356 }
357 
358 bool
360 {
361  if (_bytesWritten == 0)
362  XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
363 
364  // Try to write the request
365  if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
366  XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
367  // If the write fails, we had an unrecoverable error. Close the socket.
368  close();
369  return false;
370  }
371 
372  XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
373 
374  // Wait for the result
375  if (_bytesWritten == int(_request.length())) {
376  _header = "";
377  _response = "";
379  } else {
380  // On partial write, remove the portion of the output that was written from
381  // the request buffer.
382  _request = _request.substr(_bytesWritten);
383  _bytesWritten = 0;
384  }
385  return true;
386 }
387 
388 
389 // Read the header from the response
390 bool
392 {
393  // Read available data
394  if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
395  (_eof && _header.length() == 0)) {
396 
397  // If we haven't read any data yet and this is a keep-alive connection, the server may
398  // have timed out, so we try one more time.
399  if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
400  XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
403  _eof = false;
404  return setupConnection();
405  }
406 
407  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading "
408  "header (%s) on fd %d.",
409  XmlRpcSocket::getErrorMsg().c_str(), getfd());
410  // Read failed; this means the socket is in an unrecoverable state.
411  // Close the socket.
412  close();
413  return false;
414  }
415 
416  XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
417 
418  char *hp = (char*)_header.c_str(); // Start of header
419  char *ep = hp + _header.length(); // End of string
420  char *bp = 0; // Start of body
421  char *lp = 0; // Start of content-length value
422 
423  for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
424  if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
425  lp = cp + 16;
426  else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
427  bp = cp + 4;
428  else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
429  bp = cp + 2;
430  }
431 
432  // If we haven't gotten the entire header yet, return (keep reading)
433  if (bp == 0) {
434  if (_eof) // EOF in the middle of a response is an error
435  {
436  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
437  close();
438  return false; // Close the connection
439  }
440 
441  return true; // Keep reading
442  }
443 
444  // Decode content length
445  if (lp == 0) {
446  XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
447  // Close the socket because we can't make further use of it.
448  close();
449  return false; // We could try to figure it out by parsing as we read, but for now...
450  }
451 
452  // avoid overly large or improperly formatted content-length
453  long int clength = 0;
454  clength = strtol(lp, nullptr, 10);
455  if ((clength <= 0) || (clength > INT_MAX)) {
456  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified.");
457  // Close the socket because we can't make further use of it.
458  close();
459  return false;
460  }
461  _contentLength = int(clength);
462 
463  XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
464 
465  // Otherwise copy non-header data to response buffer and set state to read response.
466  _response = bp;
468  return true; // Continue monitoring this source
469 }
470 
471 
472 bool
474 {
475  // If we dont have the entire response yet, read available data
476  if (int(_response.length()) < _contentLength) {
477  std::string buff;
478  if ( ! XmlRpcSocket::nbRead(this->getfd(), buff, &_eof)) {
479  XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
480  // nbRead returned an error, indicating that the socket is in a bad state.
481  // close it and stop monitoring this client.
482  close();
483  return false;
484  }
485  _response += buff;
486 
487  // Avoid an overly large response
488  if (_response.length() > size_t(INT_MAX)) {
489  XmlRpcUtil::error("XmlRpcClient::readResponse: response length (%u) exceeds the maximum allowed size (%u).",
490  _response.length(), INT_MAX);
491  _response.clear();
492  close();
493  return false;
494  }
495  // If we haven't gotten the entire _response yet, return (keep reading)
496  if (int(_response.length()) < _contentLength) {
497  if (_eof) {
498  XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
499  // nbRead returned an eof, indicating that the socket is disconnected.
500  // close it and stop monitoring this client.
501  close();
502  return false;
503  }
504  return true;
505  }
506  }
507 
508  // Otherwise, parse and return the result
509  XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
510  XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
511 
513 
514  return false; // Stop monitoring this source (causes return from work)
515 }
516 
517 
518 // Convert the response xml into a result value
519 bool
521 {
522  // Parse response xml into result
523  int offset = 0;
525  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
526  return false;
527  }
528 
529  // Expect either <params><param>... or <fault>...
532  (XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
533  {
534  if ( ! result.fromXml(_response, &offset)) {
535  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
536  _response = "";
537  return false;
538  }
539  } else {
540  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
541  _response = "";
542  return false;
543  }
544 
545  _response = "";
546  return result.valid();
547 }
548 
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:133
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:150


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley, Austin Hendrix, Dirk Thomas
autogenerated on Mon Feb 28 2022 23:33:22