Home | History | Annotate | Download | only in src
      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.
     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 
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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