XmlRpcServerConnection.cpp
Go to the documentation of this file.
1 
3 
5 #include "xmlrpcpp/XmlRpc.h"
6 #ifndef MAKEDEPEND
7 # include <stdio.h>
8 # include <stdlib.h>
9 #ifndef _WINDOWS
10  # include <strings.h>
11 #endif
12 # include <string.h>
13 #endif
14 
15 using namespace XmlRpc;
16 
17 // Static data
18 const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>";
19 const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>";
20 const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>";
21 const char XmlRpcServerConnection::PARAM_TAG[] = "<param>";
22 const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>";
23 
24 const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall";
25 const std::string XmlRpcServerConnection::METHODNAME = "methodName";
26 const std::string XmlRpcServerConnection::PARAMS = "params";
27 
28 const std::string XmlRpcServerConnection::FAULTCODE = "faultCode";
29 const std::string XmlRpcServerConnection::FAULTSTRING = "faultString";
30 
31 
32 
33 // The server delegates handling client requests to a serverConnection object.
34 XmlRpcServerConnection::XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose /*= false*/) :
35  XmlRpcSource(fd, deleteOnClose)
36 {
37  XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
38  _server = server;
40  _contentLength = 0;
41  _bytesWritten = 0;
42  _keepAlive = true;
43 }
44 
45 
47 {
48  XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
50 }
51 
52 
53 // Handle input on the server socket by accepting the connection
54 // and reading the rpc request. Return true to continue to monitor
55 // the socket for events, false to remove it from the dispatcher.
56 unsigned
57 XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
58 {
60  if ( ! readHeader()) return 0;
61 
63  if ( ! readRequest()) return 0;
64 
66  if ( ! writeResponse()) return 0;
67 
68  return (_connectionState == WRITE_RESPONSE)
70 }
71 
72 
73 bool
75 {
76  // Read available data
77  bool eof;
78  if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof)) {
79  // Its only an error if we already have read some data
80  if (_header.length() > 0)
81  XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
82  return false;
83  }
84 
85  XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
86  char *hp = (char*)_header.c_str(); // Start of header
87  char *ep = hp + _header.length(); // End of string
88  char *bp = 0; // Start of body
89  char *lp = 0; // Start of content-length value
90  char *kp = 0; // Start of connection value
91 
92  for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
93  if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
94  lp = cp + 16;
95  else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
96  kp = cp + 12;
97  else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
98  bp = cp + 4;
99  else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
100  bp = cp + 2;
101  }
102 
103  // If we haven't gotten the entire header yet, return (keep reading)
104  if (bp == 0) {
105  // EOF in the middle of a request is an error, otherwise its ok
106  if (eof) {
107  XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
108  if (_header.length() > 0)
109  XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
110  return false; // Either way we close the connection
111  }
112 
113  return true; // Keep reading
114  }
115 
116  // Decode content length
117  if (lp == 0) {
118  XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
119  return false; // We could try to figure it out by parsing as we read, but for now...
120  }
121 
122  _contentLength = atoi(lp);
123  if (_contentLength <= 0) {
124  XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
125  return false;
126  }
127 
128  XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
129 
130  // Otherwise copy non-header data to request buffer and set state to read request.
131  _request = bp;
132 
133  // Parse out any interesting bits from the header (HTTP version, connection)
134  _keepAlive = true;
135  if (_header.find("HTTP/1.0") != std::string::npos) {
136  if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
137  _keepAlive = false; // Default for HTTP 1.0 is to close the connection
138  } else {
139  if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
140  _keepAlive = false;
141  }
142  XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
143 
144 
145  _header = "";
147  return true; // Continue monitoring this source
148 }
149 
150 bool
152 {
153  // If we dont have the entire request yet, read available data
154  if (int(_request.length()) < _contentLength) {
155  bool eof;
156  if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) {
157  XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
158  return false;
159  }
160 
161  // If we haven't gotten the entire request yet, return (keep reading)
162  if (int(_request.length()) < _contentLength) {
163  if (eof) {
164  XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
165  return false; // Either way we close the connection
166  }
167  return true;
168  }
169  }
170 
171  // Otherwise, parse and dispatch the request
172  XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
173  //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
174 
176 
177  return true; // Continue monitoring this source
178 }
179 
180 
181 bool
183 {
184  if (_response.length() == 0) {
185  executeRequest();
186  _bytesWritten = 0;
187  if (_response.length() == 0) {
188  XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
189  return false;
190  }
191  }
192 
193  // Try to write the response
194  if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) {
195  XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
196  return false;
197  }
198  XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
199 
200  // Prepare to read the next request
201  if (_bytesWritten == int(_response.length())) {
202  _header = "";
203  _request = "";
204  _response = "";
206  }
207 
208  return _keepAlive; // Continue monitoring this source if true
209 }
210 
211 // Run the method, generate _response string
212 void
214 {
215  XmlRpcValue params, resultValue;
216  std::string methodName = parseRequest(params);
217  XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'",
218  methodName.c_str());
219 
220  try {
221 
222  if ( ! executeMethod(methodName, params, resultValue) &&
223  ! executeMulticall(methodName, params, resultValue))
224  generateFaultResponse(methodName + ": unknown method name");
225  else
226  generateResponse(resultValue.toXml());
227 
228  } catch (const XmlRpcException& fault) {
229  XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.",
230  fault.getMessage().c_str());
231  generateFaultResponse(fault.getMessage(), fault.getCode());
232  }
233 }
234 
235 // Parse the method name and the argument values from the request.
236 std::string
238 {
239  int offset = 0; // Number of chars parsed from the request
240 
241  std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset);
242 
243  if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset))
244  {
245  int nArgs = 0;
246  while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) {
247  params[nArgs++] = XmlRpcValue(_request, &offset);
248  (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset);
249  }
250 
251  (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset);
252  }
253 
254  return methodName;
255 }
256 
257 // Execute a named method with the specified params.
258 bool
259 XmlRpcServerConnection::executeMethod(const std::string& methodName,
260  XmlRpcValue& params, XmlRpcValue& result)
261 {
262  XmlRpcServerMethod* method = _server->findMethod(methodName);
263 
264  if ( ! method) return false;
265 
266  method->execute(params, result);
267 
268  // Ensure a valid result value
269  if ( ! result.valid())
270  result = std::string();
271 
272  return true;
273 }
274 
275 // Execute multiple calls and return the results in an array.
276 bool
277 XmlRpcServerConnection::executeMulticall(const std::string& methodName,
278  XmlRpcValue& params, XmlRpcValue& result)
279 {
280  if (methodName != SYSTEM_MULTICALL) return false;
281 
282  // There ought to be 1 parameter, an array of structs
283  if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
284  throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)");
285 
286  int nc = params[0].size();
287  result.setSize(nc);
288 
289  for (int i=0; i<nc; ++i) {
290 
291  if ( ! params[0][i].hasMember(METHODNAME) ||
292  ! params[0][i].hasMember(PARAMS)) {
293  result[i][FAULTCODE] = -1;
294  result[i][FAULTSTRING] = SYSTEM_MULTICALL +
295  ": Invalid argument (expected a struct with members methodName and params)";
296  continue;
297  }
298 
299  const std::string& methodName = params[0][i][METHODNAME];
300  XmlRpcValue& methodParams = params[0][i][PARAMS];
301 
302  XmlRpcValue resultValue;
303  resultValue.setSize(1);
304  try {
305  if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
306  ! executeMulticall(methodName, params, resultValue[0]))
307  {
308  result[i][FAULTCODE] = -1;
309  result[i][FAULTSTRING] = methodName + ": unknown method name";
310  }
311  else
312  result[i] = resultValue;
313 
314  } catch (const XmlRpcException& fault) {
315  result[i][FAULTCODE] = fault.getCode();
316  result[i][FAULTSTRING] = fault.getMessage();
317  }
318  }
319 
320  return true;
321 }
322 
323 
324 // Create a response from results xml
325 void
326 XmlRpcServerConnection::generateResponse(std::string const& resultXml)
327 {
328  const char RESPONSE_1[] =
329  "<?xml version=\"1.0\"?>\r\n"
330  "<methodResponse><params><param>\r\n\t";
331  const char RESPONSE_2[] =
332  "\r\n</param></params></methodResponse>\r\n";
333 
334  std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
335  std::string header = generateHeader(body);
336 
337  _response = header + body;
338  XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str());
339 }
340 
341 // Prepend http headers
342 std::string
344 {
345  std::string header =
346  "HTTP/1.1 200 OK\r\n"
347  "Server: ";
348  header += XMLRPC_VERSION;
349  header += "\r\n"
350  "Content-Type: text/xml\r\n"
351  "Content-length: ";
352 
353  char buffLen[40];
354 #ifdef _MSC_VER
355  sprintf_s(buffLen,40,"%d\r\n\r\n", (int)body.size());
356 #else
357  sprintf(buffLen,"%d\r\n\r\n", (int)body.size());
358 #endif
359 
360  return header + buffLen;
361 }
362 
363 
364 void
365 XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode)
366 {
367  const char RESPONSE_1[] =
368  "<?xml version=\"1.0\"?>\r\n"
369  "<methodResponse><fault>\r\n\t";
370  const char RESPONSE_2[] =
371  "\r\n</fault></methodResponse>\r\n";
372 
373  XmlRpcValue faultStruct;
374  faultStruct[FAULTCODE] = errorCode;
375  faultStruct[FAULTSTRING] = errorMsg;
376  std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
377  std::string header = generateHeader(body);
378 
379  _response = header + body;
380 }
381 
const std::string & getMessage() const
Return the error message.
virtual void execute(XmlRpcValue &params, XmlRpcValue &result)=0
Execute the method. Subclasses must provide a definition for this method.
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
ServerConnectionState _connectionState
static const std::string SYSTEM_MULTICALL
int size() const
Return the size for string, base64, array, and struct values.
static std::string parseTag(const char *tag, std::string const &xml, int *offset)
Returns contents between <tag> and </tag>, updates offset to char after </tag>
Definition: XmlRpcUtil.cpp:109
XmlRpcServerMethod * findMethod(const std::string &name) const
Look up a method by name.
bool valid() const
Return true if the value has been set to something.
Definition: XmlRpcValue.h:108
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
int getCode() const
Return the error code.
static bool nbRead(int socket, std::string &s, bool *eof)
Read text from the specified socket. Returns false on error.
std::string generateHeader(std::string const &body)
bool executeMulticall(const std::string &methodName, XmlRpcValue &params, XmlRpcValue &result)
Type const & getType() const
Return the type of the value stored.
Definition: XmlRpcValue.h:111
bool executeMethod(const std::string &methodName, XmlRpcValue &params, XmlRpcValue &result)
static std::string getErrorMsg()
Returns message corresponding to last error.
static bool nbWrite(int socket, const std::string &s, int *bytesSoFar)
Write text to the specified socket. Returns false on error.
int getfd() const
Return the file descriptor being monitored.
Definition: XmlRpcSource.h:27
virtual ~XmlRpcServerConnection()
Destructor.
Abstract class representing a single RPC method.
static const std::string FAULTCODE
std::string toXml() const
Encode the Value in xml.
virtual void removeConnection(XmlRpcServerConnection *)
Remove a connection from the dispatcher.
void setSize(int size)
Specify the size for array values. Array values will grow beyond this size if needed.
Definition: XmlRpcValue.h:117
static const std::string FAULTSTRING
An RPC source represents a file descriptor to monitor.
Definition: XmlRpcSource.h:16
A class to handle XML RPC requests.
Definition: XmlRpcServer.h:39
static const std::string METHODNAME
void generateFaultResponse(std::string const &msg, int errorCode=-1)
void generateResponse(std::string const &resultXml)
virtual unsigned handleEvent(unsigned eventType)
const std::string header
XmlRpcServerConnection(int fd, XmlRpcServer *server, bool deleteOnClose=false)
Constructor.
std::string parseRequest(XmlRpcValue &params)
static void log(int level, const char *fmt,...)
Dump messages somewhere.
Definition: XmlRpcUtil.cpp:80
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