1 2 #include "XmlRpcServer.h" 3 #include "XmlRpcServerConnection.h" 4 #include "XmlRpcServerMethod.h" 5 #include "XmlRpcSocket.h" 6 #include "XmlRpcUtil.h" 7 #include "XmlRpcException.h" 8 9 10 using namespace XmlRpc; 11 12 13 XmlRpcServer::XmlRpcServer() 14 { 15 _introspectionEnabled = false; 16 _listMethods = 0; 17 _methodHelp = 0; 18 } 19 20 21 XmlRpcServer::~XmlRpcServer() 22 { 23 this->shutdown(); 24 _methods.clear(); 25 delete _listMethods; 26 delete _methodHelp; 27 } 28 29 30 // Add a command to the RPC server 31 void 32 XmlRpcServer::addMethod(XmlRpcServerMethod* method) 33 { 34 _methods[method->name()] = method; 35 } 36 37 // Remove a command from the RPC server 38 void 39 XmlRpcServer::removeMethod(XmlRpcServerMethod* method) 40 { 41 MethodMap::iterator i = _methods.find(method->name()); 42 if (i != _methods.end()) 43 _methods.erase(i); 44 } 45 46 // Remove a command from the RPC server by name 47 void 48 XmlRpcServer::removeMethod(const std::string& methodName) 49 { 50 MethodMap::iterator i = _methods.find(methodName); 51 if (i != _methods.end()) 52 _methods.erase(i); 53 } 54 55 56 // Look up a method by name 57 XmlRpcServerMethod* 58 XmlRpcServer::findMethod(const std::string& name) const 59 { 60 MethodMap::const_iterator i = _methods.find(name); 61 if (i == _methods.end()) 62 return 0; 63 return i->second; 64 } 65 66 67 // Create a socket, bind to the specified port, and 68 // set it in listen mode to make it available for clients. 69 bool 70 XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/) 71 { 72 int fd = XmlRpcSocket::socket(); 73 if (fd < 0) 74 { 75 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str()); 76 return false; 77 } 78 79 this->setfd(fd); 80 81 // Don't block on reads/writes 82 if ( ! XmlRpcSocket::setNonBlocking(fd)) 83 { 84 this->close(); 85 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); 86 return false; 87 } 88 89 // Allow this port to be re-bound immediately so server re-starts are not delayed 90 if ( ! XmlRpcSocket::setReuseAddr(fd)) 91 { 92 this->close(); 93 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str()); 94 return false; 95 } 96 97 // Bind to the specified port on the default interface 98 if ( ! XmlRpcSocket::bind(fd, port)) 99 { 100 this->close(); 101 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str()); 102 return false; 103 } 104 105 // Set in listening mode 106 if ( ! XmlRpcSocket::listen(fd, backlog)) 107 { 108 this->close(); 109 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); 110 return false; 111 } 112 113 XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd); 114 115 // Notify the dispatcher to listen on this source when we are in work() 116 _disp.addSource(this, XmlRpcDispatch::ReadableEvent); 117 118 return true; 119 } 120 121 122 // Process client requests for the specified time 123 void 124 XmlRpcServer::work(double msTime) 125 { 126 XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection"); 127 _disp.work(msTime); 128 } 129 130 131 132 // Handle input on the server socket by accepting the connection 133 // and reading the rpc request. 134 unsigned 135 XmlRpcServer::handleEvent(unsigned /* mask */) 136 { 137 acceptConnection(); 138 return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd 139 } 140 141 142 // Accept a client connection request and create a connection to 143 // handle method calls from the client. 144 void 145 XmlRpcServer::acceptConnection() 146 { 147 int s = XmlRpcSocket::accept(this->getfd()); 148 XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s); 149 if (s < 0) 150 { 151 //this->close(); 152 XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str()); 153 } 154 else if ( ! XmlRpcSocket::setNonBlocking(s)) 155 { 156 XmlRpcSocket::close(s); 157 XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); 158 } 159 else // Notify the dispatcher to listen for input on this source when we are in work() 160 { 161 XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection"); 162 _disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent); 163 } 164 } 165 166 167 // Create a new connection object for processing requests from a specific client. 168 XmlRpcServerConnection* 169 XmlRpcServer::createConnection(int s) 170 { 171 // Specify that the connection object be deleted when it is closed 172 return new XmlRpcServerConnection(s, this, true); 173 } 174 175 176 void 177 XmlRpcServer::removeConnection(XmlRpcServerConnection* sc) 178 { 179 _disp.removeSource(sc); 180 } 181 182 183 // Stop processing client requests 184 void 185 XmlRpcServer::exit() 186 { 187 _disp.exit(); 188 } 189 190 191 // Close the server socket file descriptor and stop monitoring connections 192 void 193 XmlRpcServer::shutdown() 194 { 195 // This closes and destroys all connections as well as closing this socket 196 _disp.clear(); 197 } 198 199 200 // Introspection support 201 static const std::string LIST_METHODS("system.listMethods"); 202 static const std::string METHOD_HELP("system.methodHelp"); 203 static const std::string MULTICALL("system.multicall"); 204 205 206 // List all methods available on a server 207 class ListMethods : public XmlRpcServerMethod 208 { 209 public: 210 ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {} 211 212 void execute(XmlRpcValue& /* params */, XmlRpcValue& result) 213 { 214 _server->listMethods(result); 215 } 216 217 std::string help() { return std::string("List all methods available on a server as an array of strings"); } 218 }; 219 220 221 // Retrieve the help string for a named method 222 class MethodHelp : public XmlRpcServerMethod 223 { 224 public: 225 MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {} 226 227 void execute(XmlRpcValue& params, XmlRpcValue& result) 228 { 229 if (params[0].getType() != XmlRpcValue::TypeString) 230 throw XmlRpcException(METHOD_HELP + ": Invalid argument type"); 231 232 XmlRpcServerMethod* m = _server->findMethod(params[0]); 233 if ( ! m) 234 throw XmlRpcException(METHOD_HELP + ": Unknown method name"); 235 236 result = m->help(); 237 } 238 239 std::string help() { return std::string("Retrieve the help string for a named method"); } 240 }; 241 242 243 // Specify whether introspection is enabled or not. Default is enabled. 244 void 245 XmlRpcServer::enableIntrospection(bool enabled) 246 { 247 if (_introspectionEnabled == enabled) 248 return; 249 250 _introspectionEnabled = enabled; 251 252 if (enabled) 253 { 254 if ( ! _listMethods) 255 { 256 _listMethods = new ListMethods(this); 257 _methodHelp = new MethodHelp(this); 258 } else { 259 addMethod(_listMethods); 260 addMethod(_methodHelp); 261 } 262 } 263 else 264 { 265 removeMethod(LIST_METHODS); 266 removeMethod(METHOD_HELP); 267 } 268 } 269 270 271 void 272 XmlRpcServer::listMethods(XmlRpcValue& result) 273 { 274 int i = 0; 275 result.setSize(_methods.size()+1); 276 for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it) 277 result[i++] = it->first; 278 279 // Multicall support is built into XmlRpcServerConnection 280 result[i] = MULTICALL; 281 } 282 283 284 285