CGEM BOSS 6.6.5.g
BESIII Offline Software System
Loading...
Searching...
No Matches
XmlRpcClient.cpp
Go to the documentation of this file.
1
2#include "XmlRpcClient.h"
3
4#include "XmlRpcSocket.h"
5#include "XmlRpc.h"
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <iostream>
10#include <cstring>
11
12using namespace XmlRpc;
13
14// Static data
15const char XmlRpcClient::REQUEST_BEGIN[] =
16 "<?xml version=\"1.0\"?>\r\n"
17 "<methodCall><methodName>";
18const char XmlRpcClient::REQUEST_END_METHODNAME[] = "</methodName>\r\n";
19const char XmlRpcClient::PARAMS_TAG[] = "<params>";
20const char XmlRpcClient::PARAMS_ETAG[] = "</params>";
21const char XmlRpcClient::PARAM_TAG[] = "<param>";
22const char XmlRpcClient::PARAM_ETAG[] = "</param>";
23const char XmlRpcClient::REQUEST_END[] = "</methodCall>\r\n";
24//const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse";//yzhang add
25const char XmlRpcClient::METHODRESPONSE_TAG[] = "<methodResponse>";//yzhang delete
26const char XmlRpcClient::FAULT_TAG[] = "<fault>";
27
28
29
30XmlRpcClient::XmlRpcClient(const char* host, int port, const char* uri/*=0*/)
31{
32 XmlRpcUtil::log(1, "XmlRpcClient new client: host %s, port %d.", host, port);
33
34 _host = host;
35 _port = port;
36 if (uri)
37 _uri = uri;
38 else
39 _uri = "/RPC2";
41 _executing = false;
42 _eof = false;
43
44 // Default to keeping the connection open until an explicit close is done
46}
47
48
50{
51}
52
53// Close the owned fd
54void
56{
57 XmlRpcUtil::log(4, "XmlRpcClient::close: fd %d.", getfd());
59 _disp.exit();
60 _disp.removeSource(this);
62}
63
64
65// Clear the referenced flag even if exceptions or errors occur.
67 ClearFlagOnExit(bool& flag) : _flag(flag) {}
68 ~ClearFlagOnExit() { _flag = false; }
69 bool& _flag;
70};
71
72// Execute the named procedure on the remote server.
73// Params should be an array of the arguments for the method.
74// Returns true if the request was sent and a result received (although the result
75// might be a fault).
76bool
77XmlRpcClient::execute(const char* method, XmlRpcValue const& params, XmlRpcValue& result)
78{
79 XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s (_connectionState %d).", method, _connectionState);
80 //std::cout<< "_connectionState "<<_connectionState << std::endl;//yzhang debug
81 // This is not a thread-safe operation, if you want to do multithreading, use separate
82 // clients for each thread. If you want to protect yourself from multiple threads
83 // accessing the same client, replace this code with a real mutex.
84 if (_executing)
85 return false;
86
87 _executing = true;
89
90 _sendAttempts = 0;
91 _isFault = false;
92
93 if ( ! setupConnection())
94 return false;
95
96 if ( ! generateRequest(method, params))
97 return false;
98 result.clear();
99 double msTime = 5.; // Process until exit is called //yzhang change
100 //double msTime = -1.0; // Process until exit is called //yzhang delete
101 _disp.work(msTime);
102
103 if (_connectionState != IDLE )//yzhang add
104 //if (_connectionState != IDLE || ! parseResponse(result))//yzhang delete
105 return false; //yzhang delete
106
107 XmlRpcUtil::log(1, "XmlRpcClient::execute: method %s completed.", method);
108 _response = "";
109 return true;
110}
111
112// XmlRpcSource interface implementation
113// Handle server responses. Called by the event dispatcher during execute.
114unsigned
115XmlRpcClient::handleEvent(unsigned eventType)
116{
117 if (eventType == XmlRpcDispatch::Exception)
118 {
120 XmlRpcUtil::error("Error in XmlRpcClient::handleEvent: could not connect to server (%s).",
121 XmlRpcSocket::getErrorMsg().c_str());
122 else
123 XmlRpcUtil::error("Error in XmlRpcClient::handleEvent (state %d): %s.",
125 return 0;
126 }
127
129 if ( ! writeRequest()) return 0;
130
132 if ( ! readHeader()) return 0;
133
135 if ( ! readResponse()) return 0;
136
137 // This should probably always ask for Exception events too
140}
141
142
143// Create the socket connection to the server if necessary
144bool
146{
147 // If an error occurred last time through, or if the server closed the connection, close our end
149 close();
150
151 _eof = false;
153 if (! doConnect())
154 return false;
155
156 // Prepare to write the request
158 _bytesWritten = 0;
159
160 // Notify the dispatcher to listen on this source (calls handleEvent when the socket is writable)
161 _disp.removeSource(this); // Make sure nothing is left over
163
164 return true;
165}
166
167
168// Connect to the xmlrpc server
169bool
171{
172 int fd = XmlRpcSocket::socket();
173 if (fd < 0)
174 {
175 XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
176 return false;
177 }
178
179 XmlRpcUtil::log(3, "XmlRpcClient::doConnect: fd %d.", fd);
180 this->setfd(fd);
181
182 // Don't block on connect/reads/writes
184 {
185 this->close();
186 XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not set socket to non-blocking IO mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
187 return false;
188 }
189
190 if ( ! XmlRpcSocket::connect(fd, _host, _port))
191 {
192 this->close();
193 XmlRpcUtil::error("Error in XmlRpcClient::doConnect: Could not connect to server (%s).", XmlRpcSocket::getErrorMsg().c_str());
194 return false;
195 }
196
197 return true;
198}
199
200// Encode the request to call the specified method with the specified parameters into xml
201bool
202XmlRpcClient::generateRequest(const char* methodName, XmlRpcValue const& params)
203{
204 std::string body = REQUEST_BEGIN;
205 body += methodName;
207
208 // If params is an array, each element is a separate parameter
209 if (params.valid()) {
210 body += PARAMS_TAG;
211 if (params.getType() == XmlRpcValue::TypeArray)
212 {
213 for (int i=0; i<params.size(); ++i) {
214 body += PARAM_TAG;
215 body += params[i].toXml();
216 body += PARAM_ETAG;
217 }
218 }
219 else
220 {
221 body += PARAM_TAG;
222 body += params.toXml();
223 body += PARAM_ETAG;
224 }
225
226 body += PARAMS_ETAG;
227 }
228 body += REQUEST_END;
229
230 std::string header = generateHeader(body);
231 XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
232 header.length(), body.length());
233
234 _request = header + body;
235 return true;
236}
237
238// Prepend http headers
239std::string
240XmlRpcClient::generateHeader(std::string const& body)
241{
242 std::string header =
243 "POST " + _uri + " HTTP/1.1\r\n"
244 "User-Agent: ";
245 header += XMLRPC_VERSION;
246 header += "\r\nHost: ";
247 header += _host;
248
249 char buff[40];
250 sprintf(buff,":%d\r\n", _port);
251
252 header += buff;
253 header += "Content-Type: text/xml\r\nContent-length: ";
254
255 sprintf(buff,"%d\r\n\r\n", body.size());
256
257 return header + buff;
258}
259
260bool
262{
263 if (_bytesWritten == 0)
264 XmlRpcUtil::log(5, "XmlRpcClient::writeRequest (attempt %d):\n%s\n", _sendAttempts+1, _request.c_str());
265
266 // Try to write the request
268 XmlRpcUtil::error("Error in XmlRpcClient::writeRequest: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
269 return false;
270 }
271
272 XmlRpcUtil::log(3, "XmlRpcClient::writeRequest: wrote %d of %d bytes.", _bytesWritten, _request.length());
273
274 // Wait for the result
275 if (_bytesWritten == int(_request.length())) {
276 _header = "";
277 _response = "";
279 }
280 return true;
281}
282
283
284// Read the header from the response
285bool
287{
288 // Read available data
289 if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &_eof) ||
290 (_eof && _header.length() == 0)) {
291
292 // If we haven't read any data yet and this is a keep-alive connection, the server may
293 // have timed out, so we try one more time.
294 if (getKeepOpen() && _header.length() == 0 && _sendAttempts++ == 0) {
295 XmlRpcUtil::log(4, "XmlRpcClient::readHeader: re-trying connection");
298 _eof = false;
299 return setupConnection();
300 }
301
302 XmlRpcUtil::error("Error in XmlRpcClient::readHeader: error while reading header (%s) on fd %d.",
303 XmlRpcSocket::getErrorMsg().c_str(), getfd());
304 return false;
305 }
306
307 XmlRpcUtil::log(4, "XmlRpcClient::readHeader: client has read %d bytes", _header.length());
308
309 char *hp = (char*)_header.c_str(); // Start of header
310 char *ep = hp + _header.length(); // End of string
311 char *bp = 0; // Start of body
312 char *lp = 0; // Start of content-length value
313
314 for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
315 if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
316 lp = cp + 16;
317 else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
318 bp = cp + 4;
319 else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
320 bp = cp + 2;
321 }
322
323 // If we haven't gotten the entire header yet, return (keep reading)
324 if (bp == 0) {
325 if (_eof) // EOF in the middle of a response is an error
326 {
327 XmlRpcUtil::error("Error in XmlRpcClient::readHeader: EOF while reading header");
328 return false; // Close the connection
329 }
330
331 return true; // Keep reading
332 }
333
334 // Decode content length
335 if (lp == 0) {
336 XmlRpcUtil::error("Error XmlRpcClient::readHeader: No Content-length specified");
337 return false; // We could try to figure it out by parsing as we read, but for now...
338 }
339
340 _contentLength = atoi(lp);
341 if (_contentLength <= 0) {
342 XmlRpcUtil::error("Error in XmlRpcClient::readHeader: Invalid Content-length specified (%d).", _contentLength);
343 return false;
344 }
345
346 XmlRpcUtil::log(4, "client read content length: %d", _contentLength);
347
348 // Otherwise copy non-header data to response buffer and set state to read response.
349 _response = bp;
350 _header = ""; // should parse out any interesting bits from the header (connection, etc)...
352 return true; // Continue monitoring this source
353}
354
355
356bool
358{
359 // If we dont have the entire response yet, read available data
360 if (int(_response.length()) < _contentLength) {
361 if ( ! XmlRpcSocket::nbRead(this->getfd(), _response, &_eof)) {
362 XmlRpcUtil::error("Error in XmlRpcClient::readResponse: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
363 return false;
364 }
365
366 // If we haven't gotten the entire _response yet, return (keep reading)
367 if (int(_response.length()) < _contentLength) {
368 if (_eof) {
369 XmlRpcUtil::error("Error in XmlRpcClient::readResponse: EOF while reading response");
370 return false;
371 }
372 return true;
373 }
374 }
375
376 // Otherwise, parse and return the result
377 XmlRpcUtil::log(3, "XmlRpcClient::readResponse (read %d bytes)", _response.length());
378 XmlRpcUtil::log(5, "response:\n%s", _response.c_str());
379
381
382 return false; // Stop monitoring this source (causes return from work)
383}
384
385
386// Convert the response xml into a result value
387bool
389{
390 // Parse response xml into result
391 int offset = 0;
393 XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
394 return false;
395 }
396
397
398 // Expect either <params><param>... or <fault>...
401 // || XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) //yzhang delete
402 && (_isFault = true))
403 {
404 if ( ! result.fromXml(_response, &offset)) {
405 XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
406 _response = "";
407 return false;
408 }
409 } else {
410 XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
411 _response = "";
412 return false;
413 }
414
415 _response = "";
416 return result.valid();
417}
418
static const char PARAM_ETAG[]
Definition: XmlRpcClient.h:33
static const char PARAMS_ETAG[]
Definition: XmlRpcClient.h:31
static const char REQUEST_END[]
Definition: XmlRpcClient.h:34
XmlRpcClient(const char *host, int port, const char *uri=0)
std::string _header
Definition: XmlRpcClient.h:96
bool execute(const char *method, XmlRpcValue const &params, XmlRpcValue &result)
XmlRpcDispatch _disp
Definition: XmlRpcClient.h:119
std::string _request
Definition: XmlRpcClient.h:95
virtual bool doConnect()
virtual std::string generateHeader(std::string const &body)
virtual void close()
Close the connection.
static const char REQUEST_END_METHODNAME[]
Definition: XmlRpcClient.h:29
ClientConnectionState _connectionState
Definition: XmlRpcClient.h:87
virtual bool parseResponse(XmlRpcValue &result)
static const char REQUEST_BEGIN[]
Definition: XmlRpcClient.h:28
static const char PARAM_TAG[]
Definition: XmlRpcClient.h:32
virtual bool setupConnection()
virtual bool writeRequest()
virtual bool readResponse()
virtual ~XmlRpcClient()
Destructor.
virtual bool readHeader()
static const char METHODRESPONSE_TAG[]
Definition: XmlRpcClient.h:36
static const char FAULT_TAG[]
Definition: XmlRpcClient.h:37
static const char PARAMS_TAG[]
Definition: XmlRpcClient.h:30
virtual bool generateRequest(const char *method, XmlRpcValue const &params)
std::string _response
Definition: XmlRpcClient.h:97
virtual unsigned handleEvent(unsigned eventType)
void removeSource(XmlRpcSource *source)
@ ReadableEvent
data available to read
@ WritableEvent
connected/data can be written without blocking
void work(double msTime)
void exit()
Exit from work routine.
void addSource(XmlRpcSource *source, unsigned eventMask)
static int socket()
Creates a stream (TCP) socket. Returns -1 on failure.
static bool nbWrite(int socket, std::string &s, int *bytesSoFar)
Write text to the specified socket. Returns false on error.
static bool connect(int socket, std::string &host, int port)
Connect a socket to a server (from a client)
static bool setNonBlocking(int socket)
Sets a stream (TCP) socket to perform non-blocking IO. Returns false on failure.
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.
virtual void close()
Close the owned fd. If deleteOnClose was specified at construction, the object is deleted.
int getfd() const
Return the file descriptor being monitored.
Definition: XmlRpcSource.h:25
void setKeepOpen(bool b=true)
Specify whether the file descriptor should be kept open if it is no longer monitored.
Definition: XmlRpcSource.h:32
bool getKeepOpen() const
Return whether the file descriptor should be kept open if it is no longer monitored.
Definition: XmlRpcSource.h:30
void setfd(int fd)
Specify the file descriptor to monitor.
Definition: XmlRpcSource.h:27
static bool nextTagIs(const char *tag, std::string const &xml, int *offset)
Definition: XmlRpcUtil.cpp:131
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.
void clear()
Erase the current value.
Definition: XmlRpcValue.h:76
bool fromXml(std::string const &valueXml, int *offset)
Decode xml. Destroys any existing value.
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.
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
ClearFlagOnExit(bool &flag)