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