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