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 11using namespace XmlRpc; 12 13// Static data 14const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>"; 15const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>"; 16const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>"; 17const char XmlRpcServerConnection::PARAM_TAG[] = "<param>"; 18const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>"; 19 20const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall"; 21const std::string XmlRpcServerConnection::METHODNAME = "methodName"; 22const std::string XmlRpcServerConnection::PARAMS = "params"; 23 24const std::string XmlRpcServerConnection::FAULTCODE = "faultCode"; 25const std::string XmlRpcServerConnection::FAULTSTRING = "faultString"; 26 27 28 29// The server delegates handling client requests to a serverConnection object. 30XmlRpcServerConnection::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 40XmlRpcServerConnection::~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. 50unsigned 51XmlRpcServerConnection::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 67bool 68XmlRpcServerConnection::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 144bool 145XmlRpcServerConnection::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 175bool 176XmlRpcServerConnection::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 206void 207XmlRpcServerConnection::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. 230std::string 231XmlRpcServerConnection::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. 254bool 255XmlRpcServerConnection::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. 272bool 273XmlRpcServerConnection::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 321void 322XmlRpcServerConnection::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 338std::string 339XmlRpcServerConnection::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 356void 357XmlRpcServerConnection::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