XmlRpcServerConnection.cpp
Go to the documentation of this file.
00001 
00002 #include "XmlRpcServerConnection.h"
00003 
00004 #include "XmlRpcSocket.h"
00005 #include "XmlRpc.h"
00006 #ifndef MAKEDEPEND
00007 # include <stdio.h>
00008 # include <stdlib.h>
00009 #ifndef _WINDOWS
00010         # include <strings.h>
00011 #endif
00012 # include <string.h>
00013 #endif
00014 
00015 using namespace XmlRpc;
00016 
00017 // Static data
00018 const char XmlRpcServerConnection::METHODNAME_TAG[] = "<methodName>";
00019 const char XmlRpcServerConnection::PARAMS_TAG[] = "<params>";
00020 const char XmlRpcServerConnection::PARAMS_ETAG[] = "</params>";
00021 const char XmlRpcServerConnection::PARAM_TAG[] = "<param>";
00022 const char XmlRpcServerConnection::PARAM_ETAG[] = "</param>";
00023 
00024 const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall";
00025 const std::string XmlRpcServerConnection::METHODNAME = "methodName";
00026 const std::string XmlRpcServerConnection::PARAMS = "params";
00027 
00028 const std::string XmlRpcServerConnection::FAULTCODE = "faultCode";
00029 const std::string XmlRpcServerConnection::FAULTSTRING = "faultString";
00030 
00031 
00032 
00033 // The server delegates handling client requests to a serverConnection object.
00034 XmlRpcServerConnection::XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose /*= false*/) :
00035   XmlRpcSource(fd, deleteOnClose)
00036 {
00037   XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd);
00038   _server = server;
00039   _connectionState = READ_HEADER;
00040   _keepAlive = true;
00041 }
00042 
00043 
00044 XmlRpcServerConnection::~XmlRpcServerConnection()
00045 {
00046   XmlRpcUtil::log(4,"XmlRpcServerConnection dtor.");
00047   _server->removeConnection(this);
00048 }
00049 
00050 
00051 // Handle input on the server socket by accepting the connection
00052 // and reading the rpc request. Return true to continue to monitor
00053 // the socket for events, false to remove it from the dispatcher.
00054 unsigned
00055 XmlRpcServerConnection::handleEvent(unsigned /*eventType*/)
00056 {
00057   if (_connectionState == READ_HEADER)
00058     if ( ! readHeader()) return 0;
00059 
00060   if (_connectionState == READ_REQUEST)
00061     if ( ! readRequest()) return 0;
00062 
00063   if (_connectionState == WRITE_RESPONSE)
00064     if ( ! writeResponse()) return 0;
00065 
00066   return (_connectionState == WRITE_RESPONSE) 
00067         ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent;
00068 }
00069 
00070 
00071 bool
00072 XmlRpcServerConnection::readHeader()
00073 {
00074   // Read available data
00075   bool eof;
00076   if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof)) {
00077     // Its only an error if we already have read some data
00078     if (_header.length() > 0)
00079       XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str());
00080     return false;
00081   }
00082 
00083   XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length());
00084   char *hp = (char*)_header.c_str();  // Start of header
00085   char *ep = hp + _header.length();   // End of string
00086   char *bp = 0;                       // Start of body
00087   char *lp = 0;                       // Start of content-length value
00088   char *kp = 0;                       // Start of connection value
00089 
00090   for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) {
00091         if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0))
00092           lp = cp + 16;
00093         else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0))
00094           kp = cp + 12;
00095         else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0))
00096           bp = cp + 4;
00097         else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0))
00098           bp = cp + 2;
00099   }
00100 
00101   // If we haven't gotten the entire header yet, return (keep reading)
00102   if (bp == 0) {
00103     // EOF in the middle of a request is an error, otherwise its ok
00104     if (eof) {
00105       XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF");
00106       if (_header.length() > 0)
00107         XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header");
00108       return false;   // Either way we close the connection
00109     }
00110     
00111     return true;  // Keep reading
00112   }
00113 
00114   // Decode content length
00115   if (lp == 0) {
00116     XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified");
00117     return false;   // We could try to figure it out by parsing as we read, but for now...
00118   }
00119 
00120   _contentLength = atoi(lp);
00121   if (_contentLength <= 0) {
00122     XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength);
00123     return false;
00124   }
00125         
00126   XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength);
00127 
00128   // Otherwise copy non-header data to request buffer and set state to read request.
00129   _request = bp;
00130 
00131   // Parse out any interesting bits from the header (HTTP version, connection)
00132   _keepAlive = true;
00133   if (_header.find("HTTP/1.0") != std::string::npos) {
00134     if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0)
00135       _keepAlive = false;           // Default for HTTP 1.0 is to close the connection
00136   } else {
00137     if (kp != 0 && strncasecmp(kp, "close", 5) == 0)
00138       _keepAlive = false;
00139   }
00140   XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive);
00141 
00142 
00143   _header = ""; 
00144   _connectionState = READ_REQUEST;
00145   return true;    // Continue monitoring this source
00146 }
00147 
00148 bool
00149 XmlRpcServerConnection::readRequest()
00150 {
00151   // If we dont have the entire request yet, read available data
00152   if (int(_request.length()) < _contentLength) {
00153     bool eof;
00154     if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) {
00155       XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str());
00156       return false;
00157     }
00158 
00159     // If we haven't gotten the entire request yet, return (keep reading)
00160     if (int(_request.length()) < _contentLength) {
00161       if (eof) {
00162         XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request");
00163         return false;   // Either way we close the connection
00164       }
00165       return true;
00166     }
00167   }
00168 
00169   // Otherwise, parse and dispatch the request
00170   XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length());
00171   //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str());
00172 
00173   _connectionState = WRITE_RESPONSE;
00174 
00175   return true;    // Continue monitoring this source
00176 }
00177 
00178 
00179 bool
00180 XmlRpcServerConnection::writeResponse()
00181 {
00182   if (_response.length() == 0) {
00183     executeRequest();
00184     _bytesWritten = 0;
00185     if (_response.length() == 0) {
00186       XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response.");
00187       return false;
00188     }
00189   }
00190 
00191   // Try to write the response
00192   if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) {
00193     XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str());
00194     return false;
00195   }
00196   XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length());
00197 
00198   // Prepare to read the next request
00199   if (_bytesWritten == int(_response.length())) {
00200     _header = "";
00201     _request = "";
00202     _response = "";
00203     _connectionState = READ_HEADER;
00204   }
00205 
00206   return _keepAlive;    // Continue monitoring this source if true
00207 }
00208 
00209 // Run the method, generate _response string
00210 void
00211 XmlRpcServerConnection::executeRequest()
00212 {
00213   XmlRpcValue params, resultValue;
00214   std::string methodName = parseRequest(params);
00215   XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'", 
00216                     methodName.c_str());
00217 
00218   try {
00219 
00220     if ( ! executeMethod(methodName, params, resultValue) &&
00221          ! executeMulticall(methodName, params, resultValue))
00222       generateFaultResponse(methodName + ": unknown method name");
00223     else
00224       generateResponse(resultValue.toXml());
00225 
00226   } catch (const XmlRpcException& fault) {
00227     XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.",
00228                     fault.getMessage().c_str()); 
00229     generateFaultResponse(fault.getMessage(), fault.getCode());
00230   }
00231 }
00232 
00233 // Parse the method name and the argument values from the request.
00234 std::string
00235 XmlRpcServerConnection::parseRequest(XmlRpcValue& params)
00236 {
00237   int offset = 0;   // Number of chars parsed from the request
00238 
00239   std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset);
00240 
00241   if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset))
00242   {
00243     int nArgs = 0;
00244     while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) {
00245       params[nArgs++] = XmlRpcValue(_request, &offset);
00246       (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset);
00247     }
00248 
00249     (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset);
00250   }
00251 
00252   return methodName;
00253 }
00254 
00255 // Execute a named method with the specified params.
00256 bool
00257 XmlRpcServerConnection::executeMethod(const std::string& methodName, 
00258                                       XmlRpcValue& params, XmlRpcValue& result)
00259 {
00260   XmlRpcServerMethod* method = _server->findMethod(methodName);
00261 
00262   if ( ! method) return false;
00263 
00264   method->execute(params, result);
00265 
00266   // Ensure a valid result value
00267   if ( ! result.valid())
00268       result = std::string();
00269 
00270   return true;
00271 }
00272 
00273 // Execute multiple calls and return the results in an array.
00274 bool
00275 XmlRpcServerConnection::executeMulticall(const std::string& methodName, 
00276                                          XmlRpcValue& params, XmlRpcValue& result)
00277 {
00278   if (methodName != SYSTEM_MULTICALL) return false;
00279 
00280   // There ought to be 1 parameter, an array of structs
00281   if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray)
00282     throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)");
00283 
00284   int nc = params[0].size();
00285   result.setSize(nc);
00286 
00287   for (int i=0; i<nc; ++i) {
00288 
00289     if ( ! params[0][i].hasMember(METHODNAME) ||
00290          ! params[0][i].hasMember(PARAMS)) {
00291       result[i][FAULTCODE] = -1;
00292       result[i][FAULTSTRING] = SYSTEM_MULTICALL +
00293               ": Invalid argument (expected a struct with members methodName and params)";
00294       continue;
00295     }
00296 
00297     const std::string& methodName = params[0][i][METHODNAME];
00298     XmlRpcValue& methodParams = params[0][i][PARAMS];
00299 
00300     XmlRpcValue resultValue;
00301     resultValue.setSize(1);
00302     try {
00303       if ( ! executeMethod(methodName, methodParams, resultValue[0]) &&
00304            ! executeMulticall(methodName, params, resultValue[0]))
00305       {
00306         result[i][FAULTCODE] = -1;
00307         result[i][FAULTSTRING] = methodName + ": unknown method name";
00308       }
00309       else
00310         result[i] = resultValue;
00311 
00312     } catch (const XmlRpcException& fault) {
00313         result[i][FAULTCODE] = fault.getCode();
00314         result[i][FAULTSTRING] = fault.getMessage();
00315     }
00316   }
00317 
00318   return true;
00319 }
00320 
00321 
00322 // Create a response from results xml
00323 void
00324 XmlRpcServerConnection::generateResponse(std::string const& resultXml)
00325 {
00326   const char RESPONSE_1[] = 
00327     "<?xml version=\"1.0\"?>\r\n"
00328     "<methodResponse><params><param>\r\n\t";
00329   const char RESPONSE_2[] =
00330     "\r\n</param></params></methodResponse>\r\n";
00331 
00332   std::string body = RESPONSE_1 + resultXml + RESPONSE_2;
00333   std::string header = generateHeader(body);
00334 
00335   _response = header + body;
00336   XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str()); 
00337 }
00338 
00339 // Prepend http headers
00340 std::string
00341 XmlRpcServerConnection::generateHeader(std::string const& body)
00342 {
00343   std::string header = 
00344     "HTTP/1.1 200 OK\r\n"
00345     "Server: ";
00346   header += XMLRPC_VERSION;
00347   header += "\r\n"
00348     "Content-Type: text/xml\r\n"
00349     "Content-length: ";
00350 
00351   char buffLen[40];
00352 #ifdef _MSC_VER
00353   sprintf_s(buffLen,40,"%d\r\n\r\n", (int)body.size());
00354 #else
00355   sprintf(buffLen,"%d\r\n\r\n", (int)body.size());
00356 #endif
00357 
00358   return header + buffLen;
00359 }
00360 
00361 
00362 void
00363 XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode)
00364 {
00365   const char RESPONSE_1[] = 
00366     "<?xml version=\"1.0\"?>\r\n"
00367     "<methodResponse><fault>\r\n\t";
00368   const char RESPONSE_2[] =
00369     "\r\n</fault></methodResponse>\r\n";
00370 
00371   XmlRpcValue faultStruct;
00372   faultStruct[FAULTCODE] = errorCode;
00373   faultStruct[FAULTSTRING] = errorMsg;
00374   std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2;
00375   std::string header = generateHeader(body);
00376 
00377   _response = header + body;
00378 }
00379 


xmlrpcpp
Author(s): Chris Morley and Konstantin Pilipchuk, slight modifications and ROS wrapping by Morgan Quigley
autogenerated on Sat Dec 28 2013 17:42:59