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