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


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley
autogenerated on Tue Mar 7 2017 03:44:43