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


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley, Austin Hendrix
autogenerated on Sun Feb 3 2019 03:29:51