浏览代码

2009-11-21 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

	Initial support of Extension for Peers to Send Metadata
	Files(BEP9). Currently aria2 only serves metadata and cannot get
	metadata from swarm.
	* src/BtConstants.h
	* src/DefaultBtInteractive.cc
	* src/DefaultExtensionMessageFactory.cc
	* src/DefaultExtensionMessageFactory.h
	* src/ExtensionMessage.h
	* src/ExtensionMessageRegistry.h
	* src/HandshakeExtensionMessage.cc
	* src/HandshakeExtensionMessage.h
	* src/Makefile.am
	* src/PeerConnection.h
	* src/PeerInteractionCommand.cc
	* src/UTMetadataDataExtensionMessage.cc
	* src/UTMetadataDataExtensionMessage.h
	* src/UTMetadataExtensionMessage.cc
	* src/UTMetadataExtensionMessage.h
	* src/UTMetadataRejectExtensionMessage.cc
	* src/UTMetadataRejectExtensionMessage.h
	* src/UTMetadataRequestExtensionMessage.cc
	* src/UTMetadataRequestExtensionMessage.h
	* src/bittorrent_helper.cc
	* src/bittorrent_helper.h
	* test/BittorrentHelperTest.cc
	* test/DefaultExtensionMessageFactoryTest.cc
	* test/HandshakeExtensionMessageTest.cc
	* test/Makefile.am
	* test/UTMetadataDataExtensionMessageTest.cc
	* test/UTMetadataRejectExtensionMessageTest.cc
	* test/UTMetadataRequestExtensionMessageTest.cc
Tatsuhiro Tsujikawa 16 年之前
父节点
当前提交
93968c4fa5

+ 34 - 0
ChangeLog

@@ -1,3 +1,37 @@
+2009-11-21  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
+
+	Initial support of Extension for Peers to Send Metadata
+	Files(BEP9). Currently aria2 only serves metadata and cannot get
+	metadata from swarm.
+	* src/BtConstants.h
+	* src/DefaultBtInteractive.cc
+	* src/DefaultExtensionMessageFactory.cc
+	* src/DefaultExtensionMessageFactory.h
+	* src/ExtensionMessage.h
+	* src/ExtensionMessageRegistry.h
+	* src/HandshakeExtensionMessage.cc
+	* src/HandshakeExtensionMessage.h
+	* src/Makefile.am
+	* src/PeerConnection.h
+	* src/PeerInteractionCommand.cc
+	* src/UTMetadataDataExtensionMessage.cc
+	* src/UTMetadataDataExtensionMessage.h
+	* src/UTMetadataExtensionMessage.cc
+	* src/UTMetadataExtensionMessage.h
+	* src/UTMetadataRejectExtensionMessage.cc
+	* src/UTMetadataRejectExtensionMessage.h
+	* src/UTMetadataRequestExtensionMessage.cc
+	* src/UTMetadataRequestExtensionMessage.h
+	* src/bittorrent_helper.cc
+	* src/bittorrent_helper.h
+	* test/BittorrentHelperTest.cc
+	* test/DefaultExtensionMessageFactoryTest.cc
+	* test/HandshakeExtensionMessageTest.cc
+	* test/Makefile.am
+	* test/UTMetadataDataExtensionMessageTest.cc
+	* test/UTMetadataRejectExtensionMessageTest.cc
+	* test/UTMetadataRequestExtensionMessageTest.cc
+
 2009-11-21  Tatsuhiro Tsujikawa  <t-tujikawa@users.sourceforge.net>
 
 	Cancel segment in prepareForRetry() because there is a chance that

+ 2 - 0
src/BtConstants.h

@@ -58,4 +58,6 @@ typedef std::map<std::string, uint8_t> Extensions;
 // Upper Bound of the number of outstanding request
 #define UB_MAX_OUTSTANDING_REQUEST 24
 
+#define METADATA_PIECE_SIZE (16*1024)
+
 #endif // _D_BT_CONSTANTS_

+ 4 - 1
src/DefaultBtInteractive.cc

@@ -167,7 +167,10 @@ void DefaultBtInteractive::addHandshakeExtendedMessageToQueue()
   m->setClientVersion(CLIENT_ARIA2);
   m->setTCPPort(_btRuntime->getListenPort());
   m->setExtensions(_extensionMessageRegistry->getExtensions());
-  
+  const BDE& attrs = _downloadContext->getAttribute(bittorrent::BITTORRENT);
+  if(attrs.containsKey(bittorrent::METADATA)) {
+    m->setMetadataSize(attrs[bittorrent::METADATA_SIZE].i());
+  }
   SharedHandle<BtMessage> msg = messageFactory->createBtExtendedMessage(m);
   dispatcher->addMessageToQueue(msg);
 }

+ 78 - 0
src/DefaultExtensionMessageFactory.cc

@@ -42,6 +42,14 @@
 #include "StringFormat.h"
 #include "PeerStorage.h"
 #include "ExtensionMessageRegistry.h"
+#include "DownloadContext.h"
+#include "BtMessageDispatcher.h"
+#include "BtMessageFactory.h"
+#include "UTMetadataRequestExtensionMessage.h"
+#include "UTMetadataDataExtensionMessage.h"
+#include "UTMetadataRejectExtensionMessage.h"
+#include "message.h"
+#include "bencode.h"
 
 namespace aria2 {
 
@@ -65,6 +73,7 @@ DefaultExtensionMessageFactory::createMessage(const unsigned char* data, size_t
     // handshake
     HandshakeExtensionMessageHandle m = HandshakeExtensionMessage::create(data, length);
     m->setPeer(_peer);
+    m->setDownloadContext(_dctx);
     return m;
   } else {
     std::string extensionName = _registry->getExtensionName(extensionMessageID);
@@ -79,6 +88,75 @@ DefaultExtensionMessageFactory::createMessage(const unsigned char* data, size_t
 	UTPexExtensionMessage::create(data, length);
       m->setPeerStorage(_peerStorage);
       return m;
+    } else if(extensionName == "ut_metadata") {
+      if(length == 0) {
+	throw DL_ABORT_EX(StringFormat(MSG_TOO_SMALL_PAYLOAD_SIZE,
+				       "ut_metadata", length).str());
+      }
+      std::string listdata;
+      listdata += 'l';
+      listdata += std::string(&data[1], &data[length]);
+      listdata += 'e';
+
+      const BDE& list = bencode::decode(listdata);
+      if(!list.isList() || list.empty()) {
+	throw DL_ABORT_EX("Bad ut_metadata");
+      }
+      const BDE& dict = list[0];
+      if(!dict.isDict()) {
+	throw DL_ABORT_EX("Bad ut_metadata: dictionary not found");
+      }
+      const BDE& msgType = dict["msg_type"];
+      if(!msgType.isInteger()) {
+	throw DL_ABORT_EX("Bad ut_metadata: msg_type not found");
+      }
+      const BDE& index = dict["piece"];
+      if(!index.isInteger()) {
+	throw DL_ABORT_EX("Bad ut_metadata: piece not found");
+      }
+      switch(msgType.i()) {
+      case 0: {
+	SharedHandle<UTMetadataRequestExtensionMessage> m
+	  (new UTMetadataRequestExtensionMessage(extensionMessageID));
+	m->setIndex(index.i());
+	m->setDownloadContext(_dctx);
+	m->setPeer(_peer);
+	m->setBtMessageFactory(_messageFactory);
+	m->setBtMessageDispatcher(_dispatcher);
+	return m;
+      }
+      case 1: {
+	if(list.size() != 2) {
+	  throw DL_ABORT_EX("Bad ut_metadata data: data not found");
+	}
+	const BDE& pieceData = list[1];
+	if(!pieceData.isString()) {
+	  throw DL_ABORT_EX("Bad ut_metadata data: data is not string");
+	}
+	const BDE& totalSize = dict["total_size"];
+	if(!totalSize.isInteger()) {
+	  throw DL_ABORT_EX("Bad ut_metadata data: total_size not found");
+	}
+	SharedHandle<UTMetadataDataExtensionMessage> m
+	  (new UTMetadataDataExtensionMessage(extensionMessageID));
+	m->setIndex(index.i());
+	m->setTotalSize(totalSize.i());
+	m->setData(pieceData.s());
+	// set tracker
+	// set piecestorage
+	return m;
+      }
+      case 2: {
+	SharedHandle<UTMetadataRejectExtensionMessage> m
+	  (new UTMetadataRejectExtensionMessage(extensionMessageID));
+	m->setIndex(index.i());
+	// set tracker if disconnecing peer on receive.
+	return m;
+      }
+      default:
+	throw DL_ABORT_EX(StringFormat("Bad ut_metadata: unknown msg_type=%u",
+				       msgType.i()).str());
+      }
     } else {
       throw DL_ABORT_EX
 	(StringFormat("Unsupported extension message received. extensionMessageID=%u, extensionName=%s",

+ 24 - 0
src/DefaultExtensionMessageFactory.h

@@ -43,6 +43,9 @@ class PeerStorage;
 class Peer;
 class Logger;
 class ExtensionMessageRegistry;
+class DownloadContext;
+class BtMessageFactory;
+class BtMessageDispatcher;
 
 class DefaultExtensionMessageFactory:public ExtensionMessageFactory {
 private:
@@ -52,6 +55,12 @@ private:
 
   SharedHandle<ExtensionMessageRegistry> _registry;
 
+  SharedHandle<DownloadContext> _dctx;
+
+  WeakHandle<BtMessageFactory> _messageFactory;
+
+  WeakHandle<BtMessageDispatcher> _dispatcher;
+
   Logger* _logger;
 
 public:
@@ -75,6 +84,21 @@ public:
   {
     _registry = registry;
   }
+
+  void setDownloadContext(const SharedHandle<DownloadContext>& dctx)
+  {
+    _dctx = dctx;
+  }
+
+  void setBtMessageFactory(const WeakHandle<BtMessageFactory>& factory)
+  {
+    _messageFactory = factory;
+  }
+
+  void setBtMessageDispatcher(const WeakHandle<BtMessageDispatcher>& disp)
+  {
+    _dispatcher = disp;
+  }
 };
 
 typedef SharedHandle<DefaultExtensionMessageFactory> DefaultExtensionMessageFactoryHandle;

+ 3 - 1
src/ExtensionMessage.h

@@ -36,9 +36,11 @@
 #define _D_EXTENSION_MESSAGE_H_
 
 #include "common.h"
-#include "SharedHandle.h"
+
 #include <string>
 
+#include "SharedHandle.h"
+
 namespace aria2 {
 class ExtensionMessage {
 public:

+ 2 - 0
src/ExtensionMessageRegistry.h

@@ -51,6 +51,8 @@ public:
   ExtensionMessageRegistry()
   {
     _extensions["ut_pex"] = 8;
+    // http://www.bittorrent.org/beps/bep_0009.html
+    _extensions["ut_metadata"] = 9;
   }
 
   const Extensions& getExtensions() const

+ 21 - 1
src/HandshakeExtensionMessage.cc

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ * 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
@@ -41,6 +41,8 @@
 #include "message.h"
 #include "StringFormat.h"
 #include "bencode.h"
+#include "DownloadContext.h"
+#include "bittorrent_helper.h"
 
 namespace aria2 {
 
@@ -48,6 +50,7 @@ const std::string HandshakeExtensionMessage::EXTENSION_NAME = "handshake";
 
 HandshakeExtensionMessage::HandshakeExtensionMessage():
   _tcpPort(0),
+  _metadataSize(0),
   _logger(LogFactory::getInstance()) {}
 
 HandshakeExtensionMessage::~HandshakeExtensionMessage() {}
@@ -68,6 +71,9 @@ std::string HandshakeExtensionMessage::getBencodedData()
     extDict[vt.first] = vt.second;
   }
   dict["m"] = extDict;
+  if(_metadataSize) {
+    dict["metadata_size"] = _metadataSize;
+  }
   return bencode::encode(dict);
 }
 
@@ -80,6 +86,9 @@ std::string HandshakeExtensionMessage::toString() const
   if(_tcpPort > 0) {
     strappend(s, ", tcpPort=", util::uitos(_tcpPort));
   }
+  if(_metadataSize) {
+    strappend(s, ", metadataSize=", util::uitos(_metadataSize));
+  }
   for(std::map<std::string, uint8_t>::const_iterator itr = _extensions.begin();
       itr != _extensions.end(); ++itr) {
     const std::map<std::string, uint8_t>::value_type& vt = *itr;
@@ -99,6 +108,12 @@ void HandshakeExtensionMessage::doReceivedAction()
     const std::map<std::string, uint8_t>::value_type& vt = *itr;
     _peer->setExtension(vt.first, vt.second);
   }
+  if(_metadataSize > 0) {
+    BDE& attrs = _dctx->getAttribute(bittorrent::BITTORRENT);
+    if(!attrs.containsKey(bittorrent::METADATA_SIZE)) {
+      attrs[bittorrent::METADATA_SIZE] = _metadataSize;
+    }
+  }
 }
 
 void HandshakeExtensionMessage::setPeer(const PeerHandle& peer)
@@ -148,6 +163,11 @@ HandshakeExtensionMessage::create(const unsigned char* data, size_t length)
       }
     }
   }
+  const BDE& metadataSize = dict["metadata_size"];
+  // Only accept metadata smaller than 1MiB
+  if(metadataSize.isInteger() && metadataSize.i() <= 1024*1024) {
+    msg->_metadataSize = metadataSize.i();
+  }
   return msg;
 }
 

+ 21 - 1
src/HandshakeExtensionMessage.h

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ * 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
@@ -46,6 +46,7 @@ namespace aria2 {
 class Peer;
 class Logger;
 class HandshakeExtensionMessage;
+class DownloadContext;
 typedef SharedHandle<HandshakeExtensionMessage> HandshakeExtensionMessageHandle;
 
 class HandshakeExtensionMessage:public ExtensionMessage {
@@ -54,8 +55,12 @@ private:
 
   uint16_t _tcpPort;
 
+  size_t _metadataSize;
+
   std::map<std::string, uint8_t> _extensions;
 
+  SharedHandle<DownloadContext> _dctx;
+
   SharedHandle<Peer> _peer;
 
   Logger* _logger;
@@ -103,6 +108,21 @@ public:
     return _tcpPort;
   }
 
+  size_t getMetadataSize()
+  {
+    return _metadataSize;
+  }
+
+  void setMetadataSize(size_t size)
+  {
+    _metadataSize = size;
+  }
+
+  void setDownloadContext(const SharedHandle<DownloadContext>& dctx)
+  {
+    _dctx = dctx;
+  }
+
   void setExtension(const std::string& name, uint8_t id)
   {
     _extensions[name] = id;

+ 5 - 0
src/Makefile.am

@@ -346,6 +346,11 @@ SRCS += PeerAbstractCommand.cc PeerAbstractCommand.h\
 	DefaultExtensionMessageFactory.cc DefaultExtensionMessageFactory.h\
 	HandshakeExtensionMessage.cc HandshakeExtensionMessage.h\
 	UTPexExtensionMessage.cc UTPexExtensionMessage.h\
+	UTMetadataExtensionMessage.cc UTMetadataExtensionMessage.h\
+	UTMetadataRequestExtensionMessage.cc\
+	UTMetadataRequestExtensionMessage.h\
+	UTMetadataRejectExtensionMessage.cc UTMetadataRejectExtensionMessage.h\
+	UTMetadataDataExtensionMessage.cc UTMetadataDataExtensionMessage.h\
 	DHTNode.cc DHTNode.h\
 	DHTBucket.cc DHTBucket.h\
 	DHTRoutingTable.cc DHTRoutingTable.h\

+ 22 - 2
src/Makefile.in

@@ -145,6 +145,11 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactory.cc DefaultExtensionMessageFactory.h\
 @ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessage.cc HandshakeExtensionMessage.h\
 @ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessage.cc UTPexExtensionMessage.h\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataExtensionMessage.cc UTMetadataExtensionMessage.h\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRequestExtensionMessage.cc\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRequestExtensionMessage.h\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRejectExtensionMessage.cc UTMetadataRejectExtensionMessage.h\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataDataExtensionMessage.cc UTMetadataDataExtensionMessage.h\
 @ENABLE_BITTORRENT_TRUE@	DHTNode.cc DHTNode.h\
 @ENABLE_BITTORRENT_TRUE@	DHTBucket.cc DHTBucket.h\
 @ENABLE_BITTORRENT_TRUE@	DHTRoutingTable.cc DHTRoutingTable.h\
@@ -489,8 +494,15 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	ExtensionMessageFactory.h DefaultExtensionMessageFactory.cc \
 	DefaultExtensionMessageFactory.h HandshakeExtensionMessage.cc \
 	HandshakeExtensionMessage.h UTPexExtensionMessage.cc \
-	UTPexExtensionMessage.h DHTNode.cc DHTNode.h DHTBucket.cc \
-	DHTBucket.h DHTRoutingTable.cc DHTRoutingTable.h \
+	UTPexExtensionMessage.h UTMetadataExtensionMessage.cc \
+	UTMetadataExtensionMessage.h \
+	UTMetadataRequestExtensionMessage.cc \
+	UTMetadataRequestExtensionMessage.h \
+	UTMetadataRejectExtensionMessage.cc \
+	UTMetadataRejectExtensionMessage.h \
+	UTMetadataDataExtensionMessage.cc \
+	UTMetadataDataExtensionMessage.h DHTNode.cc DHTNode.h \
+	DHTBucket.cc DHTBucket.h DHTRoutingTable.cc DHTRoutingTable.h \
 	DHTMessageEntry.cc DHTMessageEntry.h DHTMessageDispatcher.h \
 	DHTMessageDispatcherImpl.cc DHTMessageDispatcherImpl.h \
 	DHTMessageReceiver.cc DHTMessageReceiver.h \
@@ -644,6 +656,10 @@ am__objects_6 =
 @ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactory.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessage.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessage.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTMetadataExtensionMessage.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRequestExtensionMessage.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRejectExtensionMessage.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTMetadataDataExtensionMessage.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DHTNode.$(OBJEXT) DHTBucket.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DHTRoutingTable.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DHTMessageEntry.$(OBJEXT) \
@@ -1511,6 +1527,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TrackerWatcherCommand.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TransferStat.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/URIResult.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTMetadataDataExtensionMessage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTMetadataExtensionMessage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTMetadataRejectExtensionMessage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTMetadataRequestExtensionMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTPexExtensionMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UnknownLengthPieceStorage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UriListParser.Po@am__quote@

+ 3 - 3
src/PeerConnection.h

@@ -47,9 +47,9 @@ class SocketCore;
 class ARC4Encryptor;
 class ARC4Decryptor;
 
-// we assume maximum length of incoming message is "piece" message with 16KB
-// data. Messages beyond that size are dropped.
-#define MAX_PAYLOAD_LEN (9+16*1024)
+// The maximum length of payload. Messages beyond that length are
+// dropped.
+#define MAX_PAYLOAD_LEN (16*1024+128)
 
 class PeerConnection {
 private:

+ 5 - 0
src/PeerInteractionCommand.cc

@@ -109,6 +109,8 @@ PeerInteractionCommand::PeerInteractionCommand
   SharedHandle<DefaultExtensionMessageFactory> extensionMessageFactory
     (new DefaultExtensionMessageFactory(peer, exMsgRegistry));
   extensionMessageFactory->setPeerStorage(peerStorage);
+  extensionMessageFactory->setDownloadContext
+    (_requestGroup->getDownloadContext());
 
   SharedHandle<DefaultBtMessageFactory> factory(new DefaultBtMessageFactory());
   factory->setCuid(cuid);
@@ -189,6 +191,9 @@ PeerInteractionCommand::PeerInteractionCommand
   factory->setBtRequestFactory(reqFactory);
   factory->setPeerConnection(peerConnection);
 
+  extensionMessageFactory->setBtMessageDispatcher(dispatcher);
+  extensionMessageFactory->setBtMessageFactory(factory);
+
   peer->allocateSessionResource
     (_requestGroup->getDownloadContext()->getPieceLength(),
      _requestGroup->getDownloadContext()->getTotalLength());

+ 77 - 0
src/UTMetadataDataExtensionMessage.cc

@@ -0,0 +1,77 @@
+/* <!-- 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 "UTMetadataDataExtensionMessage.h"
+#include "BDE.h"
+#include "bencode.h"
+#include "util.h"
+#include "a2functional.h"
+
+namespace aria2 {
+
+UTMetadataDataExtensionMessage::UTMetadataDataExtensionMessage
+(uint8_t extensionMessageID):UTMetadataExtensionMessage(extensionMessageID) {}
+
+std::string UTMetadataDataExtensionMessage::getBencodedData()
+{
+  BDE list = BDE::list();
+
+  BDE dict = BDE::dict();
+  dict["msg_type"] = 1;
+  dict["piece"] = _index;
+  dict["total_size"] = _totalSize;
+
+  BDE data = _data;
+
+  list << dict;
+  list << data;
+
+  std::string encodedList = bencode::encode(list);
+  // Remove first 'l' and last 'e' and return.
+  return std::string(encodedList.begin()+1, encodedList.end()-1);
+}
+
+std::string UTMetadataDataExtensionMessage::toString() const
+{
+  return strconcat("ut_metadata data piece=", util::uitos(_index));
+}
+
+void UTMetadataDataExtensionMessage::doReceivedAction()
+{
+  // Update tracker
+
+  // Write to pieceStorage
+}
+
+} // namespace aria2

+ 79 - 0
src/UTMetadataDataExtensionMessage.h

@@ -0,0 +1,79 @@
+/* <!-- 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_UT_METADATA_DATA_EXTENSION_MESSAGE_H_
+#define _D_UT_METADATA_DATA_EXTENSION_MESSAGE_H_
+
+#include "UTMetadataExtensionMessage.h"
+
+namespace aria2 {
+
+class UTMetadataDataExtensionMessage:public UTMetadataExtensionMessage {
+private:
+  size_t _totalSize;
+
+  std::string _data;
+public:
+  UTMetadataDataExtensionMessage(uint8_t extensionMessageID);
+
+  virtual std::string getBencodedData();
+
+  virtual std::string toString() const;
+
+  virtual void doReceivedAction();
+
+  void setTotalSize(size_t totalSize)
+  {
+    _totalSize = totalSize;
+  }
+
+  size_t getTotalSize() const
+  {
+    return _totalSize;
+  }
+
+  void setData(const std::string& data)
+  {
+    _data = data;
+  }
+
+  const std::string& getData() const
+  {
+    return _data;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_UT_METADATA_DATA_EXTENSION_MESSAGE_H_

+ 46 - 0
src/UTMetadataExtensionMessage.cc

@@ -0,0 +1,46 @@
+/* <!-- 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 "UTMetadataExtensionMessage.h"
+
+namespace aria2 {
+
+const std::string UTMetadataExtensionMessage::EXTENSION_NAME = "ut_metadata";
+
+UTMetadataExtensionMessage::UTMetadataExtensionMessage
+(uint8_t extensionMessageID):
+  _extensionMessageID(extensionMessageID),
+  _index(0) {}
+
+} // namespace aria2

+ 75 - 0
src/UTMetadataExtensionMessage.h

@@ -0,0 +1,75 @@
+/* <!-- 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_UT_METADATA_EXTENSION_MESSAGE_H_
+#define _D_UT_METADATA_EXTENSION_MESSAGE_H_
+
+#include "ExtensionMessage.h"
+
+namespace aria2 {
+
+class UTMetadataExtensionMessage:public ExtensionMessage {
+protected:
+  uint8_t _extensionMessageID;
+
+  size_t _index;
+public:
+  UTMetadataExtensionMessage(uint8_t extensionMessageID);
+
+  virtual uint8_t getExtensionMessageID()
+  {
+    return _extensionMessageID;
+  }
+  
+  virtual const std::string& getExtensionName() const
+  {
+    return EXTENSION_NAME;
+  }
+
+  static const std::string EXTENSION_NAME;
+
+  void setIndex(size_t index)
+  {
+    _index = index;
+  }
+
+  size_t getIndex()
+  {
+    return _index;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_UT_METADATA_EXTENSION_MESSAGE_H_

+ 66 - 0
src/UTMetadataRejectExtensionMessage.cc

@@ -0,0 +1,66 @@
+/* <!-- 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 "UTMetadataRejectExtensionMessage.h"
+#include "BDE.h"
+#include "a2functional.h"
+#include "util.h"
+#include "bencode.h"
+
+namespace aria2 {
+
+UTMetadataRejectExtensionMessage::UTMetadataRejectExtensionMessage
+(uint8_t extensionMessageID):
+  UTMetadataExtensionMessage(extensionMessageID) {}
+
+std::string UTMetadataRejectExtensionMessage::getBencodedData()
+{
+  BDE dict = BDE::dict();
+  dict["msg_type"] = 2;
+  dict["piece"] = _index;
+  return bencode::encode(dict);
+}
+
+std::string UTMetadataRejectExtensionMessage::toString() const
+{
+  return strconcat("ut_metadata reject piece=", util::uitos(_index));
+}
+
+void UTMetadataRejectExtensionMessage::doReceivedAction()
+{
+  // TODO Remove outstanding metadata request from tracker.
+  // OR drop connection.
+}
+
+} // namespace aria2

+ 55 - 0
src/UTMetadataRejectExtensionMessage.h

@@ -0,0 +1,55 @@
+/* <!-- 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_UT_METADATA_REJECT_EXTENSION_MESSAGE_H_
+#define _D_UT_METADATA_REJECT_EXTENSION_MESSAGE_H_
+
+#include "UTMetadataExtensionMessage.h"
+
+namespace aria2 {
+
+class UTMetadataRejectExtensionMessage:public UTMetadataExtensionMessage {
+public:
+  UTMetadataRejectExtensionMessage(uint8_t extensionMessageID);
+
+  virtual std::string getBencodedData();
+
+  virtual std::string toString() const;
+
+  virtual void doReceivedAction();
+};
+
+} // namespace aria2
+
+#endif // _D_UT_METADATA_REJECT_EXTENSION_MESSAGE_H_

+ 101 - 0
src/UTMetadataRequestExtensionMessage.cc

@@ -0,0 +1,101 @@
+/* <!-- 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 "UTMetadataRequestExtensionMessage.h"
+#include "BDE.h"
+#include "bencode.h"
+#include "util.h"
+#include "a2functional.h"
+#include "bittorrent_helper.h"
+#include "DlAbortEx.h"
+#include "StringFormat.h"
+#include "BtMessageFactory.h"
+#include "BtMessageDispatcher.h"
+#include "Peer.h"
+#include "UTMetadataRejectExtensionMessage.h"
+#include "UTMetadataDataExtensionMessage.h"
+#include "BtConstants.h"
+#include "DownloadContext.h"
+#include "BtMessage.h"
+
+namespace aria2 {
+
+UTMetadataRequestExtensionMessage::UTMetadataRequestExtensionMessage
+(uint8_t extensionMessageID):UTMetadataExtensionMessage(extensionMessageID) {}
+
+std::string UTMetadataRequestExtensionMessage::getBencodedData()
+{
+  BDE dict = BDE::dict();
+  dict["msg_type"] = 0;
+  dict["piece"] = _index;
+  return bencode::encode(dict);
+}
+
+std::string UTMetadataRequestExtensionMessage::toString() const
+{
+  return strconcat("ut_metadata request piece=", util::uitos(_index));
+}
+
+void UTMetadataRequestExtensionMessage::doReceivedAction()
+{
+  const BDE& attrs = _dctx->getAttribute(bittorrent::BITTORRENT);
+  uint8_t id = _peer->getExtensionMessageID("ut_metadata");
+  if(!attrs.containsKey(bittorrent::METADATA)) {
+    SharedHandle<UTMetadataRejectExtensionMessage> m
+      (new UTMetadataRejectExtensionMessage(id));
+    m->setIndex(_index);
+    SharedHandle<BtMessage> msg = _messageFactory->createBtExtendedMessage(m);
+    _dispatcher->addMessageToQueue(msg);
+  }else if(_index*METADATA_PIECE_SIZE <
+	   (size_t)attrs[bittorrent::METADATA_SIZE].i()){
+    SharedHandle<UTMetadataDataExtensionMessage> m
+      (new UTMetadataDataExtensionMessage(id));
+    m->setIndex(_index);
+    m->setTotalSize(attrs[bittorrent::METADATA_SIZE].i());
+    const BDE& metadata = attrs[bittorrent::METADATA];
+    std::string::const_iterator begin =
+      metadata.s().begin()+_index*METADATA_PIECE_SIZE;
+    std::string::const_iterator end =
+      (_index+1)*METADATA_PIECE_SIZE <= metadata.s().size()?
+      metadata.s().begin()+(_index+1)*METADATA_PIECE_SIZE:metadata.s().end();
+    m->setData(std::string(begin, end));
+    SharedHandle<BtMessage> msg = _messageFactory->createBtExtendedMessage(m);
+    _dispatcher->addMessageToQueue(msg);
+  } else {
+    throw DL_ABORT_EX
+      (StringFormat("Metadata piece index is too big. piece=%d", _index).str());
+  }
+}
+
+} // namespace aria2

+ 88 - 0
src/UTMetadataRequestExtensionMessage.h

@@ -0,0 +1,88 @@
+/* <!-- 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_UT_METADATA_REQUEST_EXTENSION_MESSAGE_H_
+#define _D_UT_METADATA_REQUEST_EXTENSION_MESSAGE_H_
+
+#include "UTMetadataExtensionMessage.h"
+
+namespace aria2 {
+
+class DownloadContext;
+class BtMessageDispatcher;
+class BtMessageFactory;
+class Peer;
+
+class UTMetadataRequestExtensionMessage:public UTMetadataExtensionMessage {
+private:
+  SharedHandle<DownloadContext> _dctx;
+
+  SharedHandle<Peer> _peer;
+
+  WeakHandle<BtMessageDispatcher> _dispatcher;
+
+  WeakHandle<BtMessageFactory> _messageFactory;
+public:
+  UTMetadataRequestExtensionMessage(uint8_t extensionMessageID);
+
+  virtual std::string getBencodedData();
+
+  virtual std::string toString() const;
+
+  virtual void doReceivedAction();
+
+  void setDownloadContext(const SharedHandle<DownloadContext>& dctx)
+  {
+    _dctx = dctx;
+  }
+
+  void setBtMessageDispatcher(const WeakHandle<BtMessageDispatcher>& disp)
+  {
+    _dispatcher = disp;
+  }
+
+  void setBtMessageFactory(const WeakHandle<BtMessageFactory>& factory)
+  {
+    _messageFactory = factory;
+  }
+
+  void setPeer(const SharedHandle<Peer>& peer)
+  {
+    _peer = peer;
+  }
+};
+
+} // namespace aria2
+
+#endif // _D_UT_METADATA_REQUEST_EXTENSION_MESSAGE_H_

+ 6 - 0
src/bittorrent_helper.cc

@@ -125,6 +125,10 @@ const std::string MULTI("multi");
 
 const std::string SINGLE("single");
 
+const std::string METADATA_SIZE("metadataSize");
+
+const std::string METADATA("metadata");
+
 static void extractPieceHash(const SharedHandle<DownloadContext>& ctx,
 			     const std::string& hashData,
 			     size_t hashLength,
@@ -364,6 +368,8 @@ static void processRootDictionary
 			      encodedInfoDict.data(),
 			      encodedInfoDict.size());
   torrent[INFO_HASH] = std::string(&infoHash[0], &infoHash[INFO_HASH_LENGTH]);
+  torrent[METADATA] = encodedInfoDict;
+  torrent[METADATA_SIZE] = encodedInfoDict.size();
 
   // calculate the number of pieces
   const BDE& piecesData = infoDict[C_PIECES];

+ 4 - 0
src/bittorrent_helper.h

@@ -84,6 +84,10 @@ extern const std::string MULTI;
 
 extern const std::string BITTORRENT;
 
+extern const std::string METADATA_SIZE;
+
+extern const std::string METADATA;
+
 void load(const std::string& torrentFile,
 	  const SharedHandle<DownloadContext>& ctx,
 	  const std::string& overrideName = "");

+ 20 - 3
test/BittorrentHelperTest.cc

@@ -14,6 +14,8 @@
 #include "array_fun.h"
 #include "messageDigest.h"
 #include "a2netcompat.h"
+#include "bencode.h"
+#include "TestUtil.h"
 
 namespace aria2 {
 
@@ -52,9 +54,10 @@ class BittorrentHelperTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testSetFileFilter_single);
   CPPUNIT_TEST(testSetFileFilter_multi);
   CPPUNIT_TEST(testUTF8Torrent);
-  CPPUNIT_TEST(testMetaData);
+  CPPUNIT_TEST(testEtc);
   CPPUNIT_TEST(testCreatecompact);
   CPPUNIT_TEST(testCheckBitfield);
+  CPPUNIT_TEST(testMetadata);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {
@@ -90,9 +93,10 @@ public:
   void testSetFileFilter_single();
   void testSetFileFilter_multi();
   void testUTF8Torrent();
-  void testMetaData();
+  void testEtc();
   void testCreatecompact();
   void testCheckBitfield();
+  void testMetadata();
 };
 
 
@@ -634,7 +638,7 @@ void BittorrentHelperTest::testUTF8Torrent()
 		       dctx->getAttribute(BITTORRENT)[COMMENT].s());
 }
 
-void BittorrentHelperTest::testMetaData()
+void BittorrentHelperTest::testEtc()
 {
   SharedHandle<DownloadContext> dctx(new DownloadContext());
   load("test.torrent", dctx);
@@ -678,6 +682,19 @@ void BittorrentHelperTest::testCheckBitfield()
   }
 }
 
+void BittorrentHelperTest::testMetadata() {
+  SharedHandle<DownloadContext> dctx(new DownloadContext());
+  load("test.torrent", dctx);
+  std::string torrentData = readFile("test.torrent");
+  BDE tr = bencode::decode(torrentData);
+  BDE infoDic = tr["info"];
+  std::string metadata = bencode::encode(infoDic);
+  const BDE& attrs = dctx->getAttribute(bittorrent::BITTORRENT);
+  CPPUNIT_ASSERT(metadata == attrs[bittorrent::METADATA].s());
+  CPPUNIT_ASSERT_EQUAL(metadata.size(),
+		       (size_t)attrs[bittorrent::METADATA_SIZE].i());
+}
+
 } // namespace bittorrent
 
 } // namespace aria2

+ 75 - 11
test/DefaultExtensionMessageFactoryTest.cc

@@ -12,6 +12,15 @@
 #include "Exception.h"
 #include "FileEntry.h"
 #include "ExtensionMessageRegistry.h"
+#include "DownloadContext.h"
+#include "MockBtMessageDispatcher.h"
+#include "MockBtMessageFactory.h"
+#include "DownloadContext.h"
+#include "BtHandshakeMessage.h"
+#include "StringFormat.h"
+#include "UTMetadataRequestExtensionMessage.h"
+#include "UTMetadataDataExtensionMessage.h"
+#include "UTMetadataRejectExtensionMessage.h"
 
 namespace aria2 {
 
@@ -21,12 +30,18 @@ class DefaultExtensionMessageFactoryTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testCreateMessage_unknown);
   CPPUNIT_TEST(testCreateMessage_Handshake);
   CPPUNIT_TEST(testCreateMessage_UTPex);
+  CPPUNIT_TEST(testCreateMessage_UTMetadataRequest);
+  CPPUNIT_TEST(testCreateMessage_UTMetadataData);
+  CPPUNIT_TEST(testCreateMessage_UTMetadataReject);
   CPPUNIT_TEST_SUITE_END();
 private:
   SharedHandle<MockPeerStorage> _peerStorage;
   SharedHandle<Peer> _peer;
   SharedHandle<DefaultExtensionMessageFactory> _factory;
   SharedHandle<ExtensionMessageRegistry> _registry;
+  SharedHandle<MockBtMessageDispatcher> _dispatcher;
+  SharedHandle<MockBtMessageFactory> _messageFactory;
+  SharedHandle<DownloadContext> _dctx;
 public:
   void setUp()
   {
@@ -38,15 +53,41 @@ public:
 
     _registry.reset(new ExtensionMessageRegistry());
 
+    _dispatcher.reset(new MockBtMessageDispatcher());
+
+    _messageFactory.reset(new MockBtMessageFactory());
+
+    _dctx.reset(new DownloadContext());
+
     _factory.reset(new DefaultExtensionMessageFactory());
     _factory->setPeerStorage(_peerStorage);
     _factory->setPeer(_peer);
     _factory->setExtensionMessageRegistry(_registry);
+    _factory->setBtMessageDispatcher(_dispatcher);
+    _factory->setBtMessageFactory(_messageFactory);
+    _factory->setDownloadContext(_dctx);
+  }
+
+  std::string getExtensionMessageID(const std::string& name)
+  {
+    char id[1] = { _registry->getExtensionMessageID(name) };
+    return std::string(&id[0], &id[1]);
+  }
+
+  template<typename T>
+  SharedHandle<T> createMessage(const std::string& data)
+  {
+    return dynamic_pointer_cast<T>
+      (_factory->createMessage
+       (reinterpret_cast<const unsigned char*>(data.c_str()), data.size()));
   }
 
   void testCreateMessage_unknown();
   void testCreateMessage_Handshake();
   void testCreateMessage_UTPex();
+  void testCreateMessage_UTMetadataRequest();
+  void testCreateMessage_UTMetadataData();
+  void testCreateMessage_UTMetadataReject();
 };
 
 
@@ -74,10 +115,8 @@ void DefaultExtensionMessageFactoryTest::testCreateMessage_Handshake()
   char id[1] = { 0 };
 
   std::string data = std::string(&id[0], &id[1])+"d1:v5:aria2e";
-  SharedHandle<HandshakeExtensionMessage> m
-    (dynamic_pointer_cast<HandshakeExtensionMessage>
-     (_factory->createMessage
-      (reinterpret_cast<const unsigned char*>(data.c_str()), data.size())));
+  SharedHandle<HandshakeExtensionMessage> m =
+    createMessage<HandshakeExtensionMessage>(data);
   CPPUNIT_ASSERT_EQUAL(std::string("aria2"), m->getClientVersion());
 }
 
@@ -92,20 +131,45 @@ void DefaultExtensionMessageFactoryTest::testCreateMessage_UTPex()
   bittorrent::createcompact(c3, "192.168.0.2", 6882);
   bittorrent::createcompact(c4, "10.1.1.3",10000);
 
-  char id[1] = { _registry->getExtensionMessageID("ut_pex") };
-
-  std::string data = std::string(&id[0], &id[1])+"d5:added12:"+
+  std::string data = getExtensionMessageID("ut_pex")+"d5:added12:"+
     std::string(&c1[0], &c1[6])+std::string(&c2[0], &c2[6])+
     "7:added.f2:207:dropped12:"+
     std::string(&c3[0], &c3[6])+std::string(&c4[0], &c4[6])+
     "e";
 
-  SharedHandle<UTPexExtensionMessage> m
-    (dynamic_pointer_cast<UTPexExtensionMessage>
-     (_factory->createMessage
-      (reinterpret_cast<const unsigned char*>(data.c_str()), data.size())));
+  SharedHandle<UTPexExtensionMessage> m =
+    createMessage<UTPexExtensionMessage>(data);
   CPPUNIT_ASSERT_EQUAL(_registry->getExtensionMessageID("ut_pex"),
 		       m->getExtensionMessageID());
 }
 
+void DefaultExtensionMessageFactoryTest::testCreateMessage_UTMetadataRequest()
+{
+  std::string data = getExtensionMessageID("ut_metadata")+
+    "d8:msg_typei0e5:piecei1ee";
+  SharedHandle<UTMetadataRequestExtensionMessage> m =
+    createMessage<UTMetadataRequestExtensionMessage>(data);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, m->getIndex());
+}
+
+void DefaultExtensionMessageFactoryTest::testCreateMessage_UTMetadataData()
+{
+  std::string data = getExtensionMessageID("ut_metadata")+
+    "d8:msg_typei1e5:piecei1e10:total_sizei300ee10:0000000000";
+  SharedHandle<UTMetadataDataExtensionMessage> m =
+    createMessage<UTMetadataDataExtensionMessage>(data);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, m->getIndex());
+  CPPUNIT_ASSERT_EQUAL((size_t)300, m->getTotalSize());
+  CPPUNIT_ASSERT_EQUAL(std::string(10, '0'), m->getData());
+}
+
+void DefaultExtensionMessageFactoryTest::testCreateMessage_UTMetadataReject()
+{
+  std::string data = getExtensionMessageID("ut_metadata")+
+    "d8:msg_typei2e5:piecei1ee";
+  SharedHandle<UTMetadataRejectExtensionMessage> m =
+    createMessage<UTMetadataRejectExtensionMessage>(data);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, m->getIndex());
+}
+
 } // namespace aria2

+ 28 - 8
test/HandshakeExtensionMessageTest.cc

@@ -7,6 +7,8 @@
 #include "Peer.h"
 #include "Exception.h"
 #include "FileEntry.h"
+#include "DownloadContext.h"
+#include "bittorrent_helper.h"
 
 namespace aria2 {
 
@@ -57,11 +59,18 @@ void HandshakeExtensionMessageTest::testGetBencodedData()
   msg.setTCPPort(6889);
   msg.setExtension("ut_pex", 1);
   msg.setExtension("a2_dht", 2);
-  CPPUNIT_ASSERT_EQUAL(std::string("d"
-				   "1:md6:a2_dhti2e6:ut_pexi1ee"
-				   "1:pi6889e"
-				   "1:v5:aria2"
-				   "e"), msg.getBencodedData());
+  msg.setMetadataSize(1024);
+  CPPUNIT_ASSERT_EQUAL
+    (std::string("d"
+		 "1:md6:a2_dhti2e6:ut_pexi1ee"
+		 "13:metadata_sizei1024e"
+		 "1:pi6889e"
+		 "1:v5:aria2"
+		 "e"), msg.getBencodedData());
+
+  msg.setMetadataSize(0);
+  CPPUNIT_ASSERT
+    (msg.getBencodedData().find("metadata_size") == std::string::npos);
 }
 
 void HandshakeExtensionMessageTest::testToString()
@@ -71,11 +80,18 @@ void HandshakeExtensionMessageTest::testToString()
   msg.setTCPPort(6889);
   msg.setExtension("ut_pex", 1);
   msg.setExtension("a2_dht", 2);
-  CPPUNIT_ASSERT_EQUAL(std::string("handshake client=aria2, tcpPort=6889, a2_dht=2, ut_pex=1"), msg.toString());
+  msg.setMetadataSize(1024);
+  CPPUNIT_ASSERT_EQUAL
+    (std::string("handshake client=aria2, tcpPort=6889, metadataSize=1024,"
+		 " a2_dht=2, ut_pex=1"), msg.toString());
 }
 
 void HandshakeExtensionMessageTest::testDoReceivedAction()
 {
+  SharedHandle<DownloadContext> ctx(new DownloadContext());
+  BDE attrs = BDE::dict();
+  ctx->setAttribute(bittorrent::BITTORRENT, attrs);
+
   SharedHandle<Peer> peer(new Peer("192.168.0.1", 0));
   peer->allocateSessionResource(1024, 1024*1024);
   HandshakeExtensionMessage msg;
@@ -83,25 +99,29 @@ void HandshakeExtensionMessageTest::testDoReceivedAction()
   msg.setTCPPort(6889);
   msg.setExtension("ut_pex", 1);
   msg.setExtension("a2_dht", 2);
+  msg.setMetadataSize(1024);
   msg.setPeer(peer);
+  msg.setDownloadContext(ctx);
 
   msg.doReceivedAction();
 
   CPPUNIT_ASSERT_EQUAL((uint16_t)6889, peer->port);
   CPPUNIT_ASSERT_EQUAL((uint8_t)1, peer->getExtensionMessageID("ut_pex"));
   CPPUNIT_ASSERT_EQUAL((uint8_t)2, peer->getExtensionMessageID("a2_dht"));
+  CPPUNIT_ASSERT_EQUAL((int64_t)1024, attrs[bittorrent::METADATA_SIZE].i());
 }
 
 void HandshakeExtensionMessageTest::testCreate()
 {
-  std::string in = "0d1:pi6881e1:v5:aria21:md6:ut_pexi1eee";
+  std::string in = 
+    "0d1:pi6881e1:v5:aria21:md6:ut_pexi1ee13:metadata_sizei1024ee";
   SharedHandle<HandshakeExtensionMessage> m =
     HandshakeExtensionMessage::create(reinterpret_cast<const unsigned char*>(in.c_str()),
 				      in.size());
   CPPUNIT_ASSERT_EQUAL(std::string("aria2"), m->getClientVersion());
   CPPUNIT_ASSERT_EQUAL((uint16_t)6881, m->getTCPPort());
   CPPUNIT_ASSERT_EQUAL((uint8_t)1, m->getExtensionMessageID("ut_pex"));
-
+  CPPUNIT_ASSERT_EQUAL((size_t)1024, m->getMetadataSize());
   try {
     // bad payload format
     std::string in = "011:hello world";

+ 3 - 0
test/Makefile.am

@@ -136,6 +136,9 @@ aria2c_SOURCES += BtAllowedFastMessageTest.cc\
 	BtExtendedMessageTest.cc\
 	HandshakeExtensionMessageTest.cc\
 	UTPexExtensionMessageTest.cc\
+	UTMetadataRequestExtensionMessageTest.cc\
+	UTMetadataDataExtensionMessageTest.cc\
+	UTMetadataRejectExtensionMessageTest.cc\
 	DefaultBtMessageFactoryTest.cc\
 	DefaultExtensionMessageFactoryTest.cc\
 	DHTNodeTest.cc\

+ 14 - 1
test/Makefile.in

@@ -86,6 +86,9 @@ check_PROGRAMS = $(am__EXEEXT_1)
 @ENABLE_BITTORRENT_TRUE@	BtExtendedMessageTest.cc\
 @ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessageTest.cc\
 @ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessageTest.cc\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRequestExtensionMessageTest.cc\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataDataExtensionMessageTest.cc\
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRejectExtensionMessageTest.cc\
 @ENABLE_BITTORRENT_TRUE@	DefaultBtMessageFactoryTest.cc\
 @ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactoryTest.cc\
 @ENABLE_BITTORRENT_TRUE@	DHTNodeTest.cc\
@@ -224,7 +227,11 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 	BtRegistryTest.cc BtDependencyTest.cc \
 	BtPostDownloadHandlerTest.cc TimeSeedCriteriaTest.cc \
 	BtExtendedMessageTest.cc HandshakeExtensionMessageTest.cc \
-	UTPexExtensionMessageTest.cc DefaultBtMessageFactoryTest.cc \
+	UTPexExtensionMessageTest.cc \
+	UTMetadataRequestExtensionMessageTest.cc \
+	UTMetadataDataExtensionMessageTest.cc \
+	UTMetadataRejectExtensionMessageTest.cc \
+	DefaultBtMessageFactoryTest.cc \
 	DefaultExtensionMessageFactoryTest.cc DHTNodeTest.cc \
 	DHTBucketTest.cc DHTRoutingTableTest.cc \
 	DHTMessageTrackerEntryTest.cc DHTMessageTrackerTest.cc \
@@ -296,6 +303,9 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 @ENABLE_BITTORRENT_TRUE@	BtExtendedMessageTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	HandshakeExtensionMessageTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	UTPexExtensionMessageTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRequestExtensionMessageTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTMetadataDataExtensionMessageTest.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	UTMetadataRejectExtensionMessageTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DefaultBtMessageFactoryTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DefaultExtensionMessageFactoryTest.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	DHTNodeTest.$(OBJEXT) \
@@ -840,6 +850,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TestUtil.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TimeSeedCriteriaTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TimeTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTMetadataDataExtensionMessageTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTMetadataRejectExtensionMessageTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTMetadataRequestExtensionMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UTPexExtensionMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UriListParserTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UtilTest.Po@am__quote@

+ 60 - 0
test/UTMetadataDataExtensionMessageTest.cc

@@ -0,0 +1,60 @@
+#include "UTMetadataDataExtensionMessage.h"
+
+#include <iostream>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "BtConstants.h"
+
+namespace aria2 {
+
+class UTMetadataDataExtensionMessageTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(UTMetadataDataExtensionMessageTest);
+  CPPUNIT_TEST(testGetExtensionMessageID);
+  CPPUNIT_TEST(testGetBencodedData);
+  CPPUNIT_TEST(testToString);
+  CPPUNIT_TEST(testDoReceivedAction);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testGetExtensionMessageID();
+  void testGetBencodedData();
+  void testToString();
+  void testDoReceivedAction();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(UTMetadataDataExtensionMessageTest);
+
+void UTMetadataDataExtensionMessageTest::testGetExtensionMessageID()
+{
+  UTMetadataDataExtensionMessage msg(1);
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, msg.getExtensionMessageID());
+}
+
+void UTMetadataDataExtensionMessageTest::testGetBencodedData()
+{
+  std::string data(METADATA_PIECE_SIZE, '0');
+
+  UTMetadataDataExtensionMessage msg(1);
+  msg.setIndex(1);
+  msg.setTotalSize(data.size());
+  msg.setData(data);
+  CPPUNIT_ASSERT_EQUAL
+    (std::string("d8:msg_typei1e5:piecei1e10:total_sizei16384ee16384:")+data,
+     msg.getBencodedData());
+}
+
+void UTMetadataDataExtensionMessageTest::testToString()
+{
+  UTMetadataDataExtensionMessage msg(1);
+  msg.setIndex(100);
+  CPPUNIT_ASSERT_EQUAL(std::string("ut_metadata data piece=100"),
+		       msg.toString());
+}
+
+void UTMetadataDataExtensionMessageTest::testDoReceivedAction()
+{
+}
+
+} // namespace aria2

+ 55 - 0
test/UTMetadataRejectExtensionMessageTest.cc

@@ -0,0 +1,55 @@
+#include "UTMetadataRejectExtensionMessage.h"
+
+#include <iostream>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "BtConstants.h"
+
+namespace aria2 {
+
+class UTMetadataRejectExtensionMessageTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(UTMetadataRejectExtensionMessageTest);
+  CPPUNIT_TEST(testGetExtensionMessageID);
+  CPPUNIT_TEST(testGetBencodedReject);
+  CPPUNIT_TEST(testToString);
+  CPPUNIT_TEST(testDoReceivedAction);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  void testGetExtensionMessageID();
+  void testGetBencodedReject();
+  void testToString();
+  void testDoReceivedAction();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(UTMetadataRejectExtensionMessageTest);
+
+void UTMetadataRejectExtensionMessageTest::testGetExtensionMessageID()
+{
+  UTMetadataRejectExtensionMessage msg(1);
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, msg.getExtensionMessageID());
+}
+
+void UTMetadataRejectExtensionMessageTest::testGetBencodedReject()
+{
+  UTMetadataRejectExtensionMessage msg(1);
+  msg.setIndex(1);
+  CPPUNIT_ASSERT_EQUAL
+    (std::string("d8:msg_typei2e5:piecei1ee"), msg.getBencodedData());
+}
+
+void UTMetadataRejectExtensionMessageTest::testToString()
+{
+  UTMetadataRejectExtensionMessage msg(1);
+  msg.setIndex(100);
+  CPPUNIT_ASSERT_EQUAL(std::string("ut_metadata reject piece=100"),
+		       msg.toString());
+}
+
+void UTMetadataRejectExtensionMessageTest::testDoReceivedAction()
+{
+}
+
+} // namespace aria2

+ 186 - 0
test/UTMetadataRequestExtensionMessageTest.cc

@@ -0,0 +1,186 @@
+#include "UTMetadataRequestExtensionMessage.h"
+
+#include <iostream>
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Peer.h"
+#include "DownloadContext.h"
+#include "MockBtMessage.h"
+#include "MockBtMessageDispatcher.h"
+#include "MockBtMessageFactory.h"
+#include "bittorrent_helper.h"
+#include "BtHandshakeMessage.h"
+#include "UTMetadataRejectExtensionMessage.h"
+#include "UTMetadataDataExtensionMessage.h"
+
+namespace aria2 {
+
+class UTMetadataRequestExtensionMessageTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(UTMetadataRequestExtensionMessageTest);
+  CPPUNIT_TEST(testGetExtensionMessageID);
+  CPPUNIT_TEST(testGetExtensionName);
+  CPPUNIT_TEST(testGetBencodedData);
+  CPPUNIT_TEST(testToString);
+  CPPUNIT_TEST(testDoReceivedAction_reject);
+  CPPUNIT_TEST(testDoReceivedAction_data);
+  CPPUNIT_TEST_SUITE_END();
+public:
+  class MockExtensionMessage:public MockBtMessage {
+  public:
+    SharedHandle<ExtensionMessage> _m;
+    
+    MockExtensionMessage(const SharedHandle<ExtensionMessage>& m):_m(m) {}
+  };
+
+  class MockBtMessageFactory2:public MockBtMessageFactory {
+  public:
+    virtual SharedHandle<BtMessage>
+    createBtExtendedMessage(const SharedHandle<ExtensionMessage>& extmsg)
+    {
+      return SharedHandle<BtMessage>(new MockExtensionMessage(extmsg));
+    }
+  };
+
+  SharedHandle<DownloadContext> _dctx;
+  SharedHandle<MockBtMessageFactory2> _messageFactory;
+  SharedHandle<MockBtMessageDispatcher> _dispatcher;
+  SharedHandle<Peer> _peer;
+
+  void setUp()
+  {
+    _messageFactory.reset(new MockBtMessageFactory2());
+    _dispatcher.reset(new MockBtMessageDispatcher());
+    _dctx.reset(new DownloadContext());
+    BDE attrs = BDE::dict();
+    _dctx->setAttribute(bittorrent::BITTORRENT, attrs);
+    _peer.reset(new Peer("host", 6880));
+    _peer->allocateSessionResource(0, 0);
+    _peer->setExtension("ut_metadata", 1);
+  }
+
+  template<typename T>
+  SharedHandle<T> getFirstDispatchedMessage()
+  {
+    SharedHandle<MockExtensionMessage> wrapmsg =
+      dynamic_pointer_cast<MockExtensionMessage>
+      (_dispatcher->messageQueue.front());
+    
+    SharedHandle<T> msg = dynamic_pointer_cast<T>(wrapmsg->_m);
+    return msg;
+  }
+
+  void testGetExtensionMessageID();
+  void testGetExtensionName();
+  void testGetBencodedData();
+  void testToString();
+  void testDoReceivedAction_reject();
+  void testDoReceivedAction_data();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(UTMetadataRequestExtensionMessageTest);
+
+void UTMetadataRequestExtensionMessageTest::testGetExtensionMessageID()
+{
+  UTMetadataRequestExtensionMessage msg(1);
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, msg.getExtensionMessageID());
+}
+
+void UTMetadataRequestExtensionMessageTest::testGetExtensionName()
+{
+  UTMetadataRequestExtensionMessage msg(1);
+  CPPUNIT_ASSERT_EQUAL(std::string("ut_metadata"), msg.getExtensionName());
+}
+
+void UTMetadataRequestExtensionMessageTest::testGetBencodedData()
+{
+  UTMetadataRequestExtensionMessage msg(1);
+  msg.setIndex(99);
+  CPPUNIT_ASSERT_EQUAL
+    (std::string("d8:msg_typei0e5:piecei99ee"), msg.getBencodedData());
+}
+
+void UTMetadataRequestExtensionMessageTest::testToString()
+{
+  UTMetadataRequestExtensionMessage msg(1);
+  msg.setIndex(100);
+  CPPUNIT_ASSERT_EQUAL(std::string("ut_metadata request piece=100"),
+		       msg.toString());
+}
+
+void UTMetadataRequestExtensionMessageTest::testDoReceivedAction_reject()
+{
+  UTMetadataRequestExtensionMessage msg(1);
+  msg.setIndex(10);
+  msg.setDownloadContext(_dctx);
+  msg.setPeer(_peer);
+  msg.setBtMessageFactory(_messageFactory);
+  msg.setBtMessageDispatcher(_dispatcher);
+  msg.doReceivedAction();
+
+  SharedHandle<UTMetadataRejectExtensionMessage> m =
+    getFirstDispatchedMessage<UTMetadataRejectExtensionMessage>();
+
+  CPPUNIT_ASSERT(!m.isNull());
+  CPPUNIT_ASSERT_EQUAL((size_t)10, m->getIndex());
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, m->getExtensionMessageID());
+}
+
+void UTMetadataRequestExtensionMessageTest::testDoReceivedAction_data()
+{
+  UTMetadataRequestExtensionMessage msg(1);
+  msg.setIndex(1);
+  msg.setDownloadContext(_dctx);
+  msg.setPeer(_peer);
+  msg.setBtMessageFactory(_messageFactory);
+  msg.setBtMessageDispatcher(_dispatcher);
+
+  size_t metadataSize = METADATA_PIECE_SIZE*2;
+  BDE& attrs = _dctx->getAttribute(bittorrent::BITTORRENT);
+  std::string first(METADATA_PIECE_SIZE, '0');
+  std::string second(METADATA_PIECE_SIZE, '1');
+  attrs[bittorrent::METADATA] = first+second;
+  attrs[bittorrent::METADATA_SIZE] = metadataSize;
+
+  msg.doReceivedAction();
+
+  SharedHandle<UTMetadataDataExtensionMessage> m =
+    getFirstDispatchedMessage<UTMetadataDataExtensionMessage>();
+
+  CPPUNIT_ASSERT(!m.isNull());
+  CPPUNIT_ASSERT_EQUAL((size_t)1, m->getIndex());
+  CPPUNIT_ASSERT_EQUAL(second, m->getData());
+  CPPUNIT_ASSERT_EQUAL(metadataSize, m->getTotalSize());
+  CPPUNIT_ASSERT_EQUAL((uint8_t)1, m->getExtensionMessageID());
+
+  _dispatcher->messageQueue.clear();
+
+  msg.setIndex(2);
+
+  metadataSize += 100;
+  std::string third(100, '2');
+  attrs[bittorrent::METADATA] = attrs[bittorrent::METADATA].s()+third;
+  attrs[bittorrent::METADATA_SIZE] = metadataSize;
+
+  msg.doReceivedAction();
+
+  m = getFirstDispatchedMessage<UTMetadataDataExtensionMessage>();
+
+  CPPUNIT_ASSERT(!m.isNull());
+  CPPUNIT_ASSERT_EQUAL((size_t)2, m->getIndex());
+  CPPUNIT_ASSERT_EQUAL(third, m->getData());
+  CPPUNIT_ASSERT_EQUAL(metadataSize, m->getTotalSize());
+
+  msg.setIndex(3);
+
+  try {
+    msg.doReceivedAction();
+    CPPUNIT_FAIL("exception must be thrown.");
+  } catch(DlAbortEx& e) {
+    // success
+  }
+}
+
+} // namespace aria2