diff --git a/ChangeLog b/ChangeLog index 246101a0..3204b7c8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2009-05-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> + + Added XmlRpcResponse class. XmlRpcMethod::execute() now returns + XmlRpcResponse. + * src/HttpServerBodyCommand.cc + * src/Makefile.am + * src/XmlRpcMethod.cc + * src/XmlRpcMethod.h + * src/XmlRpcMethodImpl.cc + * src/XmlRpcResponse.cc + * src/XmlRpcResponse.h + * test/XmlRpcMethodTest.cc + 2009-05-14 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> In XML-RPC option struct, header and index-out option can take diff --git a/src/HttpServerBodyCommand.cc b/src/HttpServerBodyCommand.cc index a302a690..b5f7b482 100644 --- a/src/HttpServerBodyCommand.cc +++ b/src/HttpServerBodyCommand.cc @@ -48,6 +48,7 @@ #include "XmlRpcRequestParserStateMachine.h" #include "XmlRpcMethod.h" #include "XmlRpcMethodFactory.h" +#include "XmlRpcResponse.h" namespace aria2 { @@ -86,8 +87,8 @@ bool HttpServerBodyCommand::execute() SharedHandle<xmlrpc::XmlRpcMethod> method = xmlrpc::XmlRpcMethodFactory::create(req._methodName); - std::string response = method->execute(req, _e); - _httpServer->feedResponse(response, "text/xml"); + xmlrpc::XmlRpcResponse res = method->execute(req, _e); + _httpServer->feedResponse(res.toXml(), "text/xml"); Command* command = new HttpServerResponseCommand(cuid, _httpServer, _e, _socket); command->setStatus(Command::STATUS_ONESHOT_REALTIME); diff --git a/src/Makefile.am b/src/Makefile.am index 5ea95636..16d8f97e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -217,6 +217,7 @@ SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\ XmlRpcMethod.cc XmlRpcMethod.h\ XmlRpcMethodImpl.cc XmlRpcMethodImpl.h\ XmlRpcMethodFactory.cc XmlRpcMethodFactory.h\ + XmlRpcResponse.cc XmlRpcResponse.h\ HttpListenCommand.cc HttpListenCommand.h\ HttpServerCommand.cc HttpServerCommand.h\ HttpServerResponseCommand.cc HttpServerResponseCommand.h\ diff --git a/src/Makefile.in b/src/Makefile.in index 9a5149a5..fa4201c2 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -46,6 +46,7 @@ bin_PROGRAMS = aria2c$(EXEEXT) @ENABLE_XML_RPC_TRUE@ XmlRpcMethod.cc XmlRpcMethod.h\ @ENABLE_XML_RPC_TRUE@ XmlRpcMethodImpl.cc XmlRpcMethodImpl.h\ @ENABLE_XML_RPC_TRUE@ XmlRpcMethodFactory.cc XmlRpcMethodFactory.h\ +@ENABLE_XML_RPC_TRUE@ XmlRpcResponse.cc XmlRpcResponse.h\ @ENABLE_XML_RPC_TRUE@ HttpListenCommand.cc HttpListenCommand.h\ @ENABLE_XML_RPC_TRUE@ HttpServerCommand.cc HttpServerCommand.h\ @ENABLE_XML_RPC_TRUE@ HttpServerResponseCommand.cc HttpServerResponseCommand.h\ @@ -433,16 +434,17 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \ XmlRpcRequestProcessor.h HttpServerBodyCommand.cc \ HttpServerBodyCommand.h XmlRpcMethod.cc XmlRpcMethod.h \ XmlRpcMethodImpl.cc XmlRpcMethodImpl.h XmlRpcMethodFactory.cc \ - XmlRpcMethodFactory.h HttpListenCommand.cc HttpListenCommand.h \ - HttpServerCommand.cc HttpServerCommand.h \ - HttpServerResponseCommand.cc HttpServerResponseCommand.h \ - HttpServer.cc HttpServer.h Xml2XmlRpcRequestProcessor.cc \ - Xml2XmlRpcRequestProcessor.h ExpatXmlRpcRequestProcessor.cc \ - ExpatXmlRpcRequestProcessor.h FallocFileAllocationIterator.cc \ - FallocFileAllocationIterator.h EpollEventPoll.cc \ - EpollEventPoll.h TLSContext.h LibgnutlsTLSContext.cc \ - LibgnutlsTLSContext.h LibsslTLSContext.cc LibsslTLSContext.h \ - GZipDecoder.cc GZipDecoder.h Sqlite3MozCookieParser.cc \ + XmlRpcMethodFactory.h XmlRpcResponse.cc XmlRpcResponse.h \ + HttpListenCommand.cc HttpListenCommand.h HttpServerCommand.cc \ + HttpServerCommand.h HttpServerResponseCommand.cc \ + HttpServerResponseCommand.h HttpServer.cc HttpServer.h \ + Xml2XmlRpcRequestProcessor.cc Xml2XmlRpcRequestProcessor.h \ + ExpatXmlRpcRequestProcessor.cc ExpatXmlRpcRequestProcessor.h \ + FallocFileAllocationIterator.cc FallocFileAllocationIterator.h \ + EpollEventPoll.cc EpollEventPoll.h TLSContext.h \ + LibgnutlsTLSContext.cc LibgnutlsTLSContext.h \ + LibsslTLSContext.cc LibsslTLSContext.h GZipDecoder.cc \ + GZipDecoder.h Sqlite3MozCookieParser.cc \ Sqlite3MozCookieParser.h AsyncNameResolver.cc \ AsyncNameResolver.h IteratableChunkChecksumValidator.cc \ IteratableChunkChecksumValidator.h \ @@ -601,6 +603,7 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \ @ENABLE_XML_RPC_TRUE@ XmlRpcMethod.$(OBJEXT) \ @ENABLE_XML_RPC_TRUE@ XmlRpcMethodImpl.$(OBJEXT) \ @ENABLE_XML_RPC_TRUE@ XmlRpcMethodFactory.$(OBJEXT) \ +@ENABLE_XML_RPC_TRUE@ XmlRpcResponse.$(OBJEXT) \ @ENABLE_XML_RPC_TRUE@ HttpListenCommand.$(OBJEXT) \ @ENABLE_XML_RPC_TRUE@ HttpServerCommand.$(OBJEXT) \ @ENABLE_XML_RPC_TRUE@ HttpServerResponseCommand.$(OBJEXT) \ @@ -1571,6 +1574,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcRequestParserController.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcRequestParserStateImpl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcRequestParserStateMachine.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpcResponse.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ZeroBtMessage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asctime_r.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bencode.Po@am__quote@ diff --git a/src/XmlRpcMethod.cc b/src/XmlRpcMethod.cc index 9901374f..89a4579b 100644 --- a/src/XmlRpcMethod.cc +++ b/src/XmlRpcMethod.cc @@ -33,10 +33,6 @@ */ /* copyright --> */ #include "XmlRpcMethod.h" - -#include <cassert> -#include <sstream> - #include "DownloadEngine.h" #include "BDE.h" #include "LogFactory.h" @@ -48,6 +44,7 @@ #include "array_fun.h" #include "download_helper.h" #include "XmlRpcRequest.h" +#include "XmlRpcResponse.h" #include "prefs.h" namespace aria2 { @@ -66,113 +63,14 @@ static BDE createErrorResponse(const Exception& e) return params; } -static std::string xmlEscape(const std::string& s) -{ - std::string d; - for(std::string::const_iterator i = s.begin(); i != s.end(); ++i) { - if(*i == '<') { - d += "<"; - } else if(*i == '>') { - d += ">"; - } else if(*i == '&') { - d += "&"; - } else { - d += *i; - } - } - return d; -} - -static void encodeValue(const BDE& value, std::ostream& o); - -template<typename InputIterator> -static void encodeArray -(InputIterator first, InputIterator last, std::ostream& o) -{ - o << "<array>" << "<data>"; - for(; first != last; ++first) { - encodeValue(*first, o); - } - o << "</data>" << "</array>"; -} - -template<typename InputIterator> -static void encodeStruct -(InputIterator first, InputIterator last, std::ostream& o) -{ - o << "<struct>"; - for(; first != last; ++first) { - o << "<member>" - << "<name>" << xmlEscape((*first).first) << "</name>"; - encodeValue((*first).second, o); - o << "</member>"; - } - o << "</struct>"; -} - -static void encodeValue(const BDE& value, std::ostream& o) -{ - o << "<value>"; - if(value.isString()) { - o << "<string>" << xmlEscape(value.s()) << "</string>"; - } else if(value.isInteger()) { - o << "<int>" << value.i() << "</int>"; - } else if(value.isList()) { - encodeArray(value.listBegin(), value.listEnd(), o); - } else if(value.isDict()) { - encodeStruct(value.dictBegin(), value.dictEnd(), o); - } - o << "</value>"; -} - -template<typename InputIterator> -static void encodeParams -(InputIterator first, InputIterator last, std::ostream& o) -{ - o << "<params>"; - for(; first != last; ++first) { - o << "<param>"; - encodeValue(*first, o); - o << "</param>"; - } - o << "</params>"; -} - -static std::string encodeXml(const BDE& params) -{ - assert(params.isList()); - std::stringstream o; - o << "<?xml version=\"1.0\"?>" << "<methodResponse>"; - encodeParams(params.listBegin(), params.listEnd(), o); - o << "</methodResponse>"; - return o.str(); -} - -static void encodeFault(const BDE& faultValue, std::ostream& o) -{ - o << "<fault>"; - encodeValue(faultValue, o); - o << "</fault>"; -} - -static std::string encodeErrorXml(const BDE& faultValue) -{ - assert(faultValue.isDict()); - std::stringstream o; - o << "<?xml version=\"1.0\"?>" << "<methodResponse>"; - encodeFault(faultValue, o); - o << "</methodResponse>"; - return o.str(); -} - -std::string XmlRpcMethod::execute(const XmlRpcRequest& req, DownloadEngine* e) +XmlRpcResponse XmlRpcMethod::execute +(const XmlRpcRequest& req, DownloadEngine* e) { try { - BDE retparams = process(req, e); - return encodeXml(retparams); + return XmlRpcResponse(0, process(req, e)); } catch(RecoverableException& e) { _logger->debug(EX_EXCEPTION_CAUGHT, e); - return encodeErrorXml(createErrorResponse(e)); + return XmlRpcResponse(1, createErrorResponse(e)); } } diff --git a/src/XmlRpcMethod.h b/src/XmlRpcMethod.h index ab8e6e09..9e7ad919 100644 --- a/src/XmlRpcMethod.h +++ b/src/XmlRpcMethod.h @@ -52,6 +52,7 @@ class Option; namespace xmlrpc { struct XmlRpcRequest; +struct XmlRpcResponse; class XmlRpcMethod { protected: @@ -68,7 +69,7 @@ public: virtual ~XmlRpcMethod() {} - std::string execute(const XmlRpcRequest& req, DownloadEngine* e); + XmlRpcResponse execute(const XmlRpcRequest& req, DownloadEngine* e); }; } // namespace xmlrpc diff --git a/src/XmlRpcMethodImpl.cc b/src/XmlRpcMethodImpl.cc index 25091845..fc7f5559 100644 --- a/src/XmlRpcMethodImpl.cc +++ b/src/XmlRpcMethodImpl.cc @@ -67,9 +67,7 @@ namespace xmlrpc { static BDE createGIDResponse(int32_t gid) { - BDE resParams = BDE::list(); - resParams << Util::itos(gid); - return resParams; + return BDE(Util::itos(gid)); } BDE AddUriXmlRpcMethod::process(const XmlRpcRequest& req, DownloadEngine* e) @@ -157,14 +155,12 @@ BDE AddMetalinkXmlRpcMethod::process createRequestGroupForMetalink(result, requestOption, params[0].s()); if(!result.empty()) { e->_requestGroupMan->addReservedGroup(result); - BDE resParams = BDE::list(); BDE gids = BDE::list(); for(std::deque<SharedHandle<RequestGroup> >::const_iterator i = result.begin(); i != result.end(); ++i) { gids << BDE(Util::itos((*i)->getGID())); } - resParams << gids; - return resParams; + return gids; } else { throw DlAbortEx("No files to download."); } @@ -334,9 +330,7 @@ BDE GetFilesXmlRpcMethod::process group->getDownloadContext()->getFileEntries(); createFileEntry(files, fileEntries.begin(), fileEntries.end()); } - BDE resParams = BDE::list(); - resParams << files; - return resParams; + return files; } BDE GetUrisXmlRpcMethod::process @@ -365,9 +359,7 @@ BDE GetUrisXmlRpcMethod::process entry["uri"] = *i; uriList << entry; } - BDE resParams = BDE::list(); - resParams << uriList; - return resParams; + return uriList; } BDE GetPeersXmlRpcMethod::process @@ -399,9 +391,7 @@ BDE GetPeersXmlRpcMethod::process gatherPeer(peers, ps); } } - BDE resParams = BDE::list(); - resParams << peers; - return resParams; + return peers; } BDE TellStatusXmlRpcMethod::process @@ -437,10 +427,7 @@ BDE TellStatusXmlRpcMethod::process entryDict["status"] = BDE("active"); gatherProgress(entryDict, group, e); } - - BDE resParams = BDE::list(); - resParams << entryDict; - return resParams; + return entryDict; } BDE TellActiveXmlRpcMethod::process @@ -462,9 +449,7 @@ BDE TellActiveXmlRpcMethod::process } list << entryDict; } - BDE resParams = BDE::list(); - resParams << list; - return resParams; + return list; } BDE NoSuchMethodXmlRpcMethod::process diff --git a/src/XmlRpcResponse.cc b/src/XmlRpcResponse.cc new file mode 100644 index 00000000..434c139a --- /dev/null +++ b/src/XmlRpcResponse.cc @@ -0,0 +1,134 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#include "XmlRpcResponse.h" + +#include <cassert> +#include <sstream> + +namespace aria2 { + +namespace xmlrpc { + +static std::string xmlEscape(const std::string& s) +{ + std::string d; + for(std::string::const_iterator i = s.begin(); i != s.end(); ++i) { + if(*i == '<') { + d += "<"; + } else if(*i == '>') { + d += ">"; + } else if(*i == '&') { + d += "&"; + } else { + d += *i; + } + } + return d; +} + +static void encodeValue(const BDE& value, std::ostream& o); + +template<typename InputIterator> +static void encodeArray +(InputIterator first, InputIterator last, std::ostream& o) +{ + o << "<array>" << "<data>"; + for(; first != last; ++first) { + encodeValue(*first, o); + } + o << "</data>" << "</array>"; +} + +template<typename InputIterator> +static void encodeStruct +(InputIterator first, InputIterator last, std::ostream& o) +{ + o << "<struct>"; + for(; first != last; ++first) { + o << "<member>" + << "<name>" << xmlEscape((*first).first) << "</name>"; + encodeValue((*first).second, o); + o << "</member>"; + } + o << "</struct>"; +} + +static void encodeValue(const BDE& value, std::ostream& o) +{ + o << "<value>"; + if(value.isString()) { + o << "<string>" << xmlEscape(value.s()) << "</string>"; + } else if(value.isInteger()) { + o << "<int>" << value.i() << "</int>"; + } else if(value.isList()) { + encodeArray(value.listBegin(), value.listEnd(), o); + } else if(value.isDict()) { + encodeStruct(value.dictBegin(), value.dictEnd(), o); + } + o << "</value>"; +} + +static std::string encodeXml(const BDE& param) +{ + std::stringstream o; + o << "<?xml version=\"1.0\"?>" << "<methodResponse>" + << "<params>" << "<param>"; + encodeValue(param, o); + o << "</param>" << "</params>" << "</methodResponse>"; + return o.str(); +} + +static std::string encodeErrorXml(const BDE& faultValue) +{ + assert(faultValue.isDict()); + std::stringstream o; + o << "<?xml version=\"1.0\"?>" << "<methodResponse>" << "<fault>"; + encodeValue(faultValue, o); + o << "</fault>" << "</methodResponse>"; + return o.str(); +} + +std::string XmlRpcResponse::toXml() const +{ + if(_code == 0) { + return encodeXml(_param); + } else { + return encodeErrorXml(_param); + } +} + +} // namespace xmlrpc + +} // namespace aria2 diff --git a/src/XmlRpcResponse.h b/src/XmlRpcResponse.h new file mode 100644 index 00000000..2af1d579 --- /dev/null +++ b/src/XmlRpcResponse.h @@ -0,0 +1,63 @@ +/* <!-- copyright */ +/* + * aria2 - The high speed download utility + * + * Copyright (C) 2009 Tatsuhiro Tsujikawa + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +/* copyright --> */ +#ifndef _D_XML_RPC_RESPONSE_H_ +#define _D_XML_RPC_RESPONSE_H_ + +#include "common.h" + +#include <string> + +#include "BDE.h" + +namespace aria2 { + +namespace xmlrpc { + +struct XmlRpcResponse { + // 0 for success, non-zero for error + int _code; + + BDE _param; + + XmlRpcResponse(int code, const BDE& param):_code(code), _param(param) {} + + std::string toXml() const; +}; + +} // namespace xmlrpc + +} // namespace aria2 + +#endif // _D_XML_RPC_RESPONSE_H_ diff --git a/test/XmlRpcMethodTest.cc b/test/XmlRpcMethodTest.cc index 5a29ae37..59689a9d 100644 --- a/test/XmlRpcMethodTest.cc +++ b/test/XmlRpcMethodTest.cc @@ -13,6 +13,7 @@ #include "OptionParser.h" #include "OptionHandler.h" #include "XmlRpcRequest.h" +#include "XmlRpcResponse.h" namespace aria2 { @@ -53,7 +54,8 @@ void XmlRpcMethodTest::testAddUri() XmlRpcRequest req("aria2.addUri", BDE::list()); req._params << BDE::list(); req._params[0] << BDE("http://localhost/"); - std::string res = m.execute(req, _e.get()); + XmlRpcResponse res = m.execute(req, _e.get()); + CPPUNIT_ASSERT_EQUAL(0, res._code); const std::deque<SharedHandle<RequestGroup> > rgs = _e->_requestGroupMan->getReservedGroups(); CPPUNIT_ASSERT_EQUAL((size_t)1, rgs.size()); @@ -65,7 +67,10 @@ void XmlRpcMethodTest::testNoSuchMethod() { NoSuchMethodXmlRpcMethod m; XmlRpcRequest req("make.hamburger", BDE::none); - std::string res = m.execute(req, 0); + XmlRpcResponse res = m.execute(req, 0); + CPPUNIT_ASSERT_EQUAL(1, res._code); + CPPUNIT_ASSERT_EQUAL(std::string("No such method: make.hamburger"), + res._param["faultString"].s()); CPPUNIT_ASSERT_EQUAL (std::string("<?xml version=\"1.0\"?>" "<methodResponse>" @@ -85,7 +90,7 @@ void XmlRpcMethodTest::testNoSuchMethod() "</value>" "</fault>" "</methodResponse>"), - res); + res.toXml()); } } // namespace xmlrpc