From 3a81b3c3d7cd9825d24ed548c707514f03ba0ba5 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 8 May 2009 03:24:24 +0000 Subject: [PATCH] 2009-05-08 Tatsuhiro Tsujikawa Added xml-rpc request parser. The supported value types are: i4/int, string, double, struct, array, base64. Currently only libxml2 version is provided. * src/Xml2XmlRpcRequestProcessor.cc * src/Xml2XmlRpcRequestProcessor.h * src/XmlRpcElements.cc * src/XmlRpcElements.h * src/XmlRpcRequest.h * src/XmlRpcRequestParserController.cc * src/XmlRpcRequestParserController.h * src/XmlRpcRequestParserState.h * src/XmlRpcRequestParserStateImpl.cc * src/XmlRpcRequestParserStateImpl.h * src/XmlRpcRequestParserStateMachine.cc * src/XmlRpcRequestParserStateMachine.h * src/XmlRpcRequestProcessor.h * test/XmlRpcRequestParserControllerTest.cc * test/XmlRpcRequestProcessorTest.cc --- ChangeLog | 21 ++ src/Xml2XmlRpcRequestProcessor.cc | 148 ++++++++++ src/Xml2XmlRpcRequestProcessor.h | 65 +++++ src/XmlRpcElements.cc | 61 ++++ src/XmlRpcElements.h | 68 +++++ src/XmlRpcRequest.h | 60 ++++ src/XmlRpcRequestParserController.cc | 77 +++++ src/XmlRpcRequestParserController.h | 102 +++++++ src/XmlRpcRequestParserState.h | 68 +++++ src/XmlRpcRequestParserStateImpl.cc | 329 ++++++++++++++++++++++ src/XmlRpcRequestParserStateImpl.h | 238 ++++++++++++++++ src/XmlRpcRequestParserStateMachine.cc | 118 ++++++++ src/XmlRpcRequestParserStateMachine.h | 174 ++++++++++++ src/XmlRpcRequestProcessor.h | 46 +++ test/XmlRpcRequestParserControllerTest.cc | 157 +++++++++++ test/XmlRpcRequestProcessorTest.cc | 77 +++++ 16 files changed, 1809 insertions(+) create mode 100644 src/Xml2XmlRpcRequestProcessor.cc create mode 100644 src/Xml2XmlRpcRequestProcessor.h create mode 100644 src/XmlRpcElements.cc create mode 100644 src/XmlRpcElements.h create mode 100644 src/XmlRpcRequest.h create mode 100644 src/XmlRpcRequestParserController.cc create mode 100644 src/XmlRpcRequestParserController.h create mode 100644 src/XmlRpcRequestParserState.h create mode 100644 src/XmlRpcRequestParserStateImpl.cc create mode 100644 src/XmlRpcRequestParserStateImpl.h create mode 100644 src/XmlRpcRequestParserStateMachine.cc create mode 100644 src/XmlRpcRequestParserStateMachine.h create mode 100644 src/XmlRpcRequestProcessor.h create mode 100644 test/XmlRpcRequestParserControllerTest.cc create mode 100644 test/XmlRpcRequestProcessorTest.cc diff --git a/ChangeLog b/ChangeLog index 7e9f9548..83a0c24a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2009-05-08 Tatsuhiro Tsujikawa + + Added xml-rpc request parser. The supported value types are: + i4/int, string, double, struct, array, base64. Currently only + libxml2 version is provided. + * src/Xml2XmlRpcRequestProcessor.cc + * src/Xml2XmlRpcRequestProcessor.h + * src/XmlRpcElements.cc + * src/XmlRpcElements.h + * src/XmlRpcRequest.h + * src/XmlRpcRequestParserController.cc + * src/XmlRpcRequestParserController.h + * src/XmlRpcRequestParserState.h + * src/XmlRpcRequestParserStateImpl.cc + * src/XmlRpcRequestParserStateImpl.h + * src/XmlRpcRequestParserStateMachine.cc + * src/XmlRpcRequestParserStateMachine.h + * src/XmlRpcRequestProcessor.h + * test/XmlRpcRequestParserControllerTest.cc + * test/XmlRpcRequestProcessorTest.cc + 2009-05-08 Tatsuhiro Tsujikawa * Release 1.3.3 diff --git a/src/Xml2XmlRpcRequestProcessor.cc b/src/Xml2XmlRpcRequestProcessor.cc new file mode 100644 index 00000000..4a54c272 --- /dev/null +++ b/src/Xml2XmlRpcRequestProcessor.cc @@ -0,0 +1,148 @@ +/* */ +#include "Xml2XmlRpcRequestProcessor.h" + +#include + +#include "XmlRpcRequestParserStateMachine.h" +#include "Util.h" +#include "DlAbortEx.h" + +namespace aria2 { + +namespace xmlrpc { + +struct SessionData { + XmlRpcRequestParserStateMachine* _stm; + + std::stack _charactersStack; + + SessionData(XmlRpcRequestParserStateMachine* stm):_stm(stm) {} +}; + +static void mlStartElement(void* userData, const xmlChar* name, + const xmlChar** attrs) +{ + SessionData* sd = reinterpret_cast(userData); + std::map attrmap; + if(attrs) { + const xmlChar** p = attrs; + while(*p != 0) { + std::string name = (const char*)*p++; + if(*p == 0) { + break; + } + std::string value = Util::trim((const char*)*p++); + attrmap[name] = value; + } + } + sd->_stm->beginElement((const char*)name, attrmap); + if(sd->_stm->needsCharactersBuffering()) { + sd->_charactersStack.push(std::string()); + } +} + +static void mlEndElement(void* userData, const xmlChar* name) +{ + SessionData* sd = reinterpret_cast(userData); + std::string characters; + if(sd->_stm->needsCharactersBuffering()) { + characters = Util::trim(sd->_charactersStack.top()); + sd->_charactersStack.pop(); + } + sd->_stm->endElement((const char*)name, characters); +} + +static void mlCharacters(void* userData, const xmlChar* ch, int len) +{ + SessionData* sd = reinterpret_cast(userData); + if(sd->_stm->needsCharactersBuffering()) { + sd->_charactersStack.top() += std::string(&ch[0], &ch[len]); + } +} + +static xmlSAXHandler mySAXHandler = +{ + 0, // internalSubsetSAXFunc + 0, // isStandaloneSAXFunc + 0, // hasInternalSubsetSAXFunc + 0, // hasExternalSubsetSAXFunc + 0, // resolveEntitySAXFunc + 0, // getEntitySAXFunc + 0, // entityDeclSAXFunc + 0, // notationDeclSAXFunc + 0, // attributeDeclSAXFunc + 0, // elementDeclSAXFunc + 0, // unparsedEntityDeclSAXFunc + 0, // setDocumentLocatorSAXFunc + 0, // startDocumentSAXFunc + 0, // endDocumentSAXFunc + &mlStartElement, // startElementSAXFunc + &mlEndElement, // endElementSAXFunc + 0, // referenceSAXFunc + &mlCharacters, // charactersSAXFunc + 0, // ignorableWhitespaceSAXFunc + 0, // processingInstructionSAXFunc + 0, // commentSAXFunc + 0, // warningSAXFunc + 0, // errorSAXFunc + 0, // fatalErrorSAXFunc + 0, // getParameterEntitySAXFunc + 0, // cdataBlockSAXFunc + 0, // externalSubsetSAXFunc + 0, // unsigned int initialized + 0, // void * _private + 0, // startElementNsSAX2Func + 0, // endElementNsSAX2Func + 0, // xmlStructuredErrorFunc +}; + +XmlRpcRequest +XmlRpcRequestProcessor::parseMemory(const std::string& xml) +{ + _stm.reset(new XmlRpcRequestParserStateMachine()); + SharedHandle sessionData(new SessionData(_stm.get())); + + int r = xmlSAXUserParseMemory(&mySAXHandler, sessionData.get(), + xml.data(), xml.size()); + if(r != 0) { + throw DlAbortEx("Failed to parse xml-rpc request."); + } + return XmlRpcRequest(_stm->getMethodName(), _stm->getCurrentFrameValue()); +} + +} // namespace xmlrpc + +} // namespace aria2 diff --git a/src/Xml2XmlRpcRequestProcessor.h b/src/Xml2XmlRpcRequestProcessor.h new file mode 100644 index 00000000..a680f665 --- /dev/null +++ b/src/Xml2XmlRpcRequestProcessor.h @@ -0,0 +1,65 @@ +/* */ +#ifndef _D_XML2_XML_RPC_REQUEST_PROCESSOR_H_ +#define _D_XML2_XML_RPC_REQUEST_PROCESSOR_H_ + +#include "common.h" + +#include + +#include +#include + +#include "SharedHandle.h" +#include "XmlRpcRequest.h" + +namespace aria2 { + +namespace xmlrpc { + +class XmlRpcRequestParserStateMachine; + +class XmlRpcRequestProcessor { +private: + SharedHandle _stm; +public: + XmlRpcRequest parseMemory(const std::string& xml); +}; + +} // namespace xmlrpc + +} // namespace aria2 + +#endif // _D_XML2_XML_RPC_REQUEST_PROCESSOR_H_ diff --git a/src/XmlRpcElements.cc b/src/XmlRpcElements.cc new file mode 100644 index 00000000..6fbc7e8e --- /dev/null +++ b/src/XmlRpcElements.cc @@ -0,0 +1,61 @@ +/* */ +#include "XmlRpcElements.h" + +namespace aria2 { +namespace xmlrpc { +namespace elements { + +const std::string METHOD_CALL("methodCall"); +const std::string METHOD_NAME("methodName"); +const std::string PARAMS("params"); +const std::string PARAM("param"); +const std::string VALUE("value"); +const std::string I4("i4"); +const std::string INT("int"); +const std::string BOOLEAN("boolean"); +const std::string STRING("string"); +const std::string DOUBLE("double"); +const std::string DATE_TIME_ISO8601("dateTime.iso8601"); +const std::string BASE64("base64"); +const std::string STRUCT("struct"); +const std::string MEMBER("member"); +const std::string NAME("name"); +const std::string ARRAY("array"); +const std::string DATA("data"); + +} // namespace elements +} // namespace xmlrpc +} // namespace aria2 diff --git a/src/XmlRpcElements.h b/src/XmlRpcElements.h new file mode 100644 index 00000000..1b18ae12 --- /dev/null +++ b/src/XmlRpcElements.h @@ -0,0 +1,68 @@ +/* */ +#ifndef _D_XML_RPC_ELEMENTS_H_ +#define _D_XML_RPC_ELEMENTS_H_ + +#include "common.h" + +#include + +namespace aria2 { +namespace xmlrpc { +namespace elements { + +extern const std::string METHOD_CALL; +extern const std::string METHOD_NAME; +extern const std::string PARAMS; +extern const std::string PARAM; +extern const std::string VALUE; +extern const std::string I4; +extern const std::string INT; +extern const std::string BOOLEAN; +extern const std::string STRING; +extern const std::string DOUBLE; +extern const std::string DATE_TIME_ISO8601; +extern const std::string BASE64; +extern const std::string STRUCT; +extern const std::string MEMBER; +extern const std::string NAME; +extern const std::string ARRAY; +extern const std::string DATA; + +} // namespace elements +} // namespace xmlrpc +} // namespace aria2 + +#endif // _D_XML_RPC_ELEMENTS_H_ diff --git a/src/XmlRpcRequest.h b/src/XmlRpcRequest.h new file mode 100644 index 00000000..f1587205 --- /dev/null +++ b/src/XmlRpcRequest.h @@ -0,0 +1,60 @@ +/* */ +#ifndef _D_XML_RPC_REQUEST_H_ +#define _D_XML_RPC_REQUEST_H_ + +#include "common.h" + +#include + +#include "BDE.h" + +namespace aria2 { + +namespace xmlrpc { + +struct XmlRpcRequest { + std::string _methodName; + BDE _params; + + XmlRpcRequest(const std::string& methodName, const BDE& params): + _methodName(methodName), _params(params) {} +}; + +} // namespace xmlrpc + +} // namespace aria2 + +#endif // _D_XML_RPC_REQUEST_H_ diff --git a/src/XmlRpcRequestParserController.cc b/src/XmlRpcRequestParserController.cc new file mode 100644 index 00000000..6d69dfcb --- /dev/null +++ b/src/XmlRpcRequestParserController.cc @@ -0,0 +1,77 @@ +/* */ +#include "XmlRpcRequestParserController.h" + +#include + +namespace aria2 { + +namespace xmlrpc { + +void XmlRpcRequestParserController::pushFrame() +{ + _frameStack.push(_currentFrame); + _currentFrame = StateFrame(); +} + +void XmlRpcRequestParserController::popStructFrame() +{ + assert(!_frameStack.empty()); + + StateFrame parentFrame = _frameStack.top(); + assert(parentFrame._value.isDict()); + _frameStack.pop(); + if(_currentFrame.validMember()) { + parentFrame._value[_currentFrame._name] = _currentFrame._value; + } + _currentFrame = parentFrame; +} + +void XmlRpcRequestParserController::popArrayFrame() +{ + assert(!_frameStack.empty()); + + StateFrame parentFrame = _frameStack.top(); + assert(parentFrame._value.isList()); + _frameStack.pop(); + if(!_currentFrame._value.isNone()) { + parentFrame._value << _currentFrame._value; + } + _currentFrame = parentFrame; +} + +} // namespace xmlrpc + +} // namespace aria2 diff --git a/src/XmlRpcRequestParserController.h b/src/XmlRpcRequestParserController.h new file mode 100644 index 00000000..31ba96be --- /dev/null +++ b/src/XmlRpcRequestParserController.h @@ -0,0 +1,102 @@ +/* */ +#ifndef _D_XML_RPC_REQUEST_PARSER_CONTROLLER_H_ +#define _D_XML_RPC_REQUEST_PARSER_CONTROLLER_H_ + +#include "common.h" + +#include +#include + +#include "BDE.h" + +namespace aria2 { + +namespace xmlrpc { + +class XmlRpcRequestParserController { +private: + + struct StateFrame { + BDE _value; + std::string _name; + + bool validMember() const + { + return !_value.isNone() && !_name.empty(); + } + }; + + std::stack _frameStack; + + StateFrame _currentFrame; + + std::string _methodName; +public: + void pushFrame(); + + // Pops StateFrame p from _frameStack and set p[_currentFrame._name] + // = _currentFrame._value and _currentFrame = p; + void popStructFrame(); + + // Pops StateFrame p from _frameStack and add _currentFrame._value + // to p and _currentFrame = p; + void popArrayFrame(); + + void setCurrentFrameValue(const BDE& value) + { + _currentFrame._value = value; + } + + void setCurrentFrameName(const std::string& name) + { + _currentFrame._name = name; + } + + const BDE& getCurrentFrameValue() const { return _currentFrame._value; } + + void setMethodName(const std::string& methodName) + { + _methodName = methodName; + } + + const std::string& getMethodName() const { return _methodName; } +}; + +} // namespace xmlrpc + +} // namespace aria2 + +#endif // _D_XML_RPC_REQUEST_PARSER_CONTROLLER_H_ diff --git a/src/XmlRpcRequestParserState.h b/src/XmlRpcRequestParserState.h new file mode 100644 index 00000000..6d20e6b9 --- /dev/null +++ b/src/XmlRpcRequestParserState.h @@ -0,0 +1,68 @@ +/* */ +#ifndef _D_XML_RPC_REQUEST_PARSER_STATE_H_ +#define _D_XML_RPC_REQUEST_PARSER_STATE_H_ + +#include "common.h" + +#include +#include + +namespace aria2 { + +namespace xmlrpc { + +class XmlRpcRequestParserStateMachine; + +class XmlRpcRequestParserState { +public: + virtual ~XmlRpcRequestParserState() {} + + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs)= 0; + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) = 0; + + virtual bool needsCharactersBuffering() const = 0; +}; + +} // namespace xmlrpc + +} // namespace aria2 + +#endif // _D_XML_RPC_REQUEST_PARSER_STATE_H_ diff --git a/src/XmlRpcRequestParserStateImpl.cc b/src/XmlRpcRequestParserStateImpl.cc new file mode 100644 index 00000000..57d3e2a4 --- /dev/null +++ b/src/XmlRpcRequestParserStateImpl.cc @@ -0,0 +1,329 @@ +/* */ +#include "XmlRpcRequestParserStateImpl.h" +#include "XmlRpcRequestParserStateMachine.h" +#include "XmlRpcElements.h" +#include "RecoverableException.h" +#include "Util.h" +#include "Base64.h" + +namespace aria2 { + +namespace xmlrpc { + +// InitialXmlRpcRequestParserState + +void InitialXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::METHOD_CALL) { + stm->pushMethodCallState(); + } else { + stm->pushUnknownElementState(); + } +} + +void InitialXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{} + +// UnknownElementXmlRpcRequestParserState + +void UnknownElementXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + stm->pushUnknownElementState(); +} + +// MethodCallXmlRpcRequestParserState + +void MethodCallXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::METHOD_NAME) { + stm->pushMethodNameState(); + } else if(name == elements::PARAMS) { + stm->setCurrentFrameValue(BDE::list()); + stm->pushParamsState(); + } else { + stm->pushUnknownElementState(); + } +} + +// MethodNameXmlRpcRequestParserState + +void MethodNameXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + stm->pushUnknownElementState(); +} + +void MethodNameXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + stm->setMethodName(characters); +} + +// ParamsXmlRpcRequestParserState + +void ParamsXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::PARAM) { + stm->pushFrame(); + stm->pushParamState(); + } else { + stm->pushUnknownElementState(); + } +} + +// ParamXmlRpcRequestParserState + +void ParamXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::VALUE) { + stm->pushValueState(); + } else { + stm->pushUnknownElementState(); + } +} + +void ParamXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + stm->popArrayFrame(); +} + +// ValueXmlRpcRequestParserState + +void ValueXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::I4 || name == elements::INT) { + stm->pushIntState(); + } else if(name == elements::STRUCT) { + stm->setCurrentFrameValue(BDE::dict()); + stm->pushStructState(); + } else if(name == elements::ARRAY) { + stm->setCurrentFrameValue(BDE::list()); + stm->pushArrayState(); + } else if(name == elements::STRING || name == elements::DOUBLE) { + stm->pushStringState(); + } else if(name == elements::BASE64) { + stm->pushBase64State(); + } else { + stm->pushUnknownElementState(); + } +} + +// IntXmlRpcRequestParserState + +void IntXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + stm->pushUnknownElementState(); +} + +void IntXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + try { + int64_t value = Util::parseLLInt(characters); + stm->setCurrentFrameValue(BDE(value)); + } catch(RecoverableException& e) { + // nothing to do here: We just leave current frame value to BDE::none + } +} + +// StringXmlRpcRequestParserState + +void StringXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + stm->pushUnknownElementState(); +} + +void StringXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + stm->setCurrentFrameValue(BDE(characters)); +} + +// Base64XmlRpcRequestParserState + +void Base64XmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + stm->pushUnknownElementState(); +} + +void Base64XmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + stm->setCurrentFrameValue(BDE(Base64::decode(characters))); +} + +// StructXmlRpcRequestParserState + +void StructXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::MEMBER) { + stm->pushFrame(); + stm->pushMemberState(); + } else { + stm->pushUnknownElementState(); + } +} + +// MemberXmlRpcRequestParserState + +void MemberXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::NAME) { + stm->pushNameState(); + } else if(name == elements::VALUE) { + stm->pushValueState(); + } else { + stm->pushUnknownElementState(); + } +} + +void MemberXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + stm->popStructFrame(); +} + +// NameXmlRpcRequestParserState + +void NameXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + stm->pushUnknownElementState(); +} + +void NameXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + stm->setCurrentFrameName(characters); +} + +// ArrayXmlRpcRequestParserState + +void ArrayXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::DATA) { + stm->pushDataState(); + } else { + stm->pushUnknownElementState(); + } +} + +// DataXmlRpcRequestParserState + +void DataXmlRpcRequestParserState::beginElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs) +{ + if(name == elements::VALUE) { + stm->pushFrame(); + stm->pushArrayValueState(); + } else { + stm->pushUnknownElementState(); + } +} + +// ArrayValueXmlRpcRequestParserState + +void ArrayValueXmlRpcRequestParserState::endElement +(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) +{ + stm->popArrayFrame(); +} + +} // namespace xmlrpc + +} // namespace aria2 diff --git a/src/XmlRpcRequestParserStateImpl.h b/src/XmlRpcRequestParserStateImpl.h new file mode 100644 index 00000000..04b8164f --- /dev/null +++ b/src/XmlRpcRequestParserStateImpl.h @@ -0,0 +1,238 @@ +/* */ +#ifndef _D_XML_RPC_REQUEST_PARSER_STATE_IMPL_H_ +#define _D_XML_RPC_REQUEST_PARSER_STATE_IMPL_H_ + +#include "XmlRpcRequestParserState.h" + +namespace aria2 { + +namespace xmlrpc { + +class InitialXmlRpcRequestParserState:public XmlRpcRequestParserState { +public: + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class UnknownElementXmlRpcRequestParserState:public XmlRpcRequestParserState { +public: + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) {} + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class MethodCallXmlRpcRequestParserState:public XmlRpcRequestParserState { +public: + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) {} + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class MethodNameXmlRpcRequestParserState:public XmlRpcRequestParserState { +public: + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return true; } +}; + +class ParamsXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) {} + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class ParamXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class ValueXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) {} + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class IntXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return true; } +}; + +class StringXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return true; } +}; + +class Base64XmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return true; } +}; + +class StructXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) {} + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class MemberXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class NameXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); + + virtual bool needsCharactersBuffering() const { return true; } +}; + +class ArrayXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) {} + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class DataXmlRpcRequestParserState:public XmlRpcRequestParserState { + virtual void beginElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::map& attrs); + + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters) {} + + virtual bool needsCharactersBuffering() const { return false; } +}; + +class ArrayValueXmlRpcRequestParserState:public ValueXmlRpcRequestParserState { + virtual void endElement(XmlRpcRequestParserStateMachine* stm, + const std::string& name, + const std::string& characters); +}; + +} // namespace xmlrpc + +} // namespace aria2 + +#endif // _D_XML_RPC_REQUEST_PARSER_STATE_IMPL_H_ diff --git a/src/XmlRpcRequestParserStateMachine.cc b/src/XmlRpcRequestParserStateMachine.cc new file mode 100644 index 00000000..4a06d0bc --- /dev/null +++ b/src/XmlRpcRequestParserStateMachine.cc @@ -0,0 +1,118 @@ +/* */ +#include "XmlRpcRequestParserStateMachine.h" + +namespace aria2 { + +namespace xmlrpc { + +InitialXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_initialState = + new InitialXmlRpcRequestParserState(); + +UnknownElementXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_unknownElementState = + new UnknownElementXmlRpcRequestParserState(); + +MethodCallXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_methodCallState = + new MethodCallXmlRpcRequestParserState(); + +MethodNameXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_methodNameState = + new MethodNameXmlRpcRequestParserState(); + +ParamsXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_paramsState = + new ParamsXmlRpcRequestParserState(); + +ParamXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_paramState = + new ParamXmlRpcRequestParserState(); + +ValueXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_valueState = + new ValueXmlRpcRequestParserState(); + +IntXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_intState = + new IntXmlRpcRequestParserState(); + +StringXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_stringState = + new StringXmlRpcRequestParserState(); + +Base64XmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_base64State = + new Base64XmlRpcRequestParserState(); + +StructXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_structState = + new StructXmlRpcRequestParserState(); + +MemberXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_memberState = + new MemberXmlRpcRequestParserState(); + +NameXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_nameState = + new NameXmlRpcRequestParserState(); + +ArrayXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_arrayState = + new ArrayXmlRpcRequestParserState(); + +DataXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_dataState = + new DataXmlRpcRequestParserState(); + +ArrayValueXmlRpcRequestParserState* +XmlRpcRequestParserStateMachine::_arrayValueState = + new ArrayValueXmlRpcRequestParserState(); + +XmlRpcRequestParserStateMachine::XmlRpcRequestParserStateMachine(): + _controller(new XmlRpcRequestParserController()) +{ + _stateStack.push(_initialState); +} + +XmlRpcRequestParserStateMachine::~XmlRpcRequestParserStateMachine() +{ + delete _controller; +} + +} // namespace xmlrpc + +} // namespace aria2 diff --git a/src/XmlRpcRequestParserStateMachine.h b/src/XmlRpcRequestParserStateMachine.h new file mode 100644 index 00000000..50846acc --- /dev/null +++ b/src/XmlRpcRequestParserStateMachine.h @@ -0,0 +1,174 @@ +/* */ +#ifndef _D_XML_RPC_REQUEST_PARSER_STATE_MACHINE_H_ +#define _D_XML_RPC_REQUEST_PARSER_STATE_MACHINE_H_ + +#include "common.h" + +#include +#include +#include + +#include "BDE.h" +#include "XmlRpcRequestParserController.h" +#include "XmlRpcRequestParserStateImpl.h" + +namespace aria2 { + +class BDE; + +namespace xmlrpc { + +class XmlRpcRequestParserStateMachine { +private: + XmlRpcRequestParserController* _controller; + + std::stack _stateStack; + + static InitialXmlRpcRequestParserState* _initialState; + static MethodCallXmlRpcRequestParserState* _methodCallState; + static MethodNameXmlRpcRequestParserState* _methodNameState; + static ParamsXmlRpcRequestParserState* _paramsState; + static ParamXmlRpcRequestParserState* _paramState; + static ValueXmlRpcRequestParserState* _valueState; + static IntXmlRpcRequestParserState* _intState; + static StringXmlRpcRequestParserState* _stringState; + static Base64XmlRpcRequestParserState* _base64State; + static StructXmlRpcRequestParserState* _structState; + static MemberXmlRpcRequestParserState* _memberState; + static NameXmlRpcRequestParserState* _nameState; + static ArrayXmlRpcRequestParserState* _arrayState; + static DataXmlRpcRequestParserState* _dataState; + static ArrayValueXmlRpcRequestParserState* _arrayValueState; + + static UnknownElementXmlRpcRequestParserState* _unknownElementState; +public: + XmlRpcRequestParserStateMachine(); + + ~XmlRpcRequestParserStateMachine(); + + void beginElement(const std::string& name, + const std::map& attrs) + { + _stateStack.top()->beginElement(this, name, attrs); + } + + void endElement(const std::string& name, const std::string& characters) + { + _stateStack.top()->endElement(this, name, characters); + _stateStack.pop(); + } + + void setMethodName(const std::string& methodName) + { + _controller->setMethodName(methodName); + } + + const std::string& getMethodName() const + { + return _controller->getMethodName(); + } + + void popArrayFrame() + { + _controller->popArrayFrame(); + } + + void popStructFrame() + { + _controller->popStructFrame(); + } + + void pushFrame() + { + _controller->pushFrame(); + } + + void setCurrentFrameValue(const BDE& value) + { + _controller->setCurrentFrameValue(value); + } + + const BDE& getCurrentFrameValue() const + { + return _controller->getCurrentFrameValue(); + } + + void setCurrentFrameName(const std::string& name) + { + _controller->setCurrentFrameName(name); + } + + bool needsCharactersBuffering() const + { + return _stateStack.top()->needsCharactersBuffering(); + } + + void pushUnknownElementState() { _stateStack.push(_unknownElementState); } + + void pushMethodCallState() { _stateStack.push(_methodCallState); } + + void pushMethodNameState() { _stateStack.push(_methodNameState); } + + void pushParamsState() { _stateStack.push(_paramsState); } + + void pushParamState() { _stateStack.push(_paramState); } + + void pushValueState() { _stateStack.push(_valueState); } + + void pushIntState() { _stateStack.push(_intState); } + + void pushStringState() { _stateStack.push(_stringState); } + + void pushBase64State() { _stateStack.push(_base64State); } + + void pushStructState() { _stateStack.push(_structState); } + + void pushMemberState() { _stateStack.push(_memberState); } + + void pushNameState() { _stateStack.push(_nameState); } + + void pushArrayState() { _stateStack.push(_arrayState); } + + void pushDataState() { _stateStack.push(_dataState); } + + void pushArrayValueState() { _stateStack.push(_arrayValueState); } +}; + +} // namespace xmlrpc + +} // namespace aria2 + +#endif // _D_XML_RPC_REQUEST_PARSER_STATE_MACHINE_H_ diff --git a/src/XmlRpcRequestProcessor.h b/src/XmlRpcRequestProcessor.h new file mode 100644 index 00000000..a8c21a77 --- /dev/null +++ b/src/XmlRpcRequestProcessor.h @@ -0,0 +1,46 @@ +/* */ +#ifndef _D_XML_RPC_REQUEST_PROCESSOR_H_ +#define _D_XML_RPC_REQUEST_PROCESSOR_H_ + +#include "common.h" + +#ifdef HAVE_LIBXML2 +# include "Xml2XmlRpcRequestProcessor.h" +#elif HAVE_LIBEXPAT +# include "ExpatXmlRpcRequestProcessor.h" +#endif // HAVE_LIBEXPAT + +#endif // _D_XML_RPC_REQUEST_PROCESSOR_H_ diff --git a/test/XmlRpcRequestParserControllerTest.cc b/test/XmlRpcRequestParserControllerTest.cc new file mode 100644 index 00000000..a317007b --- /dev/null +++ b/test/XmlRpcRequestParserControllerTest.cc @@ -0,0 +1,157 @@ +#include "XmlRpcRequestParserController.h" + +#include + +namespace aria2 { + +namespace xmlrpc { + +class XmlRpcRequestParserControllerTest:public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(XmlRpcRequestParserControllerTest); + CPPUNIT_TEST(testPopStructFrame); + CPPUNIT_TEST(testPopStructFrame_noName); + CPPUNIT_TEST(testPopStructFrame_noValue); + CPPUNIT_TEST(testPopArrayFrame); + CPPUNIT_TEST(testPopArrayFrame_noValue); + CPPUNIT_TEST(testPopArrayFrame_compound); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp() {} + + void tearDown() {} + + void testPopStructFrame(); + void testPopStructFrame_noName(); + void testPopStructFrame_noValue(); + void testPopArrayFrame(); + void testPopArrayFrame_noValue(); + void testPopArrayFrame_compound(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(XmlRpcRequestParserControllerTest); + +void XmlRpcRequestParserControllerTest::testPopStructFrame() +{ + XmlRpcRequestParserController controller; + controller.setCurrentFrameValue(BDE::dict()); + controller.pushFrame(); + controller.setCurrentFrameValue(BDE("Hello, aria2")); + controller.setCurrentFrameName("greeting"); + controller.popStructFrame(); + const BDE& structValue = controller.getCurrentFrameValue(); + CPPUNIT_ASSERT_EQUAL((size_t)1, structValue.size()); + CPPUNIT_ASSERT_EQUAL(std::string("Hello, aria2"), + structValue["greeting"].s()); +} + +void XmlRpcRequestParserControllerTest::testPopStructFrame_noName() +{ + XmlRpcRequestParserController controller; + controller.setCurrentFrameValue(BDE::dict()); + controller.pushFrame(); + controller.setCurrentFrameValue(BDE("Hello, aria2")); + controller.popStructFrame(); + const BDE& structValue = controller.getCurrentFrameValue(); + CPPUNIT_ASSERT(structValue.empty()); +} + +void XmlRpcRequestParserControllerTest::testPopStructFrame_noValue() +{ + XmlRpcRequestParserController controller; + controller.setCurrentFrameValue(BDE::dict()); + controller.pushFrame(); + controller.setCurrentFrameName("greeting"); + controller.popStructFrame(); + const BDE& structValue = controller.getCurrentFrameValue(); + CPPUNIT_ASSERT(structValue.empty()); +} + +void XmlRpcRequestParserControllerTest::testPopArrayFrame() +{ + XmlRpcRequestParserController controller; + controller.setCurrentFrameValue(BDE::list()); + controller.pushFrame(); + controller.setCurrentFrameValue(BDE(100)); + controller.popArrayFrame(); + const BDE& array = controller.getCurrentFrameValue(); + CPPUNIT_ASSERT_EQUAL((size_t)1, array.size()); + CPPUNIT_ASSERT_EQUAL((BDE::Integer)100, array[0].i()); +} + +void XmlRpcRequestParserControllerTest::testPopArrayFrame_noValue() +{ + XmlRpcRequestParserController controller; + controller.setCurrentFrameValue(BDE::list()); + controller.pushFrame(); + controller.popArrayFrame(); + const BDE& array = controller.getCurrentFrameValue(); + CPPUNIT_ASSERT(array.empty()); +} + +void XmlRpcRequestParserControllerTest::testPopArrayFrame_compound() +{ + XmlRpcRequestParserController controller; + + // We are making following structs. [] = array, {key:value .. } = dict + + // [ { "uris":["http://example.org/aria2","http://aria2.sf.net/"], + // "options":{ "timeout":120 } }, + // [ "jp","us" ] ] + + controller.setCurrentFrameValue(BDE::list()); + controller.pushFrame(); + + controller.setCurrentFrameValue(BDE::dict()); + controller.pushFrame(); + + controller.setCurrentFrameName("uris"); + controller.setCurrentFrameValue(BDE::list()); + controller.pushFrame(); + + controller.setCurrentFrameValue(BDE("http://example.org/aria2")); + controller.popArrayFrame(); + controller.pushFrame(); + + controller.setCurrentFrameValue(BDE("http://aria2.sf.net/")); + controller.popArrayFrame(); + + controller.popStructFrame(); + controller.pushFrame(); + + controller.setCurrentFrameName("options"); + controller.setCurrentFrameValue(BDE::dict()); + controller.pushFrame(); + + controller.setCurrentFrameName("timeout"); + controller.setCurrentFrameValue(BDE(120)); + controller.popStructFrame(); + + controller.popStructFrame(); + + controller.popArrayFrame(); + controller.pushFrame(); + + controller.setCurrentFrameValue(BDE::list()); + controller.pushFrame(); + + controller.setCurrentFrameValue(BDE("jp")); + controller.popArrayFrame(); + controller.pushFrame(); + + controller.setCurrentFrameValue(BDE("us")); + controller.popArrayFrame(); + + controller.popArrayFrame(); + + const BDE& result = controller.getCurrentFrameValue(); + CPPUNIT_ASSERT_EQUAL(std::string("http://aria2.sf.net/"), + result[0]["uris"][1].s()); + CPPUNIT_ASSERT_EQUAL((BDE::Integer)120, result[0]["options"]["timeout"].i()); + CPPUNIT_ASSERT_EQUAL(std::string("jp"), result[1][0].s()); +} + +} // namespace xmlrpc + +} // namespace aria2 diff --git a/test/XmlRpcRequestProcessorTest.cc b/test/XmlRpcRequestProcessorTest.cc new file mode 100644 index 00000000..47090ec3 --- /dev/null +++ b/test/XmlRpcRequestProcessorTest.cc @@ -0,0 +1,77 @@ +#include "XmlRpcRequestProcessor.h" + +#include + +#include "XmlRpcRequestParserStateMachine.h" + +namespace aria2 { + +namespace xmlrpc { + +class XmlRpcRequestProcessorTest:public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(XmlRpcRequestProcessorTest); + CPPUNIT_TEST(testParseMemory); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp() {} + + void tearDown() {} + + void testParseMemory(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(XmlRpcRequestProcessorTest); + +void XmlRpcRequestProcessorTest::testParseMemory() +{ + XmlRpcRequestProcessor proc; + XmlRpcRequest req = + proc.parseMemory("" + "" + " aria2.addURI" + " " + " " + " 100" + " " + " " + " " + " " + " " + " max-count" + " 65535" + " " + " " + " seed-ratio" + " 0.99" + " " + " " + " " + " " + " " + " " + " " + " " + " pudding" + " aGVsbG8gd29ybGQ=" + " " + " " + " " + " " + " " + ""); + + CPPUNIT_ASSERT_EQUAL(std::string("aria2.addURI"), req._methodName); + CPPUNIT_ASSERT_EQUAL((size_t)3, req._params.size()); + CPPUNIT_ASSERT_EQUAL((int64_t)100, req._params[0].i()); + CPPUNIT_ASSERT_EQUAL((int64_t)65535, req._params[1]["max-count"].i()); + // Current implementation handles double as string. + CPPUNIT_ASSERT_EQUAL(std::string("0.99"), req._params[1]["seed-ratio"].s()); + CPPUNIT_ASSERT_EQUAL(std::string("pudding"), req._params[2][0].s()); + CPPUNIT_ASSERT_EQUAL(std::string("hello world"), req._params[2][1].s()); +} + +} // namespace xmlrpc + +} // namespace aria2