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  // avoid overly large or improperly formatted content-length
123  long int clength = 0;
124  clength = strtol(lp, NULL, 10);
125  if ((clength < 0) || (clength > __INT_MAX__)) {
126  XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified.");
127  return false;
128  }
129  _contentLength = int(clength);
130 
131  XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
132 
133  // Otherwise copy non-header data to request buffer and set state to read request.
134  _request = bp;
135 
136  // Parse out any interesting bits from the header (HTTP version, connection)
137  _keepAlive = true;
138  if (_header.find("HTTP/1.0") != std::string::npos) {
139  if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
140  _keepAlive = false; // Default for HTTP 1.0 is to close the connection
141  } else {
142  if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
143  _keepAlive = false;
144  }
145  XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
146 
147 
148  _header = "";
150  return true; // Continue monitoring this source
151 }
152 
153 bool
155 {
156  // If we dont have the entire request yet, read available data
157  if (int(_request.length()) < _contentLength) {
158  bool eof;
159  if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) {
160  XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
161  return false;
162  }
163  // Avoid an overly large request
164  if (_request.length() > size_t(__INT_MAX__)) {
165  XmlRpcUtil::error("XmlRpcServerConnection::readRequest: request length (%u) exceeds the maximum allowed size (%u)",
166  _request.length(), __INT_MAX__);
167  _request.resize(__INT_MAX__);
168  return false;
169  }
170 
171  // If we haven't gotten the entire request yet, return (keep reading)
172  if (int(_request.length()) < _contentLength) {
173  if (eof) {
174  XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
175  return false; // Either way we close the connection
176  }
177  return true;
178  }
179  }
180 
181  // Otherwise, parse and dispatch the request
182  XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
183  //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
184 
186 
187  return true; // Continue monitoring this source
188 }
189 
190 
191 bool
193 {
194  if (_response.length() == 0) {
195  executeRequest();
196  _bytesWritten = 0;
197  if (_response.length() == 0) {
198  XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
199  return false;
200  }
201  }
202 
203  // Try to write the response
204  if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) {
205  XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
206  return false;
207  }
208  XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
209 
210  // Prepare to read the next request
211  if (_bytesWritten == int(_response.length())) {
212  _header = "";
213  _request = "";
214  _response = "";
216  }
217 
218  return _keepAlive; // Continue monitoring this source if true
219 }
220 
221 // Run the method, generate _response string
222 void
224 {
225  XmlRpcValue params, resultValue;
226  std::string methodName = parseRequest(params);
227  XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'",
228  methodName.c_str());
229 
230  try {
231 
232  if ( ! executeMethod(methodName, params, resultValue) &&
233  ! executeMulticall(methodName, params, resultValue))
234  generateFaultResponse(methodName + ": unknown method name");
235  else
236  generateResponse(resultValue.toXml());
237 
238  } catch (const XmlRpcException& fault) {
239  XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.",
240  fault.getMessage().c_str());
241  generateFaultResponse(fault.getMessage(), fault.getCode());
242  }
243 }
244 
245 // Parse the method name and the argument values from the request.
246 std::string
248 {
249  int offset = 0; // Number of chars parsed from the request
250 
251  std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset);
252 
253  if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset))
254  {
255  int nArgs = 0;
256  while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) {
257  params[nArgs++] = XmlRpcValue(_request, &offset);
258  (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset);
259  }
260 
261  (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset);
262  }
263 
264  return methodName;
265 }
266 
267 // Execute a named method with the specified params.
268 bool
269 XmlRpcServerConnection::executeMethod(const std::string& methodName,
270  XmlRpcValue& params, XmlRpcValue& result)
271 {
272  XmlRpcServerMethod* method = _server->findMethod(methodName);
273 
274  if ( ! method) return false;
275 
276  method->execute(params, result);
277 
278  // Ensure a valid result value
279  if ( ! result.valid())
280  result = std::string();
281 
282  return true;
283 }
284 
285 // Execute multiple calls and return the results in an array.
286 bool
287 XmlRpcServerConnection::executeMulticall(const std::string& methodName,
288  XmlRpcValue& params, XmlRpcValue& result)
289 {
290  if (methodName != SYSTEM_MULTICALL) return false;
291 
292  // There ought to be 1 parameter, an array of structs
293  if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
294  throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)");
295 
296  int nc = params[0].size();
297  result.setSize(nc);
298 
299  for (int i=0; i<nc; ++i) {
300 
301  if ( ! params[0][i].hasMember(METHODNAME) ||
302  ! params[0][i].hasMember(PARAMS)) {
303  result[i][FAULTCODE] = -1;
304  result[i][FAULTSTRING] = SYSTEM_MULTICALL +
305  ": Invalid argument (expected a struct with members methodName and params)";
306  continue;
307  }
308 
309  const std::string& methodName = params[0][i][METHODNAME];
310  XmlRpcValue& methodParams = params[0][i][PARAMS];
311 
312  XmlRpcValue resultValue;
313  resultValue.setSize(1);
314  try {
315  if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
316  ! executeMulticall(methodName, params, resultValue[0]))
317  {
318  result[i][FAULTCODE] = -1;
319  result[i][FAULTSTRING] = methodName + ": unknown method name";
320  }
321  else
322  result[i] = resultValue;
323 
324  } catch (const XmlRpcException& fault) {
325  result[i][FAULTCODE] = fault.getCode();
326  result[i][FAULTSTRING] = fault.getMessage();
327  }
328  }
329 
330  return true;
331 }
332 
333 
334 // Create a response from results xml
335 void
336 XmlRpcServerConnection::generateResponse(std::string const& resultXml)
337 {
338  const char RESPONSE_1[] =
339  "<?xml version=\"1.0\"?>\r\n"
340  "<methodResponse><params><param>\r\n\t";
341  const char RESPONSE_2[] =
342  "\r\n</param></params></methodResponse>\r\n";
343 
344  std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
345  std::string header = generateHeader(body);
346 
347  // Avoid an overly large response
348  if ((header.length() + body.length()) > size_t(__INT_MAX__)) {
349  XmlRpcUtil::error("XmlRpcServerConnection::generateResponse: response length (%u) exceeds the maximum allowed size (%u).",
350  _response.length(), __INT_MAX__);
351  _response = "";
352  }
353  else {
354  _response = header + body;
355  XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str());
356  }
357 }
358 
359 // Prepend http headers
360 std::string
362 {
363  std::string header =
364  "HTTP/1.1 200 OK\r\n"
365  "Server: ";
366  header += XMLRPC_VERSION;
367  header += "\r\n"
368  "Content-Type: text/xml\r\n"
369  "Content-length: ";
370 
371  char buffLen[40];
372 #ifdef _MSC_VER
373  sprintf_s(buffLen,40,"%d\r\n\r\n", (int)body.size());
374 #else
375  sprintf(buffLen,"%d\r\n\r\n", (int)body.size());
376 #endif
377 
378  return header + buffLen;
379 }
380 
381 
382 void
383 XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode)
384 {
385  const char RESPONSE_1[] =
386  "<?xml version=\"1.0\"?>\r\n"
387  "<methodResponse><fault>\r\n\t";
388  const char RESPONSE_2[] =
389  "\r\n</fault></methodResponse>\r\n";
390 
391  XmlRpcValue faultStruct;
392  faultStruct[FAULTCODE] = errorCode;
393  faultStruct[FAULTSTRING] = errorMsg;
394  std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
395  std::string header = generateHeader(body);
396 
397  _response = header + body;
398 }
399 
const std::string & getMessage() const
Return the error message.
Definition: XmlRpc.h:39
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:114
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:129
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:117
bool executeMethod(const std::string &methodName, XmlRpcValue &params, XmlRpcValue &result)
static std::string getErrorMsg()
Returns message corresponding to last 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:123
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:37
static const std::string METHODNAME
void generateFaultResponse(std::string const &msg, int errorCode=-1)
static bool nbWrite(int socket, std::string &s, int *bytesSoFar)
Write text to the specified socket. Returns false on error.
void generateResponse(std::string const &resultXml)
virtual unsigned handleEvent(unsigned eventType)
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:145


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley, Austin Hendrix, Dirk Thomas
autogenerated on Mon Nov 2 2020 03:52:24