From 922cb5559b9f2f97279fa24cc9c5862c8b666495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 8 Mar 2005 10:23:43 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r2603, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/iem/iemxmlrpc/; revision=2604 --- xmlrpc++/src/XmlRpcServerConnection.cpp | 371 ++++++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 xmlrpc++/src/XmlRpcServerConnection.cpp (limited to 'xmlrpc++/src/XmlRpcServerConnection.cpp') diff --git a/xmlrpc++/src/XmlRpcServerConnection.cpp b/xmlrpc++/src/XmlRpcServerConnection.cpp new file mode 100644 index 0000000..b9d6def --- /dev/null +++ b/xmlrpc++/src/XmlRpcServerConnection.cpp @@ -0,0 +1,371 @@ + +#include "XmlRpcServerConnection.h" + +#include "XmlRpcSocket.h" +#include "XmlRpc.h" +#ifndef MAKEDEPEND +# include +# include +#endif + +using namespace XmlRpc; + +// Static data +const char XmlRpcServerConnection::METHODNAME_TAG[] = ""; +const char XmlRpcServerConnection::PARAMS_TAG[] = ""; +const char XmlRpcServerConnection::PARAMS_ETAG[] = ""; +const char XmlRpcServerConnection::PARAM_TAG[] = ""; +const char XmlRpcServerConnection::PARAM_ETAG[] = ""; + +const std::string XmlRpcServerConnection::SYSTEM_MULTICALL = "system.multicall"; +const std::string XmlRpcServerConnection::METHODNAME = "methodName"; +const std::string XmlRpcServerConnection::PARAMS = "params"; + +const std::string XmlRpcServerConnection::FAULTCODE = "faultCode"; +const std::string XmlRpcServerConnection::FAULTSTRING = "faultString"; + + + +// The server delegates handling client requests to a serverConnection object. +XmlRpcServerConnection::XmlRpcServerConnection(int fd, XmlRpcServer* server, bool deleteOnClose /*= false*/) : + XmlRpcSource(fd, deleteOnClose) +{ + XmlRpcUtil::log(2,"XmlRpcServerConnection: new socket %d.", fd); + _server = server; + _connectionState = READ_HEADER; + _keepAlive = true; +} + + +XmlRpcServerConnection::~XmlRpcServerConnection() +{ + XmlRpcUtil::log(4,"XmlRpcServerConnection dtor."); + _server->removeConnection(this); +} + + +// Handle input on the server socket by accepting the connection +// and reading the rpc request. Return true to continue to monitor +// the socket for events, false to remove it from the dispatcher. +unsigned +XmlRpcServerConnection::handleEvent(unsigned /*eventType*/) +{ + if (_connectionState == READ_HEADER) + if ( ! readHeader()) return 0; + + if (_connectionState == READ_REQUEST) + if ( ! readRequest()) return 0; + + if (_connectionState == WRITE_RESPONSE) + if ( ! writeResponse()) return 0; + + return (_connectionState == WRITE_RESPONSE) + ? XmlRpcDispatch::WritableEvent : XmlRpcDispatch::ReadableEvent; +} + + +bool +XmlRpcServerConnection::readHeader() +{ + // Read available data + bool eof; + if ( ! XmlRpcSocket::nbRead(this->getfd(), _header, &eof)) { + // Its only an error if we already have read some data + if (_header.length() > 0) + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: error while reading header (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: read %d bytes.", _header.length()); + char *hp = (char*)_header.c_str(); // Start of header + char *ep = hp + _header.length(); // End of string + char *bp = 0; // Start of body + char *lp = 0; // Start of content-length value + char *kp = 0; // Start of connection value + + for (char *cp = hp; (bp == 0) && (cp < ep); ++cp) { + if ((ep - cp > 16) && (strncasecmp(cp, "Content-length: ", 16) == 0)) + lp = cp + 16; + else if ((ep - cp > 12) && (strncasecmp(cp, "Connection: ", 12) == 0)) + kp = cp + 12; + else if ((ep - cp > 4) && (strncmp(cp, "\r\n\r\n", 4) == 0)) + bp = cp + 4; + else if ((ep - cp > 2) && (strncmp(cp, "\n\n", 2) == 0)) + bp = cp + 2; + } + + // If we haven't gotten the entire header yet, return (keep reading) + if (bp == 0) { + // EOF in the middle of a request is an error, otherwise its ok + if (eof) { + XmlRpcUtil::log(4, "XmlRpcServerConnection::readHeader: EOF"); + if (_header.length() > 0) + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: EOF while reading header"); + return false; // Either way we close the connection + } + + return true; // Keep reading + } + + // Decode content length + if (lp == 0) { + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: No Content-length specified"); + return false; // We could try to figure it out by parsing as we read, but for now... + } + + _contentLength = atoi(lp); + if (_contentLength <= 0) { + XmlRpcUtil::error("XmlRpcServerConnection::readHeader: Invalid Content-length specified (%d).", _contentLength); + return false; + } + + XmlRpcUtil::log(3, "XmlRpcServerConnection::readHeader: specified content length is %d.", _contentLength); + + // Otherwise copy non-header data to request buffer and set state to read request. + _request = bp; + + // Parse out any interesting bits from the header (HTTP version, connection) + _keepAlive = true; + if (_header.find("HTTP/1.0") != std::string::npos) { + if (kp == 0 || strncasecmp(kp, "keep-alive", 10) != 0) + _keepAlive = false; // Default for HTTP 1.0 is to close the connection + } else { + if (kp != 0 && strncasecmp(kp, "close", 5) == 0) + _keepAlive = false; + } + XmlRpcUtil::log(3, "KeepAlive: %d", _keepAlive); + + + _header = ""; + _connectionState = READ_REQUEST; + return true; // Continue monitoring this source +} + +bool +XmlRpcServerConnection::readRequest() +{ + // If we dont have the entire request yet, read available data + if (int(_request.length()) < _contentLength) { + bool eof; + if ( ! XmlRpcSocket::nbRead(this->getfd(), _request, &eof)) { + XmlRpcUtil::error("XmlRpcServerConnection::readRequest: read error (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + // If we haven't gotten the entire request yet, return (keep reading) + if (int(_request.length()) < _contentLength) { + if (eof) { + XmlRpcUtil::error("XmlRpcServerConnection::readRequest: EOF while reading request"); + return false; // Either way we close the connection + } + return true; + } + } + + // Otherwise, parse and dispatch the request + XmlRpcUtil::log(3, "XmlRpcServerConnection::readRequest read %d bytes.", _request.length()); + //XmlRpcUtil::log(5, "XmlRpcServerConnection::readRequest:\n%s\n", _request.c_str()); + + _connectionState = WRITE_RESPONSE; + + return true; // Continue monitoring this source +} + + +bool +XmlRpcServerConnection::writeResponse() +{ + if (_response.length() == 0) { + executeRequest(); + _bytesWritten = 0; + if (_response.length() == 0) { + XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: empty response."); + return false; + } + } + + // Try to write the response + if ( ! XmlRpcSocket::nbWrite(this->getfd(), _response, &_bytesWritten)) { + XmlRpcUtil::error("XmlRpcServerConnection::writeResponse: write error (%s).",XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + XmlRpcUtil::log(3, "XmlRpcServerConnection::writeResponse: wrote %d of %d bytes.", _bytesWritten, _response.length()); + + // Prepare to read the next request + if (_bytesWritten == int(_response.length())) { + _header = ""; + _request = ""; + _response = ""; + _connectionState = READ_HEADER; + } + + return _keepAlive; // Continue monitoring this source if true +} + +// Run the method, generate _response string +void +XmlRpcServerConnection::executeRequest() +{ + XmlRpcValue params, resultValue; + std::string methodName = parseRequest(params); + XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: server calling method '%s'", + methodName.c_str()); + + try { + + if ( ! executeMethod(methodName, params, resultValue) && + ! executeMulticall(methodName, params, resultValue)) + generateFaultResponse(methodName + ": unknown method name"); + else + generateResponse(resultValue.toXml()); + + } catch (const XmlRpcException& fault) { + XmlRpcUtil::log(2, "XmlRpcServerConnection::executeRequest: fault %s.", + fault.getMessage().c_str()); + generateFaultResponse(fault.getMessage(), fault.getCode()); + } +} + +// Parse the method name and the argument values from the request. +std::string +XmlRpcServerConnection::parseRequest(XmlRpcValue& params) +{ + int offset = 0; // Number of chars parsed from the request + + std::string methodName = XmlRpcUtil::parseTag(METHODNAME_TAG, _request, &offset); + + if (methodName.size() > 0 && XmlRpcUtil::findTag(PARAMS_TAG, _request, &offset)) + { + int nArgs = 0; + while (XmlRpcUtil::nextTagIs(PARAM_TAG, _request, &offset)) { + params[nArgs++] = XmlRpcValue(_request, &offset); + (void) XmlRpcUtil::nextTagIs(PARAM_ETAG, _request, &offset); + } + + (void) XmlRpcUtil::nextTagIs(PARAMS_ETAG, _request, &offset); + } + + return methodName; +} + +// Execute a named method with the specified params. +bool +XmlRpcServerConnection::executeMethod(const std::string& methodName, + XmlRpcValue& params, XmlRpcValue& result) +{ + XmlRpcServerMethod* method = _server->findMethod(methodName); + + if ( ! method) return false; + + method->execute(params, result); + + // Ensure a valid result value + if ( ! result.valid()) + result = std::string(); + + return true; +} + +// Execute multiple calls and return the results in an array. +bool +XmlRpcServerConnection::executeMulticall(const std::string& methodName, + XmlRpcValue& params, XmlRpcValue& result) +{ + if (methodName != SYSTEM_MULTICALL) return false; + + // There ought to be 1 parameter, an array of structs + if (params.size() != 1 || params[0].getType() != XmlRpcValue::TypeArray) + throw XmlRpcException(SYSTEM_MULTICALL + ": Invalid argument (expected an array)"); + + int nc = params[0].size(); + result.setSize(nc); + + for (int i=0; i\r\n" + "\r\n\t"; + const char RESPONSE_2[] = + "\r\n\r\n"; + + std::string body = RESPONSE_1 + resultXml + RESPONSE_2; + std::string header = generateHeader(body); + + _response = header + body; + XmlRpcUtil::log(5, "XmlRpcServerConnection::generateResponse:\n%s\n", _response.c_str()); +} + +// Prepend http headers +std::string +XmlRpcServerConnection::generateHeader(std::string const& body) +{ + std::string header = + "HTTP/1.1 200 OK\r\n" + "Server: "; + header += XMLRPC_VERSION; + header += "\r\n" + "Content-Type: text/xml\r\n" + "Content-length: "; + + char buffLen[40]; + sprintf(buffLen,"%d\r\n\r\n", body.size()); + + return header + buffLen; +} + + +void +XmlRpcServerConnection::generateFaultResponse(std::string const& errorMsg, int errorCode) +{ + const char RESPONSE_1[] = + "\r\n" + "\r\n\t"; + const char RESPONSE_2[] = + "\r\n\r\n"; + + XmlRpcValue faultStruct; + faultStruct[FAULTCODE] = errorCode; + faultStruct[FAULTSTRING] = errorMsg; + std::string body = RESPONSE_1 + faultStruct.toXml() + RESPONSE_2; + std::string header = generateHeader(body); + + _response = header + body; +} + -- cgit v1.2.1