2
#include "XmlRpcServerConnection.h"
4
#include "XmlRpcSocket.h"
13
using namespace XmlRpc;
16
const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>";
17
const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>";
18
const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>";
19
const char XmlRpcServerConnection::PARAM_TAG[] = "<param>";
20
const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>";
22
const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall";
23
const std::string XmlRpcServerConnection::METHODNAME = "methodName";
24
const std::string XmlRpcServerConnection::PARAMS = "params";
26
const std::string XmlRpcServerConnection::FAULTCODE = "faultCode";
27
const std::string XmlRpcServerConnection::FAULTSTRING = "faultString";
31
// The server delegates handling client requests to a serverConnection object.
32
XmlRpcServerConnection::XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose /*= false*/) :
33
XmlRpcSource(fd, deleteOnClose)
35
XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
37
_connectionState = READ_HEADER;
42
XmlRpcServerConnection::~XmlRpcServerConnection()
44
XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
45
_server->removeConnection(this);
49
// Handle input on the server socket by accepting the connection
50
// and reading the rpc request. Return true to continue to monitor
51
// the socket for events, false to remove it from the dispatcher.
53
XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
55
if (_connectionState == READ_HEADER)
56
if ( ! readHeader()) return 0;
58
if (_connectionState == READ_REQUEST)
59
if ( ! readRequest()) return 0;
61
if (_connectionState == WRITE_RESPONSE)
62
if ( ! writeResponse()) return 0;
64
return (_connectionState == WRITE_RESPONSE)
65
? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
70
XmlRpcServerConnection::readHeader()
72
// Read available data
74
if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof)) {
75
// Its only an error if we already have read some data
76
if (_header.length() > 0)
77
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
81
XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
82
char *hp = (char*)_header.c_str(); // Start of header
83
char *ep = hp + _header.length(); // End of string
84
char *bp = 0; // Start of body
85
char *lp = 0; // Start of content-length value
86
char *kp = 0; // Start of connection value
88
for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
89
if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
91
else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
93
else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
95
else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
99
// If we haven't gotten the entire header yet, return (keep reading)
101
// EOF in the middle of a request is an error, otherwise its ok
103
XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
104
if (_header.length() > 0)
105
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
106
return false; // Either way we close the connection
109
return true; // Keep reading
112
// Decode content length
114
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
115
return false; // We could try to figure it out by parsing as we read, but for now...
118
_contentLength = atoi(lp);
119
if (_contentLength <= 0) {
120
XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
124
XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
126
// Otherwise copy non-header data to request buffer and set state to read request.
129
// Parse out any interesting bits from the header (HTTP version, connection)
131
if (_header.find("HTTP/1.0") != std::string::npos) {
132
if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
133
_keepAlive = false; // Default for HTTP 1.0 is to close the connection
135
if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
138
XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
142
_connectionState = READ_REQUEST;
143
return true; // Continue monitoring this source
147
XmlRpcServerConnection::readRequest()
149
// If we dont have the entire request yet, read available data
150
if (int(_request.length()) < _contentLength) {
152
if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) {
153
XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
157
// If we haven't gotten the entire request yet, return (keep reading)
158
if (int(_request.length()) < _contentLength) {
160
XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
161
return false; // Either way we close the connection
167
// Otherwise, parse and dispatch the request
168
XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
169
//XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
171
_connectionState = WRITE_RESPONSE;
173
return true; // Continue monitoring this source
178
XmlRpcServerConnection::writeResponse()
180
if (_response.length() == 0) {
183
if (_response.length() == 0) {
184
XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
189
// Try to write the response
190
if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) {
191
XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
194
XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
196
// Prepare to read the next request
197
if (_bytesWritten == int(_response.length())) {
201
_connectionState = READ_HEADER;
204
return _keepAlive; // Continue monitoring this source if true
207
// Run the method, generate _response string
209
XmlRpcServerConnection::executeRequest()
211
XmlRpcValue params, resultValue;
212
std::string methodName = parseRequest(params);
213
XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'",
218
if ( ! executeMethod(methodName, params, resultValue) &&
219
! executeMulticall(methodName, params, resultValue))
220
generateFaultResponse(methodName + ": unknown method name");
222
generateResponse(resultValue.toXml());
224
} catch (const XmlRpcException& fault) {
225
XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.",
226
fault.getMessage().c_str());
227
generateFaultResponse(fault.getMessage(), fault.getCode());
231
// Parse the method name and the argument values from the request.
233
XmlRpcServerConnection::parseRequest(XmlRpcValue& params)
235
int offset = 0; // Number of chars parsed from the request
237
std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset);
239
if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset))
242
while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) {
243
params[nArgs++] = XmlRpcValue(_request, &offset);
244
(void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset);
247
(void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset);
253
// Execute a named method with the specified params.
255
XmlRpcServerConnection::executeMethod(const std::string& methodName,
256
XmlRpcValue& params, XmlRpcValue& result)
258
XmlRpcServerMethod* method = _server->findMethod(methodName);
260
if ( ! method) return false;
262
method->execute(params, result);
264
// Ensure a valid result value
265
if ( ! result.valid())
266
result = std::string();
271
// Execute multiple calls and return the results in an array.
273
XmlRpcServerConnection::executeMulticall(const std::string& methodName,
274
XmlRpcValue& params, XmlRpcValue& result)
276
if (methodName != SYSTEM_MULTICALL) return false;
278
// There ought to be 1 parameter, an array of structs
279
if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
280
throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)");
282
int nc = params[0].size();
285
for (int i=0; i<nc; ++i) {
287
if ( ! params[0][i].hasMember(METHODNAME) ||
288
! params[0][i].hasMember(PARAMS)) {
289
result[i][FAULTCODE] = -1;
290
result[i][FAULTSTRING] = SYSTEM_MULTICALL +
291
": Invalid argument (expected a struct with members methodName and params)";
295
const std::string& methodName = params[0][i][METHODNAME];
296
XmlRpcValue& methodParams = params[0][i][PARAMS];
298
XmlRpcValue resultValue;
299
resultValue.setSize(1);
301
if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
302
! executeMulticall(methodName, params, resultValue[0]))
304
result[i][FAULTCODE] = -1;
305
result[i][FAULTSTRING] = methodName + ": unknown method name";
308
result[i] = resultValue;
310
} catch (const XmlRpcException& fault) {
311
result[i][FAULTCODE] = fault.getCode();
312
result[i][FAULTSTRING] = fault.getMessage();
320
// Create a response from results xml
322
XmlRpcServerConnection::generateResponse(std::string const& resultXml)
324
const char RESPONSE_1[] =
325
"<?xml version=\"1.0\"?>\r\n"
326
"<methodResponse><params><param>\r\n\t";
327
const char RESPONSE_2[] =
328
"\r\n</param></params></methodResponse>\r\n";
330
std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
331
std::string header = generateHeader(body);
333
_response = header + body;
334
XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str());
337
// Prepend http headers
339
XmlRpcServerConnection::generateHeader(std::string const& body)
342
"HTTP/1.1 200 OK\r\n"
344
header += XMLRPC_VERSION;
346
"Content-Type: text/xml\r\n"
350
sprintf(buffLen,"%d\r\n\r\n", body.size());
352
return header + buffLen;
357
XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode)
359
const char RESPONSE_1[] =
360
"<?xml version=\"1.0\"?>\r\n"
361
"<methodResponse><fault>\r\n\t";
362
const char RESPONSE_2[] =
363
"\r\n</fault></methodResponse>\r\n";
365
XmlRpcValue faultStruct;
366
faultStruct[FAULTCODE] = errorCode;
367
faultStruct[FAULTSTRING] = errorMsg;
368
std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
369
std::string header = generateHeader(body);
371
_response = header + body;