XmlRpcClient.cpp
Go to the documentation of this file.
1 
3 
5 #include "xmlrpcpp/XmlRpc.h"
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #ifndef _WINDOWS
10  # include <strings.h>
11 #endif
12 #include <string.h>
13 
14 
15 using namespace XmlRpc;
16 
17 // Static data
18 const char XmlRpcClient::REQUEST_BEGIN[] =
19  "<?xml version=\"1.0\"?>\r\n"
20  "<methodCall><methodName>";
21 const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
22 const char XmlRpcClient::PARAMS_TAG[] = "<params>";
23 const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
24 const char XmlRpcClient::PARAM_TAG[] = "<param>";
25 const char XmlRpcClient::PARAM_ETAG[] = "</param>";
26 const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
27 const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";
28 const char XmlRpcClient::FAULT_TAG[] = "<fault>";
29 
30 
31 
32 XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
33 {
34  XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
35 
36  _host = host;
37  _port = port;
38  if (uri)
39  _uri = uri;
40  else
41  _uri = "/RPC2";
43  _executing = false;
44  _eof = false;
45 
46  // Default to keeping the connection open until an explicit close is done
47  setKeepOpen();
48 }
49 
50 
52 {
53  this->close();
54 }
55 
56 // Close the owned fd
57 void
59 {
60  XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
62  _disp.exit();
63  _disp.removeSource(this);
65 }
66 
67 
68 // Clear the referenced flag even if exceptions or errors occur.
70  ClearFlagOnExit(bool& flag) : _flag(flag) {}
71  ~ClearFlagOnExit() { _flag = false; }
72  bool& _flag;
73 };
74 
75 // Execute the named procedure on the remote server.
76 // Params should be an array of the arguments for the method.
77 // Returns true if the request was sent and a result received (although the result
78 // might be a fault).
79 bool
80 XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
81 {
82  XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
83 
84  // This is not a thread-safe operation, if you want to do multithreading, use separate
85  // clients for each thread. If you want to protect yourself from multiple threads
86  // accessing the same client, replace this code with a real mutex.
87  if (_executing)
88  return false;
89 
90  _executing = true;
92 
93  _sendAttempts = 0;
94  _isFault = false;
95 
96  if ( ! setupConnection())
97  return false;
98 
99  if ( ! generateRequest(method, params))
100  return false;
101 
102  result.clear();
103  double msTime = -1.0; // Process until exit is called
104  _disp.work(msTime);
105 
106  if (_connectionState != IDLE || ! parseResponse(result))
107  return false;
108 
109  XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
110  _response = "";
111  return true;
112 }
113 
114 // Execute the named procedure on the remote server, non-blocking.
115 // Params should be an array of the arguments for the method.
116 // Returns true if the request was sent and a result received (although the result
117 // might be a fault).
118 bool
119 XmlRpcClient::executeNonBlock(const char* method, XmlRpcValue const& params)
120 {
121  XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
122 
123  // This is not a thread-safe operation, if you want to do multithreading, use separate
124  // clients for each thread. If you want to protect yourself from multiple threads
125  // accessing the same client, replace this code with a real mutex.
126  if (_executing)
127  return false;
128 
129  _executing = true;
131 
132  _sendAttempts = 0;
133  _isFault = false;
134 
135  if ( ! setupConnection())
136  return false;
137 
138  if ( ! generateRequest(method, params))
139  return false;
140 
141  return true;
142 }
143 
144 bool
146 {
147  result.clear();
148  // Are we done yet?
149  if (_connectionState != IDLE)
150  return false;
151  if (! parseResponse(result))
152  {
153  // Hopefully the caller can determine that parsing failed.
154  }
155  //XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
156  _response = "";
157  return true;
158 }
159 
160 // XmlRpcSource interface implementation
161 // Handle server responses. Called by the event dispatcher during execute.
162 unsigned
163 XmlRpcClient::handleEvent(unsigned eventType)
164 {
165  if (eventType == XmlRpcDispatch::Exception)
166  {
168  XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
169  XmlRpcSocket::getErrorMsg().c_str());
170  else
171  XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
173  return 0;
174  }
175 
177  if ( ! writeRequest()) return 0;
178 
180  if ( ! readHeader()) return 0;
181 
183  if ( ! readResponse()) return 0;
184 
185  // This should probably always ask for Exception events too
186  return (_connectionState == WRITE_REQUEST)
188 }
189 
190 
191 // Create the socket connection to the server if necessary
192 bool
194 {
195  // If an error occurred last time through, or if the server closed the connection, close our end
197  close();
198 
199  _eof = false;
201  if (! doConnect())
202  return false;
203 
204  // Prepare to write the request
206  _bytesWritten = 0;
207 
208  // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
209  _disp.removeSource(this); // Make sure nothing is left over
211 
212  return true;
213 }
214 
215 
216 // Connect to the xmlrpc server
217 bool
219 {
220  int fd = XmlRpcSocket::socket();
221  if (fd < 0)
222  {
223  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
224  return false;
225  }
226 
227  XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
228  this->setfd(fd);
229 
230  // Don't block on connect/reads/writes
231  if ( ! XmlRpcSocket::setNonBlocking(fd))
232  {
233  this->close();
234  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
235  return false;
236  }
237 
238  if ( ! XmlRpcSocket::connect(fd, _host, _port))
239  {
240  this->close();
241  XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
242  return false;
243  }
244 
245  return true;
246 }
247 
248 // Encode the request to call the specified method with the specified parameters into xml
249 bool
250 XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
251 {
252  std::string body = REQUEST_BEGIN;
253  body += methodName;
254  body += REQUEST_END_METHODNAME;
255 
256  // If params is an array, each element is a separate parameter
257  if (params.valid()) {
258  body += PARAMS_TAG;
259  if (params.getType() == XmlRpcValue::TypeArray)
260  {
261  for (int i=0; i<params.size(); ++i) {
262  body += PARAM_TAG;
263  body += params[i].toXml();
264  body += PARAM_ETAG;
265  }
266  }
267  else
268  {
269  body += PARAM_TAG;
270  body += params.toXml();
271  body += PARAM_ETAG;
272  }
273 
274  body += PARAMS_ETAG;
275  }
276  body += REQUEST_END;
277 
278  std::string header = generateHeader(body);
279  XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
280  header.length(), body.length());
281 
282  _request = header + body;
283  return true;
284 }
285 
286 // Prepend http headers
287 std::string
288 XmlRpcClient::generateHeader(std::string const& body)
289 {
290  std::string header =
291  "POST " + _uri + " HTTP/1.1\r\n"
292  "User-Agent: ";
293  header += XMLRPC_VERSION;
294  header += "\r\nHost: ";
295  header += _host;
296 
297  char buff[40];
298 #ifdef _MSC_VER
299  sprintf_s(buff,40,":%d\r\n", _port);
300 #else
301  sprintf(buff,":%d\r\n", _port);
302 #endif
303 
304  header += buff;
305  header += "Content-Type: text/xml\r\nContent-length: ";
306 
307 #ifdef _MSC_VER
308  sprintf_s(buff,40,"%d\r\n\r\n", (int)body.size());
309 #else
310  sprintf(buff,"%d\r\n\r\n", (int)body.size());
311 #endif
312 
313  return header + buff;
314 }
315 
316 bool
318 {
319  if (_bytesWritten == 0)
320  XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
321 
322  // Try to write the request
323  if ( ! XmlRpcSocket::nbWrite(this->getfd(), _request, &_bytesWritten)) {
324  XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
325  return false;
326  }
327 
328  XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
329 
330  // Wait for the result
331  if (_bytesWritten == int(_request.length())) {
332  _header = "";
333  _response = "";
335  }
336  return true;
337 }
338 
339 
340 // Read the header from the response
341 bool
343 {
344  // Read available data
345  if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
346  (_eof && _header.length() == 0)) {
347 
348  // If we haven't read any data yet and this is a keep-alive connection, the server may
349  // have timed out, so we try one more time.
350  if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
351  XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
354  _eof = false;
355  return setupConnection();
356  }
357 
358  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
359  XmlRpcSocket::getErrorMsg().c_str(), getfd());
360  return false;
361  }
362 
363  XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
364 
365  char *hp = (char*)_header.c_str(); // Start of header
366  char *ep = hp + _header.length(); // End of string
367  char *bp = 0; // Start of body
368  char *lp = 0; // Start of content-length value
369 
370  for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
371  if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
372  lp = cp + 16;
373  else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
374  bp = cp + 4;
375  else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
376  bp = cp + 2;
377  }
378 
379  // If we haven't gotten the entire header yet, return (keep reading)
380  if (bp == 0) {
381  if (_eof) // EOF in the middle of a response is an error
382  {
383  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
384  return false; // Close the connection
385  }
386 
387  return true; // Keep reading
388  }
389 
390  // Decode content length
391  if (lp == 0) {
392  XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
393  return false; // We could try to figure it out by parsing as we read, but for now...
394  }
395 
396  _contentLength = atoi(lp);
397  if (_contentLength <= 0) {
398  XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
399  return false;
400  }
401 
402  XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
403 
404  // Otherwise copy non-header data to response buffer and set state to read response.
405  _response = bp;
406  _header = ""; // should parse out any interesting bits from the header (connection, etc)...
408  return true; // Continue monitoring this source
409 }
410 
411 
412 bool
414 {
415  // If we dont have the entire response yet, read available data
416  if (int(_response.length()) < _contentLength) {
417  if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) {
418  XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
419  return false;
420  }
421 
422  // If we haven't gotten the entire _response yet, return (keep reading)
423  if (int(_response.length()) < _contentLength) {
424  if (_eof) {
425  XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
426  return false;
427  }
428  return true;
429  }
430  }
431 
432  // Otherwise, parse and return the result
433  XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
434  XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
435 
437 
438  return false; // Stop monitoring this source (causes return from work)
439 }
440 
441 
442 // Convert the response xml into a result value
443 bool
445 {
446  // Parse response xml into result
447  int offset = 0;
449  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
450  return false;
451  }
452 
453  // Expect either <params><param>... or <fault>...
456  (XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true)))
457  {
458  if ( ! result.fromXml(_response, &offset)) {
459  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
460  _response = "";
461  return false;
462  }
463  } else {
464  XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
465  _response = "";
466  return false;
467  }
468 
469  _response = "";
470  return result.valid();
471 }
472 
static const char REQUEST_BEGIN[]
Definition: XmlRpcClient.h:29
virtual bool generateRequest(const char *method, XmlRpcValue const &params)
Definition: XmlRpc.h:39
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:103
static bool connect(int socket, std::string &host, int port)
Connect a socket to a server (from a client)
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:127
static const char PARAM_ETAG[]
Definition: XmlRpcClient.h:34
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 const char PARAMS_ETAG[]
Definition: XmlRpcClient.h:32
XmlRpcClient(const char *host, int port, const char *uri=0)
virtual ~XmlRpcClient()
Destructor.
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(std::string const &body)
virtual unsigned handleEvent(unsigned eventType)
static bool nbWrite(int socket, std::string &s, int *bytesSoFar)
Write text to the specified socket. Returns false on error.
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:105
bool executeNonBlock(const char *method, XmlRpcValue const &params)
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
autogenerated on Sat Oct 5 2019 03:58:22